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

GitLab CI/CD

ステージ名のタイプミス一つでデプロイジョブが静かに消えた

TT Labで続きを見る

目標

GitLabの設定スキーマで検証するツールをゲートで包んでチームのポリシーを加え、コミット前フック・パイプラインの最初のジョブ・includeを解いたリポジトリ全体の検証に掛け、マージリクエストのレビュー用に、2つのコミットの間のジョブ一覧の変化を取り出します。

なぜ重要なのか

パイプライン設定のエラーは、たいてい静かです。ステージ名の綴りミスやインデント1つのずれが、ジョブを消したりパイプラインの生成を止めたりしますが、その事実はプッシュしたあとで初めて明らかになります。検証を人の目の代わりにツールに任せつつ、ツールが何を捕まえて何を見逃すのかを知っておかないと、その隙間をポリシーで埋められません。同じ検査をコミット前とパイプラインの最初のジョブの2か所に置くのは、ローカルのフックは飛ばせるからで、レビューでは、テキストのdiffよりも「どのジョブが生まれ、消え、自動になるのか」のほうが重要な情報です。

ステップ

  1. /root/glci-gateをgitリポジトリにし(.gitignoreに.gitlab-ci-local/を置く)、/root/glci-gate/ci-validate.sh <설정파일>を作ってください。そのファイル1つを一時的なgitリポジトリの.gitlab-ci.ymlとして入れ、gitlab-ci-local --listで検証して、拒否されればINVALID <이유>(ツールの出力で意味のある最初の行)と1、通ればVALIDと0で終了します(プレースホルダーは、順に設定ファイル、理由です)。.gitlab-ci.ymlには下のfilesにある正常な設定を置いて、コミットしてください。採点ツールは、存在しないステージ・許容外のwhen・存在しないneedsの対象・YAML文法エラーのサンプルで確認します。
  2. ci-validate.shが、ツールの検証を通ったファイルに、チームのポリシーを2つ追加で適用するようにしてください。1つのジョブにrulesとonly/exceptが一緒にあればPOLICY <잡> rules-with-only-except、artifactsにpathsがあるのにexpire_inがなければPOLICY <잡> artifacts-without-expire_inを、ジョブ名順に1行ずつ出力して2で終了します(プレースホルダーはジョブ名です)。ポリシーをすべて守っていればVALIDと0です。隠しジョブ(ドットで始まる)と予約キー(stages・variables・default・include・workflowなど)は、ジョブではありません。
  3. /root/glci-gate/hooks/pre-commitを作ってコミットし、同じファイルを.git/hooks/pre-commitにコピーして実行権限を与えてください。フックは、ステージングされた.gitlab-ci.ymlがあるときだけ、そのステージングされた内容(git show :.gitlab-ci.yml)をci-validate.shで検証し、通らなければ理由を標準エラー出力に出力して、コミットを止めます。採点ツールは、コピーで間違った設定とポリシー違反の設定をコミットしてみて、設定と無関係なファイルのコミットを止めないかも確認します。
  4. /root/glci-gate/broken.ymlに下のfilesにある間違った設定をそのまま置き、ci-validate.shで一度に1つずつ明らかになる問題を直して、/root/glci-gate/fixed.ymlを作ってください。ジョブ名(build・unit・deploy)と各ジョブのscriptは変えません。fixed.ymlはVALIDである必要があり、unitはbuildを待つ必要があり、deployはmainで手動承認(allow_failureはfalse)として作られる必要があります。2つのファイルともコミットします(フックは.gitlab-ci.ymlだけを見ます)。
  5. .gitlab-ci.ymlにジョブci-lint(stage.pre、bash ci-validate.sh .gitlab-ci.yml)を追加して、コミットしてください。実行すると、ci-lintがVALIDで通り、残りのジョブが動くはずです。採点ツールは、コピーでポリシー違反(有効期限のないアーティファクト)を入れた設定で実行して、ci-lintが失敗しbuildが始まらないかを確認します。
  6. /root/glci-gate/ci-validate-repo.sh <저장소>を作ってください。リポジトリを一時的なコピーにして(フックは除く)コミットしたあと、gitlab-ci-local --previewでincludeが解かれたマージ後の設定を得て、失敗すればINVALID <이유>と1、成功すればマージ後の設定をci-validate.shに渡して、その結果(VALIDは0・POLICYは2)をそのまま出力します(プレースホルダーは、順にリポジトリ、理由です)。このリポジトリで実行するとVALIDになるはずです。採点ツールは、includeしたファイル側にエラーやポリシー違反を入れたコピーで確認します。
  7. /root/glci-gate/ci-diff.sh <저장소> <옛커밋> <새커밋> [브랜치]を作ってください。2つのコミットをそれぞれ一時的な作業ツリーに取り出し、ブランチ(デフォルトはmain)のパイプラインとして計算するように--variable CI_COMMIT_BRANCH=<브랜치>を与えたgitlab-ci-local --list-csv-allで、ジョブ名とwhenを得て、ジョブ名順にADDED <잡>、REMOVED <잡>、CHANGED <잡> <옛when>-><새when>を出力します(プレースホルダーは、順にリポジトリ、旧コミット、新コミット、ブランチ、ジョブ名、旧when、新whenです)。元のリポジトリの作業ツリーとブランチには触れず、一時的な作業ツリーは削除します。採点ツールは、コピーでジョブを追加し、削除し、whenを変えたコミットを作って確認します。

参考

スキーマと参照をツールに検証させる

/root/glci-gateをgitリポジトリにし(.gitignoreに.gitlab-ci-local/を置く)、/root/glci-gate/ci-validate.sh <설정파일>を作ってください。そのファイル1つを一時的なgitリポジトリの.gitlab-ci.ymlとして入れ、gitlab-ci-local --listで検証して、拒否されればINVALID <이유>(ツールの出力で意味のある最初の行)と1、通ればVALIDと0で終了します(プレースホルダーは、順に設定ファイル、理由です)。.gitlab-ci.ymlには下のfilesにある正常な設定を置いて、コミットしてください。採点ツールは、存在しないステージ・許容外のwhen・存在しないneedsの対象・YAML文法エラーのサンプルで確認します。

gitlab-ci-localは、GitLabの設定スキーマでまず検証し、ステージ・needs・extendsの参照が実際にあるかも見ます。問題があれば終了コードが0ではありません。出力にはリモートリポジトリの案内のようなノイズが混ざるので、取り除いて最初の理由だけを残してください。

ツールが通すものをチームのポリシーで捕まえる

ci-validate.shが、ツールの検証を通ったファイルに、チームのポリシーを2つ追加で適用するようにしてください。1つのジョブにrulesとonly/exceptが一緒にあればPOLICY <잡> rules-with-only-except、artifactsにpathsがあるのにexpire_inがなければPOLICY <잡> artifacts-without-expire_inを、ジョブ名順に1行ずつ出力して2で終了します(プレースホルダーはジョブ名です)。ポリシーをすべて守っていればVALIDと0です。隠しジョブ(ドットで始まる)と予約キー(stages・variables・default・include・workflowなど)は、ジョブではありません。

このツールは、rulesとonlyを混ぜたジョブと、有効期限のないアーティファクトを通します(実測)。GitLabサーバーは前者をkey may not be used with rulesで拒否します(GitLabのソースの設定検証)。ツール1つの判定をゲートのすべてだと信じず、違いを知っていれば、その分をポリシーで埋めます。

間違った設定はコミットの時点で止める

/root/glci-gate/hooks/pre-commitを作ってコミットし、同じファイルを.git/hooks/pre-commitにコピーして実行権限を与えてください。フックは、ステージングされた.gitlab-ci.ymlがあるときだけ、そのステージングされた内容(git show :.gitlab-ci.yml)をci-validate.shで検証し、通らなければ理由を標準エラー出力に出力して、コミットを止めます。採点ツールは、コピーで間違った設定とポリシー違反の設定をコミットしてみて、設定と無関係なファイルのコミットを止めないかも確認します。

フックは、作業ツリーのファイルではなく、コミットされる内容(インデックス)を見る必要があります。直したあとaddしていない状態でコミットすると、作業ツリーは問題なくても、コミットされるのは古い内容です。.git/hooksはリポジトリにアップロードされないので、チームで共有するには、追跡される場所に置いて、インストール方法を案内します。

4か所の間違った設定を直す

/root/glci-gate/broken.ymlに下のfilesにある間違った設定をそのまま置き、ci-validate.shで一度に1つずつ明らかになる問題を直して、/root/glci-gate/fixed.ymlを作ってください。ジョブ名(build・unit・deploy)と各ジョブのscriptは変えません。fixed.ymlはVALIDである必要があり、unitはbuildを待つ必要があり、deployはmainで手動承認(allow_failureはfalse)として作られる必要があります。2つのファイルともコミットします(フックは.gitlab-ci.ymlだけを見ます)。

ツールは最初のエラーで止まるので、直すたびにもう一度動かして、次のエラーを見ます。ステージ名の綴りミス、存在しないジョブを指すneeds、許可されないwhenの値、rulesとonlyの併用、有効期限のないアーティファクトが混ざっています。

パイプラインの最初のジョブが自分の設定を検証する

.gitlab-ci.ymlにジョブci-lint(stage.pre、bash ci-validate.sh .gitlab-ci.yml)を追加して、コミットしてください。実行すると、ci-lintがVALIDで通り、残りのジョブが動くはずです。採点ツールは、コピーでポリシー違反(有効期限のないアーティファクト)を入れた設定で実行して、ci-lintが失敗しbuildが始まらないかを確認します。

フックはローカルで飛ばせる(--no-verify)ので、サーバー側にも同じ検査が必要です。.preステージに置けば、ほかのすべてのジョブより先に動くので、間違った設定でランナーの時間を使う前に止まります。検査スクリプトはリポジトリの中にあるので、ジョブからそのまま呼び出せます。

includeしたファイルまで合わせて検証する

/root/glci-gate/ci-validate-repo.sh <저장소>を作ってください。リポジトリを一時的なコピーにして(フックは除く)コミットしたあと、gitlab-ci-local --previewでincludeが解かれたマージ後の設定を得て、失敗すればINVALID <이유>と1、成功すればマージ後の設定をci-validate.shに渡して、その結果(VALIDは0・POLICYは2)をそのまま出力します(プレースホルダーは、順にリポジトリ、理由です)。このリポジトリで実行するとVALIDになるはずです。採点ツールは、includeしたファイル側にエラーやポリシー違反を入れたコピーで確認します。

ファイル1つだけの検証は、includeしたファイルのエラーを見られません。そのファイルはコピーにないからです。--previewは、include・extends・アンカーをすべて解決した結果を出すので、その結果にポリシーを適用すれば、複数のファイルに散らばった設定も一度に検査されます。

レビュアーにパイプラインがどう変わるかを見せる

/root/glci-gate/ci-diff.sh <저장소> <옛커밋> <새커밋> [브랜치]を作ってください。2つのコミットをそれぞれ一時的な作業ツリーに取り出し、ブランチ(デフォルトはmain)のパイプラインとして計算するように--variable CI_COMMIT_BRANCH=<브랜치>を与えたgitlab-ci-local --list-csv-allで、ジョブ名とwhenを得て、ジョブ名順にADDED <잡>、REMOVED <잡>、CHANGED <잡> <옛when>-><새when>を出力します(プレースホルダーは、順にリポジトリ、旧コミット、新コミット、ブランチ、ジョブ名、旧when、新whenです)。元のリポジトリの作業ツリーとブランチには触れず、一時的な作業ツリーは削除します。採点ツールは、コピーでジョブを追加し、削除し、whenを変えたコミットを作って確認します。

設定ファイルのdiffは、include・extends・rulesが混ざると、実際に何が変わるのかを教えてくれません。パイプラインが作るジョブ一覧どうしを比べれば、「このマージで本番デプロイが自動になる」といった変化が1行で明らかになります。git worktree add --detachでコミットを別のディレクトリに取り出せますが、その状態にはブランチがなく、ブランチ条件のrulesがすべて抜けるので、ブランチを変数で渡します。その警告は--ignore-predefined-varsで消します。