# GitHub Actions Workflow Lockfiles：uses@SHA の固定だけでは不十分な理由

> Canonical: https://vxi.me/ja/posts/github-actions-workflow-lockfiles-transitive-dependencies/
> Locale: ja-jp
> Published: 2026-10-18
> Updated: 2026-10-18
> Markdown: https://vxi.me/ja/posts/github-actions-workflow-lockfiles-transitive-dependencies.md

# GitHub Actions Workflow Lockfiles：uses@SHA の固定だけでは不十分な理由

GitHub Actions の `uses: some-org/action@v2` をコミット SHA に置き換えると、その入口のコードは固定されます。しかし、固定した Composite Action が内部で別の `example-org/upload@v1` を呼び出していたらどうでしょう。外側の SHA が変わらなくても、内側のタグは移動できます。Workflow の YAML だけでは、実際に実行される Action の依存関係をすべて把握できません。

GitHub が[公開プレビューを発表](https://github.blog/changelog/2026-10-14-github-actions-workflow-lockfiles-in-public-preview)した Workflow Lockfiles は、`.github/workflows/actions.lock` に直接・間接の Action 依存を記録し、固定したコミットとリポジトリの識別情報を実行時に検証する仕組みです。

**すでに `uses@SHA` を採用しているなら、移行を決める前に依存グラフを見ましょう。** 利点は同じ入口をもう一度固定することではなく、間接依存をレビューできることと、リポジトリの識別情報を検証できることです。

| CI の現状 | 最初の判断 | 根拠 |
| --- | --- | --- |
| 直接の Action はすべて完全な SHA で固定済み、依存も単純 | まず棚卸し。全面移行は急がない | Lockfile の維持コストが上回る可能性 |
| Composite Action がさらに `@v1` や `@main` を参照 | 非重要 Workflow で試行 | 間接的な Action もロック対象にできる |
| 複数リポジトリで Reusable Workflow を共有 | 呼び出し元と定義元の責任を確認 | 定義元が内部 Action の Lockfile を持つ |
| 組織で Allowed Actions / SHA 固定ポリシーを適用中 | 先に許可ルールを点検 | ロック後はコミット SHA で照合される場合がある |

条件は重複します。以下は公式ドキュメントと固定版 [gh-actions-lock v0.2.0](https://github.com/github/gh-actions-lock/releases/tag/v0.2.0) の実装を照合した内容です。

## 直接の SHA 固定と依存グラフの固定は別物

例として、`ci.yml` が完全な SHA で `example-org/build` を呼び、その `action.yml` に `example-org/upload@v1` があるケースを考えます。外側のソースは変わりませんが、下流のバージョンタグは更新される可能性があります。

| 参照先 | 直接 `uses@SHA` | Workflow Lockfile 導入後 |
| --- | --- | --- |
| Workflow から直接使う第三者 Action | その入口を固定 | コミットとリポジトリ ID を記録 |
| Composite Action 内の追加 Action | 内側の可変タグは自動固定されない | 間接 Action まで管理 |
| 別リポジトリの Reusable Workflow | 呼び出し先を固定 | 呼び出しは管理するが、内部 Action は**定義側リポジトリ**が管理 |
| 同一リポジトリの `uses: $/…` | 実行中のコミットに対応 | もともと固定されるため別の Pin は不要 |
| `run:` 内のスクリプト取得や可変イメージタグ | 対象外 | Action Lockfile だけでは固定されない |

特に外部 Reusable Workflow の境界を誤解しやすい点に注意が必要です。[GitHub 公式の概念資料](https://docs.github.com/en/actions/concepts/security/workflow-dependency-lockfiles)では、そのリポジトリで定義した Workflow をその Lockfile が担当します。別リポジトリに定義された Reusable Workflow の内部 Action は、**定義している側のリポジトリ**の Lockfile が担当します。呼び出し元の一行を固定しただけで、相手側の内部まで監査したことにはなりません。

## actions.lock の中身：commit、ID、uses

`workflows` は Workflow のパスから依存 Pin への対応を持ちます。`dependencies` には `ref`、`commit`、`owner_id`、`repo_id`、さらに Composite Action による呼び出しを表す `uses` が入ります。次は GitHub の形式リポジトリにある[固定コミットの `benchV003` テストフィクスチャ](https://github.com/github/actions-lockfile/blob/ac158834765237ee94c36606cfc7d87316aa49ab/go/pkg/lockfile/bench_test.go#L31-L58)です。**`v0.0.3` の構造を示す公式テストデータ**であり、繰り返される a/b の SHA は合成されたテスト値です。

```yaml
version: v0.0.3
workflows:
  .github/workflows/ci.yml:
    - actions/checkout@v4
    - actions/setup-go@v5
    - actions/cache@v4
dependencies:
  actions/checkout@v4:
    hostname: github.com
    ref: v4
    commit: sha1-11bd71901bbe5b1630ceea73d27597364c9af683
    owner_id: 44036562
    repo_id: 197814629
  actions/setup-go@v5:
    hostname: github.com
    ref: v5
    commit: sha1-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
    owner_id: 44036562
    repo_id: 249058325
  actions/cache@v4:
    hostname: github.com
    ref: v4
    commit: sha1-bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
    owner_id: 44036562
    repo_id: 251882839
    uses:
      - actions/checkout@v4
```

このフィクスチャでは `hostname: github.com` を明示していますが、通常の github.com 上で生成される Lockfile は、デフォルトのホスト名を省略する場合があります。`workflows` の Pin 一覧と、依存項目内の `uses` が表す子依存は、同じ情報ではありません。

固定版 [`state_marshal.go` のソース](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/internal/lockfile/state_marshal.go)にもフィールドの書き出し処理があります。

```go
a := file.Dependencies[k]
entry := &yaml.Node{Kind: yaml.MappingNode}
// hostname の処理部分は省略
if a.Ref != "" {
    addQuotedField(entry, "ref", a.Ref)
}
if a.Commit != "" {
    addQuotedField(entry, "commit", a.Commit)
}
addIntField(entry, "owner_id", a.OwnerID)
addIntField(entry, "repo_id", a.RepoID)
```

Commit は Git オブジェクトを、数値の owner/repo ID は名前の背後にあるリポジトリの識別情報を示します。GitHub の概念資料には `version: v0.0.2` の例も残っていますが、今回確認した [v0.2.0 Release](https://github.com/github/gh-actions-lock/releases/tag/v0.2.0) と[形式仕様の README](https://github.com/github/actions-lockfile/blob/ac158834765237ee94c36606cfc7d87316aa49ab/README.md)は **`v0.0.3`** です。CLI の版とファイルのスキーマ版を混同しないことが重要です。

## 強制適用の前に差分をレビューする

まずはロールバックしやすいブランチで非重要な Workflow 一つを試します。GitHub CLI がインストール・認証済みで、対象リポジトリの読み取り権限があることを前提とした、固定版のコマンドです。

```bash
# 確認対象の拡張機能をバージョン固定で導入
gh extension install github/gh-actions-lock --pin v0.2.0
gh actions-lock .github/workflows/ci.yml

# Workflow・ローカル Action・未追跡ファイルの差分を確認
git status --short
git diff
cat .github/workflows/actions.lock

# 試験対象の Workflow だけを検証
gh actions-lock --verify .github/workflows/ci.yml
```

Workflow パスを指定した検証は推測ではありません。固定版の [root.go](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/root.go#L64-L70) はパス引数を受け取り、[verify.go](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/verify.go#L24-L40) とテストもそのスコープを使います。ただし共通の `actions.lock` 自体は妥当な形式である必要があります。他の Workflow をこのコマンドで自動登録することはありません。

導入時は新しい Lockfile 以外も変わる場合があります。バージョン型タグがより具体的な版に書き換えられたり、実際に `action.yml` があるローカル Action が `./actions/build` から `$/actions/build` に移されたりします。

```diff
 steps:
-  - uses: ./actions/build
+  - uses: $/actions/build
```

`$/` は実行中のリポジトリ Commit に束縛されます。パスの誤りまで自動修正されるわけではありません。また未追跡の `actions.lock` は通常の `git diff` には出ません。`git status --short` と実ファイルの確認が必要です。

組織の Allowed Actions も確認しておきます。Lockfile に入った Workflow では、許可リストが**固定された Commit SHA**と照合されるため、従来の `actions/checkout@v6` のようなタグによる許可条件が不一致になる可能性があります。例外的にすべての第三者 Action を許可するのではなく、承認済みコミットを基準にポリシーを調整します。

検証モードは二つあります。

```bash
gh actions-lock --verify-local .github/workflows/ci.yml
gh actions-lock --verify .github/workflows/ci.yml
```

`--verify-local` はネットワークを使わず、ロック対象の参照があるか確認します。上流のリポジトリ ID や到達性まで検証するのはオンラインの `--verify` です。全 Workflow を登録し終えたら、パスを省略した全体検証へ拡大します。途中で全体チェックを行うと未登録 Workflow や不要な Pin が阻害要因になります。

失敗時は `--json=valid,findings` を付け、終了コードと Finding を見ます。終了コード 1 は阻害事項、2 はコマンド・I/O・ネットワーク側のエラーを示します。[固定版の Finding 定義](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/internal/pipeline/checks/category.go)には `not-pinned`、`stale`、`local-action`、`repo-replaced`、`unreachable-pin` があります。

**最初の試行を五つの作業に分けるなら：**
1. **棚卸し**：直接 `uses`、内側の Composite Action、外部 Reusable Workflow を列挙し、定義側の担当者を確認する。
2. **生成**：テストブランチで固定版を使い、Workflow、`action.yml`、`actions.lock` の変更を調べる。
3. **ポリシー確認**：Lockfile のコミット SHA と組織の Allowed Actions を照合し、差分を通常の PR としてレビューする。
4. **二段階検証**：まず `--verify-local`、次に `--verify`。`not-pinned`、`stale`、`local-action` は参照と差分を見直し、`repo-replaced` や `unreachable-pin` は上流の履歴と ID を調べるまでマージしない。
5. **撤回可能にする**：解決できない解析やポリシーの失敗が出た場合は試験ブランチの変更を破棄する。安定してから全体へ広げる。

## 導入後の依存更新と Release Cooldown

最初に固定される Commit は元の `uses` 参照とリゾルバーで決まります。v0.2.0 に**常に七日待つデフォルト値はありません**。`.github/dependabot.yml` の `github-actions` に正の `cooldown.default-days` があればそれが優先され、次にユーザー設定の `cooldown_days` が使われます。いずれも設定しなければ経過日数によるフィルターはなく、公開三日以内のタグに非阻害の `fresh-tag` 通知が付くだけです。[設定解決](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/fresh_tag.go#L16-L32)と[通知処理](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/fresh_tag.go#L48-L75)を参照してください。生成された `ref`、`commit`、Workflow 差分を確認するのが確実です。

**Sticky pins** により、通常の `gh actions-lock` 再実行では上流 `main` や `v3` の移動を自動的に採用しません。意図的な更新には `gh actions-lock --relock` を使い、コミット差分をレビューします。履歴の書き換えで古い Commit が到達不能な場合、`--relock --accept-moved` は別の信頼判断です。CI を緑にするためだけに付けるオプションではありません。

Dependabot は登録済みの Workflow の記録を更新できますが、未登録の Workflow を初回登録してくれるわけではありません。[GitHub の強制適用ガイド](https://docs.github.com/en/actions/how-tos/administer/enforce-workflow-lockfiles)は、小規模試行と CODEOWNERS のレビューを勧めています。`Require lockfile` を有効にする前にプランと組織権限を確かめましょう。固定版 README によれば GitHub Enterprise Server (GHES) はこのプレビューの対象外で、Enterprise Cloud とは別物です。

Lockfile が保証するのは、どの Action コードを実行するかという識別情報です。そのコードに脆弱性がないかは判断できません。`run:` のダウンロード、Digest を固定していないイメージ、パッケージ依存、CI Token の権限にはそれぞれ別の対策が必要です。レビューで価値があるのは、**新たに固定した間接 Action はどれか、誰がそのリポジトリ ID を承認したか、共有 Reusable Workflow 内の Action は誰が管理するか**を答えられること。関連する別のゲートの問題は [GitHub AI Scan の分析](/posts/github-ai-scan-pr-security-checks/)で扱っています。

---

*証拠と時点：GitHub の Workflow Lockfiles 公開プレビューは 2026 年 10 月 14 日に発表。検証対象の固定コミットは `gh-actions-lock v0.2.0` の `d5dca22a06ff8590d50b41612038996800cfb336` と形式リポジトリの `ac158834765237ee94c36606cfc7d87316aa49ab`。ソースを確認したのは 10 月 18 日です。YAML は合成 SHA を含む上流テストデータであり、実際の Runner 実行・CLI 移行・タグ変更実験は行っていません。利用できる機能とポリシーはアカウントによって異なります。*
