Skip to content

Enable runner-group runs-on for safe-outputs.jobs (and runner alias) - #53725

Closed
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/fix-runs-on-rejects-runner-group
Closed

Enable runner-group runs-on for safe-outputs.jobs (and runner alias)#53725
pelikhan with Copilot wants to merge 5 commits into
mainfrom
copilot/fix-runs-on-rejects-runner-group

Conversation

Copilot AI commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

safe-outputs.jobs.<id>.runs-on accepted only string/array forms, unlike the rest of the workflow surface, so runner-group-only fleets could not use custom safe-jobs. This change aligns schema, parsing, rendering, and validation so custom safe-jobs support the same runs-on forms as top-level and safe-output jobs.

  • Schema alignment

    • safe-outputs.jobs.<id>.runs-on and safe-outputs.jobs.<id>.runner now reference #/$defs/github_actions_runs_on (string, array, object with group/labels).
  • Safe-job rendering fix

    • Preserves and renders raw runs-on shape for safe-jobs, including object form.
    • Eliminates the previous object-form fallthrough that could omit runs-on from emitted safe-job YAML.
  • Validation coverage parity

    • Extends runs-on value validation and macOS guard checks to:
      • safe-outputs.jobs.<id>.runs-on
      • safe-outputs.jobs.<id>.runner
  • Targeted coverage and docs

    • Adds safe-job runs-on variant coverage (string, array, object group-only, object group+labels, alias, default/unset).
    • Adds compile-path assertion for emitted grouped runner YAML in safe-jobs.
    • Updates runner/safe-outputs reference docs to explicitly include custom safe-job object-form support.
safe-outputs:
  jobs:
    notify:
      runs-on:
        group: sj-group
        labels: [linux]
      inputs:
        msg:
          description: Message text
      steps:
        - run: echo "${{ inputs.msg }}"

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix runs-on rejects runner-group object form in safe-jobs Enable runner-group runs-on for safe-outputs.jobs (and runner alias) Aug 18, 2026
Copilot AI requested a review from pelikhan August 18, 2026 15:26
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add exhaustive test suite

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

Copilot AI commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@copilot add exhaustive test suite

Addressed in ee234ae8f0 with expanded, exhaustive coverage for safe-job runs-on and runner alias forms, including parser/build variants, compile-path assertions, and validation permutations.

@pelikhan
pelikhan marked this pull request as ready for review August 18, 2026 16:46
Copilot AI balanced review requested due to automatic review settings August 18, 2026 16:46
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Reviewed PR #53725; no actionable review comments to post because the change is consistent and no correctness/performance/security issues were found in the diff.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Ponytail Reviewer completed successfully!

Generated by Ponytail Reviewer for #53725

@github-actions

github-actions Bot commented Aug 18, 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 18, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Aug 18, 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

@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 implementation is clean and correct. Runner-group object form now works for safe-outputs.jobs runs-on (and the runner alias) by storing rawRunsOn before lossy conversion and using resolveSafeJobRunsOn to render it. Validation is consistently extended across both validation paths with good test coverage. LGTM.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 17.9 AIC · ⌖ 8.78 AIC · ⊞ 5.7K

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

reviewed_at: 2026-08-18T00:00:00Z
review_event: COMMENT
top_themes:
  - safe-jobs runs-on object-form parity
  - validation/schema/rendering alignment
  - tests added for object/group/labels variants
files_reviewed:
  - docs/src/content/docs/reference/safe-outputs.md
  - docs/src/content/docs/reference/self-hosted-runners.md
  - pkg/parser/schemas/main_workflow_schema.json
  - pkg/workflow/frontmatter_parsing.go
  - pkg/workflow/runs_on_snippet.go
  - pkg/workflow/runs_on_validation.go
  - pkg/workflow/safe_jobs.go
  - pkg/workflow/runs_on_validation_test.go
  - pkg/workflow/safe_jobs_test.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 · 10.2 AIC · ⌖ 6.94 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.

Skills-Based Review 🧠

Applied /diagnosing-bugs and /tdd — requesting changes on two issues; one non-blocking documentation suggestion.

📋 Key Themes & Highlights

Key Themes

  1. Error messages lack job-name context (frontmatter_parsing.go L52–63): validateRunsOnValue errors don't include the job name, so a malformed group: 42 would surface as "runs-on.group has type int" with no pointer to which job failed. The validateRunsOn function in the same file already uses qualified field names — this block should follow that pattern.

  2. Fallback branch in resolveSafeJobRunsOn is undocumented (runs_on_snippet.go L125–130): when exactly does the fallback to formatSafeJobRunsOn fire? Adding a comment (or a targeted test) clarifies the intent for future readers.

  3. Missing validation test for both runs-on + runner set simultaneously (safe_jobs_test.go): precedence is tested at render time but not at validation time — confirm that setting both fields still validates the runner alias independently.

Positive Highlights

  • rawRunsOn field is a clean, minimal fix that preserves the original shape through the rendering pipeline without structural changes.
  • resolveSafeJobRunsOn correctly layers raw-form priority over the legacy formatSafeJobRunsOn path.
  • ✅ Comprehensive test matrix: all runs-on variants (string, array, group-only object, group+labels object, runner alias) are covered at both the unit and compile/lock-file level.
  • ✅ Schema alignment with $ref: "#/$defs/github_actions_runs_on" is minimal and correct.
  • ✅ Documentation in safe-outputs.md and self-hosted-runners.md is clear and includes a working example.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 46.3 AIC · ⌖ 10.4 AIC · ⊞ 7.8K
Comment /matt to run again

Comment thread pkg/workflow/frontmatter_parsing.go Outdated
}
if err := validateRunsOnValue(jobMap["runs-on"]); err != nil {
return nil, err
}

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] validateRunsOnValue errors in this new block lack job-name context — a malformed runs-on (e.g. group: 42) surfaces as "runs-on.group has type int" with no pointer to which job failed, making the error harder to act on.

💡 Suggested fix

Iterate with the job name in scope and wrap errors with the qualified field path:

for jobName, jobRaw := range jobsRaw {
    jobMap, ok := jobRaw.(map[string]any)
    if !ok {
        continue
    }
    if err := validateRunsOnValue(jobMap["runs-on"]); err != nil {
        return nil, fmt.Errorf("safe-outputs.jobs.%s.runs-on: %w", jobName, err)
    }
    if err := validateRunsOnValue(jobMap["runner"]); err != nil {
        return nil, fmt.Errorf("safe-outputs.jobs.%s.runner: %w", jobName, err)
    }
}

This matches the error message style used in validateRunsOn (safe-outputs.jobs.<id>.runs-on).

@copilot please address this.

Comment thread pkg/workflow/runs_on_snippet.go Outdated
func resolveSafeJobRunsOn(jobConfig *SafeJobConfig, defaultRunsOn string) string {
if snippet := renderRunsOnSnippet(jobConfig.rawRunsOn); snippet != "" {
return snippet
}

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] resolveSafeJobRunsOn falls back to formatSafeJobRunsOn when renderRunsOnSnippet returns "", but renderRunsOnSnippet also returns "" for non-empty string values (because isEmptyRunsOnValue returns false but the YAML path encodes them normally). That means a plain string rawRunsOn does get rendered by renderRunsOnSnippet — but it would be good to add a test that explicitly covers the fallback branch to prove it isn't reachable in practice (or document when it is).

💡 Suggested clarification

Add a comment to resolveSafeJobRunsOn that explains exactly when the fallback fires (i.e. only for nil/empty rawRunsOn when nothing was configured), so future readers don't need to trace the logic:

// resolveSafeJobRunsOn renders the runs-on YAML snippet for a safe job.
// It prefers rawRunsOn (set during parsing) to preserve the original shape,
// including object-form runner groups. The fallback is reached only when
// rawRunsOn is nil/empty (no runs-on or runner key was set).
func resolveSafeJobRunsOn(jobConfig *SafeJobConfig, defaultRunsOn string) string {

@copilot please address this.

Comment thread pkg/workflow/safe_jobs_test.go Outdated
},
expected: "runs-on:\n group: runner-group",
},
{

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 test "runs-on takes precedence over runner alias" (line ~510) includes a runner with labels: ["windows"] — yet windows isn't a macOS label and would pass validation. The precedence rule is tested at the rendering level, but there's no test confirming that when both runs-on and runner are set in frontmatter, validation is only applied to runs-on (i.e. the runner value isn't double-counted or ignored). Adding a validation-layer test for this combination would close that gap.

💡 Suggested test case for runs_on_validation_test.go
{
    name: "runs-on takes precedence; runner alias is also validated independently",
    frontmatter: map[string]any{
        "safe-outputs": map[string]any{
            "jobs": map[string]any{
                "notify": map[string]any{
                    "runs-on": "ubuntu-latest",
                    "runner":  "macos-latest", // runner alias still validated
                },
            },
        },
    },
    wantErr:    true,
    errorInMsg: "safe-outputs.jobs.notify.runner",
},

@copilot please address this.

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

Adds runner-group object support to custom safe-output jobs.

Changes:

  • Aligns safe-job schema, parsing, rendering, and validation.
  • Adds variant and compilation tests.
  • Documents object-form runs-on and runner.
Show a summary per file
File Description
pkg/workflow/safe_jobs.go Preserves raw runner configuration.
pkg/workflow/safe_jobs_test.go Tests supported runner forms.
pkg/workflow/runs_on_validation.go Validates safe-job runners.
pkg/workflow/runs_on_validation_test.go Tests validation and macOS guards.
pkg/workflow/runs_on_snippet.go Renders raw safe-job runner shapes.
pkg/workflow/frontmatter_parsing.go Validates safe-job runner values.
pkg/parser/schemas/main_workflow_schema.json Allows shared runs-on forms.
docs/src/content/docs/reference/self-hosted-runners.md Documents supported locations.
docs/src/content/docs/reference/safe-outputs.md Adds safe-job runner-group example.

Review details

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

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

Comment thread pkg/workflow/runs_on_validation.go Outdated
Comment on lines +66 to +71
if runsOn, exists := jobMap["runs-on"]; exists {
runsOnFields = append(runsOnFields, runnerField{name: "safe-outputs.jobs." + jobName + ".runs-on", value: runsOn})
}
if runner, exists := jobMap["runner"]; exists {
runsOnFields = append(runsOnFields, runnerField{name: "safe-outputs.jobs." + jobName + ".runner", value: runner})
}
@github-actions github-actions Bot mentioned this pull request Aug 18, 2026

@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.

Ponytail review: three inline findings, mostly redundant state (rawRunsOn plus the older RunsOn/runsOnArray fields) and duplicated validation loops that could collapse into a shared helper.

net: -25 lines possible.

Generated by ✂️ Ponytail Reviewer for #53725 · auto · 99.5 AIC · ⌖ 6.92 AIC · ⊞ 7.3K
Comment /ponytail to run again

Comments that could not be inline-anchored

pkg/workflow/safe_jobs.go:362

yagni: rawRunsOn duplicates RunsOn+runsOnArray, now three fields storing the same source config. resolveSafeJobRunsOn only falls back to the old pair when the raw-snippet render is empty. Drop RunsOn/runsOnArray and formatSafeJobRunsOn, always render from rawRunsOn.

pkg/workflow/runs_on_snippet.go:125

yagni: two-tier fallback (renderRunsOnSnippet(rawRunsOn) then formatSafeJobRunsOn(RunsOn, runsOnArray, ...)) for one job's runner config. renderRunsOnSnippet already handles string/array/object/empty shapes; the second branch only exists to serve the redundant RunsOn/runsOnArray fields being removed above.

pkg/workflow/frontmatter_parsing.go:54

yagni: this jobs map iteration validating runs-on/runner per job duplicates the near-identical loop added in runs_on_validation.go (validateRunsOn, building runnerFields for the macOS check). Extract one shared "for each safe-job, get runs-on/runner values" helper used by both.

Documents the architectural decision to align safe-outputs.jobs.<id>.runs-on
and runner with the full github_actions_runs_on schema shape.
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot safe-outputs.jobs..runner should be deprecated in favor of safe-outputs.jobs..runs-on . Add codemod to fix.

Copilot AI commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@copilot safe-outputs.jobs..runner should be deprecated in favor of safe-outputs.jobs..runs-on . Add codemod to fix.

Could you clarify the requested change for safe-outputs.jobs?

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot remove support for safe-outputs.jobs..runner . only safe-outputs.jobs..runs-on should be supported.

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

Copilot AI commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@copilot remove support for safe-outputs.jobs..runner . only safe-outputs.jobs..runs-on should be supported.

Removed the safe-outputs.jobs runner support in ccf85b58c9.

@github-actions

Copy link
Copy Markdown
Contributor

PR Triage

Enables runner-group runs-on for safe-outputs.jobs. Note: this fetch reported 0 changed files, which may indicate a stale/rebased diff — please re-verify diff contents before review. Has an outstanding dismissed change-request review to reconcile.

Generated by 🔧 PR Triage Agent · auto · 45 AIC · ⌖ 2.09 AIC · ⊞ 8.3K ·

@flynnjustin24

Copy link
Copy Markdown

@copilot Fix the code for all comments in this review thread.

When a review comment includes a suggested change, apply the suggestion exactly.

Do not make changes beyond what is described in the linked review thread.

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.

safe-outputs.jobs.<id>.runs-on rejects the runner-group object form

4 participants