Skip to content

Default runs-on for non-reusable custom jobs when omitted - #48397

Merged
pelikhan merged 3 commits into
mainfrom
copilot/investigate-aw-compilation-issue
Jul 28, 2026
Merged

Default runs-on for non-reusable custom jobs when omitted#48397
pelikhan merged 3 commits into
mainfrom
copilot/investigate-aw-compilation-issue

Conversation

CopilotAI commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

A workflow in gh-aw-test failed to compile because a custom job using restore-memory: true omitted runs-on, and the generated .lock.yml emitted that job without a runner. GitHub Actions rejects such jobs with Required property is missing: runs-on.

  • Root cause

    • Non-reusable custom jobs did not inherit any runner when jobs.<id>.runs-on was absent.
    • restore-memory path made this easy to hit in deterministic setup jobs.
  • Compiler behavior change

    • In non-reusable custom job compilation, when job.RunsOn is empty:
      • use workflow-level data.RunsOn if present
      • otherwise fallback to runs-on: ubuntu-latest
    • Reusable-workflow call jobs (uses:) are unchanged.
  • Regression coverage

    • Added a targeted test for restore-memory custom jobs without explicit runs-on to ensure generated job YAML always includes a runner.
jobs:
setup:
restore-memory: truesteps:
- run: echo "ok"

With this change, the compiled setup job includes a valid runs-on stanza inherited from workflow defaults.


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 9.88 AIC · ⌖ 7.74 AIC · ⊞ 7.1K ·
Comment /souschef to run again


pr-sous-chef run: https://github.com/github/gh-aw/actions/runs/30293408015

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 28.1 AIC · ⌖ 8.01 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 15.6 AIC · ⌖ 7.45 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 31.8 AIC · ⌖ 8.29 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30302221322

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13 AIC · ⌖ 6.16 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30304405147

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 15.2 AIC · ⌖ 6.68 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 15.5 AIC · ⌖ 7.12 AIC · ⊞ 7.1K ·
Comment /souschef to run again


run: https://github.com/github/gh-aw/actions/runs/30308333914

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 18.8 AIC · ⌖ 7.74 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30311684823

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 10.1 AIC · ⌖ 6.51 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 13.9 AIC · ⌖ 6.46 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Run: https://github.com/github/gh-aw/actions/runs/30316592253

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 14.3 AIC · ⌖ 9.66 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
CopilotAI changed the title Default missing runs-on for non-reusable custom jobsDefault runs-on for non-reusable custom jobs when omittedJul 27, 2026
CopilotAI requested a review from pelikhanJuly 27, 2026 15:43
@pelikhan
pelikhan marked this pull request as ready for review July 27, 2026 16:55
CopilotAI review requested due to automatic review settings July 27, 2026 16:55

CopilotAI 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

Defaults non-reusable custom jobs to the workflow runner or ubuntu-latest when runs-on is omitted.

Changes:

  • Adds custom-job runner fallback logic.
  • Adds regression coverage for restore-memory jobs.
Show a summary per file
FileDescription
pkg/workflow/compiler_custom_jobs.goApplies default runner configuration.
pkg/workflow/compiler_custom_job_memory_test.goTests omitted runs-on behavior.

Review details

Tip

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

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

Comment threadpkg/workflow/compiler_custom_jobs.go Outdated
@github-actions

github-actionsBot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actionsBot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actionsBot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR #48397 does not have the 'implementation' label and has 48 new lines of code in business logic directories (below the 100-line threshold).

@github-actions

github-actionsBot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actionsgithub-actionsBot 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 — requesting one change on test coverage.

📋 Key Themes & Highlights

Key Themes

  • Partial branch coverage: the new test only exercises the ubuntu-latest fallback; the data.RunsOn inheritance branch is untested.
  • Existing comment (Copilot, line 481): data.RunsOn may be multi-line, which the direct assignment does not indent correctly — that concern is already filed.

Positive Highlights

  • ✅ Clean, targeted fix with a clear fallback chain
  • ✅ Regression test added in the right file alongside similar memory-job tests
  • ✅ PR description clearly explains root cause, compiler behavior change, and test coverage

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 27.7 AIC · ⌖ 4.63 AIC · ⊞ 6.7K
Comment /matt to run again

Comment threadpkg/workflow/compiler_custom_jobs.go Outdated

func (c *Compiler) configureCustomJobSteps(job *Job, jobName string, configMap map[string]any, data *WorkflowData) error {
if job.RunsOn == "" {
job.RunsOn = data.RunsOn

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]data.RunsOn inheritance path is untested — only the ubuntu-latest fallback is exercised by the new test.

The new test omits a workflow-level runs-on:, so the job.RunsOn = data.RunsOn branch (line 481) is never taken. A regression there would go undetected.

💡 Suggested additional test case

Add a second test that sets a workflow-level runs-on: self-hosted in the frontmatter and asserts the compiled setup job inherits runs-on: self-hosted instead of the ubuntu-latest fallback. This covers both branches of the new logic.

@copilot please address this.

@github-actionsgithub-actionsBot mentioned this pull request Jul 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️Test Quality Score: 65/100 — Acceptable

Analyzed 15 test(s): 10 design, 5 implementation, 1 violation.

📊 Metrics (15 tests)
MetricValue
Analyzed15 (Go: 15, JS: 0)
✅ Design10 (67%)
⚠️ Implementation5 (33%)
Edge/error coverage9 (60%)
Duplicate clusters0
InflationYES (594 test lines / 194 prod lines ≈ 3.1:1)
🚨 Violations1 (implementation ratio > 30%)
TestFileClassificationIssues
TestCustomJobRestoreMemoryCacheMemorycompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryUsesDefaultRunsOncompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryRepoMemorycompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryCommentMemorycompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryMultipleTypescompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryStepOrdercompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryErrorWhenNotConfiguredcompiler_custom_job_memory_test.godesign_test (error path)
TestCustomJobRestoreMemoryOnlyEmitsRestoreStepscompiler_custom_job_memory_test.godesign_test
TestCustomJobRestoreMemoryStandaloneJobcompiler_custom_job_memory_test.godesign_test
TestExtractRestoreMemoryConfigcompiler_custom_job_memory_test.godesign_test (table-driven, 7 rows)
TestGenerateCacheMemoryRestoreLinescompiler_custom_job_memory_test.goimplementation_testInternal function test
TestGenerateCacheMemoryRestoreLinesNilDatacompiler_custom_job_memory_test.goimplementation_testNil-guard only
TestBuildCacheRestoreKeysEmptyForSinglePartKeycompiler_custom_job_memory_test.goimplementation_testInternal key-builder detail
TestGenerateRepoMemoryRestoreLinesNilDatacompiler_custom_job_memory_test.goimplementation_testNil-guard only
TestGenerateCommentMemoryRestoreLinesNilDatacompiler_custom_job_memory_test.goimplementation_testNil-guard only
⚠️ Flagged Tests (5)

TestGenerateCacheMemoryRestoreLines (compiler_custom_job_memory_test.go:542) — implementation_test, low_value. Tests an internal helper function directly; behavior is fully covered by the higher-level TestCustomJobRestoreMemoryCacheMemory.

TestGenerateCacheMemoryRestoreLinesNilData (compiler_custom_job_memory_test.go:566) — implementation_test, low_value. Nil-guard on an internal function already covered indirectly when no cache config is set.

TestBuildCacheRestoreKeysEmptyForSinglePartKey (compiler_custom_job_memory_test.go:573) — implementation_test, low_value. Tests an internal helper with no observable behavioral impact at the compiler level.

TestGenerateRepoMemoryRestoreLinesNilData (compiler_custom_job_memory_test.go:581) — implementation_test, low_value. Nil/empty guards on an internal helper.

TestGenerateCommentMemoryRestoreLinesNilData (compiler_custom_job_memory_test.go:589) — implementation_test, low_value. Same nil-guard pattern as the repo-memory variant.

Inflation flagcompiler_custom_job_memory_test.go (594 lines) vs compiler_custom_job_memory.go (194 lines) → ratio ≈ 3.1:1 (threshold 2:1). Consolidating nil-guard tests would bring the ratio into range.

Verdict

Failed. 33% implementation tests (threshold: 30%). The ten behavioral contract tests covering restore-memory compilation are excellent. The five internal-helper tests push the ratio just over threshold — consolidating them would bring this into passing range.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 · 69.4 AIC · ⌖ 7.87 AIC · ⊞ 8.1K ·
Comment /review to run again

@github-actionsgithub-actionsBot 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: 65/100. 33% implementation tests (threshold: 30%). Review flagged tests in the comment above.

@github-actionsgithub-actionsBot 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.

Review: Default runs-on for non-reusable custom jobs

Overall: The fix is correct and targeted. One non-blocking reliability concern worth addressing:

Multi-line runs-on indentation (line 481):

data.RunsOn is a pre-rendered YAML snippet. When the workflow declares a complex runner (group/labels object), the snippet spans multiple lines. Inheriting it via job.RunsOn = data.RunsOn and emitting through Job.Write's fmt.Fprintf(b, " %s\n", job.RunsOn) mis-indents continuation lines (4 spaces instead of 6). No test exercises this path.

Consider using normalizeRunsOnSnippet or formatIndentedYAMLField (both used elsewhere) before the assignment.

Test gap: The new test only exercises the fallback path (ubuntu-latest). A second case with an explicit workflow-level runs-on label array would validate the inheritance path for non-trivial runner forms.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 41.8 AIC · ⌖ 5.3 AIC · ⊞ 5K

Comment threadpkg/workflow/compiler_custom_jobs.go Outdated

func (c *Compiler) configureCustomJobSteps(job *Job, jobName string, configMap map[string]any, data *WorkflowData) error {
if job.RunsOn == "" {
job.RunsOn = data.RunsOn

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 inherited data.RunsOn snippet can be a multi-line YAML block (e.g., a matrix runner or group/labels object). Assigning it directly to job.RunsOn and emitting it via fmt.Fprintf(b, " %s ", job.RunsOn) in Job.Write will mis-indent continuation lines—they will render at 4 spaces instead of the required 6.

Consider normalizing with the same helper used elsewhere (e.g., normalizeRunsOnSnippet or formatIndentedYAMLField) before assigning, or document that data.RunsOn is guaranteed to be a pre-indented snippet.

@copilot please address this.

github-actions[bot]
github-actionsBot previously requested changes Jul 27, 2026

@github-actionsgithub-actionsBot 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.

REQUEST_CHANGES — fix is mostly correct but has a correctness bug and a gap in test coverage.

### Findings

High — broken YAML for multi-line runs-on inheritance (compiler_custom_jobs.go:481)

data.RunsOn is a raw multi-line YAML snippet. Every other site that feeds it into a Job struct calls c.indentYAMLLines(data.RunsOn, " ") first. This line assigns it bare, so array/object runner forms will produce invalid YAML with unindented continuation lines. The hardcoded "runs-on: ubuntu-latest" fallback is fine; only the data.RunsOn branch needs the indent call.

Medium — test misses the actual bug path (compiler_custom_job_memory_test.go:46)

The new test exercises the ubuntu-latest hardcoded fallback (no workflow-level runs-on present), not the data.RunsOn inheritance path. A test with a complex (array/object) workflow-level runner would catch the indentation regression.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 45.1 AIC · ⌖ 4.83 AIC · ⊞ 5.7K
Comment /review to run again

Comments that could not be inline-anchored

pkg/workflow/compiler_custom_jobs.go:481

Missing indentation for multi-line runs-on fallback: assigning data.RunsOn directly will produce broken YAML when the workflow uses an array or object runs-on form.

<details>
<summary>💡 Details and fix</summary>

data.RunsOn stores a raw YAML snippet such as:

runs-on:
group: my-runnerslabels: [ubuntu-latest]

When job.RunsOn is written by jobs.go it is emitted as %s — a single 4-space prefix on the first line only. Multi-line continuation lines are not…

pkg/workflow/compiler_custom_job_memory_test.go:46

Test only covers ubuntu-latest fallback — the actual regression (multi-line runs-on) is untested: the new test verifies runs-on: ubuntu-latest is present, but the correctness bug is in the data.RunsOn branch, not the hardcoded fallback branch.

<details>
<summary>💡 Details</summary>

The test at line 46 asserts runs-on: ubuntu-latest — this hits the job.RunsOn = &quot;runs-on: ubuntu-latest&quot; fallback (line 483) which is fine. It does not exercise the job.RunsOn = data.RunsOn branc…

@github-actions

Copy link
Copy Markdown
Contributor

Great work, @Copilot! 🎉 This fix looks solid — you have addressed a critical compiler bug where custom jobs without explicit runs-on would fail GitHub Actions validation.

What looks good:

  • Targeted fix: The logic is clear — inherit workflow-level runs-on, fall back to ubuntu-latest.
  • Regression test: The new TestCustomJobRestoreMemoryUsesDefaultRunsOn directly covers the restore-memory scenario that triggered this bug.
  • Minimal scope: Just 2 files, 48 lines. Easy to review and merge.

This PR is ready for review! 🚀

Generated by ✅ Contribution Check · sonnet46 · 63.8 AIC · ⌖ 7.55 AIC · ⊞ 6.2K ·

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

FieldValue
Categorybug
Risklow
Score70/100 (impact:28, urgency:25, quality:17)
Actionauto_merge

Rationale: Fixes compiler omitting runs-on for custom jobs using restore-memory: true, causing GHA to reject the workflow. 2-file change, CI fully passing. Auto-merge candidate.

Generated by 🔧 PR Triage Agent · sonnet46 · 54.1 AIC · ⌖ 8.18 AIC · ⊞ 5.7K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

CopilotAI commented Jul 27, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot run pr-finisher skill

Applied the fix in commit Fix multi-line runs-on indentation for inherited custom job runner.

  • Changed job.RunsOn = data.RunsOn to job.RunsOn = c.indentYAMLLines(data.RunsOn, " ") so continuation lines of multi-line YAML (arrays/objects) are correctly indented at job depth.
  • Added TestCustomJobRestoreMemoryInheritsArrayRunsOn which exercises a group/labels object runner inherited from the workflow level and asserts every non-empty line inside the job section is indented with at least 4 spaces.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

FieldValue
Categorybug
Risk🟢 low
Score68/100 (impact:32 urgency:22 quality:14)
Actionauto_merge

Adds default runs-on for non-reusable custom jobs when omitted, preventing compilation failures. Small change (104 adds, 0 dels, 2 files). CI passing (23 success, 5 skipped). Auto-merge candidate.

Generated by 🔧 PR Triage Agent · sonnet46 · 63.9 AIC · ⌖ 5.94 AIC · ⊞ 5.7K ·

@pelikhan
pelikhan merged commit c24715d into mainJul 28, 2026
28 checks passed
@pelikhan
pelikhan deleted the copilot/investigate-aw-compilation-issue branch July 28, 2026 01:26
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.83.5

Sign up for freeto 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.

3 participants

@pelikhan