When compiling workflows locally, the version comment in action pinning may flip between different versions that resolve to the same SHA. For example:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8may sometimes change to:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0This happens even though both v8 and v8.0.0 resolve to the same SHA (ed597411...).
The issue occurs because:
- Multiple Version References: Different workflows may reference the same action with different version tags (e.g.,
v8vsv8.0.0) - Dynamic Resolution: Each version tag is resolved independently via the GitHub API
- Cache Storage: Each unique version tag creates a separate cache entry
- Version Comment Source: The version comment in the compiled output comes from whichever version was requested, not a canonical version
Workflow A: uses actions/github-script@v8
→ Resolves to SHA ed597411...
→ Cache stores: actions/github-script@v8 → ed597411...
→ Lock file: uses: actions/github-script@ed597411... # v8
Workflow B: uses actions/github-script@v8.0.0
→ Resolves to SHA ed597411... (same SHA!)
→ Cache stores: actions/github-script@v8.0.0 → ed597411...
→ Lock file: uses: actions/github-script@ed597411... # v8.0.0
Next compile of Workflow A:
→ Cache has both v8 and v8.0.0
→ Deduplication keeps v8.0.0 (more precise)
→ But if v8 is requested, it gets resolved again
→ Lock file may now show: # v8.0.0 instead of # v8
Enable debug logging for action pinning components:
# Enable all action pinning logs
DEBUG=workflow:action_* gh aw compile
# Enable specific components
DEBUG=workflow:action_pins gh aw compile # Action pin resolution
DEBUG=workflow:action_cache gh aw compile # Cache operations
DEBUG=workflow:action_resolver gh aw compile # GitHub API resolution
# Enable multiple components
DEBUG=workflow:action_pins,workflow:action_cache gh aw compile
# Save debug output to file
DEBUG=workflow:action_* gh aw compile 2> debug.logThe action cache is stored at .github/aw/actions-lock.json. Examine it for duplicate entries:
# Pretty-print the cache
cat .github/aw/actions-lock.json | jq .
# Find entries for a specific action
cat .github/aw/actions-lock.json | jq '.entries | to_entries[] | select(.value.repo == "actions/github-script")'
# Check for duplicate SHAs
cat .github/aw/actions-lock.json | jq -r '.entries | to_entries[] | "\(.value.sha) \(.key)"' | sort | uniq -d -w 40Some actions have multiple version tags pointing to the same commit:
# Use gh CLI to check what v8 and v8.0.0 point to
gh api /repos/actions/github-script/git/ref/tags/v8 --jq '.object.sha'
gh api /repos/actions/github-script/git/ref/tags/v8.0.0 --jq '.object.sha'Check your workflow files for inconsistent version references:
# Find all uses of an action across workflows
grep -r "uses: actions/github-script@" .github/workflows/
# Extract just the version tags
grep -r "uses: actions/github-script@" .github/workflows/ | sed 's/.*@//' | sort | uniqWhen you run with DEBUG=workflow:action_*, look for these key messages:
workflow:action_pins Resolving action pin: repo=actions/github-script, version=v8
workflow:action_pins Attempting dynamic resolution for actions/github-script@v8
workflow:action_resolver Cache hit for actions/github-script@v8: ed597411...
workflow:action_pins Dynamic resolution succeeded: actions/github-script@v8 → ed597411...
workflow:action_cache Loading action cache from: .github/aw/actions-lock.json
workflow:action_cache Successfully loaded cache with 15 entries
workflow:action_cache Setting cache entry: key=actions/github-script@v8, sha=ed597411...
workflow:action_cache Deduplicating: keeping actions/github-script@v8.0.0, removing actions/github-script@v8
workflow:action_cache Deduplicated 1 entries, 14 entries remaining
The system logs warnings when cache keys don't match their stored version:
workflow:action_pins WARNING: Key/version mismatch in action_pins.json:
key=actions/github-script@v8 has version=v8 but pin.Version=v8.0.0
The action cache automatically deduplicates entries when saving. The deduplication logic:
- Groups by repo+SHA: Identifies entries that resolve to the same commit
- Keeps most precise: Retains the version with more specificity (e.g.,
v8.0.0overv8) - Removes others: Deletes less precise entries
Versions are ranked by precision:
v8.0.0 (most precise - 3 components)
v8.0 (medium - 2 components)
v8 (least precise - 1 component)
Ensure all workflows use the same version tag format:
# ✅ Good - consistent
uses: actions/github-script@v8.0.0
uses: actions/setup-node@v6.1.0
# ❌ Avoid - mixing precision levels
uses: actions/github-script@v8 # Some workflows
uses: actions/github-script@v8.0.0 # Other workflowsIf you suspect cache corruption, clear it and recompile:
# Remove the cache file
rm .github/aw/actions-lock.json
# Recompile all workflows
gh aw compilePin directly to SHAs in your workflow files:
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cdThis bypasses version tag resolution entirely.
If you control the repository, ensure pkg/workflow/data/action_pins.json uses canonical versions:
{
"entries": {
"actions/github-script@v8.0.0": {
"repo": "actions/github-script",
"version": "v8.0.0",
"sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd"
}
}
}If you're using shared workflows that reference actions differently than your local workflows:
- Adopt the version format from shared workflows
- Or update shared workflows to match your local format
- Ensure consistency across all workflow sources
CI environments may use a fresh cache on each run, while local development persists the cache:
- CI may show different version comments than local
- Solution: Commit
.github/aw/actions-lock.jsonto version control - This ensures consistent resolution across environments
Action maintainers may create new tags pointing to existing commits:
- Example:
v8andv8.0.0both added, pointing to same SHA - Cache may need cleanup when this happens
- Solution: Clear cache or wait for deduplication to converge
workflow:action_pins Resolving action pin: repo=actions/github-script, version=v8.0.0
workflow:action_cache Cache hit for actions/github-script@v8.0.0: ed597411...
workflow:action_pins Dynamic resolution succeeded
Interpretation: Action is in cache, no API call needed.
workflow:action_pins Resolving action pin: repo=actions/github-script, version=v8
workflow:action_cache Cache miss for key=actions/github-script@v8
workflow:action_resolver Querying GitHub API: /repos/actions/github-script/git/ref/tags/v8
workflow:action_resolver Successfully resolved actions/github-script@v8 to SHA: ed597411...
workflow:action_cache Setting cache entry: key=actions/github-script@v8, sha=ed597411...
Interpretation: New version tag being resolved and cached.
workflow:action_cache Deduplicating: keeping actions/github-script@v8.0.0, removing actions/github-script@v8
workflow:action_cache Deduplicated 1 entries, 14 entries remaining
Interpretation: Cache cleanup removed less precise version tags.
workflow:action_pins Dynamic resolution failed for actions/github-script@v8: API error
workflow:action_pins Falling back to hardcoded pins
workflow:action_pins Exact version match: requested=v8.0.0, found=v8.0.0
Interpretation: API unavailable, using embedded action_pins.json.
Create a style guide for your team:
## Action Version Format
All workflow files must use full semantic versioning for actions:
- ✅ `actions/checkout@v6.0.2`
- ✅ `actions/setup-node@v6.1.0`
- ❌ `actions/checkout@v6`
- ❌ `actions/setup-node@v6`Add .github/aw/actions-lock.json to version control:
git add .github/aw/actions-lock.json
git commit -m "chore: add action cache for consistent pinning"This ensures all developers and CI use the same resolved versions.
Add to your maintenance workflow:
# Monthly: clear and regenerate cache
rm .github/aw/actions-lock.json
gh aw compile
git add .github/aw/actions-lock.json
git commit -m "chore: refresh action cache"Add a pre-commit hook to detect inconsistent version formats:
#!/bin/bash
# Check for inconsistent action versions
if git diff --cached --name-only | grep -q '\.md$'; then
# Extract action uses
actions=$(git diff --cached --diff-filter=AM | grep -o 'uses: [^@]*@v[0-9]\+\s' || true)
if [ -n "$actions" ]; then
echo "Warning: Found actions using short version tags:"
echo "$actions"
echo "Consider using full semantic versions (e.g., v8.0.0 instead of v8)"
fi
fiFor deep debugging, trace the complete resolution path:
DEBUG='workflow:action_*' gh aw compile workflow.md 2>&1 | tee debug.log
# Then analyze:
grep "Resolving action pin" debug.log
grep "Dynamic resolution" debug.log
grep "Deduplicating" debug.logTrack cache evolution across compiles:
# Before
cp .github/aw/actions-lock.json before.json
# Compile
gh aw compile
# After
cp .github/aw/actions-lock.json after.json
# Compare
diff -u before.json after.jsonCheck for unexpected cache entries:
# List all SHAs with their version tags
jq -r '.entries | to_entries[] | "\(.value.sha) \(.value.version) \(.key)"' .github/aw/actions-lock.json | sort
# Find duplicate SHAs
jq -r '.entries | to_entries[] | .value.sha' .github/aw/actions-lock.json | sort | uniq -d- Action Pinning Architecture - Implementation details
- Action Cache Design - Cache structure and deduplication
- GitHub Actions Security - Why pin to SHAs
- Enabled debug logging:
DEBUG=workflow:action_* gh aw compile - Checked
.github/aw/actions-lock.jsonfor duplicate entries - Verified version tags point to same SHA via GitHub API
- Searched workflows for inconsistent version formats
- Cleared cache and recompiled:
rm .github/aw/actions-lock.json && gh aw compile - Checked for upstream version tag changes
- Reviewed action_pins.json for canonical versions
- Consulted team on preferred version format
- Committed action cache to version control
- Set up pre-commit hooks for version consistency
If the issue persists after following this guide:
- Capture debug logs:
DEBUG=workflow:action_* gh aw compile 2> debug.log - Export cache state:
cat .github/aw/actions-lock.json > cache.json - List workflow action references:
grep -r "uses: " .github/workflows/ > actions.txt - Create an issue with these artifacts attached
This debugging guide addresses:
- Version comment flipping between equivalent tags
- Cache inconsistencies in local development
- Action resolution behavior differences across environments