Skip to content

Commit ce17949

Browse files
CopilotlpcoxCopilot
authored
fix: use GH_HOST env var instead of --hostname flag for gh repo view and gh pr create (#26311)
* Initial plan * fix: use GH_HOST env var instead of --hostname flag for gh repo view and gh pr create The --hostname flag is only valid for `gh api`, not for `gh repo view` or `gh pr create`. This caused failures on GHES/Proxima with: "unknown flag: --hostname" Replace the invalid --hostname flag with the GH_HOST environment variable, which is the correct approach for these commands. Extract the env var setup into a reusable setGHHostEnv helper to avoid duplication. Fixes: gh aw add-wizard --create-pull-request on GHES/Proxima deployments Agent-Logs-Url: https://github.com/github/gh-aw/sessions/9ad4e273-0356-4650-a286-ade3682b979c Co-authored-by: lpcox <[email protected]> * fix: replace --hostname with GH_HOST env var for non-api gh commands Address review feedback on the original PR: 1. Add RunGHWithHost() and SetGHHostEnv() to pkg/workflow/github_cli.go as shared helpers, preserving spinner UX and enrichGHError wrapping. 2. Fix pr_command.go to use RunGHWithHost() for gh repo view and gh pr create (restores spinner + error enrichment lost in original PR). 3. Fix audit.go:588 — gh run view also does not support --hostname. This was the third affected call site documented in #26310 but missed in the original PR. 4. Remove the local setGHHostEnv() from pr_command.go in favor of the shared exported SetGHHostEnv() in github_cli.go. 5. Add TestSetGHHostEnv with 5 test cases covering github.com no-op, empty host no-op, GHES host, Proxima host, and append-to-existing-env. Fixes #26310 Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: lpcox <[email protected]> Co-authored-by: Landon Cox <[email protected]> Co-authored-by: Copilot <[email protected]>
1 parent c25673e commit ce17949

File tree

4 files changed

+111
-22
lines changed

4 files changed

+111
-22
lines changed

pkg/cli/audit.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -580,14 +580,10 @@ func auditJobRun(runID int64, jobID int64, stepNumber int, owner, repo, hostname
580580
return fmt.Errorf("failed to create output directory: %w", err)
581581
}
582582

583-
// Fetch job logs using gh CLI
583+
// Fetch job logs using gh CLI.
584+
// Use GH_HOST env var instead of --hostname (which is only valid for gh api, not gh run view).
584585
args := []string{"run", "view"}
585586

586-
// Add hostname flag if specified (for GitHub Enterprise)
587-
if hostname != "" && hostname != "github.com" {
588-
args = append(args, "--hostname", hostname)
589-
}
590-
591587
// Add repository flag if specified
592588
if owner != "" && repo != "" {
593589
args = append(args, "-R", fmt.Sprintf("%s/%s", owner, repo))
@@ -600,7 +596,9 @@ func auditJobRun(runID int64, jobID int64, stepNumber int, owner, repo, hostname
600596
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Executing: gh "+strings.Join(args, " ")))
601597
}
602598

603-
output, err := workflow.RunGHCombined("Fetching job logs...", args...)
599+
cmd := workflow.ExecGH(args...)
600+
workflow.SetGHHostEnv(cmd, hostname)
601+
output, err := cmd.CombinedOutput()
604602
if err != nil {
605603
return fmt.Errorf("failed to fetch job logs: %w\nOutput: %s", err, string(output))
606604
}

pkg/cli/pr_command.go

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -771,14 +771,9 @@ func createPR(branchName, title, body string, verbose bool) (int, string, error)
771771
// repositories are targeted correctly instead of defaulting to github.com.
772772
remoteHost := getHostFromOriginRemote()
773773

774-
// Build gh repo view args, adding --hostname for GHES instances.
775-
repoViewArgs := []string{"repo", "view", "--json", "owner,name"}
776-
if remoteHost != "github.com" {
777-
repoViewArgs = append(repoViewArgs, "--hostname", remoteHost)
778-
}
779-
780-
// Get the current repository info to ensure PR is created in the correct repo
781-
repoOutput, err := workflow.RunGH("Fetching repository info...", repoViewArgs...)
774+
// Get the current repository info to ensure PR is created in the correct repo.
775+
// Use GH_HOST env var instead of --hostname (which is only valid for gh api, not gh repo view).
776+
repoOutput, err := workflow.RunGHWithHost("Fetching repository info...", remoteHost, "repo", "view", "--json", "owner,name")
782777
if err != nil {
783778
return 0, "", fmt.Errorf("failed to get current repository info: %w", err)
784779
}
@@ -797,14 +792,10 @@ func createPR(branchName, title, body string, verbose bool) (int, string, error)
797792
repoSpec := fmt.Sprintf("%s/%s", repoInfo.Owner.Login, repoInfo.Name)
798793

799794
// Build gh pr create args. Explicitly specifying --repo ensures the PR is created in the
800-
// current repo (not an upstream fork). For GHES instances, --hostname routes the request
801-
// to the correct GitHub Enterprise host instead of defaulting to github.com.
795+
// current repo (not an upstream fork). Use GH_HOST env var instead of --hostname
796+
// (which is only valid for gh api, not gh pr create).
802797
prCreateArgs := []string{"pr", "create", "--repo", repoSpec, "--title", title, "--body", body, "--head", branchName}
803-
if remoteHost != "github.com" {
804-
prCreateArgs = append(prCreateArgs, "--hostname", remoteHost)
805-
}
806-
807-
output, err := workflow.RunGH("Creating pull request...", prCreateArgs...)
798+
output, err := workflow.RunGHWithHost("Creating pull request...", remoteHost, prCreateArgs...)
808799
if err != nil {
809800
// Try to get stderr for better error reporting
810801
var exitError *exec.ExitError

pkg/workflow/github_cli.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,43 @@ func RunGHContext(ctx context.Context, spinnerMessage string, args ...string) ([
184184
func RunGHCombined(spinnerMessage string, args ...string) ([]byte, error) {
185185
return runGHWithSpinner(spinnerMessage, true, args...)
186186
}
187+
188+
// RunGHWithHost executes a gh CLI command with a spinner, targeting a specific GitHub host.
189+
// For non-github.com hosts (GHES, Proxima/data residency), the GH_HOST environment variable
190+
// is set on the command. This is necessary because most gh subcommands (repo, pr, run, etc.)
191+
// do not accept a --hostname flag — only `gh api` does.
192+
//
193+
// Usage:
194+
//
195+
// output, err := RunGHWithHost("Fetching repo info...", "myorg.ghe.com", "repo", "view", "--json", "owner,name")
196+
func RunGHWithHost(spinnerMessage string, host string, args ...string) ([]byte, error) {
197+
cmd := ExecGH(args...)
198+
SetGHHostEnv(cmd, host)
199+
200+
if tty.IsStderrTerminal() {
201+
spinner := console.NewSpinner(spinnerMessage)
202+
spinner.Start()
203+
output, err := cmd.Output()
204+
err = enrichGHError(err)
205+
spinner.Stop()
206+
return output, err
207+
}
208+
209+
output, err := cmd.Output()
210+
return output, enrichGHError(err)
211+
}
212+
213+
// SetGHHostEnv sets the GH_HOST environment variable on the command for non-github.com hosts.
214+
// This is needed for GitHub Enterprise Server (GHES) and Proxima (data residency) instances
215+
// because commands like `gh repo view`, `gh pr create`, and `gh run view` do not accept a
216+
// --hostname flag (unlike `gh api` which does).
217+
func SetGHHostEnv(cmd *exec.Cmd, host string) {
218+
if host == "" || host == "github.com" {
219+
return
220+
}
221+
if cmd.Env == nil {
222+
cmd.Env = append(os.Environ(), "GH_HOST="+host)
223+
} else {
224+
cmd.Env = append(cmd.Env, "GH_HOST="+host)
225+
}
226+
}

pkg/workflow/github_cli_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,3 +417,63 @@ func TestEnrichGHError(t *testing.T) {
417417
assert.Contains(t, enriched.Error(), "exit status 1", "enriched error should still contain original error")
418418
})
419419
}
420+
421+
func TestSetGHHostEnv(t *testing.T) {
422+
tests := []struct {
423+
name string
424+
host string
425+
expectSet bool
426+
initialEnv []string
427+
}{
428+
{
429+
name: "github.com is a no-op",
430+
host: "github.com",
431+
expectSet: false,
432+
},
433+
{
434+
name: "empty host is a no-op",
435+
host: "",
436+
expectSet: false,
437+
},
438+
{
439+
name: "GHES host sets GH_HOST",
440+
host: "myorg.ghe.com",
441+
expectSet: true,
442+
},
443+
{
444+
name: "Proxima host sets GH_HOST",
445+
host: "verizon.ghe.com",
446+
expectSet: true,
447+
},
448+
{
449+
name: "appends to existing env",
450+
host: "myorg.ghe.com",
451+
expectSet: true,
452+
initialEnv: []string{"FOO=bar"},
453+
},
454+
}
455+
456+
for _, tt := range tests {
457+
t.Run(tt.name, func(t *testing.T) {
458+
cmd := exec.Command("echo", "test")
459+
if tt.initialEnv != nil {
460+
cmd.Env = tt.initialEnv
461+
}
462+
463+
SetGHHostEnv(cmd, tt.host)
464+
465+
if !tt.expectSet {
466+
if tt.initialEnv == nil {
467+
assert.Nil(t, cmd.Env, "Env should remain nil for %s", tt.host)
468+
}
469+
return
470+
}
471+
472+
require.NotNil(t, cmd.Env, "Env should be set for host %s", tt.host)
473+
found := slices.ContainsFunc(cmd.Env, func(e string) bool {
474+
return e == "GH_HOST="+tt.host
475+
})
476+
assert.True(t, found, "GH_HOST=%s should be in cmd.Env", tt.host)
477+
})
478+
}
479+
}

0 commit comments

Comments
 (0)