Skip to content

Commit 19bc2aa

Browse files
committed
fixup! feat: Refactor repositories download contents
Signed-off-by: Steve Hipwell <steve.hipwell@gmail.com>
1 parent 7312075 commit 19bc2aa

2 files changed

Lines changed: 186 additions & 57 deletions

File tree

example/contents/main.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2026 The go-github AUTHORS. All rights reserved.
2+
//
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
// The commitpr command utilizes go-github as a CLI tool for
7+
// pushing files to a branch and creating a pull request from it.
8+
// It takes an auth token as an environment variable and creates
9+
// the commit and the PR under the account affiliated with that token.
10+
//
11+
// The purpose of this example is to show how to use refs, trees and commits to
12+
// create commits and pull requests.
13+
//
14+
// Note, if you want to push a single file, you probably prefer to use the
15+
// content API. An example is available here:
16+
// https://pkg.go.dev/github.com/google/go-github/v84/github#example-RepositoriesService-CreateFile
17+
//
18+
// Note, for this to work at least 1 commit is needed, so you if you use this
19+
// after creating a repository you might want to make sure you set `AutoInit` to
20+
// `true`.
21+
package main
22+
23+
import (
24+
"context"
25+
"fmt"
26+
"io"
27+
"os"
28+
29+
"github.com/google/go-github/v84/github"
30+
)
31+
32+
// downloadContents downloads the contents of a file in a repository and returns it as a byte slice.
33+
func downloadContents(ctx context.Context, client *github.Client, owner, repo, path, ref string) ([]byte, error) {
34+
rc, _, err := client.Repositories.DownloadContents(ctx, owner, repo, path, &github.RepositoryContentGetOptions{Ref: ref})
35+
if err != nil {
36+
return nil, err
37+
}
38+
defer rc.Close()
39+
40+
by, err := io.ReadAll(rc)
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
fmt.Printf("Downloaded %v/%v/%v as %d bytes\n", owner, repo, path, len(by))
46+
return by, nil
47+
}
48+
49+
func main() {
50+
client := github.NewClient(nil)
51+
52+
t := []struct {
53+
owner string
54+
repo string
55+
path string
56+
ref string
57+
}{
58+
{"google", "go-github", "README.md", "master"},
59+
{"github", "rest-api-description", "descriptions/api.github.com/api.github.com.2026-03-10.yaml", "main"},
60+
{"ScoopInstaller", "Main", "bucket/yq.json", "master"},
61+
}
62+
63+
for _, v := range t {
64+
if _, err := downloadContents(context.Background(), client, v.owner, v.repo, v.path, v.ref); err != nil {
65+
fmt.Printf("Error: %v\n", err)
66+
os.Exit(1)
67+
}
68+
}
69+
}

github/repos_contents_test.go

Lines changed: 117 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,72 @@ func TestRepositoriesService_GetReadme(t *testing.T) {
126126
})
127127
}
128128

129-
func TestRepositoriesService_DownloadContents_Success(t *testing.T) {
129+
func TestRepositoriesService_DownloadContents_SuccessWithContent(t *testing.T) {
130130
t.Parallel()
131-
client, mux, _ := setup(t)
131+
client, mux, serverURL := setup(t)
132132

133133
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
134134
testMethod(t, r, "GET")
135-
fmt.Fprint(w, `{
135+
fmt.Fprintf(w, `{
136136
"type": "file",
137137
"name": "f",
138-
"content": "foo"
139-
}`)
138+
"content": "foo",
139+
"download_url": "%v/download/f"
140+
}`, serverURL+baseURLPath)
141+
})
142+
143+
ctx := t.Context()
144+
r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
145+
if err != nil {
146+
t.Errorf("Repositories.DownloadContents returned error: %v", err)
147+
}
148+
149+
if got, want := resp.Response.StatusCode, http.StatusOK; got != want {
150+
t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want)
151+
}
152+
153+
bytes, err := io.ReadAll(r)
154+
if err != nil {
155+
t.Errorf("Error reading response body: %v", err)
156+
}
157+
r.Close()
158+
159+
if got, want := string(bytes), "foo"; got != want {
160+
t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
161+
}
162+
163+
const methodName = "DownloadContents"
164+
testBadOptions(t, methodName, func() (err error) {
165+
_, _, err = client.Repositories.DownloadContents(ctx, "\n", "\n", "\n", nil)
166+
return err
167+
})
168+
169+
testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
170+
got, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
171+
if got != nil {
172+
t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
173+
}
174+
return resp, err
175+
})
176+
}
177+
178+
func TestRepositoriesService_DownloadContents_SuccessByDownload(t *testing.T) {
179+
t.Parallel()
180+
client, mux, serverURL := setup(t)
181+
182+
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
183+
testMethod(t, r, "GET")
184+
fmt.Fprintf(w, `{
185+
"type": "file",
186+
"name": "f",
187+
"content": "",
188+
"download_url": "%v/download/f"
189+
}`, serverURL+baseURLPath)
190+
})
191+
192+
mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
193+
testMethod(t, r, "GET")
194+
fmt.Fprint(w, "foo")
140195
})
141196

142197
ctx := t.Context()
@@ -174,6 +229,46 @@ func TestRepositoriesService_DownloadContents_Success(t *testing.T) {
174229
})
175230
}
176231

232+
func TestRepositoriesService_DownloadContents_FailedResponse(t *testing.T) {
233+
t.Parallel()
234+
client, mux, serverURL := setup(t)
235+
236+
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
237+
testMethod(t, r, "GET")
238+
fmt.Fprintf(w, `{
239+
"type": "file",
240+
"name": "f",
241+
"content": "",
242+
"download_url": "%v/download/f"
243+
}`, serverURL+baseURLPath)
244+
})
245+
mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
246+
testMethod(t, r, "GET")
247+
w.WriteHeader(http.StatusInternalServerError)
248+
fmt.Fprint(w, "foo error")
249+
})
250+
251+
ctx := t.Context()
252+
r, resp, err := client.Repositories.DownloadContents(ctx, "o", "r", "d/f", nil)
253+
if err != nil {
254+
t.Errorf("Repositories.DownloadContents returned error: %v", err)
255+
}
256+
257+
if got, want := resp.Response.StatusCode, http.StatusInternalServerError; got != want {
258+
t.Errorf("Repositories.DownloadContents returned status code %v, want %v", got, want)
259+
}
260+
261+
bytes, err := io.ReadAll(r)
262+
if err != nil {
263+
t.Errorf("Error reading response body: %v", err)
264+
}
265+
r.Close()
266+
267+
if got, want := string(bytes), "foo error"; got != want {
268+
t.Errorf("Repositories.DownloadContents returned %v, want %v", got, want)
269+
}
270+
}
271+
177272
func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
178273
t.Parallel()
179274
client, mux, _ := setup(t)
@@ -182,7 +277,8 @@ func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
182277
testMethod(t, r, "GET")
183278
fmt.Fprint(w, `{
184279
"type": "file",
185-
"name": "f"
280+
"name": "f",
281+
"content": ""
186282
}`)
187283
})
188284

@@ -197,11 +293,11 @@ func TestRepositoriesService_DownloadContents_NoDownloadURL(t *testing.T) {
197293
}
198294

199295
if reader != nil {
200-
t.Error("Repositories.DownloadContents returned unexpected reader")
296+
t.Error("Repositories.DownloadContents did not return expected reader")
201297
}
202298
}
203299

204-
func TestRepositoriesService_DownloadContents_GetContentsError(t *testing.T) {
300+
func TestRepositoriesService_DownloadContents_NoFile(t *testing.T) {
205301
t.Parallel()
206302
client, mux, _ := setup(t)
207303

@@ -221,21 +317,22 @@ func TestRepositoriesService_DownloadContents_GetContentsError(t *testing.T) {
221317
}
222318

223319
if reader != nil {
224-
t.Error("Repositories.DownloadContents returned unexpected reader")
320+
t.Error("Repositories.DownloadContents did not return expected reader")
225321
}
226322
}
227323

228324
func TestRepositoriesService_DownloadContentsWithMeta_SuccessWithContent(t *testing.T) {
229325
t.Parallel()
230-
client, mux, _ := setup(t)
326+
client, mux, serverURL := setup(t)
231327

232328
mux.HandleFunc("/repos/o/r/contents/d/f", func(w http.ResponseWriter, r *http.Request) {
233329
testMethod(t, r, "GET")
234-
fmt.Fprint(w, `{
330+
fmt.Fprintf(w, `{
235331
"type": "file",
236332
"name": "f",
237-
"content": "foo"
238-
}`)
333+
"content": "foo",
334+
"download_url": "%v/download/f"
335+
}`, serverURL+baseURLPath)
239336
})
240337

241338
ctx := t.Context()
@@ -284,7 +381,7 @@ func TestRepositoriesService_DownloadContentsWithMeta_SuccessWithContent(t *test
284381
})
285382
}
286383

287-
func TestRepositoriesService_DownloadContentsWithMeta_SuccessViaDownloadURL(t *testing.T) {
384+
func TestRepositoriesService_DownloadContentsWithMeta_SuccessByDownload(t *testing.T) {
288385
t.Parallel()
289386
client, mux, serverURL := setup(t)
290387

@@ -296,6 +393,7 @@ func TestRepositoriesService_DownloadContentsWithMeta_SuccessViaDownloadURL(t *t
296393
"download_url": "%v/download/f"
297394
}`, serverURL+baseURLPath)
298395
})
396+
299397
mux.HandleFunc("/download/f", func(w http.ResponseWriter, r *http.Request) {
300398
testMethod(t, r, "GET")
301399
fmt.Fprint(w, "foo")
@@ -385,7 +483,8 @@ func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T
385483
testMethod(t, r, "GET")
386484
fmt.Fprint(w, `{
387485
"type": "file",
388-
"name": "f"
486+
"name": "f",
487+
"content": ""
389488
}`)
390489
})
391490

@@ -396,7 +495,7 @@ func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T
396495
}
397496

398497
if reader != nil {
399-
t.Error("Repositories.DownloadContentsWithMeta returned unexpected reader")
498+
t.Error("Repositories.DownloadContentsWithMeta did not return expected reader")
400499
}
401500

402501
if resp == nil {
@@ -408,7 +507,7 @@ func TestRepositoriesService_DownloadContentsWithMeta_NoDownloadURL(t *testing.T
408507
}
409508
}
410509

411-
func TestRepositoriesService_DownloadContentsWithMeta_GetContentsError(t *testing.T) {
510+
func TestRepositoriesService_DownloadContentsWithMeta_NoFile(t *testing.T) {
412511
t.Parallel()
413512
client, mux, _ := setup(t)
414513

@@ -418,53 +517,14 @@ func TestRepositoriesService_DownloadContentsWithMeta_GetContentsError(t *testin
418517
})
419518

420519
ctx := t.Context()
421-
reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
422-
if err == nil {
423-
t.Error("Repositories.DownloadContentsWithMeta did not return expected error")
424-
}
425-
426-
if reader != nil {
427-
t.Error("Repositories.DownloadContentsWithMeta returned unexpected reader")
428-
}
429-
430-
if resp == nil {
431-
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
432-
}
433-
434-
if contents != nil {
435-
t.Error("Repositories.DownloadContentsWithMeta returned unexpected content")
436-
}
437-
}
438-
439-
func TestRepositoriesService_DownloadContentsWithMeta_NilFileContent(t *testing.T) {
440-
t.Parallel()
441-
client, mux, _ := setup(t)
442-
443-
mux.HandleFunc("/repos/o/r/contents/d", func(w http.ResponseWriter, r *http.Request) {
444-
testMethod(t, r, "GET")
445-
fmt.Fprint(w, `[{
446-
"type": "file",
447-
"name": "f"
448-
}]`)
449-
})
450-
451-
ctx := t.Context()
452-
reader, contents, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d", nil)
520+
_, _, resp, err := client.Repositories.DownloadContentsWithMeta(ctx, "o", "r", "d/f", nil)
453521
if err == nil {
454522
t.Error("Repositories.DownloadContentsWithMeta did not return expected error")
455523
}
456524

457-
if reader != nil {
458-
t.Error("Repositories.DownloadContentsWithMeta returned unexpected reader")
459-
}
460-
461525
if resp == nil {
462526
t.Error("Repositories.DownloadContentsWithMeta did not return expected response")
463527
}
464-
465-
if contents != nil {
466-
t.Error("Repositories.DownloadContentsWithMeta returned unexpected content")
467-
}
468528
}
469529

470530
func TestRepositoriesService_GetContents_File(t *testing.T) {

0 commit comments

Comments
 (0)