Skip to content

Commit f111200

Browse files
authored
🐛 Fix Git client for public repos (#6)
Two bugs happen with public repos and no git token set: - isPrivateRepo does not work as an error is returned if the git client was not successfully built. This commit replaces the utility function. - there was no handling of redirects that Github API gives us for downloading release assets in the case of public repos. This commit adds it. Signed-off-by: janiskemper <[email protected]>
1 parent f8a4caa commit f111200

File tree

1 file changed

+63
-25
lines changed

1 file changed

+63
-25
lines changed

pkg/github/client/github_client.go

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func NewFactory() Factory {
6161

6262
var _ = Client(&realGhClient{})
6363

64-
func (_ *factory) NewClient(ctx context.Context) (Client, error) {
64+
func (*factory) NewClient(ctx context.Context) (Client, error) {
6565
creds, err := NewGitConfig()
6666
if err != nil {
6767
return nil, fmt.Errorf("failed to create git config: %w", err)
@@ -70,6 +70,11 @@ func (_ *factory) NewClient(ctx context.Context) (Client, error) {
7070
if err != nil {
7171
return nil, fmt.Errorf("failed to create github client: %w", err)
7272
}
73+
74+
if oAuthClient == nil {
75+
oAuthClient = http.DefaultClient
76+
}
77+
7378
return &realGhClient{
7479
client: ghclient,
7580
httpclient: oAuthClient,
@@ -113,18 +118,24 @@ func (c *realGhClient) DownloadReleaseAssets(ctx context.Context, release *githu
113118
return fmt.Errorf("failed to create temporary asset file: %w", err)
114119
}
115120

116-
resp, _, err := c.client.Repositories.DownloadReleaseAsset(ctx, c.orgName, c.repoName, asset.GetID(), c.httpclient)
121+
resp, redirectURL, err := c.client.Repositories.DownloadReleaseAsset(ctx, c.orgName, c.repoName, asset.GetID(), nil)
117122
if err != nil {
118123
return fmt.Errorf("failed to download the release asset from URL %s: %w", *asset.BrowserDownloadURL, err)
119124
}
120125

121-
// Save the asset file data to the local file
122-
if _, err = io.Copy(assetFile, resp); err != nil {
123-
return fmt.Errorf("failed to save asset file %s from HTTP response: %w", assetPath, err)
124-
}
125-
126-
if err := resp.Close(); err != nil {
127-
return fmt.Errorf("failed to close response: %w", err)
126+
// if redirectURL is set, then response is nil and vice versa
127+
if redirectURL != "" {
128+
if err := c.handleRedirect(ctx, redirectURL, assetFile); err != nil {
129+
return fmt.Errorf("failed to handle redirect: %w", err)
130+
}
131+
} else {
132+
if _, err = io.Copy(assetFile, resp); err != nil {
133+
return fmt.Errorf("failed to save asset file %s from HTTP response: %w", assetPath, err)
134+
}
135+
136+
if err := resp.Close(); err != nil {
137+
return fmt.Errorf("failed to close response: %w", err)
138+
}
128139
}
129140

130141
if err := assetFile.Close(); err != nil {
@@ -134,29 +145,56 @@ func (c *realGhClient) DownloadReleaseAssets(ctx context.Context, release *githu
134145
return nil
135146
}
136147

137-
func githubAndOAuthClientWithToken(ctx context.Context, creds GitConfig) (*github.Client, *http.Client, error) {
138-
ts := oauth2.StaticTokenSource(
139-
&oauth2.Token{AccessToken: creds.GitAccessToken},
140-
)
141-
oauthClient := oauth2.NewClient(ctx, ts)
142-
gc := github.NewClient(oauthClient)
143-
isPrivate, err := isRepoPrivate(ctx, gc, creds)
148+
func (c *realGhClient) handleRedirect(ctx context.Context, url string, assetFile *os.File) error {
149+
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
150+
if err != nil {
151+
return fmt.Errorf("failed to define http get request: %w", err)
152+
}
153+
154+
resp, err := c.httpclient.Do(req)
144155
if err != nil {
145-
return nil, &http.Client{}, fmt.Errorf("private repo: %w", err)
156+
return fmt.Errorf("failed to get URL %q: %w", url, err)
157+
}
158+
159+
if resp.StatusCode != http.StatusOK {
160+
return fmt.Errorf("failed to download asset, HTTP status code: %d", resp.StatusCode)
146161
}
147-
if isPrivate && creds.GitAccessToken == "" {
148-
return nil, &http.Client{}, fmt.Errorf("empty access token for private repo")
162+
163+
if _, err := io.Copy(assetFile, resp.Body); err != nil {
164+
return fmt.Errorf("failed to copy http response in file: %w", err)
149165
}
150-
return gc, oauthClient, nil
166+
167+
if err := resp.Body.Close(); err != nil {
168+
return fmt.Errorf("failed to close body of response: %w", err)
169+
}
170+
return nil
151171
}
152172

153-
func isRepoPrivate(ctx context.Context, client *github.Client, creds GitConfig) (bool, error) {
154-
repository, _, err := client.Repositories.Get(ctx, creds.GitOrgName, creds.GitRepoName)
155-
if err != nil {
156-
return false, fmt.Errorf("failed to get repository: %w", err)
173+
func githubAndOAuthClientWithToken(ctx context.Context, creds GitConfig) (githubClient *github.Client, oauthClient *http.Client, err error) {
174+
if creds.GitAccessToken == "" {
175+
githubClient = github.NewClient(nil)
176+
} else {
177+
ts := oauth2.StaticTokenSource(
178+
&oauth2.Token{AccessToken: creds.GitAccessToken},
179+
)
180+
181+
oauthClient = oauth2.NewClient(ctx, ts)
182+
githubClient = github.NewClient(oauthClient)
183+
}
184+
185+
if err := verifyAccess(ctx, githubClient, creds); err != nil {
186+
return nil, &http.Client{}, fmt.Errorf("failed to access Git API: %w", err)
157187
}
158188

159-
return repository.GetPrivate(), nil
189+
return githubClient, oauthClient, nil
190+
}
191+
192+
func verifyAccess(ctx context.Context, client *github.Client, creds GitConfig) error {
193+
_, _, err := client.Repositories.Get(ctx, creds.GitOrgName, creds.GitRepoName)
194+
if err != nil {
195+
return fmt.Errorf("failed to get repository: %w", err)
196+
}
197+
return nil
160198
}
161199

162200
func contains(source []string, ghAsset string) bool {

0 commit comments

Comments
 (0)