Skip to content

Commit 9a5e079

Browse files
committed
Include request IDs in errors when available.
1 parent 374a26d commit 9a5e079

File tree

4 files changed

+83
-28
lines changed

4 files changed

+83
-28
lines changed

internal/githubapiutil/githubapiutil.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import (
44
"strings"
55

66
"github.com/google/go-github/v32/github"
7+
"github.com/pkg/errors"
78
)
89

910
const xOAuthScopesHeader = "X-OAuth-Scopes"
11+
const xGitHubRequestIDHeader = "X-GitHub-Request-ID"
1012

1113
func HasAnyScope(response *github.Response, scopes ...string) bool {
1214
if response == nil {
@@ -26,3 +28,14 @@ func HasAnyScope(response *github.Response, scopes ...string) bool {
2628
}
2729
return false
2830
}
31+
32+
func EnrichResponseError(response *github.Response, err error, message string) error {
33+
requestID := ""
34+
if response != nil {
35+
requestID = response.Header.Get(xGitHubRequestIDHeader)
36+
}
37+
if requestID != "" {
38+
message = message + " (" + requestID + ")"
39+
}
40+
return errors.Wrap(err, message)
41+
}

internal/githubapiutil/githubapiutil_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package githubapiutil
22

33
import (
4+
"errors"
45
"net/http"
56
"testing"
67

@@ -23,3 +24,16 @@ func TestHasAnyScope(t *testing.T) {
2324
response.Header.Set(xOAuthScopesHeader, "gist, notifications, admin:org")
2425
require.False(t, HasAnyScope(&response, "public_repo", "repo"))
2526
}
27+
28+
func TestEnrichErrorResponse(t *testing.T) {
29+
response := github.Response{
30+
Response: &http.Response{Header: http.Header{}},
31+
}
32+
33+
require.Equal(t, "The error message.: The underlying error.", EnrichResponseError(nil, errors.New("The underlying error."), "The error message.").Error())
34+
35+
require.Equal(t, "The error message.: The underlying error.", EnrichResponseError(&response, errors.New("The underlying error."), "The error message.").Error())
36+
37+
response.Header.Set(xGitHubRequestIDHeader, "AAAA:BBBB:CCCCCCC:DDDDDDD:EEEEEEEE")
38+
require.Equal(t, "The error message. (AAAA:BBBB:CCCCCCC:DDDDDDD:EEEEEEEE): The underlying error.", EnrichResponseError(&response, errors.New("The underlying error."), "The error message.").Error())
39+
}

internal/pull/pull.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
log "github.com/sirupsen/logrus"
1414

1515
"github.com/github/codeql-action-sync/internal/actionconfiguration"
16+
"github.com/github/codeql-action-sync/internal/githubapiutil"
1617
"github.com/mitchellh/ioprogress"
1718
"golang.org/x/oauth2"
1819

@@ -191,9 +192,9 @@ func (pullService *pullService) pullReleases() error {
191192

192193
for index, releaseTag := range relevantReleases {
193194
log.Debugf("Pulling CodeQL bundle %s (%d/%d)...", releaseTag, index+1, len(relevantReleases))
194-
release, _, err := pullService.githubDotComClient.Repositories.GetReleaseByTag(pullService.ctx, sourceOwner, sourceRepository, releaseTag)
195+
release, response, err := pullService.githubDotComClient.Repositories.GetReleaseByTag(pullService.ctx, sourceOwner, sourceRepository, releaseTag)
195196
if err != nil {
196-
return errors.Wrap(err, "Error loading CodeQL release information.")
197+
return githubapiutil.EnrichResponseError(response, err, "Error loading CodeQL release information.")
197198
}
198199
err = os.MkdirAll(pullService.cacheDirectory.ReleasePath(releaseTag), 0755)
199200
if err != nil {

internal/push/push.go

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (pushService *pushService) createRepository() (*github.Repository, error) {
7272
if response != nil && response.StatusCode == http.StatusUnauthorized {
7373
return nil, usererrors.New(errorInvalidDestinationToken)
7474
}
75-
return nil, errors.Wrap(err, "Error getting current user.")
75+
return nil, githubapiutil.EnrichResponseError(response, err, "Error getting current user.")
7676
}
7777

7878
// When creating a repository we can either create it in a named organization or under the current user (represented in go-github by an empty string).
@@ -84,39 +84,39 @@ func (pushService *pushService) createRepository() (*github.Repository, error) {
8484
if destinationOrganization != "" {
8585
_, response, err := pushService.githubEnterpriseClient.Organizations.Get(pushService.ctx, pushService.destinationRepositoryOwner)
8686
if err != nil && (response == nil || response.StatusCode != http.StatusNotFound) {
87-
return nil, errors.Wrap(err, "Error checking if destination organization exists.")
87+
return nil, githubapiutil.EnrichResponseError(response, err, "Error checking if destination organization exists.")
8888
}
8989
if response != nil && response.StatusCode == http.StatusNotFound {
9090
log.Debugf("The organization %s does not exist. Creating it...", pushService.destinationRepositoryOwner)
91-
_, _, err := pushService.githubEnterpriseClient.Admin.CreateOrg(pushService.ctx, &github.Organization{
91+
_, response, err := pushService.githubEnterpriseClient.Admin.CreateOrg(pushService.ctx, &github.Organization{
9292
Login: github.String(pushService.destinationRepositoryOwner),
9393
Name: github.String(pushService.destinationRepositoryOwner),
9494
}, user.GetLogin())
9595
if err != nil {
9696
if response != nil && response.StatusCode == http.StatusNotFound && !githubapiutil.HasAnyScope(response, "site_admin") {
9797
return nil, usererrors.New("The destination token you have provided does not have the `site_admin` scope, so the destination organization cannot be created.")
9898
}
99-
return nil, errors.Wrap(err, "Error creating organization.")
99+
return nil, githubapiutil.EnrichResponseError(response, err, "Error creating organization.")
100100
}
101101
}
102102

103103
_, response, err = pushService.githubEnterpriseClient.Organizations.IsMember(pushService.ctx, pushService.destinationRepositoryOwner, user.GetLogin())
104104
if err != nil {
105-
return nil, errors.Wrap(err, "Failed to check membership of destination organization.")
105+
return nil, githubapiutil.EnrichResponseError(response, err, "Failed to check membership of destination organization.")
106106
}
107107
if (response.StatusCode == http.StatusFound || response.StatusCode == http.StatusNotFound) && githubapiutil.HasAnyScope(response, "site_admin") {
108108
log.Debugf("No access to destination organization (status code %d). Switching to impersonation token for %s...", response.StatusCode, pushService.actionsAdminUser)
109-
impersonationToken, _, err := pushService.githubEnterpriseClient.Admin.CreateUserImpersonation(pushService.ctx, pushService.actionsAdminUser, &github.ImpersonateUserOptions{Scopes: []string{minimumRepositoryScope, "workflow"}})
109+
impersonationToken, response, err := pushService.githubEnterpriseClient.Admin.CreateUserImpersonation(pushService.ctx, pushService.actionsAdminUser, &github.ImpersonateUserOptions{Scopes: []string{minimumRepositoryScope, "workflow"}})
110110
if err != nil {
111-
return nil, errors.Wrap(err, "Failed to impersonate Actions admin user.")
111+
return nil, githubapiutil.EnrichResponseError(response, err, "Failed to impersonate Actions admin user.")
112112
}
113113
pushService.destinationToken.AccessToken = impersonationToken.GetToken()
114114
}
115115
}
116116

117117
repository, response, err := pushService.githubEnterpriseClient.Repositories.Get(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName)
118118
if err != nil && (response == nil || response.StatusCode != http.StatusNotFound) {
119-
return nil, errors.Wrap(err, "Error checking if destination repository exists.")
119+
return nil, githubapiutil.EnrichResponseError(response, err, "Error checking if destination repository exists.")
120120
}
121121
if response.StatusCode != http.StatusNotFound && repositoryHomepage != repository.GetHomepage() && !pushService.force {
122122
return nil, errors.Errorf(errorAlreadyExists)
@@ -143,7 +143,7 @@ func (pushService *pushService) createRepository() (*github.Repository, error) {
143143
if response.StatusCode == http.StatusNotFound && !githubapiutil.HasAnyScope(response, acceptableRepositoryScopes...) {
144144
return nil, fmt.Errorf("The destination token you have provided does not have the `%s` scope.", minimumRepositoryScope)
145145
}
146-
return nil, errors.Wrap(err, "Error creating destination repository.")
146+
return nil, githubapiutil.EnrichResponseError(response, err, "Error creating destination repository.")
147147
}
148148
} else {
149149
log.Debug("Repository already exists. Updating its metadata...")
@@ -156,7 +156,7 @@ func (pushService *pushService) createRepository() (*github.Repository, error) {
156156
return nil, fmt.Errorf("You don't have permission to update the repository at %s/%s. If you wish to update the bundled CodeQL Action please provide a token with the `site_admin` scope.", pushService.destinationRepositoryOwner, pushService.destinationRepositoryName)
157157
}
158158
}
159-
return nil, errors.Wrap(err, "Error updating destination repository.")
159+
return nil, githubapiutil.EnrichResponseError(response, err, "Error updating destination repository.")
160160
}
161161
}
162162

@@ -212,6 +212,7 @@ func (pushService *pushService) pushGit(repository *github.Repository, initialPu
212212
}
213213
refSpecBatches = append(refSpecBatches, deleteRefSpecs)
214214

215+
defaultBrachRefSpec := "+refs/heads/main:refs/heads/main"
215216
if initialPush {
216217
releasePathStats, err := ioutil.ReadDir(pushService.cacheDirectory.ReleasesPath())
217218
if err != nil {
@@ -228,10 +229,10 @@ func (pushService *pushService) pushGit(repository *github.Repository, initialPu
228229
}
229230
refSpecBatches = append(refSpecBatches, initialRefSpecs)
230231
} else {
231-
// We've got to push `main` on its own, so that it will be made the default branch if the repository has just been created. We then push everything else afterwards.
232+
// We've got to push the default branch on its own, so that it will be made the default branch if the repository has just been created. We then push everything else afterwards.
232233
refSpecBatches = append(refSpecBatches,
233234
[]config.RefSpec{
234-
config.RefSpec("+refs/heads/main:refs/heads/main"),
235+
config.RefSpec(defaultBrachRefSpec),
235236
},
236237
[]config.RefSpec{
237238
config.RefSpec("+refs/*:refs/*"),
@@ -245,6 +246,32 @@ func (pushService *pushService) pushGit(repository *github.Repository, initialPu
245246
Auth: credentials,
246247
Progress: os.Stderr,
247248
})
249+
if err != nil && strings.Contains(err.Error(), "pre-receive hook declined") {
250+
log.Warn("Push was rejected by a pre-receive hook. This may be because force-pushing is not allowed. Will try and remove and recreate the branch.")
251+
if len(refSpecs) == 1 && refSpecs[0].String() == defaultBrachRefSpec {
252+
// todo
253+
}
254+
negativeRefSpecs := []config.RefSpec{}
255+
for _, refSpec := range refSpecs {
256+
negativeRefSpecs = append(negativeRefSpecs, config.RefSpec(":"+refSpec.Src()))
257+
err = remote.PushContext(pushService.ctx, &git.PushOptions{
258+
RefSpecs: negativeRefSpecs,
259+
Auth: credentials,
260+
Progress: os.Stderr,
261+
})
262+
if err != nil {
263+
return errors.Wrap(err, "Error removing existing refs.")
264+
}
265+
err = remote.PushContext(pushService.ctx, &git.PushOptions{
266+
RefSpecs: refSpecs,
267+
Auth: credentials,
268+
Progress: os.Stderr,
269+
})
270+
if err != nil && errors.Cause(err) != git.NoErrAlreadyUpToDate {
271+
return errors.Wrap(err, "Error pushing Action to GitHub Enterprise Server.")
272+
}
273+
}
274+
}
248275
if err != nil && errors.Cause(err) != git.NoErrAlreadyUpToDate {
249276
return errors.Wrap(err, "Error pushing Action to GitHub Enterprise Server.")
250277
}
@@ -270,20 +297,20 @@ func (pushService *pushService) createOrUpdateRelease(releaseName string) (*gith
270297

271298
release, response, err := pushService.githubEnterpriseClient.Repositories.GetReleaseByTag(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, releaseMetadata.GetTagName())
272299
if err != nil && response.StatusCode != http.StatusNotFound {
273-
return nil, errors.Wrap(err, "Error checking for existing CodeQL release.")
300+
return nil, githubapiutil.EnrichResponseError(response, err, "Error checking for existing CodeQL release.")
274301
}
275302
if release == nil {
276303
log.Debugf("Creating release %s...", releaseMetadata.GetTagName())
277-
release, _, err := pushService.githubEnterpriseClient.Repositories.CreateRelease(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, &releaseMetadata)
304+
release, response, err := pushService.githubEnterpriseClient.Repositories.CreateRelease(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, &releaseMetadata)
278305
if err != nil {
279-
return nil, errors.Wrap(err, "Error creating release.")
306+
return nil, githubapiutil.EnrichResponseError(response, err, "Error creating release.")
280307
}
281308
return release, nil
282309
}
283-
release, _, err = pushService.githubEnterpriseClient.Repositories.EditRelease(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, release.GetID(), &releaseMetadata)
310+
release, response, err = pushService.githubEnterpriseClient.Repositories.EditRelease(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, release.GetID(), &releaseMetadata)
284311
if err != nil {
285312
log.Debugf("Updating release %s...", releaseMetadata.GetTagName())
286-
return nil, errors.Wrap(err, "Error updating release.")
313+
return nil, githubapiutil.EnrichResponseError(response, err, "Error updating release.")
287314
}
288315
return release, nil
289316
}
@@ -301,7 +328,7 @@ func (pushService *pushService) uploadReleaseAsset(release *github.RepositoryRel
301328
asset := &github.ReleaseAsset{}
302329
response, err := pushService.githubEnterpriseClient.Do(pushService.ctx, request, asset)
303330
if err != nil {
304-
return nil, response, errors.Wrap(err, "Error uploading release asset.")
331+
return nil, response, githubapiutil.EnrichResponseError(response, err, "Error uploading release asset.")
305332
}
306333
return asset, response, nil
307334
}
@@ -315,9 +342,9 @@ func (pushService *pushService) createOrUpdateReleaseAsset(release *github.Repos
315342
return nil
316343
} else {
317344
log.Warnf("Removing existing release asset %s because it was only partially-uploaded (had size %d, but should have been %d)...", existingAsset.GetName(), actualSize, expectedSize)
318-
_, err := pushService.githubEnterpriseClient.Repositories.DeleteReleaseAsset(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, existingAsset.GetID())
345+
response, err := pushService.githubEnterpriseClient.Repositories.DeleteReleaseAsset(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, existingAsset.GetID())
319346
if err != nil {
320-
return errors.Wrap(err, "Error deleting existing release asset.")
347+
return githubapiutil.EnrichResponseError(response, err, "Error deleting existing release asset.")
321348
}
322349
}
323350
}
@@ -333,9 +360,9 @@ func (pushService *pushService) createOrUpdateReleaseAsset(release *github.Repos
333360
if err != nil {
334361
return errors.Wrap(err, "Error opening release asset.")
335362
}
336-
_, _, err = pushService.uploadReleaseAsset(release, assetPathStat, progressReader)
363+
_, response, err := pushService.uploadReleaseAsset(release, assetPathStat, progressReader)
337364
if err != nil {
338-
return errors.Wrap(err, "Error uploading release asset.")
365+
return githubapiutil.EnrichResponseError(response, err, "Error uploading release asset.")
339366
}
340367
return nil
341368
}
@@ -358,9 +385,9 @@ func (pushService *pushService) pushReleases() error {
358385

359386
existingAssets := []*github.ReleaseAsset{}
360387
for page := 1; ; page++ {
361-
assets, _, err := pushService.githubEnterpriseClient.Repositories.ListReleaseAssets(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, release.GetID(), &github.ListOptions{Page: page})
388+
assets, response, err := pushService.githubEnterpriseClient.Repositories.ListReleaseAssets(pushService.ctx, pushService.destinationRepositoryOwner, pushService.destinationRepositoryName, release.GetID(), &github.ListOptions{Page: page})
362389
if err != nil {
363-
return errors.Wrap(err, "Error fetching existing release assets.")
390+
return githubapiutil.EnrichResponseError(response, err, "Error fetching existing release assets.")
364391
}
365392
if len(assets) == 0 {
366393
break
@@ -376,7 +403,7 @@ func (pushService *pushService) pushReleases() error {
376403
for _, assetPathStat := range assetPathStats {
377404
err := pushService.createOrUpdateReleaseAsset(release, existingAssets, assetPathStat)
378405
if err != nil {
379-
return errors.Wrap(err, "Error uploading release assets.")
406+
return err
380407
}
381408
}
382409
}
@@ -410,7 +437,7 @@ func Push(ctx context.Context, cacheDirectory cachedirectory.CacheDirectory, des
410437
}
411438
rootResponse, err := client.Do(ctx, rootRequest, nil)
412439
if err != nil {
413-
return errors.Wrap(err, "Error checking connectivity for GitHub Enterprise client.")
440+
return githubapiutil.EnrichResponseError(rootResponse, err, "Error checking connectivity for GitHub Enterprise client.")
414441
}
415442
if rootRequest.URL != rootResponse.Request.URL {
416443
updatedBaseURL, _ := url.Parse(client.BaseURL.String())

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy