Skip to content

Refactor GitHub CLI wrappers to eliminate duplication#15137

Merged
pelikhan merged 2 commits intomainfrom
copilot/refactor-duplicate-functions
Feb 12, 2026
Merged

Refactor GitHub CLI wrappers to eliminate duplication#15137
pelikhan merged 2 commits intomainfrom
copilot/refactor-duplicate-functions

Conversation

Copy link
Contributor

Copilot AI commented Feb 12, 2026

Five GitHub CLI wrapper functions contained duplicate token configuration and spinner logic across ~50 lines.

Changes

Extracted shared implementations:

  • setupGHCommand(ctx, args...) - Consolidates token environment setup for ExecGH and ExecGHContext
  • runGHWithSpinner(msg, combined, args...) - Consolidates spinner display for RunGH and RunGHCombined

Before:

func ExecGH(args ...string) *exec.Cmd {
    ghToken := os.Getenv("GH_TOKEN")
    githubToken := os.Getenv("GITHUB_TOKEN")
    // ... 20 lines of token handling ...
}

func ExecGHContext(ctx context.Context, args ...string) *exec.Cmd {
    ghToken := os.Getenv("GH_TOKEN")
    githubToken := os.Getenv("GITHUB_TOKEN")
    // ... 20 identical lines ...
}

After:

func setupGHCommand(ctx context.Context, args ...string) *exec.Cmd {
    // Single implementation of token handling
}

func ExecGH(args ...string) *exec.Cmd {
    return setupGHCommand(nil, args...)
}

func ExecGHContext(ctx context.Context, args ...string) *exec.Cmd {
    return setupGHCommand(ctx, args...)
}

Public API signatures unchanged. 64+ call sites require no modifications.

Original prompt

Review and refactor/mergr duplicate functions

Functions to Document

ExecGH (line 25) - Basic gh CLI execution with token handling
ExecGHContext (line 59) - Context-aware variant
ExecGHWithOutput (line 91) - Captures and returns output
RunGH (line 103) - Different execution semantics
RunGHCombined (line 125) - Combined stdout/stderr handling


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Merge ExecGH and ExecGHContext into shared setupGHCommand helper
- Merge RunGH and RunGHCombined into shared runGHWithSpinner helper
- Add comprehensive tests for helper functions
- All existing tests pass with no behavior changes
- Reduce code duplication by ~50 lines

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Review and refactor duplicate functions Refactor GitHub CLI wrappers to eliminate duplication Feb 12, 2026
Copilot AI requested a review from pelikhan February 12, 2026 11:40
@pelikhan pelikhan marked this pull request as ready for review February 12, 2026 11:46
Copilot AI review requested due to automatic review settings February 12, 2026 11:46
Copy link
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.

Pull request overview

This PR refactors the GitHub CLI wrapper utilities in pkg/workflow by extracting shared logic for command setup (token/env handling) and spinner-wrapped execution, while keeping the public API unchanged.

Changes:

  • Extracted setupGHCommand(ctx, args...) to centralize exec.Cmd creation and GH token environment behavior for ExecGH/ExecGHContext.
  • Extracted runGHWithSpinner(msg, combined, args...) to centralize spinner + Output/CombinedOutput behavior for RunGH/RunGHCombined.
  • Updated/expanded unit tests and migrated assertions to testify (assert/require).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
pkg/workflow/github_cli.go Introduces shared helpers for gh command setup and spinner execution; updates wrappers to delegate to helpers.
pkg/workflow/github_cli_test.go Updates assertions to testify and adds direct tests for the new helper(s).
Comments suppressed due to low confidence (1)

pkg/workflow/github_cli.go:20

  • The setupGHCommand doc comment says it’s “core implementation shared by ExecGH and ExecGHContext” and implies token handling is tied to go-gh/v2 execution, but this helper only builds an exec.Cmd (and never uses go-gh/v2). Update the comment to reflect the actual behavior (exec.Command/exec.CommandContext + optional GH_TOKEN env injection), or change the implementation to truly use go-gh/v2 when tokens are available.
// setupGHCommand creates an exec.Cmd for gh CLI with proper token configuration.
// This is the core implementation shared by ExecGH and ExecGHContext.
// When ctx is nil, it uses exec.Command; when ctx is provided, it uses exec.CommandContext.
func setupGHCommand(ctx context.Context, args ...string) *exec.Cmd {

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

Comment on lines +28 to +37
if ghToken != "" || githubToken != "" {
githubCLILog.Printf("Using gh CLI via go-gh/v2 for command with context: gh %v", args)
} else {
githubCLILog.Printf("No token available, using default gh CLI with context for command: gh %v", args)
}
} else {
cmd = exec.Command("gh", args...)
if ghToken != "" || githubToken != "" {
githubCLILog.Printf("Using gh CLI via go-gh/v2 for command: gh %v", args)
} else {
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

These log lines claim the command is being run “via go-gh/v2”, but the code path is always exec.Command/exec.CommandContext. This is misleading for debugging/observability; consider changing the message to reflect the real execution mechanism (or refactor to actually route through go-gh/v2 here).

This issue also appears on line 17 of the same file.

Copilot uses AI. Check for mistakes.
Comment on lines +362 to +366
originalGHToken := os.Getenv("GH_TOKEN")
originalGitHubToken := os.Getenv("GITHUB_TOKEN")
defer func() {
os.Setenv("GH_TOKEN", originalGHToken)
os.Setenv("GITHUB_TOKEN", originalGitHubToken)
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

This test restores GH_TOKEN/GITHUB_TOKEN by always calling os.Setenv with the prior value. If the variable was originally unset, this will incorrectly leave it set to an empty string for subsequent tests. Capture whether the vars were set (os.LookupEnv) and Unsetenv when they weren’t, or use t.Setenv for automatic cleanup.

Suggested change
originalGHToken := os.Getenv("GH_TOKEN")
originalGitHubToken := os.Getenv("GITHUB_TOKEN")
defer func() {
os.Setenv("GH_TOKEN", originalGHToken)
os.Setenv("GITHUB_TOKEN", originalGitHubToken)
originalGHToken, ghTokenWasSet := os.LookupEnv("GH_TOKEN")
originalGitHubToken, githubTokenWasSet := os.LookupEnv("GITHUB_TOKEN")
defer func() {
if ghTokenWasSet {
_ = os.Setenv("GH_TOKEN", originalGHToken)
} else {
_ = os.Unsetenv("GH_TOKEN")
}
if githubTokenWasSet {
_ = os.Setenv("GITHUB_TOKEN", originalGitHubToken)
} else {
_ = os.Unsetenv("GITHUB_TOKEN")
}

Copilot uses AI. Check for mistakes.
Comment on lines +392 to +397
// Just verify the function can be called
// We expect it to fail since gh command requires auth
_, err := runGHWithSpinner("Test spinner...", tt.combined, "auth", "status")
// We don't care about the error - we just want to verify the function exists
// and doesn't panic when called
_ = err
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

This “helper exists” test shells out to the real gh binary (gh auth status) and then ignores the returned error. That makes the unit test non-deterministic (depends on gh being installed/configured and can be slow/flaky) while also not asserting any behavior. Prefer a deterministic approach (e.g., point PATH to a small stub gh script/binary in a temp dir and assert Output vs CombinedOutput semantics), or drop this test and rely on tests that don’t execute external commands.

Copilot uses AI. Check for mistakes.
@pelikhan pelikhan merged commit fb85724 into main Feb 12, 2026
153 checks passed
@pelikhan pelikhan deleted the copilot/refactor-duplicate-functions branch February 12, 2026 11:51
Sign up for free to 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.

2 participants