勉強会のためにGitLabのパイプライン機能で競プロっぽい環境を作ってみた

以前作ったCI環境に一手間加えて、自社の社員が楽しみながらJavaを学べるような環境を整えてみた。
できるだけ手軽にJavaのプログラムを体験できるように準備しました。

# 構成

今回のシステムの構成はこのような感じ。

![勉強会環境2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395414/dadab41f-a52f-ceab-cf5c-a4fa05cdd862.png)

1. 生徒がgitlabにソースコードをプッシュする
1. gitlab-ci.yamlにジョブを登録しておき、適当な環境でコードをコンパイル、実行する
1. 実行結果をslackに投稿

# パイプライン設定

前回ci環境を整えた後色々調べた所、gitlabの[runnerの設定を変更](https://docs.gitlab.com/runner/shells/index.html#overview)すればプリインストールされているpowershellを使えることがわかった。
それも踏まえて、下記のようなパイプラインを構築してみた。

:::note alert
下記のパイプラインを参考にする場合、絶対に不特定多数がアクセスできるようにはしないでください
:::

<details><summary>gitlab-ci.yamlのサンプル</summary><div>

```yml
variables:
  CHAT_SCRIPT: 'C:\GitLab-Runner\local_scripts\chat.py'
  CHECK_SCRIPT: 'C:\GitLab-Runner\local_scripts\score.py'
  VENV_PATH: 'C:\GitLab-Runner\local_scripts\venv\Scripts\activate.ps1'
  JAVAC_HOME: 'C:\gitlab-runner\jdk-17.0.2\bin\'

default:
  before_script:
    - CHCP 65001

stages:          # List of stages for jobs, and their order of execution
  - build
  - run

cache:
  untracked: true

build-job:       # This job runs in the build stage, which runs first.
  stage: build
  tags: 
    - windows
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'
  script:
    - $PSDefaultParameterValues['Out-File:Encoding'] = 'ascii'
    - gci *.java -Recurse | Get-ItemPropertyValue -Name FullName > files.log
    - '& "$($env:JAVAC_HOME)javac.exe" "@files.log" -encoding UTF-8'
  after_script:
    - '& "$env:VENV_PATH"'
    - $player_name = git log --pretty=format:"%an" -1
    - python "$env:CHAT_SCRIPT" "$player_name" "$env:CI_JOB_STATUS"

calc-score-job:   # This job runs in the test stage.
  stage: run    # It only starts when the job in the build stage completes successfully.
  tags:
    - windows
  script:
    - '& "$($env:JAVAC_HOME)java.exe" -cp ./src/main/java com.practice.numberguesser.Main > result.log'
    - '& "$env:VENV_PATH"'
    - $player_name = git log --pretty=format:"%an" -1
    - python "$env:CHECK_SCRIPT" "$player_name"
  artifacts:
    paths:
      - result.log
    expire_in: 1 day

```

</div></details>

## パイプライン解説

パイプラインと言うよりpowershellの解説って言ったほうが近いかも。

* variablesにJavaのパスを登録してある理由
    * 環境の再現性を取りやすいという建前、ユーザを指定してのgitlabサービスを何故か建てれなかったってのが本音
* ```CHCP 65001```
    * powershellの使用する文字コードをUTF-8に変更する。gitlab上での表示を文字化けさせないようにするために必要。
* ```$PSDefaultParameterValues['Out-File:Encoding'] = 'ascii'```
    * PowershellのパイプラインはUTF-16という圧倒的な取り回しの悪さなのでUTF-8で読み込めるように設定
* ```gci *.java -Recurse | Get-ItemPropertyValue -Name FullName > files.log```
    * コンパイル対象のコードを一覧取得してファイルに格納する。
* ```'& "$($env:JAVAC_HOME)javac.exe" "@files.log" -encoding UTF-8'```
    * 上で書いた対象ファイルをコンパイルする。gitのエンコーディングは基本的にUTF-8となる。
* ```$player_name = git log --pretty=format:"%an" -1```
    * 最後にコミットした人物名を出力するコマンド(git log以降)をプレイヤー名として記録
* ```'& "$($env:JAVAC_HOME)java.exe" -cp ./src/main/java com.practice.numberguesser.Main > result.log'```
    * コンパイルされたバイナリを実行する場合、クラスパスのルートを指定しなければきちんと動かないので注意。

pythonのコードはslackにメッセージを送るためのコードでした。[公式のAPI](https://api.slack.com/messaging/sending)にメッセージの送り方までコードが載っているので読んで参考にしました。

ちなみに今回使ったコードは[ここ](https://github.com/mickie895/NumberGuesserRace/tree/auto_guess/src/main/java/com/gsoft/practice/numberguesser)で見れるようにしています。
大雑把に説明するとハイアンドロー形式で数字を当てるときの効率の良さを競う感じにしています。
その結果を標準出力経由でファイルに出力して、点数をslackに乗せています。

# 投稿サンプル

slackの様子はこんな感じ。
100までの数字のハイアンドローの数あてゲームで10万回問い合わせした時、何回正解できたかをスコアにしています。

![結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395414/be35761b-66b1-a243-0516-630c2f889e10.png)