Locking your workflows' dependencies ensures that the actions you reference contain the same code that was originally reviewed. For more information, see Workflow dependency lockfiles.
1. Check your action references
Creating a lockfile pins your dependencies to commits based on the current branch or tag reference. Make sure these are the versions you want to lock in place.
Check that each uses reference in your workflows points to a supported, current version of the action. For best practices, see Secure use reference.
If any of your workflows call a reusable workflow in another repository, check those references too. A reusable workflow's dependencies are covered by the lockfile of the repository where it's defined, not by your lockfile. That source repository needs its own lockfile for full coverage. If your organization maintains shared workflows, onboard those repositories first. See Reusing workflow configurations.
We recommend letting Dependabot manage your GitHub Actions dependencies. If it's not already enabled, enable it before you create the lockfile. See Keeping your actions up to date with Dependabot.
2. Install the actions-lock extension
-
Install the GitHub CLI.
-
Install the extension.
Bash gh extension install github/gh-actions-lock
gh extension install github/gh-actions-lock
Tip
To update the extension later, run gh extension upgrade gh-actions-lock.
3. Generate the lockfile
From the root of your repository, run the extension with no arguments. It will scan every workflow under .github/workflows/, pin each resolvable action to a commit SHA, and write the lockfile.
gh actions-lock
gh actions-lock
To onboard a single workflow instead of the whole directory, pass its path.
gh actions-lock .github/workflows/ci.yml
gh actions-lock .github/workflows/ci.yml
The extension never follows or locks a local path action (uses: ./…). A ./ path runs whatever is checked out in the runner's workspace, so the extension can't pin it or see what it uses. A $/ path runs your repository's own action at the workflow's commit, so it's pinned and the actions it uses can be locked. For a comparison of the two, see Workflow syntax for GitHub Actions.
If a local action in your checkout uses other actions, the extension reports a local-action error and writes nothing, because those actions would fail at run time without lockfile entries. To lock the local action as a same-repository reference, run:
gh actions-lock --migrate-local-actions
gh actions-lock --migrate-local-actions
To keep the ./ reference and report it as a warning instead, pass --ignore-local-actions. See Keeping a local path action.
For the full set of options, run gh actions-lock --help. For help with a specific error or warning, see the gh-actions-lock troubleshooting guide.
Keeping a local path action
If you pass --ignore-local-actions, what happens at run time depends on the actions that the local action uses:
- It uses no other actions, for example only
run:steps: The workflow runs normally. - It uses actions that have lockfile entries for the same workflow: The workflow runs normally.
- It uses an action that has no lockfile entry for that workflow: The job fails when it reaches the local action's step, with an error such as
lockfile missing pin for octo-org/setup-tool@v2.0.0. Earlier steps have already run.
We recommend --migrate-local-actions instead, so the action runs as a pinned $/ reference. If you need to keep the ./ reference, give the workflow lockfile entries for the actions that the local action uses. Add a composite action that uses the same actions, with the same references, and call it from the workflow in a step that never runs. Then run gh actions-lock --ignore-local-actions.
# .github/actions/lock-local-deps/action.yml
name: Lock local action dependencies
description: Never runs. Lists the actions that ./.github/actions/build uses.
runs:
using: composite
steps:
- uses: octo-org/setup-tool@v2.0.0
# In the workflow
steps:
- uses: actions/checkout@v6
- if: false
uses: $/.github/actions/lock-local-deps
- uses: ./.github/actions/build
The entries must be recorded for the same workflow. An entry recorded only for a different workflow doesn't count. When the local action's dependencies change, update the composite action and run gh actions-lock --ignore-local-actions again.
4. Review the changes and commit
The extension changes more than the lockfile, so review the full diff before you commit. It also rewrites your workflow uses references to the specific version it resolved, so a reference such as octo-org/deploy-action@v3 can become octo-org/deploy-action@v3.1.0. This makes the reference more specific without changing the commit it resolves to.
Important
If your repository, organization, or enterprise only allows specified actions, check the allowlist before you commit. After a workflow is onboarded, the allowlist is checked against locked commit SHAs instead of tags, because a tag can be moved to different code but a locked SHA can't. Entries pinned to a tag, such as actions/checkout@v6, no longer match once a workflow is onboarded. Use a wildcard or the full commit SHA instead. See Managing GitHub Actions settings for a repository.
If you passed --migrate-local-actions, the extension also rewrites references to actions in your own repository, from uses: ./… to uses: $/…, in your workflows and your repository's action.yml files. It only does this when you pass the option, because the two forms behave differently. A ./ path resolves against whatever is in the runner's workspace, while a $/… reference always runs at the workflow's own commit, so it needs no lockfile entry. Review each rewritten line.
5. Keep the lockfile current
Keep the lockfile up to date with your repository's workflow files and security updates.
-
Re-run the extension when you change dependencies. Run
gh actions-lockagain whenever you add a workflow that hasusesdependencies, or add or removeusesdependencies in an existing workflow. -
Let Dependabot handle version bumps. When Dependabot opens a version update for an action in a workflow you've onboarded, it also regenerates the matching lockfile entry in the same pull request. Your workflow file and lockfile can't drift apart.
Dependabot only updates lockfile entries for workflows that are already onboarded. If it bumps an action in a workflow that isn't in the lockfile, it updates the workflow file as usual and leaves the lockfile unchanged.
-
Re-resolve references that have moved. How the extension handles a moved reference depends on the reference:
- A branch or partial version, such as
mainorv3, keeps its locked commit, and the extension reports aref-movedwarning. To advance it, check the upstream history, then rungh actions-lock --relock. - A full version tag, such as
v3.1.0, must point at its locked commit. If the tag moved, the extension reports an error until you rungh actions-lock --relockor change theuses:line. - A rewritten history, where the locked commit is no longer in the reference's history, for example after a force-push, is an error that
--relockdoesn't fix. After you've reviewed the upstream history, rungh actions-lock --accept-moved. Don't use--accept-movedin automation.
- A branch or partial version, such as
Reviewing lockfile changes
Review changes to the lockfile like any other dependency change:
- A changed
commitis the code your workflows will run. Treat it the way you'd treat any other dependency version bump. - A changed
owner_idorrepo_idshould be rare. It means the repository behind that name is not the one you originally locked, so investigate before you merge.
If a lockfile looks wrong, fix your workflow files and regenerate it. Never edit the lockfile manually.
Next steps
Once you have generated a lockfile for a repository, ensure it's enforced and up to date without developers needing to remember to run commands. See Enforcing lockfiles for action dependencies.