Skip to content

Duplicate code: spinner+gh command execution logic duplicated in github_cli helpers #26414

@github-actions

Description

@github-actions

Overview

Recent-commit duplicate analysis found one significant structural duplication pattern in pkg/workflow/github_cli.go introduced/expanded during context-propagation work.

The two internal helpers runGHWithSpinnerContext and runGHWithSpinner are nearly identical across ~24 lines each, differing mainly in command construction (ExecGHContext vs ExecGH). This exceeds the duplication threshold (>10 lines).

Critical Information

  • Pattern: Near-identical helper functions with only command-constructor variance
  • Severity: Medium
  • Occurrences: 2
  • Primary locations:
    • pkg/workflow/github_cli.go:101
    • pkg/workflow/github_cli.go:129
  • Risk: Behavior drift between context/non-context execution paths (spinner behavior, stderr handling, combined output path)
Duplication Details

Pattern Details

Both functions execute the same control flow:

  1. Build command
  2. If TTY, run spinner start/stop around command execution
  3. Branch on combined to use CombinedOutput vs Output
  4. Apply enrichGHError to non-combined path
  5. Return output

Only the command creation line changes:

  • cmd := ExecGHContext(ctx, args...)
  • cmd := ExecGH(args...)

Code Example

func runGHWithSpinnerContext(ctx context.Context, spinnerMessage string, combined bool, args ...string) ([]byte, error) {
	cmd := ExecGHContext(ctx, args...)
	// ... spinner + combined/output handling ...
}

func runGHWithSpinner(spinnerMessage string, combined bool, args ...string) ([]byte, error) {
	cmd := ExecGH(args...)
	// ... same spinner + combined/output handling ...
}

Impact Analysis

  • Maintainability: Future fixes to one path can be missed in the other.
  • Bug Risk: Error-enrichment/spinner behavior can diverge subtly across context and non-context invocations.
  • Code Bloat: Duplicate logic in a central command-execution utility.

Refactoring Recommendations

  1. Extract a single executor that accepts a command factory
  • Suggested target: pkg/workflow/github_cli.go
  • Approach: one private helper like runGHWithSpinnerCmd(cmd *exec.Cmd, spinnerMessage string, combined bool) and keep thin wrappers for context/non-context command construction.
  • Estimated effort: 1-2 hours
  • Benefit: single execution path for spinner and output handling
  1. Add focused regression tests for both wrappers
  • Ensure both wrappers share identical behavior for:
    • TTY/non-TTY branches
    • combined=true/false
    • error enrichment
  • Estimated effort: 1 hour

Implementation Checklist

  • Consolidate duplicated execution logic into one private helper
  • Keep API compatibility for RunGH* functions
  • Add/adjust tests for context and non-context parity
  • Validate no behavior change in existing callers

Analysis Metadata

  • Analyzed files: 15 non-test .go/.cjs files from latest commits
  • Detection method: Serena symbol analysis + patch inspection
  • Commits analyzed: e44f4e7fec20d266caec65919fa1a8ac57948e50, 920a4e8bdf57936b61774d9a82be8eb1e20c109c, f3831d82a6662de794d85a418d368c6dede6b115
  • Analysis date: 2026-04-15 UTC

References:

Warning

⚠️ Firewall blocked 4 domains

The following domains were blocked by the firewall during workflow execution:

  • ab.chatgpt.com
  • api.github.com
  • chatgpt.com
  • github.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "ab.chatgpt.com"
    - "api.github.com"
    - "chatgpt.com"
    - "github.com"

See Network Configuration for more information.

Generated by Duplicate Code Detector ·

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions