Skip to content

Commit ac348b0

Browse files
committed
Fix requesting REST sub-resources on GHE
GitHub REST resources typically return full URLs to fetch related resources at. We used to parse those URLs to find just the path portion and pass that in to the `REST()` function, which only accepted paths. By doing so, we are essential de-constructing a URL just to re-assemble it again. While re-assembling it for Enterprise, though, we would accidentally inject an extra `api/v3/` prefix where one was not needed. The solution is just to use raw URLs as reported by the REST API with no modifications. This extends the `REST()` function to accept full URLs in addition to just paths to resources.
1 parent 09b0981 commit ac348b0

File tree

6 files changed

+49
-36
lines changed

6 files changed

+49
-36
lines changed

api/client.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ func graphQLClient(h *http.Client, hostname string) *graphql.Client {
194194

195195
// REST performs a REST request and parses the response.
196196
func (c Client) REST(hostname string, method string, p string, body io.Reader, data interface{}) error {
197-
url := ghinstance.RESTPrefix(hostname) + p
198-
req, err := http.NewRequest(method, url, body)
197+
req, err := http.NewRequest(method, restURL(hostname, p), body)
199198
if err != nil {
200199
return err
201200
}
@@ -230,6 +229,13 @@ func (c Client) REST(hostname string, method string, p string, body io.Reader, d
230229
return nil
231230
}
232231

232+
func restURL(hostname string, pathOrURL string) string {
233+
if strings.HasPrefix(pathOrURL, "https://") || strings.HasPrefix(pathOrURL, "http://") {
234+
return pathOrURL
235+
}
236+
return ghinstance.RESTPrefix(hostname) + pathOrURL
237+
}
238+
233239
func handleResponse(resp *http.Response, data interface{}) error {
234240
success := resp.StatusCode >= 200 && resp.StatusCode < 300
235241

api/client_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ func TestRESTGetDelete(t *testing.T) {
8080
assert.NoError(t, err)
8181
}
8282

83+
func TestRESTWithFullURL(t *testing.T) {
84+
http := &httpmock.Registry{}
85+
client := NewClient(ReplaceTripper(http))
86+
87+
http.Register(
88+
httpmock.REST("GET", "api/v3/user/repos"),
89+
httpmock.StatusStringResponse(200, "{}"))
90+
http.Register(
91+
httpmock.REST("GET", "user/repos"),
92+
httpmock.StatusStringResponse(200, "{}"))
93+
94+
err := client.REST("example.com", "GET", "user/repos", nil, nil)
95+
assert.NoError(t, err)
96+
err = client.REST("example.com", "GET", "https://another.net/user/repos", nil, nil)
97+
assert.NoError(t, err)
98+
99+
assert.Equal(t, "example.com", http.Requests[0].URL.Hostname())
100+
assert.Equal(t, "another.net", http.Requests[1].URL.Hostname())
101+
}
102+
83103
func TestRESTError(t *testing.T) {
84104
fakehttp := &httpmock.Registry{}
85105
client := NewClient(ReplaceTripper(fakehttp))

pkg/cmd/run/shared/shared.go

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,13 +243,7 @@ type JobsPayload struct {
243243

244244
func GetJobs(client *api.Client, repo ghrepo.Interface, run Run) ([]Job, error) {
245245
var result JobsPayload
246-
parsed, err := url.Parse(run.JobsURL)
247-
if err != nil {
248-
return nil, err
249-
}
250-
251-
err = client.REST(repo.RepoHost(), "GET", parsed.Path[1:], nil, &result)
252-
if err != nil {
246+
if err := client.REST(repo.RepoHost(), "GET", run.JobsURL, nil, &result); err != nil {
253247
return nil, err
254248
}
255249
return result.Jobs, nil

pkg/cmd/run/shared/test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ func TestRun(name string, id int, s Status, c Conclusion) Run {
2626
Conclusion: c,
2727
Event: "push",
2828
HeadBranch: "trunk",
29-
JobsURL: fmt.Sprintf("/runs/%d/jobs", id),
29+
JobsURL: fmt.Sprintf("https://api.github.com/runs/%d/jobs", id),
3030
HeadCommit: Commit{
3131
Message: "cool commit",
3232
},
3333
HeadSha: "1234567890",
34-
URL: fmt.Sprintf("runs/%d", id),
34+
URL: fmt.Sprintf("https://github.com/runs/%d", id),
3535
HeadRepository: Repo{
3636
Owner: struct{ Login string }{Login: "OWNER"},
3737
Name: "REPO",
@@ -68,7 +68,7 @@ var SuccessfulJob Job = Job{
6868
Name: "cool job",
6969
StartedAt: created(),
7070
CompletedAt: updated(),
71-
URL: "jobs/10",
71+
URL: "https://github.com/jobs/10",
7272
RunID: 3,
7373
Steps: []Step{
7474
{
@@ -93,7 +93,7 @@ var FailedJob Job = Job{
9393
Name: "sad job",
9494
StartedAt: created(),
9595
CompletedAt: updated(),
96-
URL: "jobs/20",
96+
URL: "https://github.com/jobs/20",
9797
RunID: 1234,
9898
Steps: []Step{
9999
{

pkg/cmd/run/view/view_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func TestViewRun(t *testing.T) {
207207
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/10/annotations"),
208208
httpmock.JSONResponse([]shared.Annotation{}))
209209
},
210-
wantOut: "\n✓ trunk successful #2898 · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: runs/3\n",
210+
wantOut: "\n✓ trunk successful #2898 · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: https://github.com/runs/3\n",
211211
},
212212
{
213213
name: "exit status, failed run",
@@ -236,7 +236,7 @@ func TestViewRun(t *testing.T) {
236236
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/20/annotations"),
237237
httpmock.JSONResponse(shared.FailedJobAnnotations))
238238
},
239-
wantOut: "\nX trunk failed · 1234\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nTo see what failed, try: gh run view 1234 --log-failed\nView this run on GitHub: runs/1234\n",
239+
wantOut: "\nX trunk failed · 1234\nTriggered via push about 59 minutes ago\n\nJOBS\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nTo see what failed, try: gh run view 1234 --log-failed\nView this run on GitHub: https://github.com/runs/1234\n",
240240
wantErr: true,
241241
},
242242
{
@@ -278,7 +278,7 @@ func TestViewRun(t *testing.T) {
278278
artifact-3
279279
280280
For more information about a job, try: gh run view --job=<job-id>
281-
View this run on GitHub: runs/3
281+
View this run on GitHub: https://github.com/runs/3
282282
`),
283283
},
284284
{
@@ -308,7 +308,7 @@ func TestViewRun(t *testing.T) {
308308
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/10/annotations"),
309309
httpmock.JSONResponse([]shared.Annotation{}))
310310
},
311-
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: runs/3\n",
311+
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: https://github.com/runs/3\n",
312312
},
313313
{
314314
name: "verbose",
@@ -343,7 +343,7 @@ func TestViewRun(t *testing.T) {
343343
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/20/annotations"),
344344
httpmock.JSONResponse(shared.FailedJobAnnotations))
345345
},
346-
wantOut: "\nX trunk failed · 1234\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nTo see what failed, try: gh run view 1234 --log-failed\nView this run on GitHub: runs/1234\n",
346+
wantOut: "\nX trunk failed · 1234\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nTo see what failed, try: gh run view 1234 --log-failed\nView this run on GitHub: https://github.com/runs/1234\n",
347347
},
348348
{
349349
name: "prompts for choice, one job",
@@ -380,7 +380,7 @@ func TestViewRun(t *testing.T) {
380380
opts: &ViewOptions{
381381
Prompt: true,
382382
},
383-
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: runs/3\n",
383+
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: https://github.com/runs/3\n",
384384
},
385385
{
386386
name: "interactive with log",
@@ -664,7 +664,7 @@ func TestViewRun(t *testing.T) {
664664
httpmock.REST("GET", "repos/OWNER/REPO/check-runs/10/annotations"),
665665
httpmock.JSONResponse([]shared.Annotation{}))
666666
},
667-
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\nTo see the full job log, try: gh run view --log --job=10\nView this run on GitHub: runs/3\n",
667+
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\nTo see the full job log, try: gh run view --log --job=10\nView this run on GitHub: https://github.com/runs/3\n",
668668
},
669669
{
670670
name: "interactive, multiple jobs, choose all jobs",
@@ -703,7 +703,7 @@ func TestViewRun(t *testing.T) {
703703
as.StubOne(2)
704704
as.StubOne(0)
705705
},
706-
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: runs/3\n",
706+
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\nJOBS\n✓ cool job in 4m34s (ID 10)\nX sad job in 4m34s (ID 20)\n ✓ barf the quux\n X quux the barf\n\nANNOTATIONS\nX the job is sad\nsad job: blaze.py#420\n\n\nFor more information about a job, try: gh run view --job=<job-id>\nView this run on GitHub: https://github.com/runs/3\n",
707707
},
708708
{
709709
name: "interactive, multiple jobs, choose specific jobs",
@@ -736,7 +736,7 @@ func TestViewRun(t *testing.T) {
736736
as.StubOne(2)
737737
as.StubOne(1)
738738
},
739-
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\nTo see the full job log, try: gh run view --log --job=10\nView this run on GitHub: runs/3\n",
739+
wantOut: "\n✓ trunk successful · 3\nTriggered via push about 59 minutes ago\n\n✓ cool job in 4m34s (ID 10)\n ✓ fob the barz\n ✓ barz the fob\n\nTo see the full job log, try: gh run view --log --job=10\nView this run on GitHub: https://github.com/runs/3\n",
740740
},
741741
{
742742
name: "web run",
@@ -750,8 +750,8 @@ func TestViewRun(t *testing.T) {
750750
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/3"),
751751
httpmock.JSONResponse(shared.SuccessfulRun))
752752
},
753-
browsedURL: "runs/3",
754-
wantOut: "Opening runs/3 in your browser.\n",
753+
browsedURL: "https://github.com/runs/3",
754+
wantOut: "Opening github.com/runs/3 in your browser.\n",
755755
},
756756
{
757757
name: "web job",
@@ -768,8 +768,8 @@ func TestViewRun(t *testing.T) {
768768
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/3"),
769769
httpmock.JSONResponse(shared.SuccessfulRun))
770770
},
771-
browsedURL: "jobs/10?check_suite_focus=true",
772-
wantOut: "Opening jobs/10 in your browser.\n",
771+
browsedURL: "https://github.com/jobs/10?check_suite_focus=true",
772+
wantOut: "Opening github.com/jobs/10 in your browser.\n",
773773
},
774774
{
775775
name: "hide job header, failure",
@@ -788,7 +788,7 @@ func TestViewRun(t *testing.T) {
788788
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/123/artifacts"),
789789
httpmock.StringResponse(`{}`))
790790
},
791-
wantOut: "\nX trunk failed no job · 123\nTriggered via push about 59 minutes ago\n\nX This run likely failed because of a workflow file issue.\n\nFor more information, see: runs/123\n",
791+
wantOut: "\nX trunk failed no job · 123\nTriggered via push about 59 minutes ago\n\nX This run likely failed because of a workflow file issue.\n\nFor more information, see: https://github.com/runs/123\n",
792792
},
793793
{
794794
name: "hide job header, startup_failure",
@@ -807,7 +807,7 @@ func TestViewRun(t *testing.T) {
807807
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/123/artifacts"),
808808
httpmock.StringResponse(`{}`))
809809
},
810-
wantOut: "\nX trunk failed no job · 123\nTriggered via push about 59 minutes ago\n\nX This run likely failed because of a workflow file issue.\n\nFor more information, see: runs/123\n",
810+
wantOut: "\nX trunk failed no job · 123\nTriggered via push about 59 minutes ago\n\nX This run likely failed because of a workflow file issue.\n\nFor more information, see: https://github.com/runs/123\n",
811811
},
812812
}
813813

pkg/cmd/secret/list/list.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package list
33
import (
44
"fmt"
55
"net/http"
6-
"net/url"
76
"strings"
87
"time"
98

@@ -160,14 +159,8 @@ func getOrgSecrets(client *api.Client, host, orgName string) ([]*Secret, error)
160159
if secret.SelectedReposURL == "" {
161160
continue
162161
}
163-
u, err := url.Parse(secret.SelectedReposURL)
164-
if err != nil {
165-
return nil, fmt.Errorf("failed determining selected repositories for %s: %w", secret.Name, err)
166-
}
167-
168162
var result responseData
169-
err = client.REST(u.Host, "GET", u.Path[1:], nil, &result)
170-
if err != nil {
163+
if err := client.REST(host, "GET", secret.SelectedReposURL, nil, &result); err != nil {
171164
return nil, fmt.Errorf("failed determining selected repositories for %s: %w", secret.Name, err)
172165
}
173166
secret.NumSelectedRepos = result.TotalCount

0 commit comments

Comments
 (0)