11 November 2026

GitHub Actions now supports workflow lockfiles.
Because GitHub Actions are fetched from Git, the previous advice was to pin to a full SHA instead of a tag. This is because tags are mutable and susceptible to supply-chain attacks where an attacker points a previously safe tag at malicious code.
For example, an old workflow looks like:
name: Lint
on: [push, pull_request, workflow_dispatch]
permissions: {}
env:
FORCE_COLOR: 1
RUFF_OUTPUT_FORMAT: github
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: j178/prek-action@44924fe9ef4a6f88253c4b6b1bf11c689d3ce53d # v3.1.0
Here’s how I set up lockfiles.
Install the gh-actions-lock extension to the gh CLI:
$ gh extension install github/gh-actions-lock
✓ Installed extension github/gh-actions-lock
In your repo, run:
$ gh actions-lock
✓ All 1 workflows valid
The workflow now uses vX.Y.Z tags and looks like this:
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 946971c..3503607 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ - uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- - uses: j178/prek-action@44924fe9ef4a6f88253c4b6b1bf11c689d3ce53d # v3.1.0
+ - uses: j178/prek-action@v3.1.0
And it generates a lockfile at .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/lint.yml':
- 'actions/checkout@v7.0.1'
- 'j178/prek-action@v3.1.0'
dependencies:
'actions/checkout@v7.0.1':
ref: 'v7.0.1'
commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1'
owner_id: 44036562
repo_id: 197814629
'j178/prek-action@v3.1.0':
ref: 'v3.1.0'
commit: 'sha1-44924fe9ef4a6f88253c4b6b1bf11c689d3ce53d'
owner_id: 10510431
repo_id: 1052387486
Commit modified workflows and the new lockfile.
Good news, both already support lockfiles! Your periodic updates will also update the lockfile. Remember to use cooldowns.
GitHub also recommends verifying lockfiles in CI to ensure it’s up-to-date and not corrupt.
They suggest copying this verify-lockfile.yml into your repo. I decided to put it into my existing lint workflow, but in its own job so it runs independently and in parallel:
name: Lint
on: [push, pull_request, workflow_dispatch]
permissions: {}
env:
FORCE_COLOR: 1
RUFF_OUTPUT_FORMAT: github
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- uses: j178/prek-action@v3.1.0
# Fails when .github/workflows/actions.lock is out of sync with the
# workflows or a locked pin no longer verifies upstream.
lockfile:
runs-on: ubuntu-slim
env:
GH_TOKEN: ${{ github.token }}
steps:
- uses: actions/checkout@v7.0.1
with:
persist-credentials: false
- run: gh extension install github/gh-actions-lock --pin v0.4.1
- run: gh actions-lock --verify
Note I’m using the lightweight ubuntu-slim runner here. Also, it’s pinning to v0.4.1; we’ll have to update that at some point or figure out a way to automate it. That’s for another time.
Don’t forget that we just modified the actions in a workflow, so need to relock with gh actions-lock. Nothing actually changes if adding to lint.yml because it’s already locking actions/checkout. But if you created a new file like verify-lockfile.yml, you’ll need to relock for that. The CI will let you know.
zizmor is a security linter for GitHub Actions and strongly recommended. If you don’t use it, please do.
Versions before v1.32.0 tell you to hash-pin actions, and v1.32.0 is the first to support lockfiles.
I use zizmor in prek, so add/update zizmor in .pre-commit-config.yaml:
- repo: https://github.com/zizmorcore/zizmor-pre-commit
rev: v1.32.0
hooks:
- id: zizmor
OK, commit and push!
Header photo: Slaymaker Barry Co. Sprocket Locks in The Bearings (vol. XII no. 23, 2nd January 1896) in the Smithsonian Libraries and Archives, with no known copyright restrictions.