Skip to content

Consolidate duplicate runs-on normalization helpers across config parsers - #52975

Merged
pelikhan merged 6 commits into
mainfrom
copilot/duplicate-code-runs-on-normalization
Aug 15, 2026
Merged

Consolidate duplicate runs-on normalization helpers across config parsers#52975
pelikhan merged 6 commits into
mainfrom
copilot/duplicate-code-runs-on-normalization

Conversation

Copilot AI commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

repo_config.go (aw.json) and safe_jobs.go (safe-outputs jobs) each carried their own independent runs-on parsing/rendering helpers, separate from the shared normalization logic already living in runs_on_snippet.go. This duplication meant runner-shape fixes could land in one parser and miss the others.

Changes

  • Moved shared runs-on types/helpers into runs_on_snippet.go: relocated RunsOnValue (+ its UnmarshalJSON), toRunsOnValue, isRunsOnArrayValue, and FormatRunsOn out of repo_config.go so all runs-on normalization/rendering code lives alongside the existing renderRunsOnSnippet/normalizeRunsOnSnippet helpers.
  • Centralized array-vs-scalar rendering decision: added formatSafeJobRunsOn, replacing the inline branching previously duplicated in safe_jobs.go's job-building loop.
  • Simplified safe_jobs.go: it now calls the single shared helper instead of re-implementing defaulting and array/scalar rendering logic.
// Before: inline branching in safe_jobs.go
if jobConfig.runsOnArray && len(jobConfig.RunsOn) > 0 {
    if len(jobConfig.RunsOn) == 1 && jobConfig.RunsOn[0] == "" {
        job.RunsOn = "runs-on: " + defaultRunsOn
    } else {
        job.RunsOn = c.indentYAMLLines(renderRunsOnSnippet([]string(jobConfig.RunsOn)), "    ")
    }
} else {
    job.RunsOn = "runs-on: " + FormatRunsOn(jobConfig.RunsOn, defaultRunsOn)
}

// After: single shared helper
job.RunsOn = c.indentYAMLLines(formatSafeJobRunsOn(jobConfig.RunsOn, jobConfig.runsOnArray, defaultRunsOn), "    ")

This is a pure code-organization refactor — no change to supported runs-on shapes or rendered output.


Run context: https://github.com/github/gh-aw/actions/runs/31914206398> Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.56 AIC · ⌖ 6.97 AIC · ⊞ 8.7K ·

Comment /souschef to run again

…pet.go

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor runs-on normalization to eliminate code duplication Consolidate duplicate runs-on normalization helpers across config parsers Aug 15, 2026
Copilot AI requested a review from pelikhan August 15, 2026 22:10
@pelikhan
pelikhan marked this pull request as ready for review August 15, 2026 22:30
Copilot AI balanced review requested due to automatic review settings August 15, 2026 22:30
@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

No test files were added or modified in this PR. Test Quality Sentinel skipped.

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #52975

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

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

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

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

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

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

Consolidates runs-on parsing and rendering helpers into a domain-focused module, though custom-job normalization remains separate.

Changes:

  • Moves shared RunsOnValue helpers into runs_on_snippet.go.
  • Extracts safe-job rendering into formatSafeJobRunsOn.
  • Simplifies safe-job construction.
Show a summary per file
File Description
pkg/workflow/safe_jobs.go Uses the extracted formatter.
pkg/workflow/runs_on_snippet.go Centralizes shared types and formatting helpers.
pkg/workflow/repo_config.go Removes relocated helper implementations.

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +114 to +118
func formatSafeJobRunsOn(runsOn RunsOnValue, runsOnArray bool, defaultRunsOn string) string {
// Keep []string{""} semantically unset, matching FormatRunsOn behavior.
if runsOnArray && len(runsOn) > 0 && (len(runsOn) != 1 || runsOn[0] != "") {
if snippet := renderRunsOnSnippet([]string(runsOn)); snippet != "" {
return snippet
@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-15T00:00:00Z
review_event: COMMENT
top_themes:
  - pure refactor; no changed behavior found
  - runs-on normalization/rendering remains covered by existing workflow tests
files_reviewed:
  - pkg/workflow/repo_config.go
  - pkg/workflow/runs_on_snippet.go
  - pkg/workflow/safe_jobs.go
comment_count: 0

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 5.95 AIC · ⌖ 5.33 AIC · ⊞ 6.9K ·
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.

Verdict

I did not find a blocking issue in the changed lines.

Highlights

This patch is a mechanical consolidation of existing runs-on parsing and rendering helpers into runs_on_snippet.go, plus a small call-site simplification in safe_jobs.go. I checked the moved logic against the deleted implementations and the behavior is preserved, including the array-vs-scalar handling and the []string{""} defaulting edge case. I also ran go test ./pkg/workflow/... and did not see regressions in the workflow package.

🔎 Code quality review by PR Code Quality Reviewer · gpt54 · 5.95 AIC · ⌖ 5.33 AIC · ⊞ 6.9K
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.

Skills-Based Review 🧠

Applied /tdd and /codebase-design — approving with one suggestion.

📋 Key Themes & Highlights

Positive Highlights

  • ✅ Clean elimination of duplicated runs-on logic — fixes are now guaranteed to propagate uniformly
  • formatSafeJobRunsOn correctly preserves the []string{""} semantic-unset edge case
  • indentYAMLLines wrapping the new helper is safe: it's a no-op on single-line strings, so the scalar path is unaffected
  • RunsOnValue, toRunsOnValue, isRunsOnArrayValue, and FormatRunsOn all belong in runs_on_snippet.go — good placement alongside their natural collaborators

Suggestion

  • formatSafeJobRunsOn has no dedicated unit test. The existing TestFormatRunsOn and the threat-detection integration tests provide some indirect coverage, but a direct table-driven test (like TestFormatRunsOn in repo_config_test.go) would lock in the array/scalar/default boundary behaviour. See inline comment for a skeleton.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 36.2 AIC · ⌖ 8.6 AIC · ⊞ 7.7K
Comment /matt to run again

// leading "runs-on:" key) for a safe-job, given its parsed RunsOnValue and
// whether the original configured value had array shape. This centralizes the
// array-vs-scalar rendering decision so callers don't need to special-case it.
func formatSafeJobRunsOn(runsOn RunsOnValue, runsOnArray bool, defaultRunsOn 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.

[/tdd] formatSafeJobRunsOn has no dedicated unit test — the previous inline logic was exercised through integration tests, but the new helper isn't tested in isolation, which reduces confidence in future changes.

💡 Suggested test skeleton
func TestFormatSafeJobRunsOn(t *testing.T) {
    def := "ubuntu-latest"
    tests := []struct {
        name        string
        runsOn      RunsOnValue
        runsOnArray bool
        want        string
    }{
        {"scalar single label", RunsOnValue{"custom"}, false, "runs-on: custom"},
        {"array empty-string falls back to default", RunsOnValue{""}, true, "runs-on: ubuntu-latest"},
        {"array multi-label yields YAML array", RunsOnValue{"self-hosted", "linux"}, true, "runs-on:\n  - self-hosted\n  - linux"},
        {"nil runsOn falls back to default", nil, false, "runs-on: ubuntu-latest"},
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := formatSafeJobRunsOn(tt.runsOn, tt.runsOnArray, def)
            assert.Equal(t, tt.want, got)
        })
    }
}

This mirrors the existing TestFormatRunsOn pattern in repo_config_test.go.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (117 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/52975-consolidate-runs-on-normalization-helpers.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR committed to your branch at docs/adr/52975-consolidate-runs-on-normalization-helpers.md — it was generated from the PR diff
  2. Complete the missing sections — add context the AI couldn't infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-52975: Consolidate runs-on Normalization Helpers into runs_on_snippet.go

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 52975-consolidate-runs-on-normalization-helpers.md for PR #52975).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 79 AIC · ⌖ 16.1 AIC · ⊞ 9K ·
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.

This is a clean consolidation refactor. The helpers RunsOnValue, toRunsOnValue, isRunsOnArrayValue, and FormatRunsOn have been correctly moved from repo_config.go into the more appropriate runs_on_snippet.go. The new formatSafeJobRunsOn helper correctly centralises the array-vs-scalar rendering logic.

The one subtle change — indentYAMLLines is now unconditionally applied in safe_jobs.go — is safe because the scalar/fallback path from formatSafeJobRunsOn always produces a single-line string, making indentYAMLLines a no-op for that path.

No blocking issues found.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 23.7 AIC · ⌖ 7.94 AIC · ⊞ 5.6K

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot Quick triage for maintainer-ready follow-up:

Please refresh the branch if needed, address the remaining maintainer-facing follow-up, and run the pr-finisher skill before handing this PR back to maintainers.

Outstanding review items (newest first):

Failed checks from the compact candidate set:

  • None listed.

Branch update was requested automatically for this run when GitHub allows it.
Run context: https://github.com/github/gh-aw/actions/runs/31914206398

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.56 AIC · ⌖ 6.97 AIC · ⊞ 8.7K ·
Comment /souschef to run again

Copilot AI and others added 2 commits August 15, 2026 23:27
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

Copilot AI commented Aug 15, 2026

Copy link
Copy Markdown
Contributor Author

@copilot Quick triage for maintainer-ready follow-up:

Please refresh the branch if needed, address the remaining maintainer-facing follow-up, and run the pr-finisher skill before handing this PR ba...

Added dedicated unit tests for formatSafeJobRunsOn covering the array/scalar/default boundary cases (nil, empty array, single empty-string element, scalar, single-/multi-element array shape, and multi-element scalar-mode fallback).

Copilot AI requested a review from gh-aw-bot August 15, 2026 23:29
@pelikhan
pelikhan merged commit f36a578 into main Aug 15, 2026
29 checks passed
@pelikhan
pelikhan deleted the copilot/duplicate-code-runs-on-normalization branch August 15, 2026 23:52
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.87.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[duplicate-code] Duplicate Code: Divergent runs-on normalization across config parsers

4 participants