gh-actions-lockfile
Generate and verify lockfiles for GitHub Actions dependencies. Pins all actions (including transitive dependencies) to the exact commit SHAs with integrity hashes.
Why?
GitHub Actions has no native lockfile mechanism. Version tags like @v4 can be silently retagged, and composite actions pull in transitive dependencies you can't see. This tool fixes that.
See "GitHub Actions Has a Package Manager, and It Might Be the Worst" for more information.
Recommended Workflow
Step 1: Generate Your Initial Lockfile
Run an action in generate mode to create your lockfile:
name: Generate Lockfile on: workflow_dispatch jobs: generate: runs-on: ubuntu-latest permissions: # Gives the default GITHUB_TOKEN write permission to commit and push the # added or changed files to the repository. contents: write steps: - uses: actions/checkout@v6 - uses: gjtorikian/gh-actions-lockfile@v1 with: mode: generate - name: Commit lockfile # Commit the changed lockfile back to the repository uses: stefanzweifel/git-auto-commit-action@v7 # or, something like # run: | # git add .github/actions.lock.json # git commit -m "Add actions lockfile" # git push
Or, locally with the CLI:
npx gh-actions-lockfile generate
git add .github/actions.lock.json
git commit -m "Add actions lockfile"You really only need to generate this initial lockfile once, so choose whichever version makes the most sense.
Step 2: Verify on Every Action Run
Add verification to your CI workflow. If verification fails, the lockfile is automatically regenerated and committed to the PR:
name: Verify Actions # change this to whichever events matter to you on: [pull_request] permissions: pull-requests: write jobs: verify-actions: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: gjtorikian/gh-actions-lockfile@v1 with: mode: verify update-lockfile: needs: verify-actions if: failure() runs-on: ubuntu-latest permissions: # Gives the default GITHUB_TOKEN write permission to commit and push the # added or changed files to the repository. contents: write steps: - uses: actions/checkout@v6 with: ref: ${{ github.head_ref }} - uses: gjtorikian/gh-actions-lockfile@v1 with: mode: generate - uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "Update actions lockfile" file_pattern: ".github/actions.lock.json"
When you update an action version (e.g., actions/checkout@v4 to @v5), or if the action ref changes outside of your control, the verify job will fail, triggering the update job to regenerate and commit the lockfile to your PR automatically.
Manual Updates
If you prefer to update the lockfile locally instead of auto-committing via GitHub Actions, you can:
- Make your workflow changes
- Regenerate the lockfile:
npx gh-actions-lockfile generate
- Review the lockfile diff to confirm expected changes
- Commit both the workflow and lockfile changes together
When Verification Fails Unexpectedly
If verify fails, but you didn't change any actions, investigate:
- New dependency detected: A composite action you use added a new transitive dependency
- SHA mismatch: An upstream maintainer force-pushed or retagged a version (this is a potential supply chain concern)
- Integrity mismatch: The tarball content has changed for the same SHA (rare, but a serious supply chain concern)
- Missing action: An action was removed from your workflow but is still in the lockfile
For unexpected changes, review the upstream action's commit history before regenerating the lockfile.
Security Features
SHA Verification
When you run verify, the tool checks that all locked action refs still resolve to the same commit SHAs. This detects if an upstream maintainer has re-pointed a tag to a different commit (a supply chain concern known as "tag hijacking").
To skip SHA verification, use --skip-sha:
gh-actions-lockfile verify --skip-sha
Integrity Verification
The verify command also:
- Re-downloads each action's tarball from GitHub
- Computes the SHA256 hash of the tarball
- Compares it against the stored
integrityhash in your lockfile
If any hash mismatches, verification fails. This detects if an action's content has been modified after your lockfile was generated—even if the commit SHA hasn't changed.
To skip integrity checking (e.g., for faster CI runs), use --skip-integrity:
gh-actions-lockfile verify --skip-integrity
Security Advisory Checking
By default, the verify command checks your locked actions against the GitHub Advisory Database for known vulnerabilities.
When vulnerabilities are found, they are reported as failures:
Checking security advisories...
actions/cache@v3
GHSA-xxxx-yyyy-zzzz (HIGH)
Cache poisoning vulnerability in versions < 3.2.0
https://github.com/advisories/GHSA-xxxx-yyyy-zzzz
Found 1 action(s) with known vulnerabilities
To disable advisory checking:
gh-actions-lockfile verify --skip-advisories
SHA-Only Mode
For maximum security, you can enforce that all action references in your workflows use full 40-character commit SHAs instead of tags or branches:
gh-actions-lockfile generate --require-sha
This fails if any workflow uses a tag like @v4 instead of a full SHA like @b4ffde65f46336ab88eb53be808477a3936bae11.
Usage
GitHub Action (recommended)
Add this action to your workflow to verify the lockfile:
- uses: gjtorikian/gh-actions-lockfile@v1 with: mode: verify # or 'generate'
Action inputs:
| Input | Description | Default |
|---|---|---|
mode |
Mode to run in: generate or verify |
verify |
token |
GitHub token for API access | ${{ github.token }} |
workflows |
Path to workflows directory | .github/workflows |
output |
Path to lockfile | .github/actions.lock.json |
comment |
Post a PR comment when verification fails (verify mode only) | true |
require-sha |
Require all action refs to be full SHAs (generate mode only) | false |
skip-sha |
Skip SHA resolution verification (verify mode only) | false |
skip-integrity |
Skip integrity hash verification (verify mode only) | false |
skip-advisories |
Skip security advisory checking (verify mode only) | false |
When comment is enabled and verification fails in a pull request, the action posts a comment detailing what changed.
Via the CLI
Install globally via npm:
npm install -g gh-actions-lockfile
Then run:
# Generate a lockfile from your workflows gh-actions-lockfile generate # Verify workflows match the lockfile (exits 1 on mismatch) gh-actions-lockfile verify # Show dependency tree gh-actions-lockfile list
Or use npx without installing:
npx gh-actions-lockfile generate
Warning
When running locally, set a GITHUB_TOKEN environment variable to avoid rate limits. Without it, you're limited to 60 API requests per hour. A personal access token with no special scopes is sufficient for public repositories.
export GITHUB_TOKEN=ghp_your_token_hereCommands
generate
Generates (or updates) the lockfile. You'll always want to do this first.
verify
Verifies that the lockfile hasn't changed.
list
Visualizes the actions dependency structure, like:
actions.lock.json (generated 2025-12-15 21:57:33)
+-- actions/checkout@v6 (8e8c483db84b)
+-- gjtorikian/actions/setup-languages@main (923ecf42f98c)
| +-- ruby/setup-ruby@v1 (ac793fdd38cc)
| +-- actions/setup-node@v4 (49933ea5288c)
| +-- denoland/setup-deno@v1 (11b63cf76cfc)
| +-- dtolnay/rust-toolchain@master (0b1efabc08b6)
| +-- Swatinem/rust-cache@v2 (779680da715d)
+-- actions/cache@v4 (0057852bfaa8)
+-- actions/configure-pages@v4 (1f0c5cde4bc7)
+-- actions/upload-pages-artifact@v3 (56afc609e742)
| +-- actions/upload-artifact@v4 (ea165f8d65b6)
+-- actions/deploy-pages@v4 (d6db90164ac5)
+-- googleapis/release-please-action@v4 (16a9c90856f4)
Options
Global options (available on all commands):
| Option | Description | Default |
|---|---|---|
-w, --workflows <path> |
Path to workflows directory | .github/workflows |
-o, --output <path> |
Path to lockfile | .github/actions.lock.json |
-t, --token <token> |
GitHub token (or use GITHUB_TOKEN env var) |
- |
generate options:
| Option | Description | Default |
|---|---|---|
--require-sha |
Require all action refs to be full SHAs | false |
verify options:
| Option | Description | Default |
|---|---|---|
-c, --comment |
Post PR comment on verification failure | true |
--skip-sha |
Skip SHA resolution verification | false |
--skip-integrity |
Skip integrity hash verification | false |
--skip-advisories |
Skip security advisory checking | false |
Use --no-comment to disable PR comments when running in CI.
Lockfile Format
Development
Requires Node.js 24+ for building:
# Clone and install git clone https://github.com/gjtorikian/gh-actions-lockfile.git cd gh-actions-lockfile # Install dependencies npm install # Run in development npm start generate # Build for distribution npm run build # Type check npm run typecheck # Run tests npm test