Early exit repo sync if merge-upstream requires workflow scope
This commit is contained in:
parent
bf7db84ca8
commit
fa8b514bf1
3 changed files with 44 additions and 2 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue