Skip to content

compile: clarify --action-mode/--action-tag/--gh-aw-ref relationship, enforce mutual exclusivity#39697

Merged
pelikhan merged 4 commits into
mainfrom
copilot/review-go-compile-cli-args
Jun 17, 2026
Merged

compile: clarify --action-mode/--action-tag/--gh-aw-ref relationship, enforce mutual exclusivity#39697
pelikhan merged 4 commits into
mainfrom
copilot/review-go-compile-cli-args

Conversation

Copilot AI commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

--gh-aw-ref was silently overwriting --action-tag and --action-mode when combined, and --action-mode help text listed the removed inline value while omitting the valid action mode.

Enforcement

  • Added MarkFlagsMutuallyExclusive("gh-aw-ref", "action-tag") and ("gh-aw-ref", "action-mode") — Cobra now errors instead of silently discarding the other flags

Help text fixes

  • --action-mode: removed inline (removed mode), added action, added per-value descriptions (dev = local paths, release = SHA-pinned from github/gh-aw, action = github/gh-aw-actions)
  • --action-tag: clarifies it accepts SHA or version tag only — branch names are not resolved; use --gh-aw-ref for that
  • --gh-aw-ref: spells out the equivalence (--action-mode release --action-tag <resolved-sha>) and notes it cannot be combined with the other two

Compile command Long description

Added an "Action mode" section documenting all three flags, their valid inputs, and when to use each — the compile help previously said nothing about this.

Internal docs

  • CompileConfig field comments corrected (removed inline)
  • setupActionMode godoc documents the priority order: actionTag → explicit actionMode → auto-detect
  • validateActionModeConfig error message updated to list only user-facing modes (dev, release, action)

Copilot AI and others added 2 commits June 16, 2026 23:25
… mutual exclusivity

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title Review and document --action-tag and --gh-aw-ref overlap in compile command compile: clarify --action-mode/--action-tag/--gh-aw-ref relationship, enforce mutual exclusivity Jun 16, 2026
Copilot AI requested a review from pelikhan June 16, 2026 23:27
@pelikhan pelikhan marked this pull request as ready for review June 16, 2026 23:30
Copilot AI review requested due to automatic review settings June 16, 2026 23:30

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

This PR improves the gh aw compile CLI UX by clarifying how --action-mode, --action-tag, and --gh-aw-ref interact, and by enforcing flag mutual exclusivity to prevent silent overwrites.

Changes:

  • Enforce mutual exclusivity so --gh-aw-ref cannot be combined with --action-mode or --action-tag.
  • Refresh compile help text and config/doc comments to reflect the supported action modes and intended usage.
  • Update validation error messaging to list only the user-facing action modes.
Show a summary per file
File Description
pkg/cli/compile_validation.go Updates the invalid action-mode error message to match the intended, user-facing mode set.
pkg/cli/compile_config.go Updates CompileConfig field comments to reflect current action-mode/action-tag semantics.
pkg/cli/compile_compiler_setup.go Improves setupActionMode docs/log wording to clarify precedence and --gh-aw-ref interaction.
cmd/gh-aw/main.go Expands compile help text, updates flag help strings, and adds Cobra mutual-exclusivity enforcement for --gh-aw-ref.

Copilot's findings

Tip

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

  • Files reviewed: 4/4 changed files
  • Comments generated: 4

Comment thread cmd/gh-aw/main.go Outdated
- Only processes workflows in the default .github/workflows directory

Action mode controls how gh-aw action scripts are referenced in compiled workflows.
Three flags govern this and only one should be used at a time:

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.

Updated in d11d8c0: the compile Long help now states that only --gh-aw-ref is mutually exclusive and that --action-tag can be combined with --action-mode action.

Comment thread cmd/gh-aw/main.go Outdated
Auto-detected from the binary build type when not set.

--action-tag <sha-or-tag>
Pin to a specific SHA or version tag (e.g. v1, v1.2.3, abc123...).

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.

Updated in d11d8c0: the --action-tag Long-help example now uses <full-sha> instead of a short SHA example.

Comment thread pkg/cli/compile_config.go Outdated
ShowAllErrors bool // Display all prioritized errors instead of the default top five
ActionMode string // Action script inlining mode: inline, dev, or release
ActionTag string // Override action SHA or tag for actions/setup (overrides action-mode to release)
ActionMode string // Action script inlining mode: dev, release, or action. Auto-detected if empty.

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.

Updated in d11d8c0: CompileConfig.ActionMode comment now says “How action scripts are referenced” to match CLI semantics.

Comment thread pkg/cli/compile_config.go Outdated
ActionMode string // Action script inlining mode: inline, dev, or release
ActionTag string // Override action SHA or tag for actions/setup (overrides action-mode to release)
ActionMode string // Action script inlining mode: dev, release, or action. Auto-detected if empty.
ActionTag string // Pin action refs to this SHA or version tag (e.g. v1, abc123…). Sets release mode unless ActionMode is already "action". Mutually exclusive with GHAwRef at the CLI layer.

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.

Updated in d11d8c0: CompileConfig.ActionTag example now uses <full-sha> instead of a short SHA example.

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

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

No ADR enforcement needed: PR #39697 does not have the 'implementation' label and has only 16 new lines of code in business logic directories (≤100 threshold).

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 16, 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. PR #39697 only modified production source files: cmd/gh-aw/main.go, pkg/cli/compile_compiler_setup.go, pkg/cli/compile_config.go, pkg/cli/compile_validation.go.

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

Two non-blocking observations. No correctness bugs or crashes found.

🔍 Review themes

script mode: hidden but still functional

validateActionModeConfig no longer lists script in the error message, and script is absent from all flag help and Long documentation. But ActionModeScript is still in IsValid(), so --action-mode script is accepted silently — no error, no indication it works. The PR description says this is intentional ("user-facing modes only"), but the intent isn't communicated in code. Either retire it from IsValid() or annotate ActionModeScript as internal/deprecated.

setupActionMode godoc preamble

The new godoc reads "Priority order when both actionTag and actionMode are provided", but the three bullets actually cover all cases (one set, other empty, neither set). The preamble makes bullet 2 ambiguous — --action-mode dev --action-tag v1 silently uses release, not dev, which contradicts what a reader of bullet 2 would expect.

🔎 Code quality review by PR Code Quality Reviewer

mode := workflow.ActionMode(actionMode)
if !mode.IsValid() {
return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', 'script', or 'action'", actionMode)
return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', or 'action'", actionMode)

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.

script mode is silently accepted by IsValid() but now absent from all help and error guidance — a user passing --action-mode script gets no validation error but nothing tells them the mode exists or is supported.

💡 Details

ActionModeScript is still included in IsValid() in pkg/workflow/action_mode.go:

func (m ActionMode) IsValid() bool {
    return m == ActionModeDev || m == ActionModeRelease || m == ActionModeScript || m == ActionModeAction
}

So --action-mode script passes validation silently. But this PR simultaneously removes script from:

  • this error message
  • the --action-mode flag description
  • the Long help text

This creates a hidden, undocumented but functional mode with no discoverability path. Two clean resolutions:

  1. Retire it: remove ActionModeScript from IsValid() so the mode is truly invalid at the CLI layer.
  2. Keep it internal: add a // Internal: or // Deprecated: godoc to ActionModeScript in action_mode.go, and leave a comment in validateActionModeConfig explaining why it is intentionally excluded from the user-facing error message.

Without one of these, a future developer will find a working hidden mode with no rationale for why it exists.

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 d11d8c0: added an explicit comment in validateActionModeConfig documenting that script is intentionally internal and excluded from user-facing error guidance.

Comment thread pkg/cli/compile_compiler_setup.go Outdated
// setupActionMode configures how gh-aw action scripts are referenced in compiled workflows.
// Priority order when both actionTag and actionMode are provided (after --gh-aw-ref resolution):
// - actionTag != "" → pin to that SHA/tag; use ActionModeRelease unless actionMode is explicitly "action"
// - actionMode != "" → honour the explicit mode

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.

Godoc preamble "when both are provided" makes bullet 2 misleading — a reader may infer that --action-mode dev --action-tag v1 uses dev mode; the code silently uses release instead.

💡 Suggested fix

The preamble scopes all three bullets to "when both actionTag and actionMode are provided", but bullet 2 only fires when actionTag is empty. For example:

  • --action-mode dev --action-tag v1 → bullet 1 fires, code sets ActionModeRelease, dev is ignored entirely
  • --action-mode dev (no tag) → bullet 2 fires, dev is honoured

Drop the "when both are provided" qualifier and add the actionTag == "" constraint to bullet 2:

// Priority order:
//   - actionTag != ""                 → pin to that SHA/tag; use ActionModeRelease unless actionMode is explicitly "action"
//   - actionTag == "" && actionMode != "" → honour the explicit mode
//   - neither set                     → auto-detect from the binary build type via DetectActionMode

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.

Updated in d11d8c0: the setupActionMode godoc preamble now removes the “when both are provided” phrasing and explicitly scopes bullet 2 to actionTag == "".

@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, /diagnose, /grill-with-docs, and /zoom-out — requesting changes on two blocking items.

📋 Key Themes & Highlights

Blocking

  • Missing regression tests (/tdd): The MarkFlagsMutuallyExclusive calls are the core bug fix, but there is no test that exercises them. A future refactor could silently revert the fix with no CI signal. The existing test patterns in cmd/gh-aw/main_entry_test.go and pkg/cli/compile_compiler_setup_test.go make this straightforward to add.

  • Documentation contradiction (/grill-with-docs): Line 253 says "only one should be used at a time" but the flag descriptions below (and the code) explicitly support combining --action-tag with --action-mode action. Only --gh-aw-ref is actually mutually exclusive.

Non-blocking

  • Misleading godoc priority order (/diagnose): The setupActionMode comment implies sequential evaluation, but the early return in the actionTag branch means actionMode is never separately evaluated when a tag is set.

  • script mode gap (/diagnose): IsValid() still accepts script, but the error message no longer lists it. The intent should be made explicit with a comment.

  • "40-character commit SHA" (/zoom-out): No length validation is enforced; the phrasing will mislead users who pass abbreviated SHAs.

Positive Highlights

  • ✅ The MarkFlagsMutuallyExclusive fix is exactly right — Cobra's error is far better than silent clobbering
  • ✅ The new Long description section fills a real documentation gap; the per-flag breakdown with examples is clear
  • ✅ Log message change from "overriding" → "defaulting" is a meaningful observability improvement
  • CompileConfig field comments are now self-consistent with the behavior

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

Comment thread cmd/gh-aw/main.go
compileCmd.MarkFlagsMutuallyExclusive("dir", "workflows-dir")
// --gh-aw-ref is a convenience alias for --action-mode release --action-tag <sha>;
// combining it with either of those flags leads to one silently overwriting the other.
compileCmd.MarkFlagsMutuallyExclusive("gh-aw-ref", "action-tag")

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 regression test for the mutual exclusivity enforcement — the core behavior fix has zero test coverage.

💡 Suggested approach

A unit test in cmd/gh-aw/main_entry_test.go (pattern already used there: rootCmd.SetArgs(...) + Execute()) could cover both error cases:

func TestCompileGhAwRefMutuallyExclusive(t *testing.T) {
    cases := []struct {
        name string
        args []string
    }{
        {"gh-aw-ref+action-tag", []string{"compile", "--gh-aw-ref", "main", "--action-tag", "v1.2.3"}},
        {"gh-aw-ref+action-mode", []string{"compile", "--gh-aw-ref", "main", "--action-mode", "dev"}},
    }
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            rootCmd.SetArgs(tc.args)
            err := rootCmd.Execute()
            if err == nil {
                t.Fatalf("expected mutual-exclusivity error, got nil")
            }
        })
    }
}

Without this, a future refactor removing MarkFlagsMutuallyExclusive would produce no CI signal.

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 d11d8c0: added TestCompileGhAwRefMutuallyExclusiveFlags in cmd/gh-aw/main_help_text_test.go to cover both --gh-aw-ref conflict cases.

Comment thread cmd/gh-aw/main.go Outdated
- Only processes workflows in the default .github/workflows directory

Action mode controls how gh-aw action scripts are referenced in compiled workflows.
Three flags govern this and only one should be used at a time:

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] "Only one should be used at a time" contradicts the documented ability to combine --action-tag with --action-mode action. Users who read only this header will think the flags are mutually exclusive, then be confused when the flag help says otherwise.

💡 Suggested fix

Replace:

Three flags govern this and only one should be used at a time:

With something like:

Three flags govern this. --gh-aw-ref is mutually exclusive with the other two;
--action-tag and --action-mode may be combined (e.g. --action-mode action --action-tag v1.2.3):

This matches the actual MarkFlagsMutuallyExclusive enforcement and the per-flag descriptions below.

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.

Updated in d11d8c0: replaced the contradictory “only one should be used at a time” header with wording that matches the actual exclusivity rules.

Comment thread pkg/cli/compile_compiler_setup.go Outdated
// setupActionMode configures how gh-aw action scripts are referenced in compiled workflows.
// Priority order when both actionTag and actionMode are provided (after --gh-aw-ref resolution):
// - actionTag != "" → pin to that SHA/tag; use ActionModeRelease unless actionMode is explicitly "action"
// - actionMode != "" → honour the explicit mode

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.

[/diagnose] The priority-order comment implies sequential evaluation, but the early return on line 223 means the actionMode != "" branch is never reached when actionTag is set — so --action-mode is effectively ignored (except for the action special-case handled inside the tag block). A reader following the godoc might incorrectly conclude that --action-mode dev --action-tag v1.2.3 would honor dev mode.

💡 Suggested clarification
// setupActionMode configures how gh-aw action scripts are referenced in compiled workflows.
// Resolution order (first match wins; function returns early after the actionTag branch):
//   - actionTag != ""  → pin to that SHA/tag; use ActionModeRelease unless actionMode is
//                        explicitly "action". Returns without evaluating actionMode further.
//   - actionMode != "" → honour the explicit mode (only reached when actionTag is empty)
//   - neither set      → auto-detect from the binary build type via DetectActionMode

The "returns without evaluating" note prevents future contributors from adding code after the return under the false belief it will fire for the tag-set case.

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.

Updated in d11d8c0: clarified setupActionMode priority wording so the actionTag branch and actionTag == "" condition for explicit actionMode are clear.

mode := workflow.ActionMode(actionMode)
if !mode.IsValid() {
return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', 'script', or 'action'", actionMode)
return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', or 'action'", actionMode)

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.

[/diagnose] ActionModeScript.IsValid() still returns true (see action_mode.go:36), so --action-mode script silently passes validation even though the error message no longer lists it as valid. Decide whether script should be accepted at the CLI layer:

💡 Options

Option A — keep it as an undocumented internal escape hatch (current state), but add a comment here to make the gap intentional:

// Note: ActionModeScript is intentionally excluded — it is an internal mode
// not meant to be set directly by end users via the CLI.
return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', or 'action'", actionMode)

Option B — explicitly block it at the CLI layer so the error message and accepted set agree:

if mode == workflow.ActionModeScript || !mode.IsValid() {
    return fmt.Errorf("invalid action mode '%s'. Must be 'dev', 'release', or 'action'", actionMode)
}

Option A is lower-risk if internal automation relies on script being passable.

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 d11d8c0: documented in validateActionModeConfig that ActionModeScript is intentionally internal and omitted from user-facing CLI mode guidance.

Comment thread cmd/gh-aw/main.go Outdated
compileCmd.Flags().String("action-mode", "", "Action script inlining mode (inline, dev, release). Auto-detected if not specified")
compileCmd.Flags().String("action-tag", "", "Override action SHA or tag for actions/setup (overrides action-mode to release). Accepts full SHA or tag name")
compileCmd.Flags().String("action-mode", "", "How gh-aw action scripts are referenced in compiled workflows: 'dev' uses local paths (for developing gh-aw itself), 'release' emits SHA-pinned remote refs from github/gh-aw, 'action' uses the github/gh-aw-actions repository. Auto-detected from the binary build type if not specified")
compileCmd.Flags().String("action-tag", "", "Pin compiled workflows to a specific version of gh-aw actions. Accepts a full 40-character commit SHA or a version tag (e.g. v1, v1.2.3). Sets --action-mode to 'release' unless --action-mode action is also specified. Cannot be combined with --gh-aw-ref; use --gh-aw-ref when you want to resolve a branch or tag name to its current 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.

[/zoom-out] "full 40-character commit SHA" is overly precise — SHA-1 is 40 hex chars, but the codebase also handles abbreviated SHAs in other contexts and does not validate length here. The prior help text said "full SHA" without the character count, which is more future-proof.

💡 Suggested wording

Change Accepts a full 40-character commit SHA or a version tag to Accepts a full commit SHA or a version tag — no length constraint is enforced by the flag parser, so stating one as fact will mislead users who pass a short SHA.

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.

Updated in d11d8c0: --action-tag flag help now says “full commit SHA” (without hard-coding 40 characters).

@github-actions

Copy link
Copy Markdown
Contributor

@copilot review all comments and address the requested changes. Then summarize any remaining blockers or follow-up checks.

Generated by 👨‍🍳 PR Sous Chef ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

… test

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

Copilot AI commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Completed a pr-finisher pass in commit d11d8c0: addressed unresolved in-scope review feedback, added a regression test for --gh-aw-ref mutual exclusion, and ran local validation (make fmt, make lint, make test-unit, make test, make recompile) plus parallel validation.

@pelikhan pelikhan merged commit 88b9e38 into main Jun 17, 2026
25 checks passed
@pelikhan pelikhan deleted the copilot/review-go-compile-cli-args branch June 17, 2026 01:33
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.

3 participants