本日は
Julia アドベントカレンダー $N=13$ 日目の記事です.
GitLab で開発をする際に用いる CI ファイル .gitlab-ci.yml
を整備した話を書こうと思います.GitHub Actions の .github/workflows/CI.yml
に対応するものです.
記事を書くことになった背景
タイトルにある大学の研究室というのは 品岡 寛 先生が運営する研究室のことです.研究室の概要は下記のリンクからどうぞ.
量子多体理論から量子/古典情報理論を駆使して、物性物理向けの計算物理学的手法を開発しています。
私は Julia などのソフトウェアに関する業務をしております.研究室が管理している一部のソースコードは諸般の事情により GitLab で管理されています.複数の Julia で書かれたプライベートリポジトリが格納されています.
業務の一環として CI(継続的インテグレーション) を行うファイルの整備(俗にいう盆栽)から始めました.MR(マージリクエスト)を出した時に自動テストができるようにする仕組みを実現できます.ローカル環境では Pkg.test()
は通るのですが下記のような項目は手が追いついてない状態でした.
- GitLab Runner のところで本質的でない箇所の失敗
- 例えばプライベートリポジトリを管理するレジストリのクローンや依存関係の取得の失敗
-
Documenter.jl
によって生成されたドキュメントのデプロイなど
この記事を執筆してる現時点では main ブランチはテストがパスをしているグリーンのバッジが見えているはずですし,ドキュメントも研究室関連のメンバーからは見られるようになっているはずです.(研究室の皆様,不備があれば専用のチャンネルでメンションくださいまし.)
誰か知見書かない?(書くことになりました)
今月はアドカレシーズンですし,下記のようにディレクトリ構成の紹介がありました.
記事に最後には以下のことが書かれています:
本記事で列挙したもの以外にも色々なパッケージ構成があると思います。
たとえば、以下の項目には意図的に触れませんでした。
一つのリポジトリで複数のパッケージを管理する方法
Documenter.jl以外でのドキュメント生成
GitLabを使った場合のディレクトリ構成
「このファイルも便利だから用意した方が良いよ!」などあればコメントください!
「GitLab で運営してる知見せっかくなので誰か書きません?」と提案したところ作業者本人(私)が適任だろうということで書くことになりました.やったね!執筆ネタが一つ増えたよ!
プライベートな環境で CI を回すために必要なこと
レジストリのセットアップ
自分自身のコードで研究・仕事が閉じれば楽ですが,世の中そうはいきません.どんどん複雑になっていきますし,規模もどんどん膨れ上がっていきます.他の開発したパッケージを利用し研究を進めるのが普通になってきているでしょう.
研究室の GitLab で管理しているコードは使い捨てのものではなく,共同研究者や学生などが依存パッケージとして用いている程度の汎用的なコードが管理されています.民間企業で言えば社員向けの共通ライブラリに相当します.パッケージは LocalRegistry.jl から作られたレジストリに必要な情報を登録し,利用者はそれを追加すれば安定版のパッケージを利用できるようになります.レジストリの追加は次のようにします.
julia> # 学生や研究者が行う手順
julia> ] # Pkg REPL に移動
pkg> registry add git@gitlab.com:<研究室の組織名>/<研究室が管理するレジストリ>.git # 初回だけ
これは研究室に入った初日にすれば良いです.以後は OSS パッケージと同様にプライベートリポジトリで管理されているパッケージを導入できます.
julia> # 先輩からこのパッケージを入れろと言われたのでとりあえず入れる
julia> ]
pkg> add PrivatePackage
julia> using PrivatePackage
julia> # おお,なんかわかんないけれど動いた
LocalRegistry.jl については過去に書いた記事が参考になると思います.
開発者がすること
利用するだけなら上記の作業でOKですが,CI を整えたりする場合は GitLab の .gitlab-ci.yml
を整えるなど盆栽が必要です.CI を実行する GitLab Runner は素朴な環境からスタートします.素朴な環境に
- 必要なコマンドを導入 (
git
コマンドとか) - プライベートリポジトリの取得ができるようにする
- セットアップ(レジストリが導入できるようにする)
- パッケージのテスト
- main ブランチが更新されたらドキュメントのデプロイ
などの事柄を GitLab Runner の環境で整える必要があります.GitLab Runner はデフォルトだと .gitlab-ci.yml
に書いてあることに従って動作するので人間は .gitlab-ci.yml
のお作法を知る必要があります.
作った .gitlab-ci.yml
について
開発者がすること
で書いた事柄を .gitlab-ci.yml
に落とし込むと次のようになります.
before_script:
- which git || (apt-get update -qq && apt-get install --no-install-recommends -qqqy git)
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/".insteadOf "git@gitlab.com:"
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/".insteadOf "https://gitlab.com/" --add
- |
julia -e '
using Pkg
Pkg.Registry.add(
RegistrySpec(url = "https://github.com/JuliaRegistries/General.git")
)
variables:
CI_JULIA_CACHE_DIR: ${CI_PROJECT_DIR}/julia_pkg
JULIA_DEPOT_PATH: ${CI_JULIA_CACHE_DIR}
cache:
key:
files:
- Project.toml
- docs/Project.toml
prefix: ${CI_JOB_NAME}
paths:
- ${CI_JULIA_CACHE_DIR}
.script:
script:
- |
julia --project=@. -e '
using Pkg
Pkg.build()
Pkg.test(coverage=true)'
.coverage:
coverage: /Test coverage (\d+\.\d+%)/
after_script:
- |
julia -e '
using Pkg
Pkg.add("Coverage")
using Coverage
c, t = get_summary(process_folder())
using Printf
@printf "Test coverage %.2f%%\n" 100c / t'
Julia 1.9:
image: julia:1.9
extends:
- .script
- .coverage
.doctest:
script:
- |
julia --project=docs -e '
using Pkg
Pkg.develop(PackageSpec(path=pwd()))
Pkg.instantiate()
using Documenter: doctest
using PrivatePackage # PrivatePackage は各自のパッケージ名とする
doctest(PrivatePackage) # PrivatePackage は各自のパッケージ名とする
include("docs/make.jl")'
doctest:
image: julia:1.6
extends:
- .doctest
pages:
image: julia:1.6
stage: deploy
extends:
- .doctest
after_script:
- mkdir -p public
- mv docs/build public/dev
artifacts:
paths:
- public
only:
- main
やってること
before_script
に関して
before_script
は doctest
, pages
, Julia 1.9
という名前で指定したジョブが実行される前に行われるコマンドを指定します.下記のリンクの ianfiske
さんのコメントがとても役に立ちました.
例えば Julia 1.9
という名前のジョブは image: julia:1.9
で指定しているように Julia 1.9 の環境からスタートします.これは Docker イメージとして配布されている julia:1.9
のことです.このイメージ内には git
コマンドがないのでそれを入れています.
CI_JOB_TOKEN
を利用しプライベートリポジトリにアクセスします.パッケージのインストール実行時には git clone git@gitlab.com:<組織名>/<パッケージ名>.git
のような処理が内部で動きます.git
の機能としてある insteadOf
を利用すると git clone git@gitlab.com:<組織名>/<パッケージ名>.git
の処理を git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/<組織名>/<パッケージ名>.git
のように置換してくれます.
insteadOf
についての日本語の解説は カック (id:kakku22)
さんのブログが参考になります.
cache
に関して
Julia のパッケージを素朴な環境でインストールするとプレコンパイル作業が走ります.依存関係の多くなるとその分プレコンパイルの時間がかかるのでテストを実行するまでに数分待たされるということが起きます.これはストレスが溜まるのでキャッシュの機構を使いましょう.
variables:
CI_JULIA_CACHE_DIR: ${CI_PROJECT_DIR}/julia_pkg
JULIA_DEPOT_PATH: ${CI_JULIA_CACHE_DIR}
cache:
key:
files:
- Project.toml
- docs/Project.toml
prefix: ${CI_JOB_NAME}
paths:
- ${CI_JULIA_CACHE_DIR}
key
で指定している Project.toml
や docs/Project.toml
のファイルに変更がなかったら CI_JULIA_CACHE_DIR
の値で指定しているディレクトリの内容を再利用することができます.Julia の依存関係はデフォルトだと ~/.julia
に入ります.ところが GitLab のキャッシュの仕様によって CI_PROJECT_DIR
以外の場所のディレクトリはキャッシュできないらしいです.
Both artifacts and caches define their paths relative to the project directory, and can’t link to files outside it.
そこで JULIA_DEPOT_PATH
で ~/.julia
以下に入る依存関係を制御します.JULIA_DEPOT_PATH
は下記のドキュメントを参考にしてください.
この機構を使うことで 7から9 分 かかっていたジョブが 2から3 分まで短縮することができています.
extends
について
Julia 1.9 を用いた Pkg.test()
は次のようにして実現できます.
Julia 1.9:
image: julia:1.9
extends:
- .script
- .coverage
extends
にある .script
や .coverage
によって script
や after_script
の処理を委譲しています.
.script:
script:
- |
julia --project=@. -e '
using Pkg
Pkg.build()
Pkg.test(coverage=true)'
.coverage:
coverage: /Test coverage (\d+\.\d+%)/
after_script:
- |
julia -e '
using Pkg
Pkg.add("Coverage")
using Coverage
c, t = get_summary(process_folder())
using Printf
@printf "Test coverage %.2f%%\n" 100c / t'
.doctest
を拡張させることで doctest
, pages
ジョブ内で実行される doctest(PrivatePackage)
などの実行スクリプトを使い回すことができます.
複数バージョンの Julia でテストをしたい
先ほどの例では Julia 1.9
という名前のジョブで Julia 1.9 の環境でテストをさせるように指示しています.複数のバージョンでテストをしたい場合は次のように parallel
を使うことができます:
julia test:
image: julia:${JULIA_VERSION}
extends:
- .script
- .coverage
parallel:
matrix:
- JULIA_VERSION:
- "1.6"
- "1.7"
- "1.8"
- "1.9"
参考リンク
以上で Julia 向けの CI を設定した話を終わります.おまけで下記の3つを追加します.
トラブルシューティング
CI_JOB_TOKEN
を利用しているのに特定のプロジェクト(リポジトリ)にアクセスできない.
例 パッケージ A のテストの時にパッケージ B が必要.Bの資源にアクセスできない.
そういう時は下記のリンクを参考にすると良いです.
To configure the job token scope:
On the left sidebar, select Search or go to and find your project.
Select Settings > CI/CD.
Expand Token Access.
Toggle Limit access from this project to enabled.
Optional. Add existing projects to the token’s access scope. The user adding a project must have the Maintainer role in both projects.
上記の指示に従い B 側の設定画面に移動します.
Add project
というところに A 側のプロジェクトを追加します.これで CI を再実行するとうまくいくはずです.
知らないとハマるのでおさえておきましょう.
コマンド
CLI で CI の再実行,実行中の様子をターミナル確認できます.
glab
コマンドをインストール・ローカル環境で認証作業をすると使えます.
$ glab ci trace
VS Code のエクステンション
CI の様子や Issue などを確認することができます.VS Codeユーザならおすすめですね.
まとめ
埼玉大学の研究室の GitLab CI 環境を整備しました.Julia + GitLab に関する知見は自分自身も含めあまりなかったのでこのような形で公開できたのは良かったです.品岡研究室の皆さんありがとうございます.