Skip to content

Auto-detect GHES host in compile for correct repo validation#40030

Merged
pelikhan merged 7 commits into
mainfrom
copilot/add-ghe-host-auto-detection
Jun 18, 2026
Merged

Auto-detect GHES host in compile for correct repo validation#40030
pelikhan merged 7 commits into
mainfrom
copilot/add-ghe-host-auto-detection

Conversation

Copilot AI commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

gh aw compile did not inherit the same GHES host auto-detection used by add and audit. As a result, --validate could send repo feature checks to the wrong GitHub host when GH_HOST was unset, and downstream host-pinning logic would not engage.

  • Compile startup host detection

    • Adds the existing git-remote-based host detection pattern to CompileWorkflows.
    • When GH_HOST is unset and the origin remote resolves to a non-github.com host, compile now calls workflow.SetDefaultGHHost(...) before validation and other gh-backed checks run.
    if os.Getenv("GH_HOST") == "" {
        if detectedHost := getHostFromOriginRemote(); detectedHost != "github.com" && detectedHost != "" {
            workflow.SetDefaultGHHost(detectedHost)
        }
    }
  • Behavior preserved for explicit/public host cases

    • Does not override an explicit GH_HOST from the environment.
    • Leaves github.com remotes unchanged.
  • Regression coverage

    • Adds focused compile-orchestrator tests covering:
      • GHES remote + unset GH_HOST → default host is set
      • explicit GH_HOST → compile does not override host selection
      • github.com remote → no-op behavior

Copilot AI and others added 3 commits June 18, 2026 11:24
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
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 [WIP] Add GHE host auto-detection to compile command for repo validation Auto-detect GHES host in compile for correct repo validation Jun 18, 2026
Copilot AI requested a review from pelikhan June 18, 2026 11:38
@pelikhan pelikhan marked this pull request as ready for review June 18, 2026 11:42
Copilot AI review requested due to automatic review settings June 18, 2026 11:42
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (>100 new lines in pkg/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/40030-auto-detect-ghes-host-during-compile.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 (compile now auto-detects the GHES host from the git origin remote).
  2. Complete the missing sections — confirm the context, refine the decision rationale, and verify the alternatives reflect what you actually considered.
  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-40030: Auto-detect GHES host during compile

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

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 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 (e.g., 40030-auto-detect-ghes-host-during-compile.md for PR #40030).

🔒 This PR cannot merge until an ADR is linked in the PR body.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ ·

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 pull request aligns gh aw compile with other commands by auto-detecting the GitHub host from the current repo’s git remote when GH_HOST is unset, ensuring repo validation and other gh-backed checks target the correct host (e.g., GHES) instead of defaulting to github.com.

Changes:

  • Add startup GH host auto-detection to CompileWorkflows and set workflow’s default GH host when a non-github.com remote is detected and GH_HOST is not set.
  • Add unit tests covering GHES remote detection, explicit GH_HOST non-overwrite behavior, and github.com no-op behavior.
  • Regenerate/update several workflow .lock.yml files to include awk in the allowed tool lists (consistent with shared Go source analysis tooling), and clarify the install script sync note.
Show a summary per file
File Description
pkg/cli/compile_orchestrator.go Adds git-remote-based host detection and sets the default GH host before validation.
pkg/cli/compile_orchestrator_host_test.go Adds regression coverage for compile host auto-detection and non-overwrite behavior.
actions/setup-cli/install.sh Updates the header note to clarify the canonical script source.
.github/workflows/spec-librarian.lock.yml Updates the compiled workflow lock to allow awk in the tool allowlist.
.github/workflows/go-fan.lock.yml Updates the compiled workflow lock to allow awk in the tool allowlist.
.github/workflows/daily-testify-uber-super-expert.lock.yml Updates the compiled workflow lock to allow awk in the tool allowlist.
.github/workflows/daily-file-diet.lock.yml Updates the compiled workflow lock to allow awk in the tool allowlist.

Copilot's findings

Tip

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

  • Files reviewed: 8/8 changed files
  • Comments generated: 0

@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 /diagnose and /tdd — 4 observations, none blocking. The fix itself is correct.

📋 Key Themes & Highlights

Key Themes

  • Condition inconsistency (compile_orchestrator.go): && detectedHost != "" is dead code that diverges from the identical pattern in add_interactive_orchestrator.go — safe to remove.
  • Test coverage gap: All three subtests use HTTPS remote URLs; SSH remotes (git@host:owner/repo.git) are a common GHES format and would complete the integration picture.
  • Cleanup pattern: require.NoError inside t.Cleanup should be assert.NoError to avoid early goroutine exit skipping other cleanup callbacks.
  • Mid-test env manipulation: The os.Unsetenv("GH_HOST") before the assertion in subtest 2 is correct but unexplained — a one-line comment would prevent future confusion.

Positive Highlights

  • ✅ Minimal, surgical fix — 7 lines that port a proven pattern already used in add_interactive_orchestrator.go and audit.go.
  • ✅ Three focused subtests cover all behavioural branches: GHES+unset, explicit env override, and public github.com no-op.
  • t.Cleanup used correctly for both global state (SetDefaultGHHost) and working directory restoration.
  • NoEmit: true keeps the test self-contained without requiring real GitHub auth.

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

}

if os.Getenv("GH_HOST") == "" {
if detectedHost := getHostFromOriginRemote(); detectedHost != "github.com" && detectedHost != "" {

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 && detectedHost != "" guard is dead code — getHostFromOriginRemote() always returns "github.com" on error (never an empty string). This creates a silent divergence from the sibling pattern in add_interactive_orchestrator.go which omits the check.

💡 Suggested fix

Remove the redundant guard to stay in sync with the existing pattern:

if detectedHost := getHostFromOriginRemote(); detectedHost != "github.com" {
    compileOrchestratorLog.Printf("Auto-detected GHES host from git remote: %s", detectedHost)
    workflow.SetDefaultGHHost(detectedHost)
}

Having the extra != "" check implies an edge case that does not exist and could mislead future readers.

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 the latest commit. The condition now uses else if detectedHost == "github.com" to reset defaultGHHost for watch-mode safety, while retaining && detectedHost != "" in the if branch as a defensive guard (in case a future code path in extractHostFromRemoteURL returns empty). This keeps the intent explicit without the silent inheritance concern.

require.NoError(t, err)
require.NoError(t, os.Chdir(tempDir))
t.Cleanup(func() {
require.NoError(t, os.Chdir(originalDir))

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] Using require.NoError inside t.Cleanup calls t.FailNow()runtime.Goexit() on the cleanup goroutine. If os.Chdir fails here, subsequent cleanup functions registered before this one (LIFO order) are skipped — including any cleanup that depends on the working directory being restored. assert.NoError is the safer idiomatic choice in cleanup callbacks.

💡 Suggested fix
t.Cleanup(func() {
    assert.NoError(t, os.Chdir(originalDir))
})

This ensures all cleanup functions run even if the Chdir fails, and the test is still marked as failed via assert.

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.

Fixed — replaced the manual os.Getwd() + t.Cleanup(func() { require.NoError(...) }) + os.Chdir sequence with a single t.Chdir(tempDir) call (Go 1.24+). This registers cleanup before the mutation and uses t.Fatal on failure rather than require.NoError inside a cleanup callback.

Comment thread pkg/cli/compile_orchestrator_host_test.go
Comment thread pkg/cli/compile_orchestrator_host_test.go
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 3 test(s): 3 design, 0 implementation, 0 guideline violations. One non-blocking guideline warning (missing assertion messages).

📊 Metrics & Test Classification (3 tests analyzed)
Metric Value
New/modified tests analyzed 3
✅ 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 YES (104 test lines vs. 7 production lines, ≈ 14.9:1; −10 pts)
🚨 Coding-guideline violations 0 (build tag ✅, no mock libraries)
Test File Classification Issues Detected
.../sets_default_host_from_GHE_origin_when_GH_HOST_is_unset compile_orchestrator_host_test.go:19 ✅ Design ⚠️ Missing assertion message
.../does_not_overwrite_default_host_when_GH_HOST_is_already_set compile_orchestrator_host_test.go:29 ✅ Design ⚠️ Missing assertion message
.../does_not_change_behavior_for_github.com_remotes compile_orchestrator_host_test.go:40 ✅ Design ⚠️ Missing assertion message

Go: 1 file (*_test.go); JavaScript: 0. Other languages: none detected.

⚠️ Flagged Tests — Non-blocking warnings (3 issues)

Missing assertion messages on assert.Equal calls (lines 26, 37, 47)

All three behavioral assertions are bare — no descriptive message argument. A failure will print only "not equal: ..." with no context about which scenario failed or why the expected value is correct.

Suggested fix — add a message as the third positional string argument:

// line 26
assert.Equal(t, "ghes.example.com", getGHHostFromCommandEnv(workflow.ExecGH("auth", "status")),
    "GH_HOST should be set to the GHES host when GH_HOST is unset")

// line 37
assert.Equal(t, "existing.default.test", getGHHostFromCommandEnv(workflow.ExecGH("auth", "status")),
    "existing default host should be preserved when GH_HOST env var is set")

// line 47
assert.Equal(t, "existing.default.test", getGHHostFromCommandEnv(workflow.ExecGH("auth", "status")),
    "github.com remote should not trigger host detection")

Note on test inflation: The ratio is high (≈ 14.9:1) but expected — the production change is a focused 7-line guard condition while the test requires real filesystem and Git repository setup. The infrastructure code (runCompileWorkflowsHostDetectionCheck, addOriginRemoteToTestRepo, getGHHostFromCommandEnv) is reusable scaffolding, not padding.

Verdict

Check passed. 0% implementation tests (threshold: 30%). All three subtests verify observable behavioral contracts of the GHES host auto-detection feature. The missing assertion messages are a non-blocking style warning.

🧪 Test quality analysis by Test Quality Sentinel ·

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

✅ Test Quality Sentinel: 90/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). All 3 subtests verify observable behavioral contracts of the GHES host auto-detection feature.

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

REQUEST_CHANGES — the 7-line production change is correct and consistent with the existing pattern; the issues are in the test file and a latent global-state concern in the production code.

Blocking themes

Test isolation: os.Chdir and os.Unsetenv leave global state dirty

  • os.Chdir(tempDir) in runCompileWorkflowsHostDetectionCheck is process-global. The cleanup is registered after the Chdir call; if the Chdir succeeds but a later require.NoError aborts the test, the cleanup still runs — but the bigger problem is that no parallel test can safely share this process while the directory is changed. Use t.Chdir (Go 1.24+) or register the restore before the mutation.
  • Sub-tests 1 and 3 call os.Unsetenv("GH_HOST") without saving/restoring the previous value. If GH_HOST was set before the test (e.g., developer machine, CI environment), it will be absent for all subsequent code in the binary.
  • Sub-test 2 mixes t.Setenv and bare os.Unsetenv on the same variable. The intent is correct (unset to force ExecGH to read defaultGHHost) but the pattern is fragile and misleading; a comment explaining the intent is the minimum required.

Production: SetDefaultGHHost is never reset after a compile

Consistent with the existing add and init paths, but CompileWorkflows is also called in watch mode. Once the GHES host is pinned on the first call, subsequent calls (potentially from a different repo) will silently inherit it. Consider resetting the global when no GHES host is detected, or documenting the single-call-per-process assumption.

🔎 Code quality review by PR Code Quality Reviewer


originalDir, err := os.Getwd()
require.NoError(t, err)
require.NoError(t, os.Chdir(tempDir))

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.

os.Chdir is a process-global side effect — cleanup is registered after the mutation, and concurrent tests will observe the temporary directory.

💡 Suggested fix

Register cleanup before calling os.Chdir, or use t.Chdir (Go 1.24+):

originalDir, err := os.Getwd()
require.NoError(t, err)
t.Cleanup(func() { require.NoError(t, os.Chdir(originalDir)) })
require.NoError(t, os.Chdir(tempDir))

As written, if os.Chdir(tempDir) fails, require.NoError calls t.FailNow/runtime.Goexit before the t.Cleanup line is ever reached — so the working directory is permanently corrupted for the test binary. Additionally, os.Chdir is shared across all goroutines in the process, meaning any concurrently scheduled work (including sub-tests that might gain t.Parallel() later) will observe the temporary directory. Use t.Chdir if available, or at minimum register the restore before the mutation.

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.

Fixed — replaced the manual os.Getwd() / os.Chdir / t.Cleanup sequence with t.Chdir(tempDir) (Go 1.24+), which registers cleanup before the mutation and eliminates the risk of a corrupted working directory if the initial Chdir fails.

Comment thread pkg/cli/compile_orchestrator_host_test.go
Comment thread pkg/cli/compile_orchestrator_host_test.go
default:
}

if os.Getenv("GH_HOST") == "" {

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.

SetDefaultGHHost is never reset — repeated CompileWorkflows calls in the same process will silently inherit the first detected GHES host even when switching repos.

💡 Detail

This matches the existing pattern in add_interactive_orchestrator.go, so it is consistent. But CompileWorkflows is also invoked in watch mode (repeated recompilations) and in tests that call it multiple times. Once the global is set on the first call, subsequent calls targeting a github.com remote (or a repo with no remote) will still have the GHES host pinned — the guard if os.Getenv("GH_HOST") == "" only prevents overwriting an explicit env var; it does not clear the global when the repo has changed.

Consider resetting the default when no GHES host is detected:

if os.Getenv("GH_HOST") == "" {
    if detectedHost := getHostFromOriginRemote(); detectedHost != "github.com" && detectedHost != "" {
        compileOrchestratorLog.Printf("Auto-detected GHES host from git remote: %s", detectedHost)
        workflow.SetDefaultGHHost(detectedHost)
    } else {
        workflow.SetDefaultGHHost("") // reset when not on GHES
    }
}

Or document clearly that CompileWorkflows must not be called repeatedly in the same process when the remote may change between calls.

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 — the block now uses else if detectedHost == "github.com" { workflow.SetDefaultGHHost("") } to reset the global for watch-mode calls targeting a github.com remote, while leaving it unchanged when detectedHost is empty (unresolvable remote). A new subtest "resets default host to empty for github.com remotes" verifies this behaviour.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

Copilot AI and others added 2 commits June 18, 2026 12:27
…st, reset host on github.com

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

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

Copilot AI commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Done. Addressed all 8 in-scope github-actions[bot] review threads:

  • Removed dead-code divergence in compile_orchestrator.go: kept && detectedHost != "" in the if guard and added else if detectedHost == "github.com" { SetDefaultGHHost("") } for watch-mode safety
  • Replaced manual os.Chdir + cleanup with t.Chdir(tempDir) (Go 1.24+)
  • Added os.LookupEnv save-and-restore for all os.Unsetenv("GH_HOST") calls
  • Added explanatory comment for the mid-test os.Unsetenv in subtest 2
  • Added SSH remote subtest (git@ghes.example.com:owner/repo.git)

All unit tests and lint pass locally. CI re-trigger needed before merge.

@pelikhan pelikhan merged commit c86d32c into main Jun 18, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/add-ghe-host-auto-detection branch June 18, 2026 12:50
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