Skip to content

feat: add two codemods for persistent cross-repo compile failures (maui, azure-rest-api-specs)#38097

Merged
pelikhan merged 5 commits into
mainfrom
copilot/aw-compat-daily-cross-repo-compatibility
Jun 9, 2026
Merged

feat: add two codemods for persistent cross-repo compile failures (maui, azure-rest-api-specs)#38097
pelikhan merged 5 commits into
mainfrom
copilot/aw-compat-daily-cross-repo-compatibility

Conversation

Copilot AI commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Daily cross-repo compile audit identified two recurring failures not covered by existing codemods: dotnet/maui (required: true inputs on auto-dispatched triggers) and Azure/azure-rest-api-specs (secrets in top-level env: block evading engine-env-secrets-to-engine-config).

workflow-dispatch-required-false-with-slash-command

When on.slash_command or on.label_command coexists with on.workflow_dispatch, rewrites inputs.*.required: truerequired: false. Auto-dispatched triggers can never supply required manual inputs, so this is a safe pure rewrite.

# Before (compile error)
on:
  slash_command:
    name: evaluate-tests
  workflow_dispatch:
    inputs:
      pr_number:
        required: true   # ← rejected by validateCommandWorkflowDispatchInputs

# After (auto-fixed)
        required: false

top-level-env-secrets-guided-error

Extends secret detection to the top-level env: block (the existing engine-env-secrets-to-engine-config codemod only inspects engine.env). Cannot auto-strip — the token may be legitimately required — so returns a guided error with a documentation link instead:

top-level env: contains secrets that will be leaked to the agent container.
Found: ${{ secrets.GITHUB_PERSONAL_ACCESS_TOKEN || secrets.GITHUB_TOKEN }}.
Manual fix required: move the secret into engine-specific secret configuration.
See: https://github.github.com/gh-aw/reference/engines/

Both codemods are registered in GetAllCodemods() with unit tests and expected-order test list updated.

Copilot AI and others added 2 commits June 9, 2026 12:08
…codemods

- Add `workflow-dispatch-required-false-with-slash-command` codemod that rewrites
  workflow_dispatch.inputs.*.required: true → required: false when on.slash_command
  or on.label_command is also present (fixes dotnet/maui pattern)
- Add `top-level-env-secrets-guided-error` codemod that detects secrets in the
  top-level env: block and returns a guided error with manual fix instructions
  (addresses Azure/azure-rest-api-specs codemod gap)
- Register both codemods in fix_codemods.go registry
- Update fix_codemods_test.go to include both new IDs
- Add comprehensive unit tests for both codemods

Co-authored-by: pelikhan <[email protected]>
Copilot AI changed the title [WIP] Fix persistent compile failures for maui and azure-rest-api-specs feat: add two codemods for persistent cross-repo compile failures (maui, azure-rest-api-specs) Jun 9, 2026
Copilot AI requested a review from pelikhan June 9, 2026 12:10
@pelikhan pelikhan marked this pull request as ready for review June 9, 2026 12:15
Copilot AI review requested due to automatic review settings June 9, 2026 12:15
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

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

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 two new gh aw fix codemods to address recurring cross-repo compile/validation failures: one auto-rewrite for workflow_dispatch.inputs.*.required when command triggers are present, and one guided-error detector for secrets in the top-level env: block.

Changes:

  • Register workflow-dispatch-required-false-with-slash-command and top-level-env-secrets-guided-error in the codemod registry and expected-order tests.
  • Implement a YAML frontmatter line-transform codemod that rewrites required: truerequired: false under on.workflow_dispatch.inputs.* when on.slash_command / on.label_command is present.
  • Add a guided-error codemod that detects ${{ secrets.* }} usage in the top-level env: block and returns a documentation-linked error.
Show a summary per file
File Description
pkg/cli/fix_codemods.go Registers the two new codemods in GetAllCodemods().
pkg/cli/fix_codemods_test.go Updates expected codemod presence + ordering lists to include the new IDs.
pkg/cli/codemod_workflow_dispatch_required.go Implements the workflow_dispatch required→false rewrite codemod.
pkg/cli/codemod_workflow_dispatch_required_test.go Adds tests for the new required→false codemod (and also includes the top-level env guided-error tests).
pkg/cli/codemod_engine_env_secrets.go Adds the top-level env: secrets guided-error codemod implementation.

Copilot's findings

Tip

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

  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment on lines +136 to +151
// Enter inputs: within workflow_dispatch: (block form only, no inline value).
if inWD && !inInputs && strings.HasPrefix(trimmed, "inputs:") &&
strings.TrimSpace(strings.TrimPrefix(trimmed, "inputs:")) == "" {
inInputs = true
inputsIndent = indent
result = append(result, line)
continue
}

// Enter an individual input entry (direct child key of inputs:).
if inInputs && !inInputEntry && len(indent) > len(inputsIndent) {
inInputEntry = true
inputEntryIndent = indent
result = append(result, line)
continue
}
Comment on lines +277 to +280
var secretRefs []string
for _, expr := range secretExpressions {
secretRefs = append(secretRefs, expr)
}
Comment on lines +302 to +304
func TestTopLevelEnvSecretsGuidedErrorCodemod(t *testing.T) {
codemod := getTopLevelEnvSecretsGuidedErrorCodemod()

@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 /grill-with-docs — requesting changes on one correctness bug and a few test coverage gaps.

📋 Key Themes & Highlights

Key Themes

  • Correctness bug (line transformer): inputs: # comment silently bypasses the inInputs state, causing the rewrite to produce a no-op. Needs a one-line condition fix and a regression test.
  • Non-deterministic error message: secretRefs is built from map iteration; sort.Strings would make the guided error stable and future-proof against snapshot tests.
  • Missing boundary test: mixed required: true / required: false inputs are not exercised; the all-true and all-false cases are covered but not the partial case.
  • Undocumented semantic trade-off: the required: false rewrite also removes input validation for manual UI runs. Worth documenting in the codemod Description or surfacing in the apply message.

Positive Highlights

  • ✅ Two-tier approach (auto-fix vs. guided error) is well-reasoned and the distinction is clearly explained
  • ✅ Test file is 2× the size of the implementation — strong coverage intent
  • ✅ No-op guards are thoroughly tested for every early-exit branch
  • ✅ The frontmatter pre-check prevents the line transformer from running unnecessarily
  • ✅ Codemod ordering placement relative to engine-env-secrets-to-engine-config is logical and the snapshot test is kept in sync

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 357.9 AIC · ⌖ 45.7 AIC


// Enter inputs: within workflow_dispatch: (block form only, no inline value).
if inWD && !inInputs && strings.HasPrefix(trimmed, "inputs:") &&
strings.TrimSpace(strings.TrimPrefix(trimmed, "inputs:")) == "" {

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] inputs: with a trailing comment (inputs: # some comment) silently bypasses this check, causing the codemod to produce a no-op when the transform should fire.

💡 Details and suggested fix

The condition strings.TrimSpace(strings.TrimPrefix(trimmed, "inputs:")) == "" rejects a line like inputs: # some comment because the trimmed remainder is "# some comment", not "".

Change the condition to also accept a trailing comment:

remainder := strings.TrimSpace(strings.TrimPrefix(trimmed, "inputs:"))
if inWD && !inInputs && strings.HasPrefix(trimmed, "inputs:") &&
	(remainder == "" || strings.HasPrefix(remainder, "#")) {

A regression test to cover this case:

t.Run("rewrites when inputs: has trailing comment", func(t *testing.T) {
	content := `---
on:
  slash_command:
    name: run
  workflow_dispatch:
    inputs: # PR inputs
      pr_number:
        required: true
---
`
	// ... assert applied == true
})


var secretRefs []string
for _, expr := range secretExpressions {
secretRefs = append(secretRefs, expr)

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 secretRefs slice is built by iterating over secretExpressions, which itself comes from map iteration — making the order of secrets in the error message non-deterministic across runs.

💡 Suggested fix

Add sort.Strings(secretRefs) after building the slice so the error message is stable:

import "sort"

// ...
var secretRefs []string
for _, expr := range secretExpressions {
    secretRefs = append(secretRefs, expr)
}
sort.Strings(secretRefs)  // ensure deterministic output

Non-deterministic error messages make log analysis harder and can cause fragile snapshot tests in future. The existing tests already use assert.Contains (order-agnostic), but any future snapshot-style test would be flaky without this fix.

result, applied, err := codemod.Apply(content, frontmatter)
require.NoError(t, err)
assert.True(t, applied)
assert.NotContains(t, result, "required: true")

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] Missing boundary condition: a mix of required: true and required: false inputs. The test suite covers all-true and all-false, but not the case where only some inputs need rewriting.

💡 Suggested test to add after this one
t.Run("rewrites only required: true inputs, leaves required: false unchanged", func(t *testing.T) {
	content := `---
on:
  slash_command:
    name: run
  workflow_dispatch:
    inputs:
      pr_number:
        description: "PR number"
        required: true
        type: number
      dry_run:
        description: "Dry run flag"
        required: false
        type: boolean
---

# Run
`
	frontmatter := map[string]any{
		"on": map[string]any{
			"slash_command": map[string]any{"name": "run"},
			"workflow_dispatch": map[string]any{
				"inputs": map[string]any{
					"pr_number": map[string]any{"required": true},
					"dry_run":   map[string]any{"required": false},
				},
			},
		},
	}

	result, applied, err := codemod.Apply(content, frontmatter)
	require.NoError(t, err)
	assert.True(t, applied)
	assert.NotContains(t, result, "required: true")
	// dry_run must still be explicitly false (not removed)
	assert.Contains(t, result, "required: false")
})

This guards against the line transformer accidentally touching required: false lines or missing one of multiple inputs.


newContent, applied, err := applyFrontmatterLineTransform(content, rewriteWorkflowDispatchRequiredFalse)
if applied {
workflowDispatchRequiredLog.Print("Applied workflow-dispatch-required-false-with-slash-command codemod")

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.

[/grill-with-docs] The apply log says the codemod fired, but there is no user-visible message explaining the semantic trade-off: setting required: false also disables the required-input guard for users who trigger the workflow manually via the GitHub UI.

💡 Suggestion

Codemods that change user semantics (not just syntax) benefit from a brief advisory in the structured error or log output. Consider returning a non-fatal warning (or at minimum surfacing a note in the compile output) similar to:

Note: workflow_dispatch inputs set to required: false also lose input validation for manual UI runs.
If strict input enforcement is needed for UI runs, consider runtime guards in the workflow body.

Alternatively, document this in the codemod Description field so it surfaces in gh aw audit output. The current description only mentions that auto-dispatched triggers are the reason, but the implication for manual runs is left unstated.

assert.Contains(t, result, "type: number")
assert.Contains(t, result, `default: "0"`)
assert.Contains(t, result, "required: false")
assert.NotContains(t, result, "required: true")

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] No test covers the inputs: trailing-comment edge case (inputs: # comment), which would silently skip the rewrite (see corresponding comment on line 138 of the implementation). A dedicated test here would either document the known limitation or prove the fix is correct.

💡 Suggested test skeleton
t.Run("rewrites when inputs: line has a trailing comment", func(t *testing.T) {
    content := `---
on:
  slash_command:
    name: run
  workflow_dispatch:
    inputs: # workflow inputs
      pr_number:
        description: "PR"
        required: true
        type: number
---

# Run
`
    frontmatter := map[string]any{
        "on": map[string]any{
            "slash_command": map[string]any{"name": "run"},
            "workflow_dispatch": map[string]any{
                "inputs": map[string]any{
                    "pr_number": map[string]any{"required": true},
                },
            },
        },
    }

    result, applied, err := codemod.Apply(content, frontmatter)
    require.NoError(t, err)
    assert.True(t, applied, "should apply despite trailing comment on inputs: line")
    assert.NotContains(t, result, "required: true")
})

@pelikhan

pelikhan commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Copilot AI commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Implemented the requested pr-finisher pass and pushed fixes in 616edee and ff33872. Changes address inline/trailing-comment workflow_dispatch input rewrites, deduplicated/sorted top-level env secret reporting, and test coverage/file organization updates, with local make fmt/lint/test-unit/test/recompile passing.

@pelikhan pelikhan merged commit 7218ce5 into main Jun 9, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/aw-compat-daily-cross-repo-compatibility branch June 9, 2026 13:37
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Request final maintainer review and post a brief completion summary.

Generated by 👨‍🍳 PR Sous Chef · 88.8 AIC · ⌖ 0.997 AIC · ⊞ 17.1K ·

@github-actions github-actions Bot mentioned this pull request Jun 10, 2026
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.

[aw-compat] Daily AW cross-repo compatibility — 2 persistent compile failures (maui, azure-rest-api-specs) [2026-06-09]

3 participants