mirror of
https://git.vectorsigma.ru/public/atlantis.git
synced 2026-08-04 07:38:35 +00:00
- switch to go-azuredevops package
- remove ADHostname - add ADOrg and ADProject
This commit is contained in:
@@ -23,7 +23,6 @@ import (
|
||||
homedir "github.com/mitchellh/go-homedir"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/runatlantis/atlantis/server"
|
||||
//"github.com/runatlantis/atlantis/server/events/vcs/azuredevops"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/bitbucketcloud"
|
||||
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
||||
"github.com/runatlantis/atlantis/server/logging"
|
||||
@@ -37,7 +36,8 @@ import (
|
||||
// 3. Add your flag's description etc. to the stringFlags, intFlags, or boolFlags slices.
|
||||
const (
|
||||
// Flag names.
|
||||
ADHostnameFlag = "azuredevops-hostname"
|
||||
ADOrgFlag = "azuredevops-org"
|
||||
ADProjectFlag = "azuredevops-project"
|
||||
ADTokenFlag = "azuredevops-token"
|
||||
ADUserFlag = "azuredevops-user"
|
||||
ADWebhookSecretFlag = "azuredevops-webhook-secret" // nolint: gosec
|
||||
@@ -75,7 +75,7 @@ const (
|
||||
TFETokenFlag = "tfe-token"
|
||||
|
||||
// Flag defaults.
|
||||
DefaultADHostname = "visualstudio.com"
|
||||
DefaultADOrg = ""
|
||||
DefaultCheckoutStrategy = "branch"
|
||||
DefaultBitbucketBaseURL = bitbucketcloud.BaseURL
|
||||
DefaultDataDir = "~/.atlantis"
|
||||
@@ -86,9 +86,13 @@ const (
|
||||
)
|
||||
|
||||
var stringFlags = map[string]stringFlag{
|
||||
ADHostnameFlag: {
|
||||
description: "Hostname of your Azure Devops installation.",
|
||||
defaultValue: DefaultADHostname,
|
||||
ADOrgFlag: {
|
||||
description: "Organization name of your Azure Devops instance.",
|
||||
defaultValue: "",
|
||||
},
|
||||
ADProjectFlag: {
|
||||
description: "Project name in your Azure Devops insance.",
|
||||
defaultValue: "",
|
||||
},
|
||||
ADUserFlag: {
|
||||
description: "Azure Devops username of API user.",
|
||||
@@ -420,8 +424,8 @@ func (s *ServerCmd) setDefaults(c *server.UserConfig) {
|
||||
if c.DataDir == "" {
|
||||
c.DataDir = DefaultDataDir
|
||||
}
|
||||
if c.AzureDevopsHostname == "" {
|
||||
c.AzureDevopsHostname = DefaultADHostname
|
||||
if c.AzureDevopsOrg == "" {
|
||||
c.AzureDevopsOrg = DefaultADOrg
|
||||
}
|
||||
if c.GithubHostname == "" {
|
||||
c.GithubHostname = DefaultGHHostname
|
||||
@@ -458,10 +462,11 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
// 1. github user and token set
|
||||
// 2. gitlab user and token set
|
||||
// 3. bitbucket user and token set
|
||||
// 4. azuredevops user and token set
|
||||
// 4. azuredevops user, token, org and project set
|
||||
// 5. any combination of the above
|
||||
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag)
|
||||
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) {
|
||||
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s/--%s/--%s must be set", GHUserFlag, GHTokenFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag, ADOrgFlag, ADProjectFlag)
|
||||
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "") ||
|
||||
(userConfig.AzureDevopsOrg == "") != (userConfig.AzureDevopsProject == "")) {
|
||||
return vcsErr
|
||||
}
|
||||
// At this point, we know that there can't be a single user/token without
|
||||
@@ -481,10 +486,6 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {
|
||||
return fmt.Errorf("--%s cannot be specified for Bitbucket Cloud because it is not supported by Bitbucket", BitbucketWebhookSecretFlag)
|
||||
}
|
||||
|
||||
if userConfig.AzureDevopsUser == DefaultADHostname && userConfig.AzureDevopsToken != "" {
|
||||
return fmt.Errorf("--%s cannot be specified for Azure Devops because it is not supported by Microsoft", BitbucketWebhookSecretFlag)
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(userConfig.BitbucketBaseURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing --%s flag value %q: %s", BitbucketWebhookSecretFlag, userConfig.BitbucketBaseURL, err)
|
||||
@@ -556,8 +557,11 @@ func (s *ServerCmd) securityWarnings(userConfig *server.UserConfig) {
|
||||
if userConfig.BitbucketUser != "" && userConfig.BitbucketBaseURL == DefaultBitbucketBaseURL && !s.SilenceOutput {
|
||||
s.Logger.Warn("Bitbucket Cloud does not support webhook secrets. This could allow attackers to spoof requests from Bitbucket. Ensure you are whitelisting Bitbucket IPs")
|
||||
}
|
||||
if userConfig.AzureDevopsUser != "" && userConfig.AzureDevopsHostname == DefaultADHostname && !s.SilenceOutput {
|
||||
s.Logger.Warn("Azure Devops does not support webhook secrets without org identifier. This could allow attackers to spoof requests from Azure. Ensure you are whitelisting Azure Devops IPs")
|
||||
if userConfig.AzureDevopsUser != "" && userConfig.AzureDevopsWebhookSecret == "" && !s.SilenceOutput {
|
||||
s.Logger.Warn("No Azure Devops webhook secret set. This could allow attackers to spoof requests from Azure Devops.")
|
||||
}
|
||||
if userConfig.AzureDevopsUser != "" && userConfig.AzureDevopsWebhookSecret == "" && !s.SilenceOutput {
|
||||
s.Logger.Warn("No Azure Devops webhook secret set. This could allow attackers to spoof requests from Azure Devops.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -379,7 +379,8 @@ func TestExecute_Defaults(t *testing.T) {
|
||||
Equals(t, "https://api.bitbucket.org", passedConfig.BitbucketBaseURL)
|
||||
Equals(t, "bitbucket-token", passedConfig.BitbucketToken)
|
||||
Equals(t, "bitbucket-user", passedConfig.BitbucketUser)
|
||||
Equals(t, "https://dev.azure.org", passedConfig.ADBaseURL)
|
||||
Equals(t, "azuredevops-org", passedConfig.AzureDevopsOrg)
|
||||
Equals(t, "azuredevops-project", passedConfig.AzureDevopsProject)
|
||||
Equals(t, "azuredevops-token", passedConfig.AzureDevopsToken)
|
||||
Equals(t, "azuredevops-user", passedConfig.AzureDevopsUser)
|
||||
Equals(t, "", passedConfig.BitbucketWebhookSecret)
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// Package bitbucketcloud holds code for Bitbucket Cloud aka (bitbucket.org).
|
||||
// It is separate from bitbucketserver because Bitbucket Server has different
|
||||
// APIs.
|
||||
package azuredevops
|
||||
|
||||
const BaseURL = "https://visualstudio.com"
|
||||
@@ -1,225 +0,0 @@
|
||||
package azuredevops
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
HTTPClient *http.Client
|
||||
Username string
|
||||
Password string
|
||||
BaseURL string
|
||||
AtlantisURL string
|
||||
}
|
||||
|
||||
// NewClient builds a Azure Devops cloud client. atlantisURL is the
|
||||
// URL for Atlantis that will be linked to from the build status icons. This
|
||||
// linking is annoying because we don't have anywhere good to link but a URL is
|
||||
// required.
|
||||
func NewClient(httpClient *http.Client, username string, password string, atlantisURL string) *Client {
|
||||
if httpClient == nil {
|
||||
httpClient = http.DefaultClient
|
||||
}
|
||||
return &Client{
|
||||
HTTPClient: httpClient,
|
||||
Username: username,
|
||||
Password: password,
|
||||
BaseURL: BaseURL,
|
||||
AtlantisURL: atlantisURL,
|
||||
}
|
||||
}
|
||||
|
||||
// GetModifiedFiles returns the names of files that were modified in the merge request.
|
||||
// The names include the path to the file from the repo root, ex. parent/child/file.txt.
|
||||
func (b *Client) GetModifiedFiles(repo models.Repo, pull models.PullRequest) ([]string, error) {
|
||||
var files []string
|
||||
|
||||
nextPageURL := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/diffstat", b.BaseURL, repo.FullName, pull.Num)
|
||||
// We'll only loop 1000 times as a safety measure.
|
||||
maxLoops := 1000
|
||||
for i := 0; i < maxLoops; i++ {
|
||||
resp, err := b.makeRequest("GET", nextPageURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var diffStat DiffStat
|
||||
if err := json.Unmarshal(resp, &diffStat); err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not parse response %q", string(resp))
|
||||
}
|
||||
if err := validator.New().Struct(diffStat); err != nil {
|
||||
return nil, errors.Wrapf(err, "API response %q was missing fields", string(resp))
|
||||
}
|
||||
for _, v := range diffStat.Values {
|
||||
if v.Old != nil {
|
||||
files = append(files, *v.Old.Path)
|
||||
}
|
||||
if v.New != nil {
|
||||
files = append(files, *v.New.Path)
|
||||
}
|
||||
}
|
||||
if diffStat.Next == nil || *diffStat.Next == "" {
|
||||
break
|
||||
}
|
||||
nextPageURL = *diffStat.Next
|
||||
}
|
||||
|
||||
// Now ensure all files are unique.
|
||||
hash := make(map[string]bool)
|
||||
var unique []string
|
||||
for _, f := range files {
|
||||
if !hash[f] {
|
||||
unique = append(unique, f)
|
||||
hash[f] = true
|
||||
}
|
||||
}
|
||||
return unique, nil
|
||||
}
|
||||
|
||||
// CreateComment creates a comment on the merge request.
|
||||
func (b *Client) CreateComment(repo models.Repo, pullNum int, comment string) error {
|
||||
// NOTE: I tried to find the maximum size of a comment for Azure Devops.org but
|
||||
// I got up to 200k chars without issue so for now I'm not going to bother
|
||||
// to detect this.
|
||||
bodyBytes, err := json.Marshal(map[string]map[string]string{"content": {
|
||||
"raw": comment,
|
||||
}})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "json encoding")
|
||||
}
|
||||
path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/comments", b.BaseURL, repo.FullName, pullNum)
|
||||
_, err = b.makeRequest("POST", path, bytes.NewBuffer(bodyBytes))
|
||||
return err
|
||||
}
|
||||
|
||||
// PullIsApproved returns true if the merge request was approved.
|
||||
func (b *Client) PullIsApproved(repo models.Repo, pull models.PullRequest) (bool, error) {
|
||||
path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d", b.BaseURL, repo.FullName, pull.Num)
|
||||
resp, err := b.makeRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var pullResp PullRequest
|
||||
if err := json.Unmarshal(resp, &pullResp); err != nil {
|
||||
return false, errors.Wrapf(err, "Could not parse response %q", string(resp))
|
||||
}
|
||||
if err := validator.New().Struct(pullResp); err != nil {
|
||||
return false, errors.Wrapf(err, "API response %q was missing fields", string(resp))
|
||||
}
|
||||
for _, participant := range pullResp.Participants {
|
||||
// Azure Devops allows the author to approve their own pull request. This
|
||||
// defeats the purpose of approvals so we don't count that approval.
|
||||
if *participant.Approved && *participant.User.Username != pull.Author {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// PullIsMergeable returns true if the merge request has no conflicts and can be merged.
|
||||
func (b *Client) PullIsMergeable(repo models.Repo, pull models.PullRequest) (bool, error) {
|
||||
// NOTE: The 1.0 API is deprecated, but the 2.0 API does not provide this endpoint.
|
||||
path := fmt.Sprintf("%s/1.0/repositories/%s/pullrequests/%d/conflict-status", b.BaseURL, repo.FullName, pull.Num)
|
||||
resp, err := b.makeRequest("GET", path, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
var conflictStatus ConflictStatus
|
||||
if err := json.Unmarshal(resp, &conflictStatus); err != nil {
|
||||
return false, errors.Wrapf(err, "Could not parse response %q", string(resp))
|
||||
}
|
||||
if err := validator.New().Struct(conflictStatus); err != nil {
|
||||
return false, errors.Wrapf(err, "API response %q was missing fields", string(resp))
|
||||
}
|
||||
if !*conflictStatus.MergeImpossible && !*conflictStatus.IsConflicted {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// UpdateStatus updates the status of a commit.
|
||||
func (b *Client) UpdateStatus(repo models.Repo, pull models.PullRequest, status models.CommitStatus, src string, description string, url string) error {
|
||||
bbState := "FAILED"
|
||||
switch status {
|
||||
case models.PendingCommitStatus:
|
||||
bbState = "INPROGRESS"
|
||||
case models.SuccessCommitStatus:
|
||||
bbState = "SUCCESSFUL"
|
||||
case models.FailedCommitStatus:
|
||||
bbState = "FAILED"
|
||||
}
|
||||
|
||||
// URL is a required field for Azure Devops statuses. We default to the
|
||||
// Atlantis server's URL.
|
||||
if url == "" {
|
||||
url = b.AtlantisURL
|
||||
}
|
||||
|
||||
bodyBytes, err := json.Marshal(map[string]string{
|
||||
"key": src,
|
||||
"url": url,
|
||||
"state": bbState,
|
||||
"description": description,
|
||||
})
|
||||
|
||||
path := fmt.Sprintf("%s/2.0/repositories/%s/commit/%s/statuses/build", b.BaseURL, repo.FullName, pull.HeadCommit)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "json encoding")
|
||||
}
|
||||
_, err = b.makeRequest("POST", path, bytes.NewBuffer(bodyBytes))
|
||||
return err
|
||||
}
|
||||
|
||||
// MergePull merges the pull request.
|
||||
func (b *Client) MergePull(pull models.PullRequest) error {
|
||||
path := fmt.Sprintf("%s/2.0/repositories/%s/pullrequests/%d/merge", b.BaseURL, pull.BaseRepo.FullName, pull.Num)
|
||||
_, err := b.makeRequest("POST", path, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// prepRequest adds auth and necessary headers.
|
||||
func (b *Client) prepRequest(method string, path string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequest(method, path, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.SetBasicAuth(b.Username, b.Password)
|
||||
if body != nil {
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
}
|
||||
// Add this header to disable CSRF checks.
|
||||
// See https://confluence.atlassian.com/cloudkb/xsrf-check-failed-when-calling-cloud-apis-826874382.html
|
||||
//req.Header.Add("X-Atlassian-Token", "no-check")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (b *Client) makeRequest(method string, path string, reqBody io.Reader) ([]byte, error) {
|
||||
req, err := b.prepRequest(method, path, reqBody)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "constructing request")
|
||||
}
|
||||
resp, err := b.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close() // nolint: errcheck
|
||||
requestStr := fmt.Sprintf("%s %s", method, path)
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
||||
respBody, _ := ioutil.ReadAll(resp.Body)
|
||||
return nil, fmt.Errorf("making request %q unexpected status code: %d, body: %s", requestStr, resp.StatusCode, string(respBody))
|
||||
}
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "reading response from request %q", requestStr)
|
||||
}
|
||||
return respBody, nil
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
package azuredevops_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/runatlantis/atlantis/server/events/models"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/azuredevops"
|
||||
. "github.com/runatlantis/atlantis/testing"
|
||||
)
|
||||
|
||||
// Should follow pagination properly.
|
||||
func TestClient_GetModifiedFilesPagination(t *testing.T) {
|
||||
respTemplate := `
|
||||
{
|
||||
"pagelen": 1,
|
||||
"values": [
|
||||
{
|
||||
"type": "diffstat",
|
||||
"status": "modified",
|
||||
"lines_removed": 1,
|
||||
"lines_added": 2,
|
||||
"old": {
|
||||
"path": "%s",
|
||||
"type": "commit_file",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://visualstudio.com/org/2.0/repositories/bitbucket/geordi/src/e1749643d655d7c7014001a6c0f58abaf42ad850/setup.py"
|
||||
}
|
||||
}
|
||||
},
|
||||
"new": {
|
||||
"path": "%s",
|
||||
"type": "commit_file",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://visualstudio.com/org/2.0/repositories/bitbucket/geordi/src/d222fa235229c55dad20b190b0b571adf737d5a6/setup.py"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"size": 1
|
||||
`
|
||||
firstResp := fmt.Sprintf(respTemplate, "file1.txt", "file2.txt")
|
||||
secondResp := fmt.Sprintf(respTemplate, "file2.txt", "file3.txt")
|
||||
var serverURL string
|
||||
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.RequestURI {
|
||||
// The first request should hit this URL.
|
||||
case "/2.0/repositories/owner/repo/pullrequests/1/diffstat":
|
||||
resp := firstResp + fmt.Sprintf(`,"next": "%s/2.0/repositories/owner/repo/pullrequests/1/diffstat?page=2"}`, serverURL)
|
||||
w.Write([]byte(resp)) // nolint: errcheck
|
||||
return
|
||||
// The second should hit this URL.
|
||||
case "/2.0/repositories/owner/repo/pullrequests/1/diffstat?page=2":
|
||||
w.Write([]byte(secondResp + "}")) // nolint: errcheck
|
||||
default:
|
||||
t.Errorf("got unexpected request at %q", r.RequestURI)
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
serverURL = testServer.URL
|
||||
client := azuredevops.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io")
|
||||
client.BaseURL = testServer.URL
|
||||
|
||||
files, err := client.GetModifiedFiles(models.Repo{
|
||||
FullName: "owner/repo",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
CloneURL: "",
|
||||
SanitizedCloneURL: "",
|
||||
VCSHost: models.VCSHost{
|
||||
Type: models.AzureDevops,
|
||||
Hostname: "visualstudio.com",
|
||||
},
|
||||
}, models.PullRequest{
|
||||
Num: 1,
|
||||
})
|
||||
Ok(t, err)
|
||||
Equals(t, []string{"file1.txt", "file2.txt", "file3.txt"}, files)
|
||||
}
|
||||
|
||||
// If the "old" key in the list of files is nil we shouldn't error.
|
||||
func TestClient_GetModifiedFilesOldNil(t *testing.T) {
|
||||
resp := `
|
||||
{
|
||||
"pagelen": 500,
|
||||
"values": [
|
||||
{
|
||||
"status": "added",
|
||||
"old": null,
|
||||
"lines_removed": 0,
|
||||
"lines_added": 2,
|
||||
"new": {
|
||||
"path": "parent/child/file1.txt",
|
||||
"type": "commit_file",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://visualstudio.com/2.0/repositories/lkysow/atlantis-example/src/1ed8205eec00dab4f1c0a8c486a4492c98c51f8e/main.tf"
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "diffstat"
|
||||
}
|
||||
],
|
||||
"page": 1,
|
||||
"size": 1
|
||||
}`
|
||||
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.RequestURI {
|
||||
// The first request should hit this URL.
|
||||
case "/2.0/repositories/owner/repo/pullrequests/1/diffstat":
|
||||
w.Write([]byte(resp)) // nolint: errcheck
|
||||
return
|
||||
default:
|
||||
t.Errorf("got unexpected request at %q", r.RequestURI)
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
client := azuredevops.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io")
|
||||
client.BaseURL = testServer.URL
|
||||
|
||||
files, err := client.GetModifiedFiles(models.Repo{
|
||||
FullName: "owner/repo",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
CloneURL: "",
|
||||
SanitizedCloneURL: "",
|
||||
VCSHost: models.VCSHost{
|
||||
Type: models.AzureDevops,
|
||||
Hostname: "visualstudio.com",
|
||||
},
|
||||
}, models.PullRequest{
|
||||
Num: 1,
|
||||
})
|
||||
Ok(t, err)
|
||||
Equals(t, []string{"parent/child/file1.txt"}, files)
|
||||
}
|
||||
|
||||
func TestClient_PullIsApproved(t *testing.T) {
|
||||
cases := []struct {
|
||||
description string
|
||||
testdata string
|
||||
exp bool
|
||||
}{
|
||||
{
|
||||
"no approvers",
|
||||
"pull-unapproved.json",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"approver is the author",
|
||||
"pull-approved-by-author.json",
|
||||
false,
|
||||
},
|
||||
{
|
||||
"single approver",
|
||||
"pull-approved.json",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"two approvers one author",
|
||||
"pull-approved-multiple.json",
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.description, func(t *testing.T) {
|
||||
json, err := ioutil.ReadFile(filepath.Join("testdata", c.testdata))
|
||||
Ok(t, err)
|
||||
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.RequestURI {
|
||||
// The first request should hit this URL.
|
||||
case "/2.0/repositories/owner/repo/pullrequests/1":
|
||||
w.Write(json) // nolint: errcheck
|
||||
return
|
||||
default:
|
||||
t.Errorf("got unexpected request at %q", r.RequestURI)
|
||||
http.Error(w, "not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
||||
client := azuredevops.NewClient(http.DefaultClient, "user", "pass", "runatlantis.io")
|
||||
client.BaseURL = testServer.URL
|
||||
|
||||
repo, err := models.NewRepo(models.AzureDevops, "owner/repo", "https://visualstudio.com/owner/repo.git", "user", "token")
|
||||
Ok(t, err)
|
||||
approved, err := client.PullIsApproved(repo, models.PullRequest{
|
||||
Num: 1,
|
||||
HeadBranch: "branch",
|
||||
Author: "author",
|
||||
BaseRepo: repo,
|
||||
})
|
||||
Ok(t, err)
|
||||
Equals(t, c.exp, approved)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package azuredevops
|
||||
|
||||
const (
|
||||
PullCreatedHeader = "git.pullrequest.created"
|
||||
PullUpdatedHeader = "git.pullrequest.updated"
|
||||
PullFulfilledHeader = "git.pullrequest.merged"
|
||||
PullRejectedHeader = "pullrequest:rejected"
|
||||
PullCommentCreatedHeader = "workitem.commented"
|
||||
)
|
||||
|
||||
type CommentEvent struct {
|
||||
CommonEventData
|
||||
Comment *Comment `json:"comment,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
type PullRequestEvent struct {
|
||||
CommonEventData
|
||||
}
|
||||
|
||||
type CommonEventData struct {
|
||||
Actor *Actor `json:"actor,omitempty" validate:"required"`
|
||||
Repository *Repository `json:"repository,omitempty" validate:"required"`
|
||||
PullRequest *PullRequest `json:"pullrequest,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
type DiffStat struct {
|
||||
Values []DiffStatValue `json:"values,omitempty" validate:"required"`
|
||||
Next *string `json:"next,omitempty"`
|
||||
}
|
||||
type DiffStatValue struct {
|
||||
// Old is the old file, this can be null.
|
||||
Old *DiffStatFile `json:"old,omitempty"`
|
||||
// New is the new file, this can be null.
|
||||
New *DiffStatFile `json:"new,omitempty"`
|
||||
}
|
||||
type DiffStatFile struct {
|
||||
Path *string `json:"path,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
type Actor struct {
|
||||
Nickname *string `json:"nickname,omitempty" validate:"required"`
|
||||
}
|
||||
type Repository struct {
|
||||
FullName *string `json:"full_name,omitempty" validate:"required"`
|
||||
Links Links `json:"links,omitempty" validate:"required"`
|
||||
}
|
||||
type PullRequest struct {
|
||||
ID *int `json:"id,omitempty" validate:"required"`
|
||||
Source *BranchMeta `json:"source,omitempty" validate:"required"`
|
||||
Destination *BranchMeta `json:"destination,omitempty" validate:"required"`
|
||||
Participants []Participant `json:"participants,omitempty" validate:"required"`
|
||||
Links *Links `json:"links,omitempty" validate:"required"`
|
||||
State *string `json:"state,omitempty" validate:"required"`
|
||||
}
|
||||
type Links struct {
|
||||
HTML *Link `json:"html,omitempty" validate:"required"`
|
||||
}
|
||||
type Link struct {
|
||||
HREF *string `json:"href,omitempty" validate:"required"`
|
||||
}
|
||||
type Participant struct {
|
||||
Approved *bool `json:"approved,omitempty" validate:"required"`
|
||||
User *struct {
|
||||
Username *string `json:"username,omitempty" validate:"required"`
|
||||
} `json:"user,omitempty" validate:"required"`
|
||||
}
|
||||
type BranchMeta struct {
|
||||
Repository *Repository `json:"repository,omitempty" validate:"required"`
|
||||
Commit *Commit `json:"commit,omitempty" validate:"required"`
|
||||
Branch *Branch `json:"branch,omitempty" validate:"required"`
|
||||
}
|
||||
type Branch struct {
|
||||
Name *string `json:"name,omitempty" validate:"required"`
|
||||
}
|
||||
type Commit struct {
|
||||
Hash *string `json:"hash,omitempty" validate:"required"`
|
||||
}
|
||||
type Comment struct {
|
||||
Content *CommentContent `json:"content,omitempty" validate:"required"`
|
||||
}
|
||||
type CommentContent struct {
|
||||
Raw *string `json:"raw,omitempty" validate:"required"`
|
||||
}
|
||||
|
||||
type ConflictStatus struct {
|
||||
MergeImpossible *bool `json:"mergeimpossible,omitempty" validate:"required"`
|
||||
IsConflicted *bool `json:"isconflicted,omitempty" validate:"required"`
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
{
|
||||
"type": "pullrequest",
|
||||
"description": "main.tf edited online with Bitbucket",
|
||||
"links": {
|
||||
"decline": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/decline"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/commits"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/comments"
|
||||
},
|
||||
"merge": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/merge"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/pull-requests/5"
|
||||
},
|
||||
"activity": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/activity"
|
||||
},
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/diff"
|
||||
},
|
||||
"approve": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/approve"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/statuses"
|
||||
}
|
||||
},
|
||||
"title": "main.tf edited online with Bitbucket",
|
||||
"close_source_branch": true,
|
||||
"reviewers": [],
|
||||
"id": 5,
|
||||
"destination": {
|
||||
"commit": {
|
||||
"hash": "fe607a7f5172",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/fe607a7f5172"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/fe607a7f5172"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bytebucket.org/ravatar/%7B94189367-116b-436a-9f77-2314b97a6067%7D?ts=default"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "master"
|
||||
}
|
||||
},
|
||||
"created_on": "2018-07-25T12:23:21.100810+00:00",
|
||||
"summary": {
|
||||
"raw": "main.tf edited online with Bitbucket",
|
||||
"markup": "markdown",
|
||||
"html": "<p>main.tf edited online with Bitbucket</p>",
|
||||
"type": "rendered"
|
||||
},
|
||||
"source": {
|
||||
"commit": {
|
||||
"hash": "3428957ade18",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/3428957ade18"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/3428957ade18"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bytebucket.org/ravatar/%7B94189367-116b-436a-9f77-2314b97a6067%7D?ts=default"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "lkysow/maintf-edited-online-with-bitbucket-1532521398289"
|
||||
}
|
||||
},
|
||||
"comment_count": 3,
|
||||
"state": "OPEN",
|
||||
"task_count": 0,
|
||||
"participants": [
|
||||
{
|
||||
"role": "PARTICIPANT",
|
||||
"participated_on": "2018-07-28T00:06:42.255492+00:00",
|
||||
"type": "participant",
|
||||
"approved": true,
|
||||
"user": {
|
||||
"username": "author",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"reason": "",
|
||||
"updated_on": "2018-07-28T00:06:42.257659+00:00",
|
||||
"author": {
|
||||
"username": "lkysow",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
},
|
||||
"merge_commit": null,
|
||||
"closed_by": null
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
{
|
||||
"type": "pullrequest",
|
||||
"description": "main.tf edited online with Bitbucket",
|
||||
"links": {
|
||||
"decline": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/decline"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/commits"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/comments"
|
||||
},
|
||||
"merge": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/merge"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/pull-requests/5"
|
||||
},
|
||||
"activity": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/activity"
|
||||
},
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/diff"
|
||||
},
|
||||
"approve": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/approve"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/statuses"
|
||||
}
|
||||
},
|
||||
"title": "main.tf edited online with Bitbucket",
|
||||
"close_source_branch": true,
|
||||
"reviewers": [],
|
||||
"id": 5,
|
||||
"destination": {
|
||||
"commit": {
|
||||
"hash": "fe607a7f5172",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/fe607a7f5172"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/fe607a7f5172"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bytebucket.org/ravatar/%7B94189367-116b-436a-9f77-2314b97a6067%7D?ts=default"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "master"
|
||||
}
|
||||
},
|
||||
"created_on": "2018-07-25T12:23:21.100810+00:00",
|
||||
"summary": {
|
||||
"raw": "main.tf edited online with Bitbucket",
|
||||
"markup": "markdown",
|
||||
"html": "<p>main.tf edited online with Bitbucket</p>",
|
||||
"type": "rendered"
|
||||
},
|
||||
"source": {
|
||||
"commit": {
|
||||
"hash": "3428957ade18",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/3428957ade18"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/3428957ade18"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bytebucket.org/ravatar/%7B94189367-116b-436a-9f77-2314b97a6067%7D?ts=default"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "lkysow/maintf-edited-online-with-bitbucket-1532521398289"
|
||||
}
|
||||
},
|
||||
"comment_count": 3,
|
||||
"state": "OPEN",
|
||||
"task_count": 0,
|
||||
"participants": [
|
||||
{
|
||||
"role": "PARTICIPANT",
|
||||
"participated_on": "2018-07-28T00:06:42.255492+00:00",
|
||||
"type": "participant",
|
||||
"approved": true,
|
||||
"user": {
|
||||
"username": "author",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"role": "PARTICIPANT",
|
||||
"participated_on": "2018-07-28T00:06:42.255492+00:00",
|
||||
"type": "participant",
|
||||
"approved": true,
|
||||
"user": {
|
||||
"username": "approver",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"reason": "",
|
||||
"updated_on": "2018-07-28T00:06:42.257659+00:00",
|
||||
"author": {
|
||||
"username": "lkysow",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
},
|
||||
"merge_commit": null,
|
||||
"closed_by": null
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
{
|
||||
"type": "pullrequest",
|
||||
"description": "main.tf edited online with Bitbucket",
|
||||
"links": {
|
||||
"decline": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/decline"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/commits"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/comments"
|
||||
},
|
||||
"merge": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/merge"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/pull-requests/5"
|
||||
},
|
||||
"activity": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/activity"
|
||||
},
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/diff"
|
||||
},
|
||||
"approve": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/approve"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/statuses"
|
||||
}
|
||||
},
|
||||
"title": "main.tf edited online with Bitbucket",
|
||||
"close_source_branch": true,
|
||||
"reviewers": [],
|
||||
"id": 5,
|
||||
"destination": {
|
||||
"commit": {
|
||||
"hash": "fe607a7f5172",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/fe607a7f5172"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/fe607a7f5172"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "master"
|
||||
}
|
||||
},
|
||||
"created_on": "2018-07-25T12:23:21.100810+00:00",
|
||||
"summary": {
|
||||
"raw": "main.tf edited online with Bitbucket",
|
||||
"markup": "markdown",
|
||||
"html": "<p>main.tf edited online with Bitbucket</p>",
|
||||
"type": "rendered"
|
||||
},
|
||||
"source": {
|
||||
"commit": {
|
||||
"hash": "3428957ade18",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/3428957ade18"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/3428957ade18"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "lkysow/maintf-edited-online-with-bitbucket-1532521398289"
|
||||
}
|
||||
},
|
||||
"comment_count": 3,
|
||||
"state": "OPEN",
|
||||
"task_count": 0,
|
||||
"participants": [
|
||||
{
|
||||
"role": "PARTICIPANT",
|
||||
"participated_on": "2018-07-28T00:06:42.255492+00:00",
|
||||
"type": "participant",
|
||||
"approved": true,
|
||||
"user": {
|
||||
"username": "approver",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
}
|
||||
}
|
||||
],
|
||||
"reason": "",
|
||||
"updated_on": "2018-07-28T00:06:42.257659+00:00",
|
||||
"author": {
|
||||
"username": "lkysow",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
},
|
||||
"merge_commit": null,
|
||||
"closed_by": null
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
{
|
||||
"type": "pullrequest",
|
||||
"description": "main.tf edited online with Bitbucket",
|
||||
"links": {
|
||||
"decline": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/decline"
|
||||
},
|
||||
"commits": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/commits"
|
||||
},
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5"
|
||||
},
|
||||
"comments": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/comments"
|
||||
},
|
||||
"merge": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/merge"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/pull-requests/5"
|
||||
},
|
||||
"activity": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/activity"
|
||||
},
|
||||
"diff": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/diff"
|
||||
},
|
||||
"approve": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/approve"
|
||||
},
|
||||
"statuses": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/pullrequests/5/statuses"
|
||||
}
|
||||
},
|
||||
"title": "main.tf edited online with Bitbucket",
|
||||
"close_source_branch": true,
|
||||
"reviewers": [],
|
||||
"id": 5,
|
||||
"destination": {
|
||||
"commit": {
|
||||
"hash": "fe607a7f5172",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/fe607a7f5172"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/fe607a7f5172"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "master"
|
||||
}
|
||||
},
|
||||
"created_on": "2018-07-25T12:23:21.100810+00:00",
|
||||
"summary": {
|
||||
"raw": "main.tf edited online with Bitbucket",
|
||||
"markup": "markdown",
|
||||
"html": "<p>main.tf edited online with Bitbucket</p>",
|
||||
"type": "rendered"
|
||||
},
|
||||
"source": {
|
||||
"commit": {
|
||||
"hash": "3428957ade18",
|
||||
"type": "commit",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example/commit/3428957ade18"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example/commits/3428957ade18"
|
||||
}
|
||||
}
|
||||
},
|
||||
"repository": {
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/repositories/lkysow/atlantis-example"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/atlantis-example"
|
||||
}
|
||||
},
|
||||
"type": "repository",
|
||||
"name": "atlantis-example",
|
||||
"full_name": "lkysow/atlantis-example",
|
||||
"uuid": "{94189367-116b-436a-9f77-2314b97a6067}"
|
||||
},
|
||||
"branch": {
|
||||
"name": "lkysow/maintf-edited-online-with-bitbucket-1532521398289"
|
||||
}
|
||||
},
|
||||
"comment_count": 3,
|
||||
"state": "OPEN",
|
||||
"task_count": 0,
|
||||
"participants": [],
|
||||
"reason": "",
|
||||
"updated_on": "2018-07-28T00:06:42.257659+00:00",
|
||||
"author": {
|
||||
"username": "lkysow",
|
||||
"display_name": "Luke",
|
||||
"account_id": "557058:dc3817de-68b5-45cd-b81c-5c39d2560090",
|
||||
"links": {
|
||||
"self": {
|
||||
"href": "https://api.bitbucket.org/2.0/users/lkysow"
|
||||
},
|
||||
"html": {
|
||||
"href": "https://bitbucket.org/lkysow/"
|
||||
},
|
||||
"avatar": {
|
||||
"href": "https://bitbucket.org/account/lkysow/avatar/"
|
||||
}
|
||||
},
|
||||
"type": "user",
|
||||
"uuid": "{bf34a99b-8a11-452c-8fbc-bdffc340e584}"
|
||||
},
|
||||
"merge_commit": null,
|
||||
"closed_by": null
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/db"
|
||||
"github.com/runatlantis/atlantis/server/events/yaml/valid"
|
||||
|
||||
"github.com/benmatselby/go-azuredevops/azuredevops"
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
@@ -41,7 +42,6 @@ import (
|
||||
"github.com/runatlantis/atlantis/server/events/runtime"
|
||||
"github.com/runatlantis/atlantis/server/events/terraform"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/azuredevops"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/bitbucketcloud"
|
||||
"github.com/runatlantis/atlantis/server/events/vcs/bitbucketserver"
|
||||
"github.com/runatlantis/atlantis/server/events/webhooks"
|
||||
@@ -237,6 +237,8 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
|
||||
BitbucketServerURL: userConfig.BitbucketBaseURL,
|
||||
AzureDevopsUser: userConfig.AzureDevopsUser,
|
||||
AzureDevopsToken: userConfig.AzureDevopsToken,
|
||||
AzureDevopsOrg: userConfig.AzureDevopsOrg,
|
||||
AzureDevopsProject: userConfig.AzureDevopsProject,
|
||||
}
|
||||
commentParser := &events.CommentParser{
|
||||
GithubUser: userConfig.GithubUser,
|
||||
|
||||
@@ -10,8 +10,8 @@ type UserConfig struct {
|
||||
AllowRepoConfig bool `mapstructure:"allow-repo-config"`
|
||||
AtlantisURL string `mapstructure:"atlantis-url"`
|
||||
Automerge bool `mapstructure:"automerge"`
|
||||
AzureDevopsBaseURL string `mapstructure:"azuredevops-base-url"`
|
||||
AzureDevopsHostname string `mapstructure:"azuredevops-hostname"`
|
||||
AzureDevopsOrg string `mapstructure:"azuredevops-org"`
|
||||
AzureDevopsProject string `mapstructure:"azuredevops-project"`
|
||||
AzureDevopsToken string `mapstructure:"azuredevops-token"`
|
||||
AzureDevopsUser string `mapstructure:"azuredevops-user"`
|
||||
AzureDevopsWebhookSecret string `mapstructure:"azuredevops-webhook-secret"`
|
||||
|
||||
Reference in New Issue
Block a user