diff --git a/go.mod b/go.mod index bdb2d1859f..3fa196abab 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/imdario/mergo v0.3.5 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/magiconair/properties v1.7.3 // indirect - github.com/mcdafydd/go-azuredevops v0.9.0 + github.com/mcdafydd/go-azuredevops v0.10.2 github.com/microcosm-cc/bluemonday v1.0.1 github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 github.com/mitchellh/go-homedir v1.0.0 diff --git a/go.sum b/go.sum index ce7d8c51fb..724e8b870c 100644 --- a/go.sum +++ b/go.sum @@ -99,6 +99,8 @@ github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-github/v28 v28.0.0 h1:+UjHI4+1W/vsXR4jJBWt0ZA74XHbvt5yBAvsf1M3bgM= @@ -194,6 +196,10 @@ github.com/mcdafydd/go-azuredevops v0.8.4 h1:MVQgoF2xep/7mMPRrv0u6+nCbFSrMTImnwv github.com/mcdafydd/go-azuredevops v0.8.4/go.mod h1:1RwqgbF/Afc2o/BLKsRy3dD/nNFQF0YqroNp0CkP4i0= github.com/mcdafydd/go-azuredevops v0.9.0 h1:WX53H3Z4Mgz9QqRutZFs8+MR8sGJch2Jd6IvPp0GNfo= github.com/mcdafydd/go-azuredevops v0.9.0/go.mod h1:1RwqgbF/Afc2o/BLKsRy3dD/nNFQF0YqroNp0CkP4i0= +github.com/mcdafydd/go-azuredevops v0.10.0 h1:gfwMWZ3Gn14qTDbx1U2Ut2YNzdu64DqPQ5/twqLl8H4= +github.com/mcdafydd/go-azuredevops v0.10.0/go.mod h1:/NYbgJ/1+9+SmG5CjETCoWm+FlLNcRwdiw1/AGW9zm0= +github.com/mcdafydd/go-azuredevops v0.10.2 h1:cVAxfGqSUK7i4ZRc7s+EpeWSOrDgkBM4SzTRI/IUfoE= +github.com/mcdafydd/go-azuredevops v0.10.2/go.mod h1:/NYbgJ/1+9+SmG5CjETCoWm+FlLNcRwdiw1/AGW9zm0= github.com/microcosm-cc/bluemonday v1.0.1 h1:SIYunPjnlXcW+gVfvm0IlSeR5U3WZUOLfVmqg85Go44= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQOF9sPfs26lsefckcFNDcIZtiACQiECzIUkw= diff --git a/server/events/vcs/azuredevops_client.go b/server/events/vcs/azuredevops_client.go index 40ab1fc8a2..10d237b679 100644 --- a/server/events/vcs/azuredevops_client.go +++ b/server/events/vcs/azuredevops_client.go @@ -3,6 +3,7 @@ package vcs import ( "context" "fmt" + "net/http" "net/url" "strings" "time" @@ -180,19 +181,57 @@ func (g *AzureDevopsClient) UpdateStatus(repo models.Repo, pull models.PullReque } genreStr := "Atlantis Bot" - status := azuredevops.GitStatus{ - State: &adState, - Description: &description, - Context: &azuredevops.GitStatusContext{ - Name: &src, - Genre: &genreStr, - }, + status := azuredevops.GitPullRequestStatus{} + status.Context = &azuredevops.GitStatusContext{ + Name: &src, + Genre: &genreStr, } + status.Description = &description + status.State = &adState if url != "" { status.TargetURL = &url } + owner, project, repoName := SplitAzureDevopsRepoFullName(repo.FullName) - _, _, err := g.Client.Git.CreateStatus(g.ctx, owner, project, repoName, pull.HeadCommit, status) + + opts := azuredevops.PullRequestListOptions{} + source, resp, err := g.Client.PullRequests.Get(g.ctx, owner, project, pull.Num, &opts) + if err != nil { + return errors.Wrap(err, "getting pull request") + } + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "http response code %d getting pull request", resp.StatusCode) + } + if source.GetSupportsIterations() { + opts := azuredevops.PullRequestIterationsListOptions{} + iterations, resp, err := g.Client.PullRequests.ListIterations(g.ctx, owner, project, repoName, pull.Num, &opts) + if err != nil { + return errors.Wrap(err, "listing pull request iterations") + } + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "http response code %d listing pull request iterations", resp.StatusCode) + } + for _, iteration := range iterations { + if sourceRef := iteration.GetSourceRefCommit(); sourceRef != nil { + if *sourceRef.CommitID == pull.HeadCommit { + status.IterationID = iteration.ID + break + } + } + } + if iterationID := status.IterationID; iterationID != nil { + if !(*iterationID >= 1) { + return errors.New("supportsIterations was true but got invalid iteration ID or no matching iteration commit SHA was found") + } + } + } + _, resp, err = g.Client.PullRequests.CreateStatus(g.ctx, owner, project, repoName, pull.Num, &status) + if err != nil { + return errors.Wrap(err, "creating pull request status") + } + if resp.StatusCode != http.StatusOK { + return errors.Wrapf(err, "http response code %d creating pull request status", resp.StatusCode) + } return err } diff --git a/server/events/vcs/azuredevops_client_test.go b/server/events/vcs/azuredevops_client_test.go index 1c91b0d650..6cfa6f196a 100644 --- a/server/events/vcs/azuredevops_client_test.go +++ b/server/events/vcs/azuredevops_client_test.go @@ -121,38 +121,69 @@ func TestAzureDevopsClient_MergePull(t *testing.T) { func TestAzureDevopsClient_UpdateStatus(t *testing.T) { cases := []struct { - status models.CommitStatus - expState string + status models.CommitStatus + expState string + supportsIterations bool }{ { models.PendingCommitStatus, "pending", + true, + }, + { + models.SuccessCommitStatus, + "succeeded", + true, + }, + { + models.FailedCommitStatus, + "failed", + true, + }, + { + models.PendingCommitStatus, + "pending", + false, }, { models.SuccessCommitStatus, "succeeded", + false, }, { models.FailedCommitStatus, "failed", + false, }, } - response := `{"context":{"genre":"Atlantis Bot","name":"src"},"description":"description","state":"%s","targetUrl":"https://google.com"} -` + iterResponse := `{"count": 2, "value": [{"id": 1, "sourceRefCommit": { "commitId": "oldsha"}}, {"id": 2, "sourceRefCommit": { "commitId": "sha"}}]}` + prResponse := `{"supportsIterations": %t}` + partResponse := `{"context":{"genre":"Atlantis Bot","name":"src"},"description":"description","state":"%s","targetUrl":"https://google.com"` + for _, c := range cases { + prResponse := fmt.Sprintf(prResponse, c.supportsIterations) t.Run(c.expState, func(t *testing.T) { gotRequest := false testServer := httptest.NewTLSServer( http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.RequestURI { - case "/owner/project/_apis/git/repositories/repo/commits/sha/statuses?api-version=5.1-preview.1": + case "/owner/project/_apis/git/repositories/repo/pullrequests/22/statuses?api-version=5.1-preview.1": gotRequest = true + defer r.Body.Close() // nolint: errcheck body, err := ioutil.ReadAll(r.Body) Ok(t, err) - exp := fmt.Sprintf(response, c.expState) + exp := fmt.Sprintf(partResponse, c.expState) + if c.supportsIterations == true { + exp = fmt.Sprintf("%s%s}\n", exp, `,"iterationId":2`) + } else { + exp = fmt.Sprintf("%s}\n", exp) + } Equals(t, exp, string(body)) - defer r.Body.Close() // nolint: errcheck - w.Write([]byte(response)) // nolint: errcheck + w.Write([]byte(exp)) // nolint: errcheck + case "/owner/project/_apis/git/repositories/repo/pullrequests/22/iterations?api-version=5.1": + w.Write([]byte(iterResponse)) // nolint: errcheck + case "/owner/project/_apis/git/pullrequests/22?api-version=5.1-preview.1": + w.Write([]byte(prResponse)) // nolint: errcheck default: t.Errorf("got unexpected request at %q", r.RequestURI) http.Error(w, "not found", http.StatusNotFound) diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go index 938f646f00..24fbae6e3c 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/sort.go @@ -19,7 +19,7 @@ func SortKeys(vs []reflect.Value) []reflect.Value { } // Sort the map keys. - sort.Slice(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) + sort.SliceStable(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) // Deduplicate keys (fails for NaNs). vs2 := vs[:1] @@ -42,6 +42,8 @@ func isLess(x, y reflect.Value) bool { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return x.Uint() < y.Uint() case reflect.Float32, reflect.Float64: + // NOTE: This does not sort -0 as less than +0 + // since Go maps treat -0 and +0 as equal keys. fx, fy := x.Float(), y.Float() return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) case reflect.Complex64, reflect.Complex128: diff --git a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go index d13a12ccfc..06a8ffd036 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/value/zero.go @@ -4,7 +4,10 @@ package value -import "reflect" +import ( + "math" + "reflect" +) // IsZero reports whether v is the zero value. // This does not rely on Interface and so can be used on unexported fields. @@ -17,9 +20,9 @@ func IsZero(v reflect.Value) bool { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: return v.Uint() == 0 case reflect.Float32, reflect.Float64: - return v.Float() == 0 + return math.Float64bits(v.Float()) == 0 case reflect.Complex64, reflect.Complex128: - return v.Complex() == 0 + return math.Float64bits(real(v.Complex())) == 0 && math.Float64bits(imag(v.Complex())) == 0 case reflect.String: return v.String() == "" case reflect.UnsafePointer: diff --git a/vendor/github.com/google/go-cmp/cmp/report_compare.go b/vendor/github.com/google/go-cmp/cmp/report_compare.go index 05efb992c5..17a05eede4 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_compare.go +++ b/vendor/github.com/google/go-cmp/cmp/report_compare.go @@ -168,7 +168,7 @@ func (opts formatOptions) formatDiffList(recs []reportRecord, k reflect.Kind) te var isZero bool switch opts.DiffMode { case diffIdentical: - isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueX) + isZero = value.IsZero(r.Value.ValueX) || value.IsZero(r.Value.ValueY) case diffRemoved: isZero = value.IsZero(r.Value.ValueX) case diffInserted: diff --git a/vendor/github.com/google/go-cmp/cmp/report_reflect.go b/vendor/github.com/google/go-cmp/cmp/report_reflect.go index 5521c604c5..2761b62892 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_reflect.go +++ b/vendor/github.com/google/go-cmp/cmp/report_reflect.go @@ -208,7 +208,6 @@ func (opts formatOptions) FormatValue(v reflect.Value, m visitedPointers) (out t func formatMapKey(v reflect.Value) string { var opts formatOptions opts.TypeMode = elideType - opts.AvoidStringer = true opts.ShallowPointers = true s := opts.FormatValue(v, visitedPointers{}).String() return strings.TrimSpace(s) diff --git a/vendor/github.com/google/go-cmp/cmp/report_slices.go b/vendor/github.com/google/go-cmp/cmp/report_slices.go index 8cb3265e76..eafcf2e4c0 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_slices.go +++ b/vendor/github.com/google/go-cmp/cmp/report_slices.go @@ -90,7 +90,7 @@ func (opts formatOptions) FormatDiffSlice(v *valueNode) textNode { } if r == '\n' { if maxLineLen < i-lastLineIdx { - lastLineIdx = i - lastLineIdx + maxLineLen = i - lastLineIdx } lastLineIdx = i + 1 numLines++ @@ -322,7 +322,7 @@ func coalesceInterveningIdentical(groups []diffStats, windowSize int) []diffStat hadX, hadY := prev.NumRemoved > 0, prev.NumInserted > 0 hasX, hasY := next.NumRemoved > 0, next.NumInserted > 0 if ((hadX || hasX) && (hadY || hasY)) && curr.NumIdentical <= windowSize { - *prev = (*prev).Append(*curr).Append(*next) + *prev = prev.Append(*curr).Append(*next) groups = groups[:len(groups)-1] // Truncate off equal group continue } diff --git a/vendor/github.com/google/go-cmp/cmp/report_text.go b/vendor/github.com/google/go-cmp/cmp/report_text.go index 80605d0e44..8b8fcab7bd 100644 --- a/vendor/github.com/google/go-cmp/cmp/report_text.go +++ b/vendor/github.com/google/go-cmp/cmp/report_text.go @@ -19,6 +19,11 @@ var randBool = rand.New(rand.NewSource(time.Now().Unix())).Intn(2) == 0 type indentMode int func (n indentMode) appendIndent(b []byte, d diffMode) []byte { + // The output of Diff is documented as being unstable to provide future + // flexibility in changing the output for more humanly readable reports. + // This logic intentionally introduces instability to the exact output + // so that users can detect accidental reliance on stability early on, + // rather than much later when an actual change to the format occurs. if flags.Deterministic || randBool { // Use regular spaces (U+0020). switch d { @@ -360,7 +365,7 @@ func (s diffStats) String() string { // Pluralize the name (adjusting for some obscure English grammar rules). name := s.Name if sum > 1 { - name = name + "s" + name += "s" if strings.HasSuffix(name, "ys") { name = name[:len(name)-2] + "ies" // e.g., "entrys" => "entries" } diff --git a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/azuredevops-accessors.go b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/azuredevops-accessors.go index b63d155722..8d3c414d17 100644 --- a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/azuredevops-accessors.go +++ b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/azuredevops-accessors.go @@ -1663,6 +1663,14 @@ func (g *GitPullRequest) GetURL() string { return *g.URL } +// GetChangeTrackingID returns the ChangeTrackingID field if it's non-nil, zero value otherwise. +func (g *GitPullRequestChange) GetChangeTrackingID() int { + if g == nil || g.ChangeTrackingID == nil { + return 0 + } + return *g.ChangeTrackingID +} + // GetID returns the ID field if it's non-nil, zero value otherwise. func (g *GitPullRequestCommentThread) GetID() int { if g == nil || g.ID == nil { @@ -1823,6 +1831,126 @@ func (g *GitPullRequestCompletionOptions) GetTriggeredByAutoComplete() bool { return *g.TriggeredByAutoComplete } +// GetAuthor returns the Author field. +func (g *GitPullRequestIteration) GetAuthor() *IdentityRef { + if g == nil { + return nil + } + return g.Author +} + +// GetCommonRefCommit returns the CommonRefCommit field. +func (g *GitPullRequestIteration) GetCommonRefCommit() *GitCommitRef { + if g == nil { + return nil + } + return g.CommonRefCommit +} + +// GetCreatedDate returns the CreatedDate field. +func (g *GitPullRequestIteration) GetCreatedDate() *Time { + if g == nil { + return nil + } + return g.CreatedDate +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetDescription() string { + if g == nil || g.Description == nil { + return "" + } + return *g.Description +} + +// GetHasMoreCommits returns the HasMoreCommits field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetHasMoreCommits() bool { + if g == nil || g.HasMoreCommits == nil { + return false + } + return *g.HasMoreCommits +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetID() int { + if g == nil || g.ID == nil { + return 0 + } + return *g.ID +} + +// GetNewTargetRefName returns the NewTargetRefName field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetNewTargetRefName() string { + if g == nil || g.NewTargetRefName == nil { + return "" + } + return *g.NewTargetRefName +} + +// GetOldTargetRefName returns the OldTargetRefName field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetOldTargetRefName() string { + if g == nil || g.OldTargetRefName == nil { + return "" + } + return *g.OldTargetRefName +} + +// GetPush returns the Push field. +func (g *GitPullRequestIteration) GetPush() *GitPushRef { + if g == nil { + return nil + } + return g.Push +} + +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIteration) GetReason() string { + if g == nil || g.Reason == nil { + return "" + } + return *g.Reason +} + +// GetSourceRefCommit returns the SourceRefCommit field. +func (g *GitPullRequestIteration) GetSourceRefCommit() *GitCommitRef { + if g == nil { + return nil + } + return g.SourceRefCommit +} + +// GetTargetRefCommit returns the TargetRefCommit field. +func (g *GitPullRequestIteration) GetTargetRefCommit() *GitCommitRef { + if g == nil { + return nil + } + return g.TargetRefCommit +} + +// GetUpdatedDate returns the UpdatedDate field. +func (g *GitPullRequestIteration) GetUpdatedDate() *Time { + if g == nil { + return nil + } + return g.UpdatedDate +} + +// GetNextSkip returns the NextSkip field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIterationChanges) GetNextSkip() int { + if g == nil || g.NextSkip == nil { + return 0 + } + return *g.NextSkip +} + +// GetNextTop returns the NextTop field if it's non-nil, zero value otherwise. +func (g *GitPullRequestIterationChanges) GetNextTop() int { + if g == nil || g.NextTop == nil { + return 0 + } + return *g.NextTop +} + // GetDetectRenameFalsePositives returns the DetectRenameFalsePositives field if it's non-nil, zero value otherwise. func (g *GitPullRequestMergeOptions) GetDetectRenameFalsePositives() bool { if g == nil || g.DetectRenameFalsePositives == nil { @@ -1839,12 +1967,12 @@ func (g *GitPullRequestMergeOptions) GetDisableRenames() bool { return *g.DisableRenames } -// GetProperties returns the Properties field. -func (g *GitPullRequestStatus) GetProperties() *Time { - if g == nil { - return nil +// GetIterationID returns the IterationID field if it's non-nil, zero value otherwise. +func (g *GitPullRequestStatus) GetIterationID() int { + if g == nil || g.IterationID == nil { + return 0 } - return g.Properties + return *g.IterationID } // GetComment returns the Comment field. diff --git a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/git.go b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/git.go index 48497b14ce..b07d538373 100644 --- a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/git.go +++ b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/git.go @@ -201,6 +201,38 @@ type GitPullRequest struct { WorkItemRefs []*ResourceRef `json:"workItemRefs,omitempty"` } +// GitPullRequestChange Change made in a pull request. +type GitPullRequestChange struct { + ChangeTrackingID *int `json:"changeTrackingId,omitempty"` +} + +// GitPullRequestIteration Provides properties that describe a Git pull request iteration. Iterations are created as a result of creating and pushing updates to a pull request. +type GitPullRequestIteration struct { + Links interface{} `json:"_links,omitempty"` + Author *IdentityRef `json:"author,omitempty"` + ChangeList []*GitPullRequestChange `json:"changeList,omitempty"` + Commits []*GitCommitRef `json:"commits,omitempty"` + CommonRefCommit *GitCommitRef `json:"commonRefCommit,omitempty"` + CreatedDate *Time `json:"createdDate,omitempty"` + Description *string `json:"description,omitempty"` + HasMoreCommits *bool `json:"hasMoreCommits,omitempty"` + ID *int `json:"id,omitempty"` + NewTargetRefName *string `json:"newTargetRefName,omitempty"` + OldTargetRefName *string `json:"oldTargetRefName,omitempty"` + Push *GitPushRef `json:"push,omitempty"` + Reason *string `json:"reason,omitempty"` + SourceRefCommit *GitCommitRef `json:"sourceRefCommit,omitempty"` + TargetRefCommit *GitCommitRef `json:"targetRefCommit,omitempty"` + UpdatedDate *Time `json:"updatedDate,omitempty"` +} + +// Collection of changes made in a pull request. +type GitPullRequestIterationChanges struct { + ChangeEntries []*GitPullRequestChange `json:"changeEntries,omitempty"` + NextSkip *int `json:"nextSkip,omitempty"` + NextTop *int `json:"nextTop,omitempty"` +} + // GitPullRequestCompletionOptions describes preferences about how the pull // request should be completed. // SquashMerge is deprecated. You should explicity set the value of MergeStrategy. If @@ -333,8 +365,8 @@ type GitStatus struct { // an iteration. type GitPullRequestStatus struct { GitStatus - IterationID int `json:"iterationId,omitempty"` - Properties *Time `json:"properties,omitempty"` + IterationID *int `json:"iterationId,omitempty"` + Properties map[string]interface{} `json:"properties,omitempty"` } // GitRefListOptions describes what the request to the API should look like @@ -363,6 +395,23 @@ type GitUserDate struct { Date *Time `json:"date,omitempty"` } +// IterationReason The reason for which the pull request iteration was created. +type IterationReason int + +// IterationReason enum declaration +const ( + IterationPush IterationReason = iota + IterationForcePush + IterationCreate + IterationRebase + IterationUnknown + IterationRetarget +) + +func (d IterationReason) String() string { + return [...]string{"push", "forcePush", "create", "rebase", "unknown", "retarget"}[d] +} + // UpdateRefs returns a list of the references for a git repo func (s *GitService) UpdateRefs(ctx context.Context, owner, project, repo, refType string, opts *GitRefListOptions) ([]*GitRef, *http.Response, error) { URL := fmt.Sprintf( diff --git a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/pull_requests.go b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/pull_requests.go index 8383b98b5e..9197bcb615 100644 --- a/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/pull_requests.go +++ b/vendor/github.com/mcdafydd/go-azuredevops/azuredevops/pull_requests.go @@ -129,6 +129,17 @@ type PullRequestGetOptions struct { Top int `url:"$top,omitempty"` } +// PullRequestIterationsListOptions describes what the request to the API should look like +type PullRequestIterationsListOptions struct { + IncludeCommits bool `url:"includeCommits,omitempty"` +} + +// PullRequestsIterationsListResponse describes a pull requests list response +type PullRequestsIterationsListResponse struct { + Count int `json:"count"` + GitPullRequestIterations []*GitPullRequestIteration `json:"value"` +} + // PullRequestsListResponse describes a pull requests list response type PullRequestsListResponse struct { Count int `json:"count"` @@ -444,3 +455,87 @@ func (s *PullRequestsService) CreateComments(ctx context.Context, owner, project return r, resp, err } + +// CreateStatus Create a pull request status. +// Azure Devops API docs: https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20statuses/create +// +func (s *PullRequestsService) CreateStatus(ctx context.Context, owner, project, repo string, pullNum int, status *GitPullRequestStatus) (*GitPullRequestStatus, *http.Response, error) { + URL := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullrequests/%d/statuses?api-version=5.1-preview.1", + owner, + project, + repo, + pullNum, + ) + + if context := status.GetContext(); context != nil { + if context.GetName() == "" { + return nil, nil, errors.New("CreateStatus: Must supply a value for Context.Name") + } + } + + req, err := s.client.NewRequest("POST", URL, status) + if err != nil { + return nil, nil, err + } + + r := new(GitPullRequestStatus) + resp, err := s.client.Execute(ctx, req, r) + if err != nil { + return nil, nil, err + } + + return r, resp, err +} + +// GetIteration Gets a single pull request iteration. +// Azure Devops API docs: https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20iterations/get?view=azure-devops-rest-5.1 +// +func (s *PullRequestsService) GetIteration(ctx context.Context, owner, project, repo string, pullNum int, iterationID int) (*GitPullRequestIteration, *http.Response, error) { + URL := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullrequests/%d/iterations/%d?api-version=5.1", + owner, + project, + repo, + pullNum, + iterationID, + ) + + req, err := s.client.NewRequest("GET", URL, nil) + if err != nil { + return nil, nil, err + } + + r := new(GitPullRequestIteration) + resp, err := s.client.Execute(ctx, req, r) + if err != nil { + return nil, nil, err + } + + return r, resp, err +} + +// ListIterations Lists all iterations on a pull request. +// Azure Devops API docs: https://docs.microsoft.com/en-us/rest/api/azure/devops/git/pull%20request%20iterations/list?view=azure-devops-rest-5.1 +// +func (s *PullRequestsService) ListIterations(ctx context.Context, owner, project, repo string, pullNum int, opts *PullRequestIterationsListOptions) ([]*GitPullRequestIteration, *http.Response, error) { + URL := fmt.Sprintf("%s/%s/_apis/git/repositories/%s/pullrequests/%d/iterations?api-version=5.1", + owner, + project, + repo, + pullNum, + ) + + URL, err := addOptions(URL, opts) + + req, err := s.client.NewRequest("GET", URL, nil) + if err != nil { + return nil, nil, err + } + + r := new(PullRequestsIterationsListResponse) + resp, err := s.client.Execute(ctx, req, r) + if err != nil { + return nil, nil, err + } + + return r.GitPullRequestIterations, resp, err +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 092a8ddd6a..7b7212283e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -85,7 +85,7 @@ github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/timestamp -# github.com/google/go-cmp v0.3.0 +# github.com/google/go-cmp v0.3.1 github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags @@ -149,7 +149,7 @@ github.com/magiconair/properties github.com/mattn/go-colorable # github.com/mattn/go-isatty v0.0.4 github.com/mattn/go-isatty -# github.com/mcdafydd/go-azuredevops v0.9.0 +# github.com/mcdafydd/go-azuredevops v0.10.2 github.com/mcdafydd/go-azuredevops/azuredevops # github.com/microcosm-cc/bluemonday v1.0.1 github.com/microcosm-cc/bluemonday