Skip to content

fix: runtime guard for empty SHA in action pins + remove invalid ruby/setup-ruby@v1.319.0 entry#47048

Merged
pelikhan merged 6 commits into
mainfrom
copilot/aw-fix-setup-ruby-hash
Jul 21, 2026
Merged

fix: runtime guard for empty SHA in action pins + remove invalid ruby/setup-ruby@v1.319.0 entry#47048
pelikhan merged 6 commits into
mainfrom
copilot/aw-fix-setup-ruby-hash

Conversation

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

ruby/setup-ruby@v1.319.0 was added to action_pins.json with an empty SHA (SHA resolution failed at entry creation time). Since it was the newest version, the compiler selected it and emitted ruby/setup-ruby@ # v1.319.0 — invalid GitHub Actions YAML.

Changes

Data fix

  • Removed ruby/setup-ruby@v1.319.0 (empty SHA) from .github/aw/actions-lock.json and the synced pkg/actionpins/data/action_pins.json

Runtime guards

  • getActionPins(): panics at load time if any embedded entry has an empty SHA, including the offending keys in the message:
    action_pins.json has 1 entries with empty SHA ["ruby/setup-ruby@v1.319.0"] — these would produce invalid workflow YAML (e.g. 'owner/repo@ # version'); remove or fix these entries before releasing
    
  • FormatPinnedActionReference(): panics if called with an empty SHA — defense-in-depth guard that catches any code path bypassing the load-time check
  • collectEntriesWithEmptySHA(): new internal helper (replaces a simple count) that returns sorted keys for use in the panic message and tests

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · gpt54 7.8 AIC · ⌖ 10.3 AIC · ⊞ 7K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 21, 2026 09:34
…/setup-ruby@v1.319.0 entry

- Remove ruby/setup-ruby@v1.319.0 with empty SHA from .github/aw/actions-lock.json and pkg/actionpins/data/action_pins.json
- Add countEntriesWithEmptySHA validation function in pkg/actionpins/actionpins.go
- Panic in getActionPins() when any embedded entry has an empty SHA to prevent invalid compiled workflows
- Add panic guard in FormatPinnedActionReference when SHA is empty (defense-in-depth)
- Add tests for countEntriesWithEmptySHA and FormatPinnedActionReference empty SHA guard

Closes #46791

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…sage

- Rename countEntriesWithEmptySHA to collectEntriesWithEmptySHA (returns []string keys)
- Include the specific problematic entry keys in the panic message for actionability
- Update test names to be consistent with the codebase (PanicsWhenSHAIsEmpty)
- Improve test cases to verify sorted key output

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix invalid hash in ruby/setup-ruby configuration fix: runtime guard for empty SHA in action pins + remove invalid ruby/setup-ruby@v1.319.0 entry Jul 21, 2026
Copilot AI requested a review from pelikhan July 21, 2026 09:37
@pelikhan
pelikhan marked this pull request as ready for review July 21, 2026 09:38
Copilot AI review requested due to automatic review settings July 21, 2026 09:38
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #47048 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (58 additions detected).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents empty action-pin SHAs from generating invalid GitHub Actions references.

Changes:

  • Removes the invalid Ruby action pin from three synchronized datasets.
  • Adds load-time and formatting guards for empty SHAs.
  • Adds unit tests for detection, ordering, and panic behavior.
Show a summary per file
File Description
.github/aw/actions-lock.json Removes the invalid source pin.
pkg/actionpins/data/action_pins.json Updates embedded action-pin data.
pkg/workflow/data/action_pins.json Updates synchronized workflow data.
pkg/actionpins/actionpins.go Adds empty-SHA runtime guards.
pkg/actionpins/actionpins_internal_test.go Tests the new safeguards.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment on lines 245 to +250
// FormatPinnedActionReference formats a pinned action reference with repo, SHA, and version comment.
// Example: "actions/checkout@abc123 # v4.1.0"
func FormatPinnedActionReference(repo, sha, version string) string {
if sha == "" {
panic(fmt.Sprintf("FormatPinnedActionReference called with empty SHA for repo=%s version=%s — this would produce invalid workflow YAML", repo, version))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9d2aee1: the FormatPinnedActionReference doc comment now documents the empty-SHA panic contract and why it is treated as an invariant violation.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is correct and well-structured. Removing the empty-SHA entry from all three JSON copies and adding two complementary guards (load-time panic in getActionPins + call-site panic in FormatPinnedActionReference) gives solid defense-in-depth. Tests cover the new helper and the panic path cleanly.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 12.6 AIC · ⌖ 4.34 AIC · ⊞ 4.9K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — two minor observations, no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Test gap: collectEntriesWithEmptySHA and FormatPinnedActionReference are well-tested, but the call-site guard wiring inside getActionPins is not independently tested — a deletion of that if block would go undetected.
  • Panic as API: Using panic in FormatPinnedActionReference is a reasonable defence-in-depth choice given the upstream load-time guard, but it warrants an explicit code comment documenting the intent and tradeoff.

Positive Highlights

  • ✅ Root cause properly fixed (bad data removed from all three JSON files).
  • ✅ Two-layer defence (load-time + call-time) is a solid pattern.
  • ✅ Sorted output in collectEntriesWithEmptySHA makes panic messages deterministic — great for tests and diagnostics.
  • ✅ Test coverage for the new helper is thorough and readable.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 20 AIC · ⌖ 4.66 AIC · ⊞ 6.6K
Comment /matt to run again


func TestInitWarnings_InitializesAndPreservesMap(t *testing.T) {
t.Run("initializes nil warnings map", func(t *testing.T) {
ctx := &PinContext{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The load-time guard added to getActionPins() (the collectEntriesWithEmptySHA call inside getActionPins) has no direct test — only the helper itself is tested. If the if emptyKeys block in getActionPins were removed, the test suite would still pass.

💡 Suggested approach

Extract the load-from-JSON path into a small internal helper (or make the test inject a fixture JSON) so the integration between the guard and the loader can be covered:

func TestGetActionPins_PanicsOnEmptySHAInEmbeddedData(t *testing.T) {
    assert.Panics(t, func() { parseActionPins(jsonWithEmptySHA) })
}

This closes the gap between the helper tests and the call-site guard.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9d2aee1: I extracted the embedded JSON load path into loadActionPinsData and added a direct panic test for empty-SHA fixture data so the loader/guard wiring is covered.

@@ -227,6 +245,9 @@ func getLatestActionPinReference(repo string) string {
// FormatPinnedActionReference formats a pinned action reference with repo, SHA, and version comment.
// Example: "actions/checkout@abc123 # v4.1.0"
func FormatPinnedActionReference(repo, sha, version string) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnosing-bugs] Using panic for a defense-in-depth guard is a strong signal, but in a library loaded at startup it will crash the entire binary for any caller — including future callers who may never reach the formatting path. Consider returning an error instead (or logging + returning a sentinel string) so the host program can handle the case gracefully.

💡 Alternative signature
func FormatPinnedActionReference(repo, sha, version string) (string, error) {
    if sha == "" {
        return "", fmt.Errorf("empty SHA for %s@%s", repo, version)
    }
    return repo + "@" + sha + " # " + version, nil
}

The load-time guard in getActionPins already prevents empty-SHA entries from reaching this function in normal usage; the panic here only fires on programming errors, which makes the tradeoff reasonable — just worth calling out explicitly in a comment so future readers understand the intent.

@copilot please address this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept the panic contract in 9d2aee1, but documented the intent explicitly: embedded data is rejected at load time, and this call-site guard remains a defense-in-depth invariant check for programming errors or corrupted pin data.

@github-actions github-actions Bot mentioned this pull request Jul 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 4 test(s): 4 design, 0 implementation, 0 violation(s).

📊 Metrics (4 subtests)
Metric Value
Analyzed 4 (Go: 4, JS: 0)
✅ Design 4 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 4 (100%)
Duplicate clusters 0
Inflation ratio 1.25:1 (acceptable)
🚨 Violations 0
Test File Classification Coverage
returns empty slice for empty entries actionpins_internal_test.go:67–69 design_test edge case: empty input
returns empty slice when all entries have non-empty SHAs actionpins_internal_test.go:71–77 design_test happy path validation
returns key of entry with empty SHA actionpins_internal_test.go:79–85 design_test core functionality: single empty
returns sorted keys of multiple entries with empty SHA actionpins_internal_test.go:87–94 design_test sorting invariant: multiple empties

Verdict

Passed. 0% implementation tests (threshold: 30%).

Strengths

  • 100% design-focused: All 4 subtests verify behavioral contracts tied to the function signature change (countEntriesWithEmptySHA() → collectEntriesWithEmptySHA())
  • Comprehensive edge coverage: Empty input, all-valid inputs, single and multiple empty entries
  • Clear assertions: Proper use of assert.Empty() (collections) and assert.Equal() (exact values) with descriptive messages
  • Sorting validation: Explicitly tests the new slices.Sort() invariant (4th subtest)
  • No violations: Zero mocking libraries, proper //go:build !integration tag, all assertions have context messages
  • Healthy test-to-code ratio: 1.25:1 (well under 2:1 threshold)

🧪 Test quality analysis by Test Quality Sentinel · 17 AIC · ⌖ 7.32 AIC · ⊞ 7K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test Quality Sentinel: 100/100 — Excellent

0% implementation tests (threshold: 30%). All 4 subtests are design-focused, verifying behavioral contracts with comprehensive edge coverage (empty inputs, all-valid inputs, single and multiple empty entries). No code smell violations. Healthy test-to-code ratio of 1.25:1.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues should be addressed before merge

The data fix and load-time guard are sound. The FormatPinnedActionReference panic and the missing integration-level test for getActionPins() need attention.

Blocking findings

HIGH — panic in FormatPinnedActionReference: This is a public formatting utility, not a startup initializer. Panicking here is the wrong error-handling contract for a library function. Return (string, error) instead.

MEDIUM — getActionPins() panic not unit-tested: The collectEntriesWithEmptySHA helper is tested, but the wiring into getActionPins() is not. A refactor could silently break the guard with no test failure.

🔎 Code quality review by PR Code Quality Reviewer · 22.5 AIC · ⌖ 4.67 AIC · ⊞ 5.6K
Comment /review to run again

// FormatPinnedActionReference formats a pinned action reference with repo, SHA, and version comment.
// Example: "actions/checkout@abc123 # v4.1.0"
func FormatPinnedActionReference(repo, sha, version string) string {
if sha == "" {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

panic in a public library function will crash the caller with no recovery path. FormatPinnedActionReference is a formatting utility — panicking on empty SHA forces all callers to accept silent crashes or wrap every call in recover.

💡 Suggested fix

Return an error instead of panicking:

func FormatPinnedActionReference(repo, sha, version string) (string, error) {
	if sha == "" {
		return "", fmt.Errorf("FormatPinnedActionReference: empty SHA for repo=%s version=%s", repo, version)
	}
	return repo + "@" + sha + " # " + version, nil
}

Panicking is appropriate for truly unrecoverable programmer errors. An empty SHA in a data file is a recoverable data error. The load-time guard in getActionPins() already catches this case; this second panic adds no safety but does introduce risk if FormatPinnedActionReference is ever called outside that guarded flow (e.g., from tests with synthetic data, future callers that build pins manually).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept the existing panic in 9d2aee1 for the same reason as the load-time guard: emitting owner/repo@ # version is treated here as an invariant violation, not a recoverable formatting outcome. The public doc comment now makes that contract explicit.

Comment thread pkg/actionpins/actionpins.go Outdated
@@ -132,6 +132,10 @@ func getActionPins() []ActionPin {
actionPinsLog.Printf("Found %d key/version mismatches in action_pins.json", n)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No test covers the getActionPins() panic path — if a regression reintroduces an empty-SHA entry, the load-time guard is only validated by the integration behavior, not a unit test.

💡 Suggested fix

Add a test that calls getActionPins() with embedded data that contains an empty-SHA entry and asserts assert.Panics. The existing test TestCollectEntriesWithEmptySHA_ReturnsOnlyEmptySHAEntries covers only the helper — it does not verify that the panic is wired in correctly at the getActionPins() call site.

This matters because the two lines are coupled: if someone refactors getActionPins() to swap the check order or removes the panic call, the helper tests pass silently while the guard is gone.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 9d2aee1: there is now a direct empty-SHA loader test via loadActionPinsData, which exercises the guard path instead of only testing the helper in isolation.

@github-actions

This comment has been minimized.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address these unresolved review threads (newest first), and rerun checks once complete:

Failed checks to fix:

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • pi.dev

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "pi.dev"

See Network Configuration for more information.

Generated by 👨‍🍳 PR Sous Chef · gpt54 7.8 AIC · ⌖ 10.3 AIC · ⊞ 7K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Done in 9d2aee1. I ran the PR finisher pass, addressed the actionable review follow-up, and validated locally with make agent-report-progress, make test, and make recompile.

@github-actions

Copy link
Copy Markdown
Contributor

Triage

Field Value
Category bug
Risk medium
Score 64/100 (impact:30 urgency:20 quality:14)
Action fast_track

Rationale: Fixes invalid YAML emitted by compiler when action pin has empty SHA. Runtime guard + data fix. CI partially in-progress (12 checks), prior runs approved. Directly affects compilation correctness.

Generated by 🔧 PR Triage Agent · sonnet46 45.9 AIC · ⌖ 5.48 AIC · ⊞ 5.6K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot please run the pr-finisher skill, address these unresolved review threads (newest first), and rerun checks once complete:

9d2aee1 already covers the actionable review feedback. I re-ran local validation on the current HEAD: make fmt, make lint, make test-unit, make test, and make recompile all passed. The prior lint-go failure was stale on 69eff5d; the prior push_experiments_state failure was an experiment-branch rebase conflict in state.json, not a code issue in this PR. I can’t re-trigger CI from the agent, so a maintainer will need to re-run checks on the current HEAD.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix https://github.com/github/gh-aw/actions/runs/29830766280/job/88636339265

Addressed in 7b298aa. The lint-go failure on 9d2aee1 was panicinlibrarycode flagging loadActionPinsData, so I documented that internal panic contract, then re-ran make golint-custom LINTER_FLAGS='-panicinlibrarycode -test=false' LINTER_PACKAGES='./pkg/actionpins', make lint, and make agent-report-progress successfully.

@github-actions

Copy link
Copy Markdown
Contributor

Great work on this bug fix! 🎯

This PR looks ready for review. The fix cleanly addresses the invalid ruby/setup-ruby@v1.319.0 entry with an empty SHA that was causing invalid workflow YAML to be generated (issue #46791).

What I like:

  • ✅ Clear data fix — removed the corrupted entry from both action_pins.json files
  • ✅ Runtime guards — added panics at load time and in FormatPinnedActionReference() to catch this issue early and prevent recurrence
  • ✅ Good test coverage — new internal test for collectEntriesWithEmptySHA() and the panic conditions
  • ✅ Focused change — single issue, no mixed concerns
  • ✅ Well-documented — clear explanation of the problem, solution, and guards

The panic messages are actionable and will help catch any future data corruption. This is solid defensive programming.

Generated by ✅ Contribution Check · sonnet46 54.7 AIC · ⌖ 7.29 AIC · ⊞ 6.2K ·

@pelikhan
pelikhan merged commit cf48659 into main Jul 21, 2026
19 checks passed
@pelikhan
pelikhan deleted the copilot/aw-fix-setup-ruby-hash branch July 21, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[aw] No sha added to "ruby/setup-ruby@" in compiled workflow

4 participants