本文へスキップ

gh-actions-lock で GitHub Actions のバージョンをコミットに固定する

📋 目次

背景

ワークフローの uses: actions/checkout@v7 のタグは、付け替えられます。 作者がタグを別のコミットに動かすと、ワークフローを変えていなくても、次の実行から別のコードが動きます。

コミットの SHA で指定すれば防げます。 ただ SHA だけが並ぶと、どのバージョンか読めません。 更新のたびに SHA を調べて書き換える手間もあります。

この手間を減らすツールに pinact があります。 GitHub も、ロックファイルの仕様 actions-lockfile を、公開プレビューで出しています。 ロックファイルを作って検証する gh 拡張が gh-actions-lock です。

ここでは gh-actions-lock を使ってみた記録です。

ロックファイルを生成する

拡張を入れて、リポジトリのルートで gh actions-lock を実行します。 .github/workflows/ のワークフローを走査して、アクションをコミットに解決します。

gh extension install github/gh-actions-lock

対象のワークフローは次のとおりです。

steps:
  - uses: actions/checkout@v7
  - uses: actions/setup-node@v7
    with:
      node-version-file: .node-version
      cache: npm
$ gh actions-lock
Scanning 1 workflow
Planning pins
Pinned 2 actions across 1 workflow
  actions/checkout@v7.0.1 (3d3c42e)
    └─ .github/workflows/test.yml
  actions/setup-node@v7.1.0 (949feb2)
    └─ .github/workflows/test.yml

test.yml の uses が完全なバージョンになり、先頭に管理コメントが入ります。

+# This workflow is managed by gh actions-lock.
 name: test
@@
-      - uses: actions/checkout@v7
-      - uses: actions/setup-node@v7
+      - uses: actions/checkout@v7.0.1
+      - uses: actions/setup-node@v7.1.0

.github/workflows/actions.lock もできます。

# This file is machine-generated by `gh actions-lock`.
# Do not edit by hand; run `gh actions-lock` to update.
# Docs: https://gh.io/actions-lockfile
version: 'v0.0.2'
workflows:
    '.github/workflows/test.yml':
        - 'actions/checkout@v7.0.1'
        - 'actions/setup-node@v7.1.0'
dependencies:
    'actions/checkout@v7.0.1':
        ref: 'v7.0.1'
        commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1'
        owner_id: 44036562
        repo_id: 197814629
    'actions/setup-node@v7.1.0':
        ref: 'v7.1.0'
        commit: 'sha1-949feb2413d6458794dcd2491c4babbbce0c15c1'
        owner_id: 44036562
        repo_id: 189476904

workflows には、ワークフローが使うアクションが並びます。 dependencies には、タグが指すコミットの SHA と、所有者とリポジトリの ID が入ります。 ID を持つのは、同じ名前で作り直された別のリポジトリに気付くためでしょう。

actions-lockfile の例は version: v0.0.3 でしたが、生成されたのは v0.0.2 でした。 仕様はまだ固まっていないようです。

ワークフローにはタグが残る

uses には、タグが残ります。 ロックファイルは、そのタグとコミットの対応を記録します。 キー(actions/checkout@v7.0.1)は uses と一致させます。

@v7 が @v7.0.1 になったのは、ツールが置き換えたからです。 初めてロックするとき、@v7 のような部分的なバージョンは、その範囲で最新の完全なバージョンに置き換わります。 完全なバージョンのタグは、あとから動くことがほとんどありません。

最新バージョンに上げる

ロックしたあとの参照は、ツールが書き換えません。 uses のタグを手で直して、gh actions-lock --relock を実行します。

- uses: actions/checkout@v7.0.1
- uses: actions/setup-node@v7.1.0

最新のタグは gh api で調べます。

gh api repos/actions/checkout/releases/latest -q .tag_name

検証のコマンド

--verify は、ワークフローとロックファイルの一致を確かめます。 何も書き換えません。 --verify-local は、オフラインで、各参照にロックファイルの項目があるかだけを見ます。 --relock は、上流で動いた参照を解決し直して、ロックファイルを書き直します。 --no-fix は、書き込まずに状態だけを見ます。

改ざんと不一致の検知

ロックファイルのコミットを 0000000000000000000000000000000000000000 に書き換えて、--verify を実行します。

$ gh actions-lock --verify
Scanning 1 workflow
1 of 1 workflow failed verification
✗ Unreachable pin actions/checkout@v7.0.1
  pinned 000000000000 is not an ancestor of 3d3c42e5aac5 — lockfile may have been tampered with
  ↳ https://github.com/actions/checkout/releases
  ! investigate immediately — verify the lockfile entry against upstream history
  see: how to fix this
  ↳ .github/workflows/test.yml

ロックファイルのコミットが、タグの指す先の祖先ではないので失敗します。 GitHub 上の履歴を見に行くので、--verify にはネットワークが要ります。

ワークフローのタグだけを v7.0.0 に変えて --verify-local を実行すると、次のようになります。

$ gh actions-lock --verify-local
  workflow uses ref "v7.0.0" but lockfile pins "v7.0.1"
! STALE actions/checkout@v7.0.1
  lockfile pins actions/checkout@v7.0.1 but no uses: in this workflow references it
1 of 1 workflow failed: 1 ref-changed, 1 stale
Run without --verify-local to resolve and pin missing actions.

タグの食い違いと、使われなくなったロックファイルの項目を、オフラインで見つけます。

CI に検証を足すか

CI で検証すると、ロックファイルのずれを、プルリクエストの段階で見つけられます。 ただ gh actions-lock は拡張なので、CI の step ごとに gh extension install が要ります。 これは新しい外部の依存で、ロックファイルでは固定できません。 公式の CI 向けの検証手段は見つかりませんでした。

私のリポジトリでは、CI に検証を足していません。 ワークフローを変えたら、手元で gh actions-lock を実行して、ロックファイルを同じコミットに入れます。

gh-actions-lock の README によると、ロックの対象のワークフローは、リポジトリ内のローカルのアクション(uses: ./...)を使えません。

まとめ

gh actions-lock を実行すると、uses が完全なバージョンになり、アクションのコミットがロックファイルに残ります。 差分がプルリクエストに出るので、アクションの更新をレビューで確認できます。 最新バージョンへの更新は手動で、CI での検証には拡張のインストールが要ります。 まだ公開プレビューですが、正式にリリースされたら、ぜひ使ってみてください。

参考リンク

この記事をシェア