# GitHub Actions Workflow Lockfiles: Warum uses@SHA allein nicht genügt

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

# GitHub Actions Workflow Lockfiles: Warum uses@SHA allein nicht genügt

Wer GitHub Actions mit vollständigen Commit-SHAs referenziert, fixiert den Quellcode an diesem Einstiegspunkt. Doch was passiert, wenn eine so fixierte Composite Action intern `example-org/upload@v1` verwendet? Die äußere SHA bleibt gleich, während das untergeordnete Tag auf einen anderen Commit zeigen kann. Die Workflow-YAML allein verrät nicht, welche Drittanbieter-Actions letztlich ausgeführt werden.

Mit den [Workflow Lockfiles in der Public Preview](https://github.blog/changelog/2026-10-14-github-actions-workflow-lockfiles-in-public-preview) führt GitHub `.github/workflows/actions.lock` ein. Die Datei dokumentiert direkte und transitive Action-Abhängigkeiten einschließlich Commit und Repository-Identität.

**Bereits alles auf `uses@SHA` gepinnt? Erst den Abhängigkeitsgraphen prüfen, dann eine Migration entscheiden.** Der Mehrwert liegt in nachvollziehbaren transitiven Pins und der Prüfung der Repository-Identität, nicht im doppelten Fixieren derselben Action.

| Ausgangslage im CI | Empfehlung | Begründung |
| --- | --- | --- |
| Wenige direkte Actions, alle mit vollständiger SHA | Erst inventarisieren, flächendeckende Migration verschieben | Der Zusatznutzen kann geringer als der Pflegeaufwand sein |
| Composite Actions referenzieren weitere Actions mit `@v1` oder `@main` | Einen unkritischen Workflow pilotieren | Transitive Abhängigkeiten werden sichtbar und fixiert |
| Mehrere Repositories verwenden einen Reusable Workflow | Verantwortung von Aufrufer und definiertem Workflow klären | Die internen Actions gehören zum Lockfile des aufgerufenen Repositories |
| Organization Allowed Actions oder SHA-Richtlinien sind aktiv | Richtlinien vor dem Piloten prüfen | Beim Lockfile können die fixierten Commit-SHAs für die Freigabe maßgeblich sein |

Die Fälle schließen sich nicht aus. Grundlage sind die GitHub-Dokumentation und der festgelegte Stand [gh-actions-lock v0.2.0](https://github.com/github/gh-actions-lock/releases/tag/v0.2.0).

## Der fixierte Einstiegspunkt ist nicht der gesamte Abhängigkeitsgraph

Nehmen wir einen hypothetischen Workflow `ci.yml`: Er referenziert `example-org/build` mit voller SHA. In dessen `action.yml` steht jedoch `example-org/upload@v1`. Die oberste Action bleibt unverändert, das untergeordnete Tag kann wandern.

| Abhängigkeit | Direkte SHA-Fixierung | Workflow Lockfile |
| --- | --- | --- |
| Externe Action im Workflow | Fixiert diesen Verweis | Speichert Commit und Repository-ID |
| Weitere Actions innerhalb einer Composite Action | Pinnt die inneren Tags nicht automatisch | Erfasst transitive Action-Abhängigkeiten |
| Externer Reusable Workflow | Fixiert den Aufruf | Der Aufrufer fixiert seinen Verweis; interne Actions verwaltet das **definierende Repository** |
| Gleicher Repository-Kontext mit `uses: $/…` | Bindet an den laufenden Repository-Commit | Bereits inhärent fixiert; kein eigener Pin nötig |
| Downloads in `run:` oder veränderliche Container-Tags | Nicht abgedeckt | Auch nicht allein durch `actions.lock` geschützt |

Diese Grenze ist entscheidend. Laut [GitHub-Konzeptdokumentation](https://docs.github.com/en/actions/concepts/security/workflow-dependency-lockfiles) deckt das Lockfile die Workflows ab, die im jeweiligen Repository definiert sind. Die Actions **innerhalb eines externen Reusable Workflow** werden durch das Lockfile seines definierenden Repositories verwaltet. Wer den Aufruf eines gemeinsamen Deployment-Workflows fixiert, hat damit nicht automatisch jede Action im anderen Repository geprüft.

## Was tatsächlich in actions.lock steht

Unter `workflows` stehen die Workflows und ihre benötigten Pins. `dependencies` enthält unter anderem `ref`, `commit`, `owner_id` und `repo_id`; bei verschachtelten Actions kann außerdem `uses` auftauchen. Das folgende vollständige YAML stammt aus der fest verankerten [offiziellen Parser-Testfixture `benchV003`](https://github.com/github/actions-lockfile/blob/ac158834765237ee94c36606cfc7d87316aa49ab/go/pkg/lockfile/bench_test.go#L31-L58). Es zeigt Schema **v0.0.3**. Die wiederholten a/b-SHAs sind synthetische Testwerte, keine produktiv erzeugten Pins.

```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
```

Die Fixture enthält ausdrücklich `hostname: github.com`. Bei Dateien, die für den Standardhost github.com erzeugt werden, kann dieses optionale Feld fehlen. Die `uses`-Beziehung innerhalb von `dependencies` und die Pin-Liste unter `workflows` sind unterschiedliche Sichten auf die Abhängigkeiten.

Auch der fixierte [Go-Quellcode `state_marshal.go`](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/internal/lockfile/state_marshal.go) serialisiert diese Felder:

```go
a := file.Dependencies[k]
entry := &yaml.Node{Kind: yaml.MappingNode}
// Hostname-Zweig hier ausgelassen
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)
```

Die Commit-SHA benennt ein Git-Objekt; numerische Owner- und Repo-IDs identifizieren das Repository hinter einem potenziell veränderlichen Namen. Im älteren Konzeptbeispiel steht noch `version: v0.0.2`, während die [Release-Notizen zu v0.2.0](https://github.com/github/gh-actions-lock/releases/tag/v0.2.0) und die [Format-README](https://github.com/github/actions-lockfile/blob/ac158834765237ee94c36606cfc7d87316aa49ab/README.md) **`v0.0.3`** beschreiben. CLI-Version und Lockfile-Schema sind unabhängig zu betrachten.

## Migration: zuerst den tatsächlichen Diff prüfen

Ich würde mit einem umkehrbaren Testbranch und einem unkritischen Workflow beginnen. Voraussetzung sind eine installierte, authentifizierte GitHub CLI und Leserechte für die betroffenen Repositories. Die Befehle verwenden bewusst die hier geprüfte Erweiterungsversion:

```bash
# Die geprüfte Erweiterungsversion installieren
gh extension install github/gh-actions-lock --pin v0.2.0
gh actions-lock .github/workflows/ci.yml

# Workflow, lokale Actions und neue Dateien prüfen
git status --short
git diff
cat .github/workflows/actions.lock

# Nur den Pilot-Workflow verifizieren
gh actions-lock --verify .github/workflows/ci.yml
```

Der Pfadparameter ist durch [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) und die Tests des fixierten Quellstands gedeckt. Die gemeinsame `actions.lock` muss weiterhin gültig sein, aber die Prüfung einer einzelnen Datei registriert nicht automatisch alle übrigen Workflows.

Das Tool erzeugt nicht nur eine neue Datei. Ein semantisches Versions-Tag kann auf eine konkrete Version eingegrenzt werden; eine lokale Action mit gültiger `action.yml` kann von `./actions/build` nach `$/actions/build` wechseln:

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

`$/` bindet den Verweis an den laufenden Commit des eigenen Repositories. Falsche lokale Pfade werden dadurch nicht repariert. Eine neue, noch unversionierte `actions.lock` erscheint zudem nicht in der normalen Ausgabe von `git diff`. Deshalb auch `git status --short` und den Dateiinhalt prüfen.

Die Organization-Richtlinie **Allowed Actions** ist ein weiterer möglicher Stolperstein. Bei Lockfile-Workflows erfolgt die Freigabe anhand der fixierten Commit-SHA. Eine bisherige Tag-Regel wie `actions/checkout@v6` kann dann nicht mehr passen. Besser die konkret geprüften Commits zulassen als die Beschränkung für alle Drittanbieter-Actions aufzuheben.

Offline- und Online-Prüfung haben unterschiedliche Aufgaben:

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

`--verify-local` kontrolliert ohne Netzwerk, ob Referenzen im Lockfile erfasst sind. Die tatsächliche Repository-Identität wird dabei nicht online überprüft. Dafür dient `--verify`, etwa als PR-Gate. Erst nach der vollständigen Registrierung aller Workflows ist ein Aufruf ohne Dateipfad als Repository-weite Prüfung sinnvoll. Während eines einzelnen Piloten würden noch nicht registrierte Workflows oder ungenutzte Pins Fehler auslösen.

Zur Fehleranalyse hilft `--json=valid,findings`. Exit-Code 1 steht für blockierende Findings, 2 für Befehls-, I/O- oder Netzwerkfehler. Im [festen Finding-Katalog](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/internal/pipeline/checks/category.go) stehen unter anderem `not-pinned`, `stale`, `local-action`, `repo-replaced` und `unreachable-pin`.

**Ein begrenzter Pilot in fünf Schritten:**
1. **Inventarisieren:** direkte `uses`-Einträge, innere Composite Actions und externe Reusable Workflows erfassen. Die Eigentümer der aufgerufenen Workflows bestätigen die Zuständigkeit für ihre internen Pins.
2. **Auf dem Testbranch generieren:** fixierte Version verwenden und Änderungen an Workflow, `action.yml` und `actions.lock` vergleichen.
3. **Richtlinien abgleichen:** die fixierten Commits mit Allowed Actions vergleichen und alles über einen normalen PR überprüfen.
4. **Zweistufig prüfen:** erst `--verify-local`, danach `--verify`. Bei `not-pinned`, `stale` oder `local-action` die lokalen Dateien prüfen; bei `repo-replaced` oder `unreachable-pin` Herkunft und Git-Historie untersuchen, bevor gemergt wird.
5. **Rückweg offenhalten:** bei nicht lösbaren Parser- oder Policy-Problemen die Änderungen des Testbranches verwerfen. Eine Ausweitung erst nach einem stabilen Piloten vornehmen.

## Abhängigkeiten aktualisieren: Sticky Pins und Release Cooldown

Welcher Commit beim ersten Onboarding gewählt wird, hängt vom ursprünglichen `uses`-Verweis und der Auflösung ab. Die Version v0.2.0 erzwingt **keine pauschale Wartezeit von sieben Tagen**. Ein positiver Wert `cooldown.default-days` für `github-actions` in `.github/dependabot.yml` hat Vorrang, danach folgt das benutzerspezifische `cooldown_days`. Ohne solche Vorgaben wird nicht nach Veröffentlichungsalter gefiltert; neue Tags unter drei Tagen erhalten nur einen nicht blockierenden `fresh-tag`-Hinweis. Siehe [Konfigurationsauflösung](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/fresh_tag.go#L16-L32) und [Hinweislogik](https://github.com/github/gh-actions-lock/blob/d5dca22a06ff8590d50b41612038996800cfb336/cmd/gh-actions-lock/fresh_tag.go#L48-L75). Entscheidend sind die erzeugten `ref`-, `commit`- und Workflow-Diffs.

Vorhandene Pins sind **sticky**: Ein erneutes `gh actions-lock` ersetzt nicht stillschweigend die gespeicherte SHA, nur weil upstream `main` oder `v3` weitergewandert ist. Für eine bewusste Übernahme neuer Versionen wird `gh actions-lock --relock` verwendet und der Diff geprüft. Ist ein alter Commit nach einer Umschreibung der Git-Historie nicht mehr erreichbar, ist `--relock --accept-moved` eine eigenständige Vertrauensentscheidung und kein allgemeines Mittel gegen rote CI-Jobs.

Dependabot kann Lockfile-Einträge für bereits registrierte Workflows aktualisieren, registriert aber keine bisher unbekannten Workflows automatisch. [GitHubs Enforcement-Anleitung](https://docs.github.com/en/actions/how-tos/administer/enforce-workflow-lockfiles) empfiehlt einen begrenzten Test und eine CODEOWNERS-Prüfung für `actions.lock`, bevor `Require lockfile` organisationsweit aktiv wird. Plan und Berechtigungen müssen passen. Die fixierte README weist außerdem darauf hin, dass GitHub Enterprise Server (GHES) in dieser Vorschau noch nicht unterstützt wird; Enterprise Cloud ist eine andere Umgebung.

Ein Lockfile bestimmt, **welcher Action-Code** ausgeführt werden soll. Es bewertet weder dessen Sicherheit noch Downloads innerhalb von `run:`, veränderliche Images, Paketabhängigkeiten oder die Berechtigungen des CI-Tokens. Ein erfolgreicher Pilot macht beantwortbar: Welche inneren Actions wurden neu fixiert, wer hat ihre Repository-Identität geprüft, und wer pflegt die Pins in gemeinsam genutzten Reusable Workflows? Zur anderen Frage, warum ein Sicherheitshinweis nicht automatisch einen Merge verhindert, gibt es den [GitHub-AI-Scan-Beitrag](/posts/github-ai-scan-pr-security-checks/).

---

*Hinweis zur Quellenlage und Zeit: GitHub stellte die Public Preview am 14. Oktober 2026 vor. Geprüft wurden der feste Quellstand `gh-actions-lock v0.2.0` (`d5dca22a06ff8590d50b41612038996800cfb336`) und das Format-Repository (`ac158834765237ee94c36606cfc7d87316aa49ab`) am 18. Oktober. Das YAML ist ein Parser-Test mit synthetischen Digests. Es wurden keine echten Runner-, CLI-Migrations- oder Tag-Umleitungsversuche ausgeführt. Die Verfügbarkeit hängt vom Tarif und der Kontokonfiguration ab.*
