Skip to content

Commit

Permalink
forge/github: Determine API URL from base URL if not set (#337)
Browse files Browse the repository at this point in the history
For GHES, usually the API URL is /api under the base URL.
So if the API URL is not set, and base URL is not github.com,
we can assume the API URL is $baseURL/api.

Users can still override this with the `spice.forge.github.apiUrl`
or `GITHUB_API_URL` environment variable.

Follow up to
#330 (comment)
Relates to #336
  • Loading branch information
abhinav authored Aug 9, 2024
1 parent 74bec79 commit b6ccc5f
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .changes/unreleased/Changed-20240808-210829.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
kind: Changed
body: 'GitHub Enterprise: `GITHUB_API_URL` is now optional. If not set, we''ll guess it from the GitHub URL.'
time: 2024-08-08T21:08:29.592021-07:00
3 changes: 2 additions & 1 deletion doc/src/cli/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ or at the repository level with the `--local` flag.
### spice.forge.github.apiUrl

URL at which the GitHub API is available.
Defaults to `$GITHUB_API_URL` if set, or `https://api.github.com` otherwise.
Defaults to `$GITHUB_API_URL` if set,
or computed from the GitHub URL if not set.

See also: [GitHub Enterprise](../setup/auth.md#github-enterprise).

Expand Down
51 changes: 35 additions & 16 deletions doc/src/setup/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,27 +190,46 @@ It is intended only for CI/CD environments where you have no other choice.
## GitHub Enterprise

To use git-spice with a GitHub Enterprise instance,
set the following environment variables in your shell configuration file,
authenticate, and use git-spice as usual.
inform it of the instance URL, authenticate, and use git-spice as usual.

```freeze language="bash"
# URL of your GitHub Enterprise instance.
export GITHUB_URL=https://github.example.com
=== "<!-- gs:version unreleased -->"

# URL at which the GitHub API is available on your instance.
export GITHUB_API_URL=https://github.example.com/api
```
Set the $$spice.forge.github.url$$ configuration option
to the address of your GitHub Enterprise instance.

Since <!-- gs:version unreleased -->,
use the $$spice.forge.github.url$$ and $$spice.forge.github.apiUrl$$
configuration options to set these values
if environment variables are inconvenient.
```freeze language="terminal"
{green}${reset} git config {red}spice.forge.github.url{reset} {mag}https://github.example.com{reset}
```

```freeze language="terminal"
git config {green}spice.forge.github.url{reset} {mag}https://github.example.com{reset}
git config {green}spice.forge.github.apiUrl{reset} {mag}https://github.example.com/api{reset}
```
The GitHub API is assumed to be at `/api` under the GitHub URL.
For example, if your GitHub Enterprise instance is at `https://github.example.com`,
the API is assumed to be at `https://github.example.com/api`.

If this is not the case, set the $$spice.forge.github.apiUrl$$ configuration option.

```freeze language="terminal"
{green}${reset} git config {red}spice.forge.github.apiUrl{reset} {mag}https://github.example.com/api{reset}
```

These values may also be set with environment variables.

```freeze language="bash"
export GITHUB_URL=https://github.example.com
export GITHUB_API_URL=https://github.example.com/api
```

=== "<!-- gs:version v0.3.1 --> or older"

Set the `GITHUB_URL` and `GITHUB_API_URL` environment variables
to the address of your GitHub Enterprise instance
and its API endpoint, respectively.

```freeze language="bash"
export GITHUB_URL=https://github.example.com
export GITHUB_API_URL=https://github.example.com/api
```

Both values must be set for git-spice to work with GitHub Enterprise.

## Safety

Expand Down
23 changes: 21 additions & 2 deletions internal/forge/github/forge.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ import (
"golang.org/x/oauth2"
)

// Default URLs for GitHub and its API.
const (
DefaultURL = "https://github.com"
DefaultAPIURL = "https://api.github.com"
)

// Options defines command line options for the GitHub Forge.
// These are all hidden in the CLI,
// and are expected to be set only via environment variables.
Expand Down Expand Up @@ -46,13 +52,26 @@ var _ forge.Forge = (*Forge)(nil)
// URL returns the base URL configured for the GitHub Forge
// or the default URL if none is set.
func (f *Forge) URL() string {
return cmp.Or(f.Options.URL, "https://github.com")
return cmp.Or(f.Options.URL, DefaultURL)
}

// APIURL returns the base API URL configured for the GitHub Forge
// or the default URL if none is set.
func (f *Forge) APIURL() string {
return cmp.Or(f.Options.APIURL, "https://api.github.com")
if f.Options.APIURL != "" {
return f.Options.APIURL
}

// If the API URL is not set, and base URL is NOT github.com,
// assume API URL is $baseURL/api.
if f.Options.URL != "" && f.Options.URL != DefaultURL {
apiURL, err := url.JoinPath(f.Options.URL, "/api")
if err == nil {
return apiURL
}
}

return DefaultAPIURL
}

// ID reports a unique key for this forge.
Expand Down
52 changes: 52 additions & 0 deletions internal/forge/github/forge_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,58 @@ import (
"github.com/stretchr/testify/require"
)

func TestURLs(t *testing.T) {
tests := []struct {
name string
opts Options

wantURL string
wantAPIURL string
}{
{
name: "Default",
wantURL: DefaultURL,
wantAPIURL: DefaultAPIURL,
},
{
name: "ExplicitURL",
opts: Options{URL: DefaultURL},
wantURL: DefaultURL,
wantAPIURL: DefaultAPIURL,
},
{
name: "CustomURL",
opts: Options{URL: "https://example.com"},
wantURL: "https://example.com",
wantAPIURL: "https://example.com/api",
},
{
name: "CustomBoth",
opts: Options{
URL: "https://example.com",
APIURL: "https://api.example.com",
},
wantURL: "https://example.com",
wantAPIURL: "https://api.example.com",
},
{
name: "InvalidURL",
opts: Options{URL: ":/"},
wantURL: ":/",
wantAPIURL: DefaultAPIURL,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := Forge{Options: tt.opts}

assert.Equal(t, tt.wantURL, f.URL())
assert.Equal(t, tt.wantAPIURL, f.APIURL())
})
}
}

func TestExtractRepoInfo(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit b6ccc5f

Please sign in to comment.