Skip to content

fix(github_actions): Use tagged release commit for SHA-pinned refs without local tag#14718

Draft
scottschreckengaust wants to merge 4 commits intodependabot:mainfrom
scottschreckengaust:fix/sha-pinned-actions-use-tagged-commit
Draft

fix(github_actions): Use tagged release commit for SHA-pinned refs without local tag#14718
scottschreckengaust wants to merge 4 commits intodependabot:mainfrom
scottschreckengaust:fix/sha-pinned-actions-use-tagged-commit

Conversation

@scottschreckengaust
Copy link
Copy Markdown

@scottschreckengaust scottschreckengaust commented Apr 14, 2026

Summary

When a SHA-pinned GitHub Action ref has no matching version tag (local_tag_for_pinned_sha returns nil), latest_commit_sha previously fell through to latest_commit_for_pinned_ref which resolved to the HEAD of the containing branch — an untagged commit. This caused:

  • Updates pointing at untagged commits (often on main) instead of tagged release commits (often on releases/vN)
  • Version comments left stale and misleading (e.g., # v4.31.9 when the SHA is actually an unrelated main branch HEAD)
  • A self-perpetuating cycle: once an untagged SHA enters a workflow, every subsequent update takes the same wrong path

This is especially common with action repos using a release branch workflow like github/codeql-action (tags on releases/v4, development on main).

Changes

  • update_checker.rb: Simplified latest_commit_sha to always use new_tag.fetch(:commit_sha) when a latest version tag exists. Removed the now-unused source_checker parameter, the local_tag_for_pinned_sha gate, and the dead latest_commit_for_pinned_ref / find_container_branch methods.
  • package_details_fetcher.rb: Same fix — always return T.must(latest_version_tag).fetch(:version) for SHA-pinned refs. Removed dead methods and the now-unnecessary rubocop:disable Metrics/PerceivedComplexity directive.
  • update_checker_spec.rb: Added new test for the bug scenario. Consolidated the four identical "git commit SHA not pointing to tip of branch" sub-contexts (default branch, different branch, multiple branches with/without default) into a single test — since find_container_branch was removed, branch membership no longer affects behavior, and RuboCop flagged the repeated bodies (RSpec/RepeatedExampleGroupBody). Updated remaining tests to expect tagged versions instead of branch HEAD commits.
  • package_details_fetcher_spec.rb: Updated "realworld repository" and "tip of master" tests to expect tagged versions instead of branch HEAD commits.
  • latest_version_finder_spec.rb: Updated SHA-at-branch-tip test to expect tagged version instead of commit SHA.

Root Cause

# Before (buggy):
def latest_commit_sha(source_checker)
  new_tag = T.must(latest_version_finder).latest_version_tag
  return unless new_tag
  if source_checker.local_tag_for_pinned_sha
    new_tag.fetch(:commit_sha)       # Only when current SHA has a tag
  else
    latest_commit_for_pinned_ref     # BUG: returns untagged branch HEAD
  end
end

# After (fixed):
def latest_commit_sha
  new_tag = T.must(latest_version_finder).latest_version_tag
  return unless new_tag
  new_tag.fetch(:commit_sha)         # Always use the tagged release commit
end

The else branch was only reachable when new_tag already existed (we return nil before it), making it contradictory to ignore the resolved tag and return a branch HEAD instead.

Test plan

  • New test: SHA-pinned ref without local tag resolves to tagged release commit SHA
  • Updated existing tests for new behavior (tagged version instead of branch HEAD)
  • Consolidated repeated RSpec context bodies to fix RSpec/RepeatedExampleGroupBody lint
  • Wrapped nilable latest_version_tag with T.must() for Sorbet compliance
  • Removed stale rubocop:disable Metrics/PerceivedComplexity directive
  • update_checker_spec.rb: 83 examples, 0 failures
  • package_details_fetcher_spec.rb: 21 examples, 0 failures
  • latest_version_finder_spec.rb: passing
  • Manual verification with dependabot/cli against a repo using github/codeql-action SHA pins

Fixes #14716
Related: #7912, #8011, #13466, #14685

🤖 Generated with Claude Code

…thout local tag

When a SHA-pinned GitHub Action ref has no matching version tag
(local_tag_for_pinned_sha returns nil), the code previously fell through
to latest_commit_for_pinned_ref which resolved to the HEAD of the branch
containing the SHA. This caused updates to point at untagged commits
(often on main) instead of tagged release commits (often on releases/vN),
leaving version comments stale and misleading.

Now, when a latest version tag is found, always use its commit_sha
regardless of whether the current pinned SHA has a matching tag. Remove
the now-unused latest_commit_for_pinned_ref and find_container_branch
methods from both update_checker.rb and package_details_fetcher.rb.

Fixes: dependabot#14716

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the L: github:actions GitHub Actions label Apr 14, 2026
scottschreckengaust and others added 3 commits April 15, 2026 21:37
… test

- Wrap latest_version_tag with T.must() to satisfy Sorbet's nil check
- Remove now-unnecessary rubocop:disable Metrics/PerceivedComplexity
- Update latest_version_finder_spec to expect tagged version instead of
  branch HEAD commit for SHA-pinned refs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The four sub-contexts testing different branch scenarios for SHA refs
not at a branch tip now all have identical behavior (return latest tagged
version), since find_container_branch was removed. Consolidate into a
single test to fix RSpec/RepeatedExampleGroupBody offenses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L: github:actions GitHub Actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GitHub Actions: SHA-pinned actions updated to untagged branch HEAD commit when current SHA lacks a direct tag, leaving version comment stale

1 participant