fix(githubapi): accept the api/v3 base that GoReleaser appends to the API URL
- commit
- 27d9aa499afdca0f0f8aaeec524d2585ad5c95e4
- author
- brice · · 2026-10-21 16:12 UTC
- committer
- brice · · 2026-10-21 17:53 UTC
- parent
- d50b7cd
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +25 -0
M internal/adapters/githubapi/handler.go +5 -0
| @@ -185,6 +185,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
| 185 | 185 | ||
| 186 | 186 | // route recognises the endpoints. ref is "" for /tarball. | |
| 187 | 187 | func route(rest string) (kind, owner, repo, ref string, ok bool) { | |
| 188 | + | // go-github's enterprise client (used by GoReleaser) appends api/v3 | |
| 189 | + | // to a custom API base. Keep the canonical URLs, accepting that alias too. | |
| 190 | + | if strings.HasPrefix(rest, "/api/v3/") { | |
| 191 | + | rest = strings.TrimPrefix(rest, "/api/v3") | |
| 192 | + | } | |
| 188 | 193 | if kind, owner, repo, ref, ok = releaseRoute(rest); ok { | |
| 189 | 194 | return | |
| 190 | 195 | } | |
| @@ -185,6 +185,11 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||||
| 185 | 185 | ||||
| 186 | // route recognises the endpoints. ref is "" for /tarball. | 186 | // route recognises the endpoints. ref is "" for /tarball. | ||
| 187 | func route(rest string) (kind, owner, repo, ref string, ok bool) { | 187 | func route(rest string) (kind, owner, repo, ref string, ok bool) { | ||
| 188 | + | // go-github's enterprise client (used by GoReleaser) appends api/v3 | |||
| 189 | + | // to a custom API base. Keep the canonical URLs, accepting that alias too. | |||
| 190 | + | if strings.HasPrefix(rest, "/api/v3/") { | |||
| 191 | + | rest = strings.TrimPrefix(rest, "/api/v3") | |||
| 192 | + | } | |||
| 188 | if kind, owner, repo, ref, ok = releaseRoute(rest); ok { | 193 | if kind, owner, repo, ref, ok = releaseRoute(rest); ok { | ||
| 189 | return | 194 | return | ||
| 190 | } | 195 | } | ||
M internal/adapters/githubapi/releases_test.go +20 -0
| @@ -75,6 +75,26 @@ func TestReleaseRoutes(t *testing.T) { | |||
| 75 | 75 | require.False(t, ok, path) | |
| 76 | 76 | } | |
| 77 | 77 | } | |
| 78 | + | ||
| 79 | + | // GoReleaser's go-github enterprise client appends api/v3 to the documented | |
| 80 | + | // custom API URL. The alias must dispatch writes through the same auth/service. | |
| 81 | + | func TestReleaseEnterpriseBase(t *testing.T) { | |
| 82 | + | api := mocks.NewGitHubReleaseAPI(t) | |
| 83 | + | h := New(Config{Releases: api}) | |
| 84 | + | body := []byte(`{"tag_name":"v1"}`) | |
| 85 | + | api.EXPECT().CreateRelease(mock.Anything, domain.Anonymous(), "acme", "proj", body).Return([]byte(`{"id":4}`), nil).Once() | |
| 86 | + | w := releaseRequest(t, h, "POST", "/api/v3"+releasePath, string(body), nil) | |
| 87 | + | require.Equal(t, 201, w.Code) | |
| 88 | + | require.JSONEq(t, `{"id":4}`, w.Body.String()) | |
| 89 | + | for _, path := range []string{releasePath, releasePath + "/4", releasePath + "/4/assets", releaseAssetPath, releasePath + "/latest", releasePath + "/tags/v1/patch"} { | |
| 90 | + | kind, owner, repo, ref, ok := route(path) | |
| 91 | + | aliasKind, aliasOwner, aliasRepo, aliasRef, aliasOK := route("/api/v3" + path) | |
| 92 | + | require.Equal(t, []any{kind, owner, repo, ref, ok}, []any{aliasKind, aliasOwner, aliasRepo, aliasRef, aliasOK}) | |
| 93 | + | } | |
| 94 | + | _, _, _, _, ok := route("/api/v3repos/acme/proj/releases") | |
| 95 | + | require.False(t, ok) | |
| 96 | + | } | |
| 97 | + | ||
| 78 | 98 | func TestReleaseDispatch(t *testing.T) { | |
| 79 | 99 | for _, method := range []string{"GET", "HEAD"} { | |
| 80 | 100 | t.Run(method, func(t *testing.T) { | |
| @@ -75,6 +75,26 @@ func TestReleaseRoutes(t *testing.T) { | |||||
| 75 | require.False(t, ok, path) | 75 | require.False(t, ok, path) | ||
| 76 | } | 76 | } | ||
| 77 | } | 77 | } | ||
| 78 | + | ||||
| 79 | + | // GoReleaser's go-github enterprise client appends api/v3 to the documented | |||
| 80 | + | // custom API URL. The alias must dispatch writes through the same auth/service. | |||
| 81 | + | func TestReleaseEnterpriseBase(t *testing.T) { | |||
| 82 | + | api := mocks.NewGitHubReleaseAPI(t) | |||
| 83 | + | h := New(Config{Releases: api}) | |||
| 84 | + | body := []byte(`{"tag_name":"v1"}`) | |||
| 85 | + | api.EXPECT().CreateRelease(mock.Anything, domain.Anonymous(), "acme", "proj", body).Return([]byte(`{"id":4}`), nil).Once() | |||
| 86 | + | w := releaseRequest(t, h, "POST", "/api/v3"+releasePath, string(body), nil) | |||
| 87 | + | require.Equal(t, 201, w.Code) | |||
| 88 | + | require.JSONEq(t, `{"id":4}`, w.Body.String()) | |||
| 89 | + | for _, path := range []string{releasePath, releasePath + "/4", releasePath + "/4/assets", releaseAssetPath, releasePath + "/latest", releasePath + "/tags/v1/patch"} { | |||
| 90 | + | kind, owner, repo, ref, ok := route(path) | |||
| 91 | + | aliasKind, aliasOwner, aliasRepo, aliasRef, aliasOK := route("/api/v3" + path) | |||
| 92 | + | require.Equal(t, []any{kind, owner, repo, ref, ok}, []any{aliasKind, aliasOwner, aliasRepo, aliasRef, aliasOK}) | |||
| 93 | + | } | |||
| 94 | + | _, _, _, _, ok := route("/api/v3repos/acme/proj/releases") | |||
| 95 | + | require.False(t, ok) | |||
| 96 | + | } | |||
| 97 | + | ||||
| 78 | func TestReleaseDispatch(t *testing.T) { | 98 | func TestReleaseDispatch(t *testing.T) { | ||
| 79 | for _, method := range []string{"GET", "HEAD"} { | 99 | for _, method := range []string{"GET", "HEAD"} { | ||
| 80 | t.Run(method, func(t *testing.T) { | 100 | t.Run(method, func(t *testing.T) { | ||