Summary
Since the auto-pin change in #40475 (shipped in v0.80.x), gh aw compile fails for any workflow whose steps reference a local composite action via a relative path, e.g.:
steps:
- uses: ./.github/actions/my-setup
Compilation aborts with:
error: imported steps: unversioned action "./.github/actions/my-setup" has no available pin; add a @ref (e.g. @v1) or include it in action-pins.json
This worked on the prior release. Local actions compiled fine before the new "fail when no pin is available" behavior was added.
Why this is a regression (and why the suggested fixes don't apply)
#40475 was intended to catch unversioned remote actions (e.g. uses: actions/checkout with no @ref) and auto-pin them, failing only when no pin exists. But the check does not special-case local actions, which are a first-class, documented GitHub Actions feature.
Neither remediation in the error message is valid for a local action:
- "add a
@ref" — local action paths cannot carry a ref. uses: ./path@v1 is invalid GitHub Actions syntax; local actions are resolved from the repository at the workflow's own checked-out SHA.
- "include it in action-pins.json" — pins are keyed by
owner/repo + version + commit SHA. A local action has no upstream repo and no independent SHA to pin to, so there is no meaningful entry to add.
So local composite actions currently have no way to compile under v0.80.x.
Root cause (code pointer)
In pkg/workflow/action_pins.go, applyActionPinToTypedStep calls extractActionRepo(step.Uses) / extractActionVersion(step.Uses). For uses: ./.github/actions/my-setup:
ExtractRepo (in pkg/actionpins/actionpins.go) does strings.Cut(uses, "@"); with no @, it returns the whole string (the local path) as the "repo" — non-empty.
ExtractVersion returns "".
- With an empty version, the code resolves a pin via
getCachedActionPin, gets "" (no embedded/cached pin for a local path), and returns the new hard error.
There's no guard for uses: values that are local action references (./..., ../...) or Docker refs (docker://...).
Minimal repro
-
Create a local composite action at .github/actions/my-setup/action.yml:
name: My Setup
description: Example local composite action
runs:
using: composite
steps:
- shell: bash
run: echo "hello"
-
Reference it from a workflow's steps (directly, or via an imported shared component's steps:):
steps:
- uses: ./.github/actions/my-setup
-
Run gh aw compile.
Expected: compiles; the local uses: ./... is emitted as-is (it's already valid Actions syntax and inherently tied to the repo SHA).
Actual: compilation fails with unversioned action "./.github/actions/my-setup" has no available pin; ....
Suggested fix
In applyActionPinToTypedStep (and the typed-steps variant), skip pin resolution entirely when step.Uses is a local action reference — i.e. starts with ./ or ../ — and emit it unchanged. (Docker docker:// references likely warrant the same passthrough.) Local actions are valid, ref-less by design, and cannot/should not be pinned.
Environment
Summary
Since the auto-pin change in #40475 (shipped in v0.80.x),
gh aw compilefails for any workflow whose steps reference a local composite action via a relative path, e.g.:Compilation aborts with:
This worked on the prior release. Local actions compiled fine before the new "fail when no pin is available" behavior was added.
Why this is a regression (and why the suggested fixes don't apply)
#40475 was intended to catch unversioned remote actions (e.g.
uses: actions/checkoutwith no@ref) and auto-pin them, failing only when no pin exists. But the check does not special-case local actions, which are a first-class, documented GitHub Actions feature.Neither remediation in the error message is valid for a local action:
@ref" — local action paths cannot carry a ref.uses: ./path@v1is invalid GitHub Actions syntax; local actions are resolved from the repository at the workflow's own checked-out SHA.owner/repo+ version + commit SHA. A local action has no upstream repo and no independent SHA to pin to, so there is no meaningful entry to add.So local composite actions currently have no way to compile under v0.80.x.
Root cause (code pointer)
In
pkg/workflow/action_pins.go,applyActionPinToTypedStepcallsextractActionRepo(step.Uses)/extractActionVersion(step.Uses). Foruses: ./.github/actions/my-setup:ExtractRepo(inpkg/actionpins/actionpins.go) doesstrings.Cut(uses, "@"); with no@, it returns the whole string (the local path) as the "repo" — non-empty.ExtractVersionreturns"".getCachedActionPin, gets""(no embedded/cached pin for a local path), and returns the new hard error.There's no guard for
uses:values that are local action references (./...,../...) or Docker refs (docker://...).Minimal repro
Create a local composite action at
.github/actions/my-setup/action.yml:Reference it from a workflow's steps (directly, or via an imported shared component's
steps:):Run
gh aw compile.Expected: compiles; the local
uses: ./...is emitted as-is (it's already valid Actions syntax and inherently tied to the repo SHA).Actual: compilation fails with
unversioned action "./.github/actions/my-setup" has no available pin; ....Suggested fix
In
applyActionPinToTypedStep(and the typed-steps variant), skip pin resolution entirely whenstep.Usesis a local action reference — i.e. starts with./or../— and emit it unchanged. (Dockerdocker://references likely warrant the same passthrough.) Local actions are valid, ref-less by design, and cannot/should not be pinned.Environment
usesrefs in compiler; fail compilation when no pin is available #40475steps:) referencing a local composite action viauses: ./...