13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JuliaAdvent Calendar 2023

Day 13

埼玉大学の研究室における JuliaLang 向け GitLab CI の整備をした話

Last updated at Posted at 2023-12-12

本日は

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_scriptdoctest, 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.tomldocs/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 によって scriptafter_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 側の設定画面に移動します.

image.png

Add project というところに A 側のプロジェクトを追加します.これで CI を再実行するとうまくいくはずです.

知らないとハマるのでおさえておきましょう.

コマンド

CLI で CI の再実行,実行中の様子をターミナル確認できます.

glab コマンドをインストール・ローカル環境で認証作業をすると使えます.

$ glab ci trace

VS Code のエクステンション

CI の様子や Issue などを確認することができます.VS Codeユーザならおすすめですね.

まとめ

埼玉大学の研究室の GitLab CI 環境を整備しました.Julia + GitLab に関する知見は自分自身も含めあまりなかったのでこのような形で公開できたのは良かったです.品岡研究室の皆さんありがとうございます.

13
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?