.png)
Lint Your GitHub Actions Workflows with actionlint


GitHub's new dependencies: section in workflow YAML locks direct and transitive action dependencies by commit SHA, making CI builds deterministic and reviewable.
Every package manager figured this out years ago. npm has its lockfile. Go has go.sum. Pip has requirements.txt with hashes. But GitHub Actions? Until now, your CI dependencies have been resolved at runtime from mutable references, with no built-in way to pin what actually runs.
That's changing. On March 26, 2026, GitHub published its 2026 Actions security roadmap, and the headline feature is workflow-level dependency locking. A new dependencies: section in your workflow YAML will lock every action, direct and transitive, to a specific commit SHA with cryptographic hash verification. Public preview is expected in 3-6 months, with GA following at 6 months.
This isn't a minor quality-of-life improvement. It's the foundational change that GitHub Actions has needed since the tj-actions/changed-files compromise proved how fragile mutable tags really are.
When you write uses: actions/checkout@v4 in a workflow, you're pointing at a mutable Git tag. The action's maintainer can move that tag to any commit at any time. A compromised maintainer account, a supply chain attack upstream, or even an accidental force-push can silently change what code your CI actually executes.
The security-conscious workaround has been manual SHA pinning: replacing @v4 with a full commit hash like @b4ffde65f46336ab88eb53be808477a3936bae11. Tools like Dependabot and Renovate can automate this, and it does work for direct dependencies.
But SHA pinning has a blind spot: transitive dependencies. Composite actions can reference other actions internally, and those nested references are invisible to you. You pin the top-level action, but whatever it calls downstream is still resolved at runtime from whatever mutable reference the action author used. You can't review it, audit it, or even see it without reading the action's source code.
This is exactly the gap that supply chain attackers have exploited.
GitHub's approach borrows directly from Go's module system. The new dependencies: section lives in your workflow YAML file itself, not in a separate lockfile. It contains commit SHAs and cryptographic hashes for every action your workflow depends on, including the transitive ones.
The workflow looks something like this:
name: CI
on: [push]
dependencies:
actions/checkout:
commit: b4ffde65f46336ab88eb53be808477a3936bae11
hash: sha256:a1b2c3d4e5f6...
actions/setup-node:
commit: 60edb5dd545a775178f52524783378180af0d1f8
hash: sha256:f6e5d4c3b2a1...
# Transitive deps from composite actions are included
actions/cache:
commit: 0c45773b623bea8c8e75f6c82b208c3cf94d9d67
hash: sha256:9a8b7c6d5e4f...
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4The uses: references in the job steps can stay as human-readable tags. The dependencies: block is what GitHub actually uses to resolve and verify at runtime. If the hash doesn't match, the workflow fails before any job executes.
The day-to-day workflow for maintainers looks like this:
SHA pinning was the best practice. We've recommended it, Dependabot automates it, and security-conscious teams have been doing it for years. But it's a workaround for a missing platform feature, and workarounds have limits.
The lockfile approach solves three things SHA pinning can't:
Transitive dependency visibility. When a composite action uses other actions internally, SHA pinning only covers the top-level reference. The lockfile flattens the entire dependency tree and locks every node. If a nested action changes, you see it in the diff.
Hash verification at runtime. A SHA pin says "use this commit." The lockfile adds "and verify the content hash matches." If someone rewrites Git history (force-push) at the same SHA, the hash check fails. This is the same defense-in-depth that go.sum provides: the commit reference gets you to the right version, and the hash proves the content hasn't been tampered with.
Fail-fast enforcement. With manual SHA pinning, if something goes wrong the workflow still runs (just with unexpected code). With lockfiles, a mismatch stops execution before any jobs start. No compromised code runs. No secrets get exposed. The build just fails, loudly.
If you've worked with any modern dependency manager, the concept is familiar. But the CI context introduces constraints that make this system distinct.
The closest analogue is Go's go.mod + go.sum, which GitHub explicitly cites as the inspiration. Like go.sum, the Actions lockfile stores content hashes alongside version references, so you get both identity (which commit) and integrity (is the content what we expect). npm's package-lock.json does something similar, recording exact versions and integrity hashes for the full dependency tree.
The key difference is where the lock data lives. npm and Go store lock data in separate files. GitHub chose to embed it directly in the workflow YAML. This makes sense for CI: a workflow file is the unit of execution, and keeping the lock data colocated means a single file review shows you exactly what will run. There's no chance of the lockfile and the workflow drifting apart because they're the same file.
Another difference: CI dependencies are Git repositories, not versioned package archives. There's no central registry with immutable tarballs. Actions are fetched from Git at specific commits. That means the lockfile has to work with Git's content-addressable storage model, using commit SHAs as the primary reference and adding content hashes as a second layer of verification.
The GitHub roadmap announcement explicitly references recent supply chain incidents. It's worth walking through how lockfiles would have changed the impact of two major ones.
Attackers compromised the tj-actions/changed-files action and modified it to dump CI runner memory, including secrets, to workflow logs. Because most users referenced the action by mutable tag, the compromised code propagated immediately. Any repository that ran a workflow after the tag was moved got the malicious version.
With a lockfile in place, the commit SHA and hash in the lock data wouldn't match the modified action. The workflow would fail immediately. No secrets dumped, no exfiltration. The blast radius drops to zero for any repository using locked dependencies.
The trivy-action compromise followed a similar pattern: attackers gained access to modify the action's code, and anyone referencing a mutable tag got the compromised version. The irony wasn't lost on anyone that a security scanning tool became the attack vector.
Again, locked dependencies with hash verification would have caught this at resolution time. The content hash wouldn't match, and the workflow would refuse to run.
In both cases, the key insight is the same: fail-fast behavior prevents exploitation. Today, a compromised action runs silently. With lockfiles, it triggers a build failure that teams notice immediately.
Public preview is 3-6 months out, so there's no YAML to write today. But there's plenty you can do now to be ready when it lands.
Audit your current action usage. Run a quick grep across your .github/workflows/ directories. How many actions are referenced by mutable tag vs. SHA? How many unique third-party actions do you depend on? This gives you the scope of the migration.
Start SHA pinning now if you haven't. Lockfiles will supersede manual pinning, but the discipline of pinning gets your team used to reviewing dependency changes as PR diffs. That review culture is what makes lockfiles effective, not just the tooling.
Identify composite actions in your dependency tree. These are the actions that have their own transitive dependencies. You'll want to know which ones pull in nested actions, because those are the references that are currently invisible and will become visible when you run the lockfile resolver.
Watch for the GitHub CLI update. Resolution happens through gh, so make sure your CI environments and developer machines have a recent version. Expect a new subcommand for dependency resolution when the preview launches.
Plan for workflow file size growth. Embedding lock data in the YAML means your workflow files will get longer. For workflows with many action dependencies, the dependencies: section could add 20-50 lines. Not a deal-breaker, but something to anticipate if you have workflows that are already pushing readability limits.
Lockfiles are the most impactful item on the 2026 roadmap, but they're not the only one. The same announcement covers scoped secrets (binding credentials to specific workflows and branches instead of broad repository access), policy-driven execution controls built on GitHub's ruleset framework, and a native egress firewall for hosted runners that operates at Layer 7.
Together, these features form a defense-in-depth strategy. Lockfiles prevent compromised dependencies from executing. Scoped secrets limit the blast radius if something does get through. Policy controls restrict who can trigger what. And the egress firewall blocks unauthorized outbound traffic from runners.
No single feature eliminates supply chain risk. But the combination of deterministic dependencies, scoped credentials, and network-level controls addresses the full attack chain that incidents like tj-actions demonstrated: compromised dependency → secret exfiltration → unrestricted network egress.
For platform teams managing Actions at scale, lockfiles are the right place to start. They're the most straightforward to adopt, they require no infrastructure changes, and they address the most frequently exploited weakness in CI dependency management. When the preview drops, prioritize it.
.png)
Lint Your GitHub Actions Workflows with actionlint

OpenTelemetry for GitHub Actions: Traces, OTLP, and Pipeline Flamegraphs

From Provision to Shutdown: The Lifecycle of a Tenki Runner
Get Tenki
Change 1 line of YAML for faster runners. Install a GitHub App for AI code reviews. No credit card, no contract. Takes about 2 minutes.