Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .changeset/patch-ghes-auto-detect-helpers.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions pkg/cli/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,48 @@ func getHostFromOriginRemote() string {
return host
}

// isGHESInstance returns true if the git remote origin points to a GitHub Enterprise Server instance
// (not github.com). This is used to enable GHES-specific behavior like auto-configuring api-target
// and network firewall domains.
func isGHESInstance() bool {
host := getHostFromOriginRemote()
isGHES := host != "github.com"
gitLog.Printf("GHES instance check: host=%s, isGHES=%v", host, isGHES)
return isGHES
Comment on lines +147 to +151
}

// getGHESAPIURL returns the API URL for a GHES instance (e.g., "https://ghes.example.com/api/v3")
// or an empty string if not a GHES instance. This is used to auto-populate engine.api-target.
func getGHESAPIURL() string {
host := getHostFromOriginRemote()
if host == "github.com" {
return ""
}

// GHES API URL format: https://<host>/api/v3
apiURL := fmt.Sprintf("https://%s/api/v3", host)
gitLog.Printf("GHES API URL: %s", apiURL)
return apiURL
}

// getGHESAllowedDomains returns a list of domains that should be added to the firewall
// allowed list for GHES instances. This includes the GHES hostname and api.<ghes-host>.
func getGHESAllowedDomains() []string {
host := getHostFromOriginRemote()
if host == "github.com" {
return nil
}

// For GHES, allow both the main host and api subdomain
domains := []string{
host,
"api." + host,
}

gitLog.Printf("GHES allowed domains: %v", domains)
return domains
}

// getRepositorySlugFromRemote extracts the repository slug (owner/repo) from git remote URL
func getRepositorySlugFromRemote() string {
gitLog.Print("Getting repository slug from git remote")
Expand Down
166 changes: 166 additions & 0 deletions pkg/cli/git_ghes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//go:build !integration

package cli

import (
"os"
"os/exec"
"testing"

"github.com/github/gh-aw/pkg/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIsGHESInstance(t *testing.T) {
tests := []struct {
name string
remoteURL string
wantIsGHES bool
}{
{
name: "public GitHub",
remoteURL: "https://github.com/org/repo.git",
wantIsGHES: false,
},
{
name: "GHES instance",
remoteURL: "https://ghes.example.com/org/repo.git",
wantIsGHES: true,
},
{
name: "GHES SSH format",
remoteURL: "git@ghes.example.com:org/repo.git",
wantIsGHES: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := testutil.TempDir(t, "test-*")
originalDir, err := os.Getwd()
require.NoError(t, err, "Failed to get current directory")
defer func() {
_ = os.Chdir(originalDir)
}()

require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")

// Initialize git repo
require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
exec.Command("git", "config", "user.name", "Test User").Run()
exec.Command("git", "config", "user.email", "test@example.com").Run()
Comment on lines +49 to +52

// Set remote URL
require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()

got := isGHESInstance()
assert.Equal(t, tt.wantIsGHES, got, "isGHESInstance() returned unexpected result")
})
}
}

func TestGetGHESAPIURL(t *testing.T) {
tests := []struct {
name string
remoteURL string
wantAPIURL string
}{
{
name: "public GitHub returns empty",
remoteURL: "https://github.com/org/repo.git",
wantAPIURL: "",
},
{
name: "GHES instance returns API URL",
remoteURL: "https://ghes.example.com/org/repo.git",
wantAPIURL: "https://ghes.example.com/api/v3",
},
{
name: "GHES SSH format returns API URL",
remoteURL: "git@contoso.ghe.com:org/repo.git",
wantAPIURL: "https://contoso.ghe.com/api/v3",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := testutil.TempDir(t, "test-*")
originalDir, err := os.Getwd()
require.NoError(t, err, "Failed to get current directory")
defer func() {
_ = os.Chdir(originalDir)
}()

require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")

// Initialize git repo
require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
exec.Command("git", "config", "user.name", "Test User").Run()
exec.Command("git", "config", "user.email", "test@example.com").Run()

// Set remote URL
require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()

got := getGHESAPIURL()
assert.Equal(t, tt.wantAPIURL, got, "getGHESAPIURL() returned unexpected result")
})
}
}

func TestGetGHESAllowedDomains(t *testing.T) {
tests := []struct {
name string
remoteURL string
wantDomains []string
}{
{
name: "public GitHub returns nil",
remoteURL: "https://github.com/org/repo.git",
wantDomains: nil,
},
{
name: "GHES instance returns host and api subdomain",
remoteURL: "https://ghes.example.com/org/repo.git",
wantDomains: []string{
"ghes.example.com",
"api.ghes.example.com",
},
},
{
name: "GHES SSH format returns domains",
remoteURL: "git@contoso-aw.ghe.com:org/repo.git",
wantDomains: []string{
"contoso-aw.ghe.com",
"api.contoso-aw.ghe.com",
},
},
Comment on lines +125 to +139
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tmpDir := testutil.TempDir(t, "test-*")
originalDir, err := os.Getwd()
require.NoError(t, err, "Failed to get current directory")
defer func() {
_ = os.Chdir(originalDir)
}()

require.NoError(t, os.Chdir(tmpDir), "Failed to change to temp directory")

// Initialize git repo
require.NoError(t, exec.Command("git", "init").Run(), "Failed to init git repo")
exec.Command("git", "config", "user.name", "Test User").Run()
exec.Command("git", "config", "user.email", "test@example.com").Run()

// Set remote URL
require.NoError(t, exec.Command("git", "remote", "add", "origin", tt.remoteURL).Run(), "Failed to add remote")
defer func() { _ = exec.Command("git", "remote", "remove", "origin").Run() }()

got := getGHESAllowedDomains()
assert.Equal(t, tt.wantDomains, got, "getGHESAllowedDomains() returned unexpected result")
})
}
}
Loading