Skip to content

Refactor deploy command orchestration to satisfy largefunc limits in pkg/cli#36144

Merged
pelikhan merged 7 commits into
mainfrom
copilot/lint-monster-fix-function-length-violations-again
May 31, 2026
Merged

Refactor deploy command orchestration to satisfy largefunc limits in pkg/cli#36144
pelikhan merged 7 commits into
mainfrom
copilot/lint-monster-fix-function-length-violations-again

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 31, 2026

pkg/cli has widespread function-length violations under the 60-line custom lint rule. This PR addresses the deploy command slice by decomposing long command wiring/orchestration paths into focused internal helpers while preserving CLI behavior and public surfaces.

  • Command construction split (NewDeployCommand)

    • Extracted arg validation and flag wiring from inline closures into dedicated helpers:
      • validateDeployArgs
      • registerDeployFlags
      • runDeployCommand
      • parseDeployCommandOptions
    • Kept all existing flags, usage text, and option semantics unchanged.
  • Execution flow decomposition (runDeploy)

    • Split deploy lifecycle into explicit stages:
      • checkout prep: prepareDeployCheckout
      • update pass: runDeployUpdatePass
      • add pass: runDeployAddPass
      • compile pass: runDeployCompilePass
      • PR creation: createDeployPR
    • Preserved existing sequencing (update -> add -> compile --purge -> create PR) and existing error paths.
  • Error-handling consistency

    • Wrapped engine validation failures with contextual error text:
      • failed to validate engine: %w
  • Focused test coverage for extracted parsing paths

    • Added cases for:
      • --name with multiple workflows (short-circuit validation behavior)
      • invalid --cool-down
      • zero-value return contract on parse errors
      • engine validation error propagation
func parseDeployCommandOptions(cmd *cobra.Command, workflows []string, validateEngine func(string) error) (AddOptions, time.Duration, error) {
    // parse flags
    // validate name/workflow cardinality
    // validate engine
    // parse cool-down
    // build AddOptions
}

Copilot AI and others added 4 commits May 31, 2026 17:45
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>
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 changed the title [WIP] Fix function length violations in pkg/cli Refactor deploy command orchestration to satisfy largefunc limits in pkg/cli May 31, 2026
Copilot AI requested a review from gh-aw-bot May 31, 2026 17:51
@pelikhan pelikhan marked this pull request as ready for review May 31, 2026 18:04
Copilot AI review requested due to automatic review settings May 31, 2026 18:04
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

🧪 Test Quality Sentinel completed test quality analysis.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

PR Code Quality Reviewer completed the code quality review.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 31, 2026

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment thread pkg/cli/deploy_command.go
Comment on lines +137 to +139
if err := validateEngine(engineOverride); err != nil {
return AddOptions{}, 0, fmt.Errorf("failed to validate engine: %w", err)
}
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean refactoring — no blocking issues

All execution-ordering invariants from the original runDeploy are preserved: the defer os.Chdir(originalDir) is correctly re-anchored in runDeploy; resolveDeployWorkflowSpecs is still called with originalDir before the second os.Chdir(checkoutDir); all sub-passes (Update -> Add -> Compile -> PR) run in the right CWD context.

The one observable behaviour change — engine-validation errors are now wrapped with failed to validate engine — is intentional, tested with errors.Is, and an improvement for user clarity.

Test coverage for parseDeployCommandOptions is solid: cardinality short-circuit, invalid cool-down, and engine-error propagation are all covered.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 1.4M

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (180 new lines in pkg/cli, above the 100-line threshold) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/36144-decompose-deploy-command-orchestration.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 — it was generated from the PR diff and mirrors the precedent set by ADR-36064 and ADR-36012 for prior largefunc decomposition work.
  2. Complete the missing sections — confirm the alternatives and consequences match your reasoning, and add any context the diff couldn't convey.
  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-36144: Decompose Deploy Command Orchestration into Focused Helpers

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

❓ Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Even mechanical decompositions encode a real decision — here, honor the largefunc gate via named helpers rather than exempting the file — and recording it keeps the policy consistent across the many pkg/cli slices still to be refactored.

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

🔒 Blocking: link the completed ADR in the PR body to clear this gate.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · opus48 776.3K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /zoom-out, /improve-codebase-architecture, and /tdd — commenting with suggestions, no blocking issues.

📋 Key Themes & Highlights

Key Themes

  • Implicit caller contract on prepareDeployCheckout: returns dirs but leaves chdir + defer to the caller as an undocumented 3-step sequence (line 59)
  • Over-wide AddOptions threading: stage helpers accept the full 10-field struct but use 2–3 fields each — dependencies are invisible
  • Engine error wrapping: subtle observable behaviour change; existing tests use assert.ErrorIs correctly but worth a grep for any message-matching tests elsewhere
  • Test coverage gap on stage helpers: the 5 new stage functions are now independently testable but have no tests yet

Positive Highlights

  • ✅ Clean linear stage sequence in runDeploy — easy to read and follow
  • ✅ New tests are well-structured with require for setup, assert for assertions, and parallel execution
  • createDeployPR correctly scopes to only what it needs (verbose bool), unlike the stage helpers
  • ✅ Zero change to public signatures, flags, or user-visible error messages (except the intentional engine validation wrapping)

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 1.7M

Comment thread pkg/cli/deploy_command.go

resolvedWorkflows := resolveDeployWorkflowSpecs(workflows, originalDir)

if err := os.Chdir(checkoutDir); err != nil {
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] prepareDeployCheckout returns the checkout dir but leaves Chdir to the caller — the 3-step sequence (defer restore → chdir → resolveWorkflowSpecs) is an implicit contract that future callers can easily break.

💡 Suggestion

Either have prepareDeployCheckout also Chdir into the checkout and return an undo func, or add a doc comment on the function explicitly documenting the caller's obligations:

// prepareDeployCheckout clones targetRepo and returns (checkoutDir, originalDir).
// The caller is responsible for:
//   1. deferring os.Chdir(originalDir) before any other work
//   2. calling os.Chdir(checkoutDir) before invoking stage helpers
func prepareDeployCheckout(...) (string, string, error)

Without this, the contract is invisible and fragile.

Comment thread pkg/cli/deploy_command.go
return checkoutDir, originalDir, nil
}

func runDeployUpdatePass(ctx context.Context, addOpts AddOptions, coolDown time.Duration) error {
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.

[/improve-codebase-architecture] runDeployUpdatePass accepts full AddOptions (10+ fields) but only reads Verbose and WorkflowDir from it. The same is true for runDeployCompilePass and runDeployAddPass.

💡 Why this matters

Passing the whole struct makes the function's actual dependencies invisible — a reader can't tell what runDeployUpdatePass actually needs without reading its body. It also tightly couples these stage helpers to AddOptions shape changes.

Since the goal here is cleaner decomposition, consider threading only the fields each stage truly needs, or at minimum adding a comment on each helper listing which fields it reads.

Comment thread pkg/cli/deploy_command.go Outdated
return AddOptions{}, 0, errors.New("--name flag cannot be used when adding multiple workflows at once")
}
if err := validateEngine(engineOverride); err != nil {
return AddOptions{}, 0, fmt.Errorf("failed to validate engine: %w", 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.

[/zoom-out] The engine validation error is now wrapped with "failed to validate engine: %w" — this wasn't present before and is a silent observable behaviour change for any caller or test that matches on the raw error message.

💡 Detail

The PR description calls this out as an intentional consistency improvement, which is fine. But the new test TestParseDeployCommandOptions_EngineValidationError uses assert.ErrorIs (correct), while the existing broader test suite may have strings.Contains(err.Error(), ...) checks that now need updating. Worth a quick grep before merging:

grep -r 'validateEngine\|engine.*invalid\|invalid.*engine' pkg/cli/ --include='*_test.go'

assert.ErrorIs(t, err, expectedErr)
assert.Equal(t, "custom-engine", validatedEngine)
assert.Equal(t, AddOptions{}, opts)
assert.Zero(t, coolDown)
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] Tests cover parseDeployCommandOptions error paths well, but the five new stage helpers (prepareDeployCheckout, runDeployUpdatePass, runDeployAddPass, runDeployCompilePass, createDeployPR) have no unit tests. The refactoring makes them independently testable for the first time.

💡 Suggestion

This PR's primary goal is lint compliance, so skipping stage-level tests is a reasonable trade-off. But since these functions now have clear signatures, they're much easier to test in isolation than the original monolith. Consider a follow-up to add at least:

  • TestRunDeployAddPass_SkipsWhenNoNewWorkflows — verifies the early-return path
  • TestRunDeployAddPass_ReturnsErrorOnAddFailure — error propagation

These are the highest-value additions given the existing happy-path is tested end-to-end.

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 95/100 — Excellent

Analyzed 3 new test(s) added in this PR: 3 design, 0 implementation, 0 guideline violations. All new tests cover error/edge-case paths for newly extracted helper functions. Minor: several assertions are missing descriptive message arguments.

📊 Metrics & Test Classification (3 new tests analyzed)
Metric Value
New/modified tests analyzed 3 (54 lines added to test file; 126 to production — ratio 0.43:1)
✅ Design tests (behavioral contracts) 3 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 3 (100%)
Duplicate test clusters 0
Test inflation detected No (0.43:1 ratio, well under 2:1 threshold)
🚨 Coding-guideline violations 0 (no mock libraries; //go:build !integration tag present ✅)
⚠️ Missing assertion messages Several assert.* calls lack a descriptive message argument

Test Classification Details

Test File Classification Issues Detected
TestParseDeployCommandOptions_NameFlagWithMultipleWorkflows pkg/cli/deploy_command_test.go:194 ✅ Design Error path: --name with multiple workflows returns the right error; verifies zero-value opts and zero coolDown returned on failure; missing assertion messages
TestParseDeployCommandOptions_InvalidCoolDown pkg/cli/deploy_command_test.go:213 ✅ Design Error path: unparseable duration string yields informative error; missing assertion messages
TestParseDeployCommandOptions_EngineValidationError pkg/cli/deploy_command_test.go:227 ✅ Design Error path: engine validator callback called with correct value; error wrapping preserved via errors.Is; missing assertion messages

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 3 new tests — unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests
⚠️ Flagged Tests — Requires Review (soft guideline: missing assertion messages)

No hard violations detected. The following soft guideline issue affects most assertions in the new tests.

⚠️ Missing assertion messages (across all 3 new tests)

The project guideline requires a descriptive message argument on every testify assertion call so CI failure output is self-explanatory without reading the source. Examples from the new tests:

// ❌ Missing message
assert.Contains(t, err.Error(), "--name flag cannot be used when adding multiple workflows at once")
assert.Equal(t, AddOptions{}, opts)
assert.Zero(t, coolDown)
assert.False(t, validateEngineCalled)

// ✅ With message
assert.Contains(t, err.Error(), "--name flag cannot be used", "--name + multi-workflow should fail")
assert.Equal(t, AddOptions{}, opts, "opts should be zero-value on error")
assert.Zero(t, coolDown, "coolDown should be zero on error")
assert.False(t, validateEngineCalled, "engine validator should not be called when --name check fails first")

The same pattern applies to the assert.* calls in TestParseDeployCommandOptions_InvalidCoolDown (lines 221–224) and TestParseDeployCommandOptions_EngineValidationError (lines 239–244).

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 3 new tests are behavioral error-path tests for the helper functions extracted in this refactor. No prohibited mock libraries or missing build tags. Please add descriptive message arguments to assert.* calls before merging.

📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls or CLI shape details
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · sonnet46 2.1M ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 95/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 3 new tests cover behavioral error-path contracts for the extracted helper functions. Please add descriptive message arguments to bare assert.* calls before merging.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot run copilot-review skill

…t with add/update commands

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 31, 2026

@copilot run copilot-review skill

Addressed the @copilot-pull-request-reviewer feedback: removed the redundant "failed to validate engine: %w" wrapping in parseDeployCommandOptions — the engine validator error is now returned directly, consistent with add_command.go and update_command.go. Fixed in 63ca5d8.

Copilot AI requested a review from pelikhan May 31, 2026 18:19
@github-actions github-actions Bot mentioned this pull request May 31, 2026
@github-actions
Copy link
Copy Markdown
Contributor

@copilot review all comments and address unresolved review feedback.

Generated by 👨‍🍳 PR Sous Chef · gpt54 13.2M ·

@github-actions
Copy link
Copy Markdown
Contributor

Please post a short completion plan for the deploy refactor blockers.

Generated by 👨‍🍳 PR Sous Chef · gpt54 13.2M ·

@github-actions
Copy link
Copy Markdown
Contributor

@copilot please post a short completion plan for the remaining deploy refactor blockers, including the ADR link needed to clear the design gate.

Generated by 👨‍🍳 PR Sous Chef · gpt54 5.5M ·

@pelikhan pelikhan merged commit 73ad414 into main May 31, 2026
25 of 26 checks passed
@pelikhan pelikhan deleted the copilot/lint-monster-fix-function-length-violations-again branch May 31, 2026 21:53
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.

[lint-monster] [Lint] Fix pkg/cli function length violations (369 issues)

4 participants