GitHub Actionsのworkflow lockfileを試す。actions.lockで依存をcommitに固定し、ズレたら実行前に止める

GitHub Actionsのworkflow lockfileを試す。actions.lockで依存をcommitに固定し、ズレたら実行前に止める

GitHub Actionsのworkflow lockfileがpublic previewになりました。gh actions-lockでactions.lockを生成し、推移的依存の記録、未更新時の実行停止、タグが動いても固定commitで動くことを試しました。
2026.10.16

どうも!オペ部の西村祐二です!

GitHub Actionsで使うアクションは、GitHubのドキュメントや各アクションのREADMEではactions/checkout@v5のようにタグで参照する形で書かれています。ただし、タグは後から動かせるため、レビューしたときと違うコードがCIで動く余地が残ります。この対策として「完全なcommit SHAで固定する」運用がありますが、ワークフローが読みにくくなります。さらに、アクションが内部で呼ぶ別のアクション(推移的依存)までは固定できません。

2026年10月14日のGitHub Changelogで、ワークフローの依存をlockfileで固定するworkflow dependency lockfilesがpublic previewになりました。npmやpnpmのlockfileと同じ発想で、.github/workflows/actions.lockに各アクションのcommitを記録しておき、実行時はその記録どおりのcommitが使われます。

この記事では、Freeプランのアカウントに作ったリポジトリで次の3点を確かめます。

  • gh actions-lockで何が生成され、ワークフローはどう書き換わるか
  • lockfileを更新せずにアクションを追加するとCIで何が起きるか
  • 参照先のタグが動いたとき、実行されるcommitはどうなるか

Require lockfileポリシーの設定操作と実際のブロック挙動、Dependabotによるlockfileの自動更新、reusable workflowのロックは今回試していません。運用の章で公式ドキュメントの説明に触れるにとどめます。

今回の変更点

  • 公開日: 2026年10月14日(public preview)
  • 対象: 2026年10月16日時点のChangelog本文では、GitHub Enterprise Cloud、GitHub Enterprise Cloud with data residency、Team、Freeが対象です。同日付のEditor's noteで対象プランが更新された旨が記されているので、公開前に本文の表記を再確認する前提で読んでください。GitHub Enterprise Serverはgh-actions-lockのREADMEで非対応と明記されています
  • lockfileの場所: .github/workflows/actions.lock。GitHub CLIの拡張gh-actions-lockが生成・更新し、手で編集しない前提です
  • 記録される内容: 直接参照しているアクション、推移的依存、reusable workflowのcommit(詳細は次の節)
  • オンボードはワークフロー単位: ワークフローをlockfileの管理対象に入れることをオンボードと呼びます。オンボードしたワークフローでは、使う依存すべてにlockfileのエントリが必要になります。Actionsは記録されたcommitを実行します
  • 周辺機能: Dependabotがアクションのバージョン更新PRで該当するlockfileエントリも更新します。lockfileを必須にするRequire lockfileポリシーもpublic previewです

actions.lockは何を記録し、SHA固定と何が違うか

公式ドキュメントによると、lockfileはworkflowsとdependenciesの2つのセクションを持ちます。workflowsはオンボードしたワークフローごとに使っている依存の一覧、dependenciesは参照ごとの解決結果です。各依存には次の項目が記録されます。

項目 内容
ref ワークフローが参照しているタグまたはブランチ
commit その参照を解決したcommit SHA
owner_id 参照先リポジトリの所有者の数値ID
repo_id 参照先リポジトリの数値ID。owner_idとともに実行時にGitHub Actionsが照合します

推移的依存を持つ依存には、呼び出している依存の一覧もuses:として付きます。

SHA固定との違いは2つあります。1つは推移的依存まで固定されることです。composite actionが内部でuses:しているアクションもdependenciesに入り、親子関係が記録されます。もう1つはリポジトリの識別子が残ることです。公式ドキュメントでは、名前の裏にあるリポジトリが固定したときと別物になった場合(元のリポジトリが別名へ移されて同名の別リポジトリが作られた場合や、乗っ取り)に、名前が一致していても実行が止まるとされています。公式ドキュメントは、lockfileを使うリポジトリではワークフロー内のSHA固定は不要になるとしています。

試してみる

環境

  • macOS 26.6.1
  • GitHub CLI 2.101.0
  • gh-actions-lock v0.2.0(2026年10月12日UTCリリース。lockfileのスキーマはv0.0.3)
  • 検証リポジトリ: Freeプランのアカウントに新規作成したynishimura/blog-verify-actions-lock

拡張は次のコマンドでインストールできます。

gh extension install github/gh-actions-lock
gh actions-lock --version

1. 導入前のワークフロー

比較の起点として、タグ参照のアクション3つとローカルのcomposite action1つを使うワークフローを用意しました。aquasecurity/trivy-actionは内部で別のアクションを呼ぶcomposite actionなので、推移的依存の確認に使います。

.github/workflows/ci.yml
name: ci
on:
  push:
    branches: [main]
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-node@v5
        with:
          node-version: 24
      - run: node --version
      - uses: ./.github/actions/hello
      - uses: aquasecurity/trivy-action@v0.36.0
        with:
          scan-type: fs
          scan-ref: .
          exit-code: 0

この状態でpushすると実行は成功し、ログの「Set up job」にはタグを解決した結果が出ます。setup-trivyがさらに呼ぶactions/cacheとactions/checkoutの2行は省略しています。

Download action repository 'actions/checkout@v5' (SHA:fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09)
Download action repository 'actions/setup-node@v5' (SHA:a0853c24544627f65ddf259abe73b1d18a591444)
Download action repository 'aquasecurity/trivy-action@v0.36.0' (SHA:ed142fd0673e97e23eac54620cfb913e5ce36c25)
Download action repository 'aquasecurity/setup-trivy@3fb12ec12f41e471780db15c232d5dd185dcb514' (SHA:3fb12ec12f41e471780db15c232d5dd185dcb514)
Download action repository 'actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae' (SHA:27d5ce7f107fe9357f9df03efb73ab90386fccae)

trivy-actionの内側でsetup-trivyとactions/cacheが取得されています。ワークフローには書いていない推移的依存で、setup-trivyの分も含めると4つあります。

2. lockfileを生成する

リポジトリのルートで引数なしで実行すると、.github/workflows/以下のワークフローをすべて走査します。

gh actions-lock

gh actions-lockの出力。直接依存3つと推移的依存4つを固定し、ローカルactionを$/形式に移行した

直接依存3つに加えて、推移的依存4つ(trivy-actionからsetup-trivyとactions/cache、setup-trivyからactions/cacheとactions/checkout)が固定されました。変更はlockfileの追加だけではなく、ワークフロー本体にも入ります。生成後はワークフロー側のdiffも含めて確認してからcommitします。

.github/workflows/ci.yml
-      - uses: actions/checkout@v5
-      - uses: actions/setup-node@v5
+      - uses: actions/checkout@v5.1.0
+      - uses: actions/setup-node@v5.0.0
         with:
           node-version: 24
       - run: node --version
-      - uses: ./.github/actions/hello
+      - uses: $/.github/actions/hello
       - uses: aquasecurity/trivy-action@v0.36.0

書き換えは2種類です。

  • 部分バージョンの絞り込み: @v5のようなメジャーバージョンだけの参照は、解決したcommitを指す完全なタグ(@v5.1.0)に書き換わります。完全なバージョンタグは公開後に動くことがほとんどないため、固定したSHAが有効な期間を長くする狙いだとgh actions-lock --helpの説明に書かれています。@v0.36.0のように最初から完全なタグはそのままです。--no-narrowで無効にできます
  • ローカルactionの$/化: ./.github/actions/helloは$/.github/actions/helloになります。$/は「このワークフローと同じリポジトリの、実行中のcommit」を指す参照で、常に実行中のSHAに解決されるため、lockfileのエントリは不要です

生成されたactions.lockは次のとおりです。actions/setup-node@v5.0.0と、setup-trivy配下のactions/cache・actions/checkoutの3エントリは省略しています。

.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.3'
workflows:
    '.github/workflows/ci.yml':
        - 'actions/checkout@v5.1.0'
        - 'actions/setup-node@v5.0.0'
        - 'aquasecurity/trivy-action@v0.36.0'
dependencies:
    'actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae':
        ref: 'v5.0.5'
        commit: 'sha1-27d5ce7f107fe9357f9df03efb73ab90386fccae'
        owner_id: 44036562
        repo_id: 215566462
    'actions/checkout@v5.1.0':
        ref: 'v5.1.0'
        commit: 'sha1-fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09'
        owner_id: 44036562
        repo_id: 197814629
    'aquasecurity/setup-trivy@3fb12ec12f41e471780db15c232d5dd185dcb514':
        ref: 'v0.2.6'
        commit: 'sha1-3fb12ec12f41e471780db15c232d5dd185dcb514'
        owner_id: 12783832
        repo_id: 869952525
        uses:
            - 'actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb'
            - 'actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8'
    'aquasecurity/trivy-action@v0.36.0':
        ref: 'v0.36.0'
        commit: 'sha1-ed142fd0673e97e23eac54620cfb913e5ce36c25'
        owner_id: 12783832
        repo_id: 268833180
        uses:
            - 'actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae'
            - 'aquasecurity/setup-trivy@3fb12ec12f41e471780db15c232d5dd185dcb514'

trivy-actionのエントリにuses:としてsetup-trivyとactions/cacheが並び、setup-trivyのエントリにはさらにその依存が続きます。推移的依存はSHAで参照されていても、refにv5.0.5のような元のタグ名が残るので、何のバージョンが使われているかをlockfileだけで追えます。

この変更をcommitしてpushすると、実行は成功しました。「Set up job」の取得ログでは、actions/checkout@v5.1.0のようにタグ名が変わった以外、SHAは導入前と同じでした。ローカルactionはynishimura/blog-verify-actions-lock@<実行中のcommit>として取得されており、$/の説明どおりでした。

3. lockfileを更新せずにアクションを追加する

オンボード後のワークフローで、lockfileを更新しないままactions/cache@v4のステップを足してpushしました。

.github/workflows/ci.yml
      - run: node --version
      - uses: actions/cache@v4
        with:
          path: ~/.npm
          key: npm-${{ runner.os }}

結果はジョブが1つも始まらないstartup_failureでした。実行ページのAnnotationsには「Invalid lockfile: .github/workflows/actions.lock#L1 The lockfile could not be validated. Regenerate it by running gh actions-lock.」と出ます。

lockfileを更新せずにpushした実行。Startup failureになり、Annotationsに「Invalid lockfile」と再生成の案内が出る

固定し忘れたアクションが混ざったワークフローは、ジョブが始まる前に止まります。

同じ状態は手元でも検出できます。--verifyは何も書き換えずに検査するモードです。

gh actions-lock --verify

gh actions-lock --verifyの出力。actions/cache@v4がlockfileに固定されていないとして失敗する

終了コードは1で、「Not pinned actions/cache@v4」に続けて、ワークフローで使われているのにlockfileに固定されていない旨が出ます。ネットワークを使わない--verify-localも同じく終了コード1でした。こちらは各参照にエントリがあるかだけを見るので、エントリの中身が正しいかを確認するには--verifyを使う必要があります。

修正はワークフローを指定して再実行するだけです。

gh actions-lock .github/workflows/ci.yml
gh actions-lock --verify

actions/cache@v4が@v4.3.0に書き換わり、lockfileにエントリが1つ追加され、--verifyは「All 1 workflow valid」になりました。pushすると実行は成功し、取得ログはactions/cache@v4.3.0 (SHA:0057852b...)でした。

4. 参照先のタグが動いたとき

lockfileの本来の目的は「タグが動いても、固定したcommitを実行する」ことです。他人のリポジトリのタグは動かせないので、検証リポジトリ自身のcomposite actionをowner/repo/path@v1の形でリモート参照するステップを追加し、タグv1を自分で動かしました。

.github/workflows/ci.yml
      - uses: ynishimura/blog-verify-actions-lock/.github/actions/hello@v1

手順は次のとおりです。

  1. タグv1を打ち、ワークフローに上のuses:を追加してgh actions-lockでロックする。lockfileのcommitは607ea0a
  2. 別のcommitをpushし、v1をそのcommit(24c8add)へgit push -fで移動する
  3. lockfileは更新せずに、workflow_dispatchでワークフローを実行する

実行ログは次のようになりました。

Download action repository 'ynishimura/blog-verify-actions-lock@v1' (SHA:607ea0a6f16c076321a582ebb583afa0d3eac3ee)

リモートのv1は24c8addを指していますが、実行されたのはlockfileに記録された607ea0aです。タグが動いただけでは実行内容は変わりません。

このとき、手元のgh actions-lock --verifyと引数なしのgh actions-lockはどちらも「All 1 workflow valid」で、ファイルは変わりませんでした。v0.2.0のリリースノートにあるとおり、固定済みのピンは「sticky」で、通常のgh actions-lock実行では再解決されません。READMEによると、--verifyはブランチや部分バージョンの参照について、固定したcommitがその参照の履歴に残っているかを確認します。今回は607ea0aが24c8addの祖先に当たるため、正常と判定されたと読めます。

新しいcommitに追従したい場合は--relockを使います。

gh actions-lock --relock

lockfileのcommitが607ea0aから24c8addに変わるdiffが出ました。固定したcommitがforce pushなどで到達不能になった場合は--relock --accept-movedが必要だとドキュメントにありますが、その状況は今回試していません。

運用に組み込むときの要点

実測した範囲と公式ドキュメントから、運用の形は次のようになりそうです。

  • PRでlockfileを検証する: READMEでは--verifyをCIでの検査用途としています。PRでgh actions-lock --verifyを実行するジョブを置き、lockfileが古ければ失敗させます
  • lockfileのdiffをレビューする: commitの変化は別のコードが動くことを意味するので、依存のバージョン更新と同じ扱いでレビューします。owner_idやrepo_idの変化はリポジトリの置き換わりを意味するので、マージ前に調べます
  • 更新のタイミング: uses:を足したり外したりしたときにgh actions-lockを再実行します。タグの移動に追従したいときは--relockです。公式ドキュメントによると、Dependabotはオンボード済みワークフローのバージョン更新PRで、該当するlockfileエントリも一緒に更新します。ただし、新しいワークフローのオンボードは行わず、エントリのないワークフローにはオンボードが必要だと報告するとのことです
  • lockfileを必須にする: 公式ドキュメントによると、Settings > Actions > Policiesの「Require lockfile」で、lockfileにピンされていないuses:を持つワークフローの実行をブロックできます。Enforcement statusには、実際にブロックする「Active」と、ブロックせずに結果だけPolicy insightsに記録する「Evaluate」があります。EvaluateはGitHub Enterprise Cloudのみです。この設定画面と実際のブロック挙動は今回試していません

導入時の注意点

  • public previewで、拡張はpre-1.0: READMEにはlockfileの形式、フラグ、挙動が予告なく変わる可能性があると書かれています。実際、v0.2.0でスキーマがv0.0.3に上がり、--rescanが削除されています。--verifyも厳しくなり、どのワークフローも使っていないエントリや、エントリのないワークフローも失敗扱いになります
  • allowlistとの相性: 公式ドキュメントによると、オンボード後のallowlist(Allow select actions)は固定したcommit SHAに対して照合されるため、actions/checkout@v5のようにタグで書いた許可エントリは一致しなくなります。ワイルドカードか完全なcommit SHAに直す必要があるとされています。今回は試していません
  • ローカルactionの扱い: ./参照は$/に自動変換されます。変換したくない場合は--no-migrate-local-actionsを付けます。READMEには、ローカルパスのactionを使うワークフローはオンボード対象外になる(短期的な制限)とありますが、公式ドキュメントによるとスキップされるのは./…が実在するactionファイルに解決できない場合です。解決できる./.github/actions/helloは、今回のv0.2.0では$/へ変換されたうえでオンボードされました
  • 書き込みはall-or-nothing: READMEとv0.2.0のリリースノートによると、解決できない参照が1つでもあると、他のワークフローの分も含めて何も書き込まれません。参照先のタグやcommitが存在しないときは、先にワークフロー側を直す必要があります

まとめ

GitHub Actionsのworkflow lockfileは、gh actions-lockで.github/workflows/actions.lockを生成し、直接依存と推移的依存のcommitとリポジトリIDを記録する仕組みでした。オンボードしたワークフローがlockfileにないアクションを使っていると、ジョブが始まる前にstartup_failureで止まり、手元でも--verifyで同じ状態を検出できます。参照先のタグが動いても実行されるのは固定したcommitのままで、追従したいときだけ--relockで更新します。

SHA固定を手で管理している場合は、推移的依存まで固定でき、ワークフローもタグ名のまま読めるので、lockfileに置き換える価値があります。導入時はuses:の書き換えとallowlistの影響を確認し、PRに--verifyのジョブを足すところから始めるのが現実的です。

参考リンク:

この記事をシェアする

関連記事