フューチャー技術ブログ

GitLab CIを新人研修に導入した話

はじめに

こんにちは。フューチャーアーキテクト株式会社、HR(新人研修)チームの柳澤です。

ブログへの投稿をサボりにサボった結果、なんと3年強もの空白期間が空いてしまいました。さすがに3年以上もブログ投稿をしていない人間をウォッチし続けている奇特な方はいないと思うので、簡単に略歴から紹介させていただこうと思います。

私はフューチャーにキャリア入社し、その後はTechnology Innovation GroupにてITコンサルタント業務に4年ほど従事したのち、昨年の6月よりHR部門にて新人研修を担当しています。ITコンサルタントとしては主にクラウドインフラをメインで担当し、小売、製造、金融など事業ドメイン問わずプロジェクトを経験してきました。

HRへ転属となった契機ですが、今後の自身のキャリアとして、チームリーディングの経験をより積んでいきたいという思いがありました。

当社では、新人研修を一つのプロジェクトとして、その企画・推進から運営まで担う研修リーダーというロールが用意されています。私自身、教育に強い興味関心があったため、このロールが自分のニーズにもはまると考え、HRという未知の領域ではありましたが、異動を決意しました。

ブログ執筆の契機

「HRなのに技術ブログ?」と思われる方がいるかもしれません。

前述の通り、当社の新人研修は現場の人間が主体となって推進していきます。

この新人研修を通して、当社には 現場の人間がバックオフィスに回り、現場で得た知見を還元できる 環境が整っていることの宣伝として執筆させていただくことにしました。

フューチャーにおける研修の全体像

まず、フューチャーにおける研修の全体像説明はこちらの記事に譲りたいと思います。

全体像の説明だけでなく、カリキュラムの背景などについても語られているので、興味のある方はぜひご覧ください。

もちろん、今回の記事のように新人研修は常に改善を繰り返しているので、細かな部分ではすでに変更されていたりもします。

余談ですが、当ブログには新人研修後の配属先にかける新人の思いを歌った投稿もありますので、ぜひご覧ください。

GitLab CIを導入した話

研修の流れ

さて、長々と「はじめに」が続きましたが、ようやく本題に入っていきたいと思います。

当社の新人研修で用意しているITカリキュラムはざっくりと アルゴリズム -> Java -> DB(SQL) -> Webアプリケーションの構築 という順番で進んでいきます。

特にアルゴリズム~DB(SQL)までは予め所定のGitLabリポジトリ1で用意された問題(ソースコード)をclone/pullし、制限時間内で解答する流れになっています。(筆記試験)

また、 とりあえず書いたら当たった ではなく、本質的な理解までできているかを確認するため、上記の筆記試験に合格したら研修リーダーが試験官を担う、社内ではITカリキュラムで一番の鬼門と噂される「口頭試問」に進みます。

そして、口頭試問まで合格して初めて、そのカリキュラムに合格となり、次のカリキュラムへと進むことができます。

表題のGitLab CIは上記の筆記試験(Java~DB(SQL))の部分に対して導入しました。

改善活動を行うまで

今回のトピックは運用改善なので、改善前の姿もお伝えしておこうと思います。
当社は年間通して100名以上の新入社員がいますが、従来は以下のフローで採点が行われていました。

  1. 新人さん一人一人がpushしたブランチを手元にpull
  2. ローカルに置いてあるJUnitのコードを所定の場所に格納
  3. ローカルでJUnitを実行
  4. テスト結果を確認し、新人さんへ共有

100人を超える新人さんに対して、このフローをオペレーションする忍耐も覚悟もなかった私はアサイン直後にこのフローを目の当たりにしたとき、即座に自動化の計画を立てる決意を固めたのでした。

なお、教育というコンテンツの性質上(単純なハードスキルのフォローだけでなく新人さんのメンタル面のフォローなど含む)、こういった運用改善活動にかける時間がおのずと少なかったことには触れておきたいと思います。

ちなみに、上記はJavaの例で、DBに関してはテストコードもなかったので、お手軽にpytestで実装しました。
今回その内容は割愛します。

改善活動開始

自動化をするにあたってまず考えたのは

  • 既存のリソースを有効利用しつつ、なるべく少ない手数で実装すること
    • 当然、自分が研修リーダーをする場合も運用改善に割ける時間は限られるため
  • 研修推進側の負担を限りなく0にしつつ、新人さん側の作業フローも大きく変わらない(増えない)こと
  • なるべく新人さんの学びになる形で改善を行うこと

ということでした。
その結果、

  • GitLabという既存のプラットフォームを利用でき
  • 既存のJUnitのテストコードも利用可能で
  • CIという、おそらく新人さんがあまりなじみのないであろう技術要素の存在を伝えられる

上記を満たすGitLab CIの利用がよいのではないかという判断をしました。

実際の実装

ここまできてようやく実装についてのお話が始まります。
いろいろと試行錯誤しながらCIをテストしていった結果、最終的にリポジトリ構成は以下の通りに落ち着きました。(一部実際のものから変更しています)

future
├── 2022
│ └── October
│ └── single-work
│ ├── 📦java
│ └── 📦db
└── training-leader
├── answer
│ ├── 📦java
│ └── 📦db
├── exam
│ ├── 📦java
│ └── 📦db
...
📦: リポジトリ

グループfutureを親として、その配下に以下のサブグループを配置しました。

サブグループパス 用途
2022/October/single-work 新人さんが試験に解答するためのリポジトリを配置
training-leader/exam single-work配下のリポジトリをForkして作成するためのマスターリポジトリを配置
training-leader/answer 試験問題、および正答が記載されたコードとテストコードを格納するリポジトリを配置

CIの機能とテストコードの配置に際して、以下を考慮しました

  • 新人さんが解答をしたあと、すぐに試験結果を確認できるようにするためには解答したリポジトリ内でテストを回す必要がある
  • ただし、テストコードが見えてしまうと正答を教えているのと同義になってしまうのでテストコードは新人さんが利用するリポジトリから分離する必要がある

結果として、新人さんが利用するリポジトリ配下に以下の.gitlab-ci.ymlを配置しました。

image: gradle:alpine

variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"

before_script:
- GRADLE_USER_HOME="$(pwd)/.gradle"
- export GRADLE_USER_HOME
- apk add git tree
# (1)
- ACCESS_TOKEN=`printenv TEST_CODE_ACCESS_TOKEN`
# (2)
- git clone https://${deploy_token_username}:${ACCESS_TOKEN}@${gitlab_url}/training-leader/answer/java.git
- mkdir -p src/test/java
# (3)
- cp -r java/src/test/java/${CI_BUILD_REF_NAME##*-} src/test/java
- tree

stages:
- test

test:
stage: test
script:
- gradle test
artifacts:
reports:
junit: build/test-results/test/**/TEST-*.xml

上記ファイルのポイントを三つ、お伝えしたいと思います。

(1) アクセストークンの隠蔽

この構成では、研修チームのリポジトリをCloneしてテストコードを取りに行く必要があります。
研修チームのリポジトリで払い出したトークンをべた書きしてしまうと悪用される恐れがあるため、環境変数に埋め込んで隠蔽します。

もちろん、新人さんには最低限の権限(Developer)しか与えていないため、環境変数を見ることはできません。

なお、GitLabにはいくつかトークンの種類がありますが、実装当時は時間の余裕がなく、強めの権限で動かせるDeploy Tokenで実装してしまっています。
GitLabの解説によると今回のケースは問答無用でCI/CD Job Tokenを利用するのが適切とのことなので、いつか差し替えたいと思います…

(2) テストコードのClone

1で環境変数から呼び出したトークンを使い、テストコードの入っている研修チームのリポジトリからコードをCloneしてきます。

(3) 所定回数の試験のテストコードのみコピー

新人さん側のリポジトリ構成(一部)は以下のようになっており、CIの中で以下のsrc配下にtest/java/java8(テストコードの格納場所)をガッチャンコするイメージです

.
├── README.md
├── build.gradle
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── main
└── java
└── java8
├── Clazz.java
├── Question1_1.java
├── Question1_2.java
├── Question2_1.java
├── Question2_2.java
├── Question3_1.java
├── Question3_2.java
├── Question4_1.java
├── Question4_2.java
...

また、特定の試験回のみテストが実行されるよう、試験のルール面でも工夫を加えました。

新人さん側がpushするブランチ名を feature/${社内アカウント名}_java${試験回数}というブランチ名に縛ることで、意図しないテストコードが実行されることを防ぐことができました。

なお、副作用としてブランチ名誤りでテストが正しく回らず、失格扱いになった新人さんも見かけましたが、Gitの訓練と思ってもらうことにしています(笑)

運用を開始してみて

まず、実際に効果を感じたのは、新人さんが過去に出した問題の復習にCIを利用しはじめてくれたということでした。
また、CIですぐに結果を確認するためにGitに触る回数もおのずと増えるため、特に未経験者が躓きがちなGitの習熟スピードが明らかに早くなったと感じました。

一方、進捗の早い新人さんの中にはCIをはじめインフラに興味を持ってくれるメンバーも現れ、こちらの予想していたよりも大きな効果が得られたかなと思っています。

最後に

技術的な密度のわりにかなり長い記事になってしまいましたが、フューチャーの新人研修は自前で運営しているからこその、現場の知見を活かした活動ができると思っています。

この記事を読んで、技術ドリブンで若手の成長を促してみたいと思った方、ぜひフューチャーで研修リーダー、やってみませんか?

もちろん、新しい技術を現場の社員から盗みたいという学生さんのエントリーも大歓迎です!!


  1. 1.GitLabではリポジトリのことをプロジェクトと呼称しますが、ここではコードの格納場所を表す単語として一般的に使われる「リポジトリ」で統一します