GitHub Actions の依存関係を lockfile で固定する
GitHub Actions でタグやブランチを参照すると、ワークフローを変更しなくても実行するコードが変わる可能性があります。Workflow dependency lockfiles を使うと、読みやすいタグ表記を残しながら依存先をコミット SHA に固定できます。この記事では GitHub Actions の依存関係を lockfile で固定する方法を紹介します。
GitHub Actions で Action を利用するとき、uses: actions/checkout@v6 のようにタグでバージョンを指定できます。しかし、タグやブランチが指すコミットは後から変更される可能性があるため、バージョンを指定したタイミングとは異なるコードが実行されることがあります。これは悪意のあるコードが実行されるリスクにつながります。実際に 2025 年 3 月には、tj-actions/changed-files のタグが書き換えられて悪意のあるコミットを指すようになり、タグを参照していた多くのワークフローで CI のシークレットがログに出力される事件が起きました。
この問題への対策として、uses に完全なコミット SHA を記述する方法があります。SHA だけではバージョンが読み取れないため、uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 のようにコメントでバージョンを補うのが一般的でした。ただし、コメントは実際の SHA と一致している保証がないうえ、呼び出した Action が内部で参照する別の Action まで固定できるとは限らないという課題がありました。
Workflow dependency lockfiles を使うと、ワークフローにはタグ表記を残し、実行対象のコミットを別のファイルで管理できます。この記事では、GitHub Actions の依存関係を lockfile で固定する方法を紹介します。
2026 年 10 月 15 日時点で public preview の機能です。
lockfile の仕組み
lockfile は .github/workflows/actions.lock に配置します。ワークフローごとに利用する依存関係を登録し、それぞれの参照が解決されたコミット SHA を記録します。登録済みのワークフローでは、GitHub Actions が記録されたコミットを実行します。
通常のタグ指定と違い、タグが移動しても新しいコードへ自動的に追従しません。タグが移動しただけであれば、記録されたコミットがそのまま実行されます。ただし、記録されたコミットや参照先のリポジトリが信頼できなくなった場合は、検証に失敗して実行できなくなります。具体的には次のような場合です。
- force push による履歴の書き換えなどで、記録されたコミットが参照先リポジトリのどのブランチからも到達できなくなった場合
- 参照先のリポジトリが削除された、またはアクセスできなくなった場合
- 同じ名前のリポジトリが、記録された ID とは異なる別のリポジトリに置き換わった場合
なお、リポジトリの名前変更や移管のように ID が変わらない場合は、リダイレクトをたどって記録されたコミットが実行されます。
また、Action が別の Action を呼び出すような「推移的な依存関係」も対象になります。一方、再利用可能なワークフローを呼び出す場合、その内部の依存関係は定義元のリポジトリの lockfile が管理する必要があります。
gh actions-lock で lockfile を生成する
lockfile は GitHub CLI の拡張である gh-actions-lock で生成します。以下のコマンドで拡張を導入します。
gh extension install github/gh-actions-lock --pin v0.2.0
gh actions-lock --version最小の例として、次のワークフローを用意します。
name: Lockfile example
on: workflow_dispatch
permissions:
contents: read
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: echo 'Lockfile example'リポジトリのルートで次のコマンドを実行します。
gh actions-lockこのコマンドは .github/workflows/ 配下のワークフローを調べて lockfile を生成します。特定のワークフローから段階的に導入したい場合は、gh actions-lock .github/workflows/ci.yml のようにパスを指定できます。
コマンドを実行すると、以下のように出力されます。
Pinned 1 action across 1 workflow
actions/[email protected] (d23441a)
└─ .github/workflows/ci.yml生成された lockfile は次のとおりです。
# 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/[email protected]'
dependencies:
'actions/[email protected]':
ref: 'v6.1.0'
commit: 'sha1-d23441a48e516b6c34aea4fa41551a30e30af803'
owner_id: 44036562
repo_id: 197814629workflows には登録したワークフローとその依存関係、dependencies には参照先の情報が記録されています。commit が実行対象のコミットです。owner_id と repo_id は、同じ名前のリポジトリが別の実体に置き換わっていないかを確認するための ID です。
ワークフローの uses も次のように変わりました。
- - uses: actions/checkout@v6
+ - uses: actions/[email protected]拡張は初めて固定する部分的なバージョン指定を、具体的なバージョンタグに絞り込みます。これを無効にするには --no-narrow を指定します。
同じリポジトリ内の Action は $/ に書き換えられる
uses: ./.github/actions/hello のように同じリポジトリ内の Action を参照している場合も、uses が書き換えられます。次のようなローカルの composite action を呼び出すワークフローで gh actions-lock を実行してみます。
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/hello実行すると Migrated 1 local action to $/… と出力され、ワークフローは次のように変わりました。
- - uses: actions/checkout@v6
- - uses: ./.github/actions/hello
+ - uses: actions/[email protected]
+ - uses: $/.github/actions/hello書き換えが必要な理由は、./ と $/ の参照先が異なるためです。
./ は、実行時のワークスペースに置かれたファイルを参照します。ワークスペースの中身は直前の actions/checkout で取得した ref や、それより前のステップの処理によって変わるため、どのコードが実行されるかを lockfile で固定できません。そのため ./ を含むワークフローは lockfile に登録できないのです。
一方、$/ は定義元のリポジトリの実行中のコミットから Action を解決する記法です。参照先は常にワークフロー自体と同じコミットになるため、lockfile にエントリーを追加しなくても固定されているとみなされます。$/ の後に @v1 のような ref を付けることはできません。また、./ と同じくリポジトリのルートからのパスで指定します。
lockfile とワークフローの不整合を検出する
依存先を変更したのに lockfile を更新し忘れた場合は、検証コマンドで検出できます。
gh actions-lock --verify--verify はファイルを書き換えず、GitHub にアクセスして固定された参照を検証します。生成直後のサンプルで gh actions-lock --verify --json を実行すると、次の JSON が出力されました。
{
"cli_version": "v0.2.0",
"findings": [],
"lockfile_version": "v0.0.3",
"valid": true,
"workflows": [
{
"path": ".github/workflows/ci.yml",
"valid": true,
"findings": [],
"dependencies": [
{
"nwo": "actions/checkout",
"ref": "v6.1.0",
"sha": "d23441a48e516b6c34aea4fa41551a30e30af803",
"hash_algo": "sha1",
"direct": true
}
]
}
]
}valid が true で、findings が空配列であれば問題はありません。dependencies の direct は、ワークフローから直接参照している依存関係かどうかを表します。Action が内部で呼び出す推移的な依存関係があれば、direct が false のエントリーとして表示されます。
ネットワークを使わず、ワークフローの参照に対応するエントリーがあるかだけを確認するには、次のコマンドを使います。
gh actions-lock --verify-local試しにワークフローだけを actions/[email protected] に変更すると、以下のように不整合が検出されます。
✗ Ref changed actions/[email protected]
workflow uses ref "v6.0.0" but lockfile pins "v6.1.0"
✗ Unused lockfile entry actions/[email protected]
lockfile pins actions/[email protected] but no uses: in this workflow references it--verify-local は手元で更新漏れに気づくために便利ですが、記録されたコミットやリポジトリの信頼性までは確認しません。CI での検証には --verify を使用します。
依存関係を更新する
Action を追加・削除したときや参照するバージョンを変更したときは、gh actions-lock を再実行します。再実行した場合、すでに固定されている参照は基本的に維持されます。
登録済みのワークフローで Dependabot が Action のバージョン更新 PR を作成すると、対応する lockfile のエントリーも同じ PR で再生成されます。
タグの移動などで参照が変わったことを反映したい場合は、--relock を使うと lockfile のエントリーを解決し直して更新できます。
gh actions-lock --relock例えば --no-narrow を指定して actions/checkout@v6 のまま固定した場合、v6 が指すコミットが A から B に移動すると、--relock で lockfile のエントリーが B に更新されます。一方、actions/[email protected] のような完全なバージョンタグは公開後に移動することがほとんどないため、--relock を実行しても通常は同じコミットのままです。拡張がデフォルトで部分的なバージョン指定を完全なバージョンタグに絞り込むのは、このように固定したコミットが長く有効であり続けるようにするためです。
なお、--relock は記録されたコミットに到達できなくなったエントリーをエラーのまま残します。このようなエントリーは改ざんの可能性もあるため、まず上流リポジトリの履歴を確認し、問題がなければ gh actions-lock --relock --accept-moved で新しいコミットを受け入れます。
まとめ
- Workflow dependency lockfiles を使うと、タグ表記を残しながら Action の実行対象をコミット SHA に固定できる
gh actions-lockは lockfile を生成するだけでなく、ワークフローの参照を書き換える場合がある--verifyは GitHub にアクセスして検証し、--verify-localはオフラインで参照とエントリーの対応を確認する- 通常の再実行は固定済みの参照を維持し、
--relockは既存の参照を解決し直す - 再利用可能なワークフロー内部の依存関係は、定義元のリポジトリの lockfile が管理する




