Early exit repo sync if merge-upstream requires workflow scope

This commit is contained in:
William Martin 2023-06-21 15:47:33 +02:00
parent bf7db84ca8
commit fa8b514bf1
3 changed files with 44 additions and 2 deletions

View file

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghrepo"
@ -33,6 +34,9 @@ type upstreamMergeErr struct{ error }
var upstreamMergeUnavailableErr = upstreamMergeErr{errors.New("upstream merge API is unavailable")}
var missingWorkflowScopeRE = regexp.MustCompile("refusing to allow.*without `workflow` scope")
var missingWorkflowScopeErr = errors.New("Upstream commits contain workflow changes, which require the `workflow` scope to merge. To request it, run: gh auth refresh -s workflow")
func triggerUpstreamMerge(client *api.Client, repo ghrepo.Interface, branch string) (string, error) {
var payload bytes.Buffer
if err := json.NewEncoder(&payload).Encode(map[string]interface{}{
@ -52,6 +56,9 @@ func triggerUpstreamMerge(client *api.Client, repo ghrepo.Interface, branch stri
if errors.As(err, &httpErr) {
switch httpErr.StatusCode {
case http.StatusUnprocessableEntity, http.StatusConflict:
if missingWorkflowScopeRE.MatchString(httpErr.Message) {
return "", missingWorkflowScopeErr
}
return "", upstreamMergeErr{errors.New(httpErr.Message)}
case http.StatusNotFound:
return "", upstreamMergeUnavailableErr

View file

@ -457,6 +457,24 @@ func Test_SyncRun(t *testing.T) {
wantErr: true,
errMsg: "trunk branch does not exist on OWNER/REPO-FORK repository",
},
{
name: "sync remote fork with missing workflow scope on token",
opts: &SyncOptions{
DestArg: "FORKOWNER/REPO-FORK",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
reg.Register(
httpmock.REST("POST", "repos/FORKOWNER/REPO-FORK/merge-upstream"),
httpmock.StatusJSONResponse(422, struct {
Message string `json:"message"`
}{Message: "refusing to allow an OAuth App to create or update workflow `.github/workflows/unimportant.yml` without `workflow` scope"}))
},
wantErr: true,
errMsg: "Upstream commits contain workflow changes, which require the `workflow` scope to merge. To request it, run: gh auth refresh -s workflow",
},
}
for _, tt := range tests {
reg := &httpmock.Registry{}

View file

@ -143,7 +143,20 @@ func StatusStringResponse(status int, body string) Responder {
func JSONResponse(body interface{}) Responder {
return func(req *http.Request) (*http.Response, error) {
b, _ := json.Marshal(body)
return httpResponse(200, req, bytes.NewBuffer(b)), nil
header := http.Header{
"Content-Type": []string{"application/json"},
}
return httpResponseWithHeader(200, req, bytes.NewBuffer(b), header), nil
}
}
func StatusJSONResponse(status int, body interface{}) Responder {
return func(req *http.Request) (*http.Response, error) {
b, _ := json.Marshal(body)
header := http.Header{
"Content-Type": []string{"application/json"},
}
return httpResponseWithHeader(status, req, bytes.NewBuffer(b), header), nil
}
}
@ -215,10 +228,14 @@ func ScopesResponder(scopes string) func(*http.Request) (*http.Response, error)
}
func httpResponse(status int, req *http.Request, body io.Reader) *http.Response {
return httpResponseWithHeader(status, req, body, http.Header{})
}
func httpResponseWithHeader(status int, req *http.Request, body io.Reader, header http.Header) *http.Response {
return &http.Response{
StatusCode: status,
Request: req,
Body: io.NopCloser(body),
Header: http.Header{},
Header: header,
}
}