TT Lab
はじめる
学ぶ 学習パス コース

GitLab CI/CD

nightly タグを付けたらリリースが出てしまった

TT Labで続きを見る

目標

ブランチ・タグ・ファイルの存在・リモートとの変更によって、ジョブが一覧に入ったり抜けたりすることを、状況を変えながら確認し、ルールの順序とルールが決める変数で、デプロイのポリシーを設定に刻み込みます。

なぜ重要なのか

rulesは、ジョブが始まるときに問う条件ではなく、パイプラインが作られるときに一覧を決めるルールです。最初のマッチだけが適用されるので、項目の順序がそのままポリシーであり、順序を間違えると、エラーなしでデプロイが消えたり、見当違いのタグでリリースが出たりします。changesは何と比べるかによって結果が変わり、条件と値(デプロイ先)を離して置くと、2つが食い違う日が来ます。こうした違いは、設定を読むだけではよく見えないので、状況を変えて実行してみる必要があります。

ステップ

  1. /root/glci-rulesをgitリポジトリにして、.gitignoreに.gitlab-ci-local/を置いてください。.gitlab-ci.ymlにstages[build, deploy]、条件のないジョブunit(build、echo "unit log=$LOG_LEVEL")、そして$CI_COMMIT_BRANCH == "main"のときだけ作られるdeploy-staging(deploy、echo staging)を置き、mainブランチにコミットしてください。採点ツールは、コピーでfeature/loginブランチに移って、deploy-stagingが一覧から消えるかを確認します。
  2. ジョブrelease-notes(deploy、echo notes)を、$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/のときだけ作られるように追加して、コミットしてください。gitlab-ci-localはgitタグを読まないので、--variable CI_COMMIT_TAG=v1.2.0でタグパイプラインを真似ます。v1.2.0なら一覧にあり、nightlyやタグなしならないはずです。
  3. ジョブpublish(deploy、echo publish)のrulesを、3つの項目にしてください。タグがあればwhen: on_success、mainブランチならwhen: manualとallow_failure: false、それ以外はwhen: neverです。コミットします。一覧で、mainならmanual(allowFailureはfalse)、タグ変数があればon_success、featureブランチならなし、になるはずです。
  4. ジョブdocker-build(build、echo docker)をrules: - exists: [Dockerfile]で追加して、コミットしてください。このリポジトリには、まだDockerfileを作りません。採点ツールは、コピーにDockerfileを入れたときだけジョブができるかを確認します。
  5. /root/glci-rules-origin.gitにbareリポジトリを作ってoriginとして登録し、mainをpushしたあとでgit remote set-head origin mainを実行してください。そのあと、ジョブdocs-build(build、echo docs)をrules: - changes: ["docs/**/*"]で追加してコミットし、もう一度pushします。採点ツールは、コピーでdocsの下を直したブランチと、ほかのファイルだけを直したブランチを作り、前者の場合にだけdocs-buildができるかを確認します。
  6. ジョブdeploy(deploy、echo "target=$DEPLOY_TARGET")を追加してください。rulesは、タグならvariables: {DEPLOY_TARGET: production}、mainならvariables: {DEPLOY_TARGET: staging}です。コミットしてpushします。mainで実行するとtarget=staging、タグ変数を与えるとtarget=productionが出力されるはずです。
  7. .gitlab-ci.ymlの一番上にworkflow: rules:を置いてください。mainならvariables: {LOG_LEVEL: warn}、それ以外は(when: always)variables: {LOG_LEVEL: debug}です。コミットしてpushします。mainではunitのログがunit log=warn、featureブランチではunit log=debugになるはずです。

参考

mainでだけステージングデプロイを作る

/root/glci-rulesをgitリポジトリにして、.gitignoreに.gitlab-ci-local/を置いてください。.gitlab-ci.ymlにstages[build, deploy]、条件のないジョブunit(build、echo "unit log=$LOG_LEVEL")、そして$CI_COMMIT_BRANCH == "main"のときだけ作られるdeploy-staging(deploy、echo staging)を置き、mainブランチにコミットしてください。採点ツールは、コピーでfeature/loginブランチに移って、deploy-stagingが一覧から消えるかを確認します。

rulesはパイプラインを作るときに評価されます。条件が1つも合わなければ、そのジョブは「never」として一覧から抜けます。ブランチを切り替えて、gitlab-ci-local --list-csv-allでwhenの列を比べてみてください。

タグ名の形まで見てリリースノートを作る

ジョブrelease-notes(deploy、echo notes)を、$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/のときだけ作られるように追加して、コミットしてください。gitlab-ci-localはgitタグを読まないので、--variable CI_COMMIT_TAG=v1.2.0でタグパイプラインを真似ます。v1.2.0なら一覧にあり、nightlyやタグなしならないはずです。

=~は正規表現の比較です。タグ変数はタグパイプラインでだけ埋まるので、その存在だけを見ると、nightlyのような一時的なタグにもリリースが出てしまいます。正規表現はスラッシュで囲みます。

最初のマッチだけが適用されるので、順序がそのままポリシーになる

ジョブpublish(deploy、echo publish)のrulesを、3つの項目にしてください。タグがあればwhen: on_success、mainブランチならwhen: manualとallow_failure: false、それ以外はwhen: neverです。コミットします。一覧で、mainならmanual(allowFailureはfalse)、タグ変数があればon_success、featureブランチならなし、になるはずです。

上から読んでいき、最初に合った項目1つだけを使います。条件のないwhen: neverを途中に置くと、その下の項目は二度と読まれず、何のエラーも出ません。manualジョブを本当の承認ゲートとして使うには、allow_failure: falseを一緒に置きます。

Dockerfileがあるリポジトリでだけイメージを作る

ジョブdocker-build(build、echo docker)をrules: - exists: [Dockerfile]で追加して、コミットしてください。このリポジトリには、まだDockerfileを作りません。採点ツールは、コピーにDockerfileを入れたときだけジョブができるかを確認します。

existsは、リポジトリにそのパスのファイルがあるかを見ます。複数のリポジトリが同じテンプレートをincludeするとき、リポジトリごとに設定を直さずに、該当するジョブだけを有効にするために使います。

ドキュメントが変わったブランチでだけドキュメントをビルドする

/root/glci-rules-origin.gitにbareリポジトリを作ってoriginとして登録し、mainをpushしたあとでgit remote set-head origin mainを実行してください。そのあと、ジョブdocs-build(build、echo docs)をrules: - changes: ["docs/**/*"]で追加してコミットし、もう一度pushします。採点ツールは、コピーでdocsの下を直したブランチと、ほかのファイルだけを直したブランチを作り、前者の場合にだけdocs-buildができるかを確認します。

changesは、「何と比べた変更か」が核心です。gitlab-ci-localはリモートのデフォルトブランチ(origin/main)と比較するので、リモートが必要です。GitLabのブランチパイプラインは直前のpushと、マージリクエストパイプラインはターゲットブランチと比較します。

どのルールに引っかかったかがデプロイ先を決める

ジョブdeploy(deploy、echo "target=$DEPLOY_TARGET")を追加してください。rulesは、タグならvariables: {DEPLOY_TARGET: production}、mainならvariables: {DEPLOY_TARGET: staging}です。コミットしてpushします。mainで実行するとtarget=staging、タグ変数を与えるとtarget=productionが出力されるはずです。

rulesの項目のvariablesは、その項目に引っかかったときだけジョブに入ります。条件と値を1か所に置けば、「タグなのにstagingに出てしまった」のような不一致が、構造的になくなります。

パイプライン全体の値はworkflowで決める

.gitlab-ci.ymlの一番上にworkflow: rules:を置いてください。mainならvariables: {LOG_LEVEL: warn}、それ以外は(when: always)variables: {LOG_LEVEL: debug}です。コミットしてpushします。mainではunitのログがunit log=warn、featureブランチではunit log=debugになるはずです。

workflow:rulesは、パイプラインを作るかどうかと、パイプライン全体に入る変数を決めます。ジョブごとに同じ条件を繰り返さなくて済みます。マージリクエストとブランチのパイプラインが重なるのを防ぐ場所もここですが、その動作はGitLabサーバーでしか確認できません。