diff --git a/README.md b/README.md index 9d05af00..388e0e6c 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Currently supported providers are: [GitHub](#github), [Bitbucket Server](#bitbuc - [Delete Webhook](#delete-webhook) - [Set Commit Status](#set-commit-status) - [Create Pull Request](#create-pull-request) + - [Latest Commit Hash](#get-latest-commit-hash) - [Webhook Parser](#webhook-parser) ### VCS Clients @@ -244,6 +245,22 @@ description := "Pull request description" err := client.CreatePullRequest(ctx, owner, repository, sourceBranch, targetBranch, title, description string) ``` +#### Get Latest Commit Hash + +```go +// Go context +ctx := context.Background() +// Organization or username +owner := "jfrog" +// VCS repository +repository := "jfrog-cli" +// VCS branch +branch := "dev" + +// SHA-1 hash of the latest commit +commitHash, err := client.GetLatestCommitHash(ctx, owner, repository, branch) +``` + ### Webhook Parser ```go diff --git a/vcsclient/bitbucketcloud.go b/vcsclient/bitbucketcloud.go index fc6fe7da..46660755 100644 --- a/vcsclient/bitbucketcloud.go +++ b/vcsclient/bitbucketcloud.go @@ -14,9 +14,7 @@ import ( ) type BitbucketCloudClient struct { - bitbucketClient *bitbucket.Client - username string - token string + vcsInfo *VcsInfo } func NewBitbucketCloudClient(vcsInfo *VcsInfo) (*BitbucketCloudClient, error) { @@ -25,26 +23,30 @@ func NewBitbucketCloudClient(vcsInfo *VcsInfo) (*BitbucketCloudClient, error) { return nil, err } bitbucketClient := &BitbucketCloudClient{ - bitbucketClient: bitbucket.NewBasicAuth(vcsInfo.Username, vcsInfo.Token), - username: vcsInfo.Username, - token: vcsInfo.Token, + vcsInfo: vcsInfo, } return bitbucketClient, nil } -func (client *BitbucketCloudClient) TestConnection(_ context.Context) error { - _, err := client.bitbucketClient.User.Profile() +func (client *BitbucketCloudClient) buildBitbucketCloudClient(_ context.Context) *bitbucket.Client { + return bitbucket.NewBasicAuth(client.vcsInfo.Username, client.vcsInfo.Token) +} + +func (client *BitbucketCloudClient) TestConnection(ctx context.Context) error { + bitbucketClient := client.buildBitbucketCloudClient(ctx) + _, err := bitbucketClient.User.Profile() return err } -func (client *BitbucketCloudClient) ListRepositories(_ context.Context) (map[string][]string, error) { +func (client *BitbucketCloudClient) ListRepositories(ctx context.Context) (map[string][]string, error) { + bitbucketClient := client.buildBitbucketCloudClient(ctx) results := make(map[string][]string) - workspaces, err := client.bitbucketClient.Workspaces.List() + workspaces, err := bitbucketClient.Workspaces.List() if err != nil { return nil, err } for _, workspace := range workspaces.Workspaces { - repositoriesRes, err := client.bitbucketClient.Repositories.ListForAccount(&bitbucket.RepositoriesOptions{Owner: workspace.Slug}) + repositoriesRes, err := bitbucketClient.Repositories.ListForAccount(&bitbucket.RepositoriesOptions{Owner: workspace.Slug}) if err != nil { return nil, err } @@ -55,8 +57,9 @@ func (client *BitbucketCloudClient) ListRepositories(_ context.Context) (map[str return results, nil } -func (client *BitbucketCloudClient) ListBranches(_ context.Context, owner, repository string) ([]string, error) { - branches, err := client.bitbucketClient.Repositories.Repository.ListBranches(&bitbucket.RepositoryBranchOptions{Owner: owner, RepoSlug: repository}) +func (client *BitbucketCloudClient) ListBranches(ctx context.Context, owner, repository string) ([]string, error) { + bitbucketClient := client.buildBitbucketCloudClient(ctx) + branches, err := bitbucketClient.Repositories.Repository.ListBranches(&bitbucket.RepositoryBranchOptions{Owner: owner, RepoSlug: repository}) if err != nil { return nil, err } @@ -68,8 +71,9 @@ func (client *BitbucketCloudClient) ListBranches(_ context.Context, owner, repos return results, nil } -func (client *BitbucketCloudClient) CreateWebhook(_ context.Context, owner, repository, _, payloadUrl string, +func (client *BitbucketCloudClient) CreateWebhook(ctx context.Context, owner, repository, _, payloadUrl string, webhookEvents ...vcsutils.WebhookEvent) (string, string, error) { + bitbucketClient := client.buildBitbucketCloudClient(ctx) token := vcsutils.CreateToken() options := &bitbucket.WebhooksOptions{ Active: true, @@ -78,7 +82,7 @@ func (client *BitbucketCloudClient) CreateWebhook(_ context.Context, owner, repo Url: payloadUrl + "?token=" + url.QueryEscape(token), Events: getBitbucketCloudWebhookEvents(webhookEvents...), } - response, err := client.bitbucketClient.Repositories.Webhooks.Create(options) + response, err := bitbucketClient.Repositories.Webhooks.Create(options) if err != nil { return "", "", err } @@ -89,8 +93,9 @@ func (client *BitbucketCloudClient) CreateWebhook(_ context.Context, owner, repo return id, token, err } -func (client *BitbucketCloudClient) UpdateWebhook(_ context.Context, owner, repository, _, payloadUrl, token, +func (client *BitbucketCloudClient) UpdateWebhook(ctx context.Context, owner, repository, _, payloadUrl, token, webhookId string, webhookEvents ...vcsutils.WebhookEvent) error { + bitbucketClient := client.buildBitbucketCloudClient(ctx) options := &bitbucket.WebhooksOptions{ Active: true, Uuid: webhookId, @@ -99,22 +104,24 @@ func (client *BitbucketCloudClient) UpdateWebhook(_ context.Context, owner, repo Url: payloadUrl + "?token=" + url.QueryEscape(token), Events: getBitbucketCloudWebhookEvents(webhookEvents...), } - _, err := client.bitbucketClient.Repositories.Webhooks.Update(options) + _, err := bitbucketClient.Repositories.Webhooks.Update(options) return err } -func (client *BitbucketCloudClient) DeleteWebhook(_ context.Context, owner, repository, webhookId string) error { +func (client *BitbucketCloudClient) DeleteWebhook(ctx context.Context, owner, repository, webhookId string) error { + bitbucketClient := client.buildBitbucketCloudClient(ctx) options := &bitbucket.WebhooksOptions{ Uuid: webhookId, Owner: owner, RepoSlug: repository, } - _, err := client.bitbucketClient.Repositories.Webhooks.Delete(options) + _, err := bitbucketClient.Repositories.Webhooks.Delete(options) return err } -func (client *BitbucketCloudClient) SetCommitStatus(_ context.Context, commitStatus CommitStatus, owner, repository, +func (client *BitbucketCloudClient) SetCommitStatus(ctx context.Context, commitStatus CommitStatus, owner, repository, ref, title, description, detailsUrl string) error { + bitbucketClient := client.buildBitbucketCloudClient(ctx) commitOptions := &bitbucket.CommitsOptions{ Owner: owner, RepoSlug: repository, @@ -126,13 +133,14 @@ func (client *BitbucketCloudClient) SetCommitStatus(_ context.Context, commitSta Description: description, Url: detailsUrl, } - _, err := client.bitbucketClient.Repositories.Commits.CreateCommitStatus(commitOptions, commitStatusOptions) + _, err := bitbucketClient.Repositories.Commits.CreateCommitStatus(commitOptions, commitStatusOptions) return err } -func (client *BitbucketCloudClient) DownloadRepository(_ context.Context, owner, repository, branch, +func (client *BitbucketCloudClient) DownloadRepository(ctx context.Context, owner, repository, branch, localPath string) error { - repo, err := client.bitbucketClient.Repositories.Repository.Get(&bitbucket.RepositoryOptions{ + bitbucketClient := client.buildBitbucketCloudClient(ctx) + repo, err := bitbucketClient.Repositories.Repository.Get(&bitbucket.RepositoryOptions{ Owner: owner, RepoSlug: repository, }) @@ -149,19 +157,20 @@ func (client *BitbucketCloudClient) DownloadRepository(_ context.Context, owner, if err != nil { return err } - if len(client.username) > 0 || len(client.token) > 0 { - getRequest.SetBasicAuth(client.username, client.token) + if len(client.vcsInfo.Username) > 0 || len(client.vcsInfo.Token) > 0 { + getRequest.SetBasicAuth(client.vcsInfo.Username, client.vcsInfo.Token) } - response, err := client.bitbucketClient.HttpClient.Do(getRequest) + response, err := bitbucketClient.HttpClient.Do(getRequest) if err != nil { return err } return vcsutils.Untar(localPath, response.Body, true) } -func (client *BitbucketCloudClient) CreatePullRequest(_ context.Context, owner, repository, sourceBranch, +func (client *BitbucketCloudClient) CreatePullRequest(ctx context.Context, owner, repository, sourceBranch, targetBranch, title, description string) error { + bitbucketClient := client.buildBitbucketCloudClient(ctx) options := &bitbucket.PullRequestsOptions{ Owner: owner, SourceRepository: owner + "/" + repository, @@ -171,10 +180,57 @@ func (client *BitbucketCloudClient) CreatePullRequest(_ context.Context, owner, Title: title, Description: description, } - _, err := client.bitbucketClient.Repositories.PullRequests.Create(options) + _, err := bitbucketClient.Repositories.PullRequests.Create(options) return err } +func (client *BitbucketCloudClient) GetLatestCommitHash(ctx context.Context, owner, repository, branch string) (string, error) { + bitbucketClient := client.buildBitbucketCloudClient(ctx) + bitbucketClient.Pagelen = 1 + options := &bitbucket.CommitsOptions{ + Owner: owner, + RepoSlug: repository, + } + if branch != "" { + options.Branchortag = branch + } + commits, err := bitbucketClient.Repositories.Commits.GetCommits(options) + if err != nil { + return "", err + } + id, err := extractCommitHashFromResponse(commits) + if err != nil { + return "", err + } + return id, nil +} + +func extractCommitHashFromResponse(commits interface{}) (string, error) { + var res commitResponse + b, err := json.Marshal(commits) + if err != nil { + return "", err + } + err = json.Unmarshal(b, &res) + if err != nil { + return "", err + } + if len(res.Values) > 0 { + h := res.Values[0] + return h.Hash, nil + } + return "", nil + +} + +type commitResponse struct { + Values []commitHash `json:"values"` +} + +type commitHash struct { + Hash string `json:"hash,omitempty"` +} + // Extract the webhook id from the webhook create response func getBitbucketCloudWebhookId(r interface{}) (string, error) { webhook := &bitbucket.WebhooksOptions{} diff --git a/vcsclient/bitbucketcloud_test.go b/vcsclient/bitbucketcloud_test.go index 71cadaf0..f042e595 100644 --- a/vcsclient/bitbucketcloud_test.go +++ b/vcsclient/bitbucketcloud_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "os" + "path/filepath" "testing" "time" @@ -17,7 +18,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestBitbucketCloudConnection(t *testing.T) { +func TestBitbucketCloud_Connection(t *testing.T) { ctx := context.Background() mockResponse := map[string][]bitbucket.User{"values": {}} client, cleanUp := createServerAndClient(t, vcsutils.BitbucketCloud, true, mockResponse, "/user", createBitbucketCloudHandler) @@ -27,31 +28,31 @@ func TestBitbucketCloudConnection(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketCloudConnectionWhenContextCancelled(t *testing.T) { +func TestBitbucketCloud_ConnectionWhenContextCancelled(t *testing.T) { t.Skip("Bitbucket cloud does not use the context") ctx := context.Background() ctxWithCancel, cancel := context.WithCancel(ctx) cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.BitbucketCloud, 0) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.BitbucketCloud, 0) + defer cleanUp() err := client.TestConnection(ctxWithCancel) assert.ErrorIs(t, err, context.Canceled) } -func TestBitbucketCloudConnectionWhenContextTimesOut(t *testing.T) { +func TestBitbucketCloud_ConnectionWhenContextTimesOut(t *testing.T) { t.Skip("Bitbucket cloud does not use the context") ctx := context.Background() ctxWithTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.BitbucketCloud, 50*time.Millisecond) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.BitbucketCloud, 50*time.Millisecond) + defer cleanUp() err := client.TestConnection(ctxWithTimeout) assert.ErrorIs(t, err, context.DeadlineExceeded) } -func TestBitbucketCloudListRepositories(t *testing.T) { +func TestBitbucketCloud_ListRepositories(t *testing.T) { ctx := context.Background() mockResponse := map[string][]bitbucket.Repository{ "values": {{Slug: repo1}, {Slug: repo2}}, @@ -64,7 +65,7 @@ func TestBitbucketCloudListRepositories(t *testing.T) { assert.Equal(t, map[string][]string{username: {repo1, repo2}}, actualRepositories) } -func TestBitbucketCloudListBranches(t *testing.T) { +func TestBitbucketCloud_ListBranches(t *testing.T) { ctx := context.Background() mockResponse := map[string][]bitbucket.BranchModel{ "values": {{Name: branch1}, {Name: branch2}}, @@ -77,7 +78,7 @@ func TestBitbucketCloudListBranches(t *testing.T) { assert.ElementsMatch(t, actualRepositories, []string{branch1, branch2}) } -func TestBitbucketCloudCreateWebhook(t *testing.T) { +func TestBitbucketCloud_CreateWebhook(t *testing.T) { ctx := context.Background() id, err := uuid.NewUUID() assert.NoError(t, err) @@ -92,7 +93,7 @@ func TestBitbucketCloudCreateWebhook(t *testing.T) { assert.Equal(t, id.String(), actualId) } -func TestBitbucketCloudUpdateWebhook(t *testing.T) { +func TestBitbucketCloud_UpdateWebhook(t *testing.T) { ctx := context.Background() id, err := uuid.NewUUID() assert.NoError(t, err) @@ -104,7 +105,7 @@ func TestBitbucketCloudUpdateWebhook(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketCloudDeleteWebhook(t *testing.T) { +func TestBitbucketCloud_DeleteWebhook(t *testing.T) { ctx := context.Background() id, err := uuid.NewUUID() assert.NoError(t, err) @@ -116,7 +117,7 @@ func TestBitbucketCloudDeleteWebhook(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketCloudSetCommitStatus(t *testing.T) { +func TestBitbucketCloud_SetCommitStatus(t *testing.T) { ctx := context.Background() ref := "9caf1c431fb783b669f0f909bd018b40f2ea3808" client, cleanUp := createServerAndClient(t, vcsutils.BitbucketCloud, true, nil, fmt.Sprintf("/repositories/jfrog/repo-1/commit/%s/statuses/build", ref), createBitbucketCloudHandler) @@ -127,7 +128,7 @@ func TestBitbucketCloudSetCommitStatus(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketCloudDownloadRepository(t *testing.T) { +func TestBitbucketCloud_DownloadRepository(t *testing.T) { ctx := context.Background() dir, err := ioutil.TempDir("", "") assert.NoError(t, err) @@ -149,7 +150,7 @@ func TestBitbucketCloudDownloadRepository(t *testing.T) { assert.True(t, readmeFound) } -func TestBitbucketCloudCreatePullRequest(t *testing.T) { +func TestBitbucketCloud_CreatePullRequest(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.BitbucketCloud, true, nil, "/repositories/jfrog/repo-1/pullrequests/", createBitbucketCloudHandler) defer cleanUp() @@ -158,9 +159,149 @@ func TestBitbucketCloudCreatePullRequest(t *testing.T) { assert.NoError(t, err) } -func createBitbucketCloudHandler(t *testing.T, expectedUri string, response []byte) http.HandlerFunc { +func TestBitbucketCloud_GetLatestCommitHash(t *testing.T) { + ctx := context.Background() + response, err := os.ReadFile(filepath.Join("testdata", "bitbucketcloud", "commit_list_response.json")) + assert.NoError(t, err) + + client, cleanUp := createServerAndClient(t, vcsutils.BitbucketCloud, true, response, + fmt.Sprintf("/repositories/%s/%s/commits/%s?pagelen=1", owner, repo1, "master"), createBitbucketCloudHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.NoError(t, err) + assert.Equal(t, "ec05bacb91d757b4b6b2a11a0676471020e89fb5", id) +} + +func TestBitbucketCloud_GetLatestCommitHashNotFound(t *testing.T) { + ctx := context.Background() + response := []byte(``) + + client, cleanUp := createServerAndClientReturningStatus(t, vcsutils.BitbucketCloud, true, response, + fmt.Sprintf("/repositories/%s/%s/commits/%s?pagelen=1", owner, repo1, "master"), http.StatusNotFound, + createBitbucketCloudHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + require.EqualError(t, err, "404 Not Found") + assert.Empty(t, id) +} + +func TestBitbucketCloudClient_ParseCommitsResponse(t *testing.T) { + response := make(map[string]interface{}) + bytes, err := os.ReadFile(filepath.Join("testdata", "bitbucketcloud", "commit_list_response.json")) + err = json.Unmarshal(bytes, &response) + require.NoError(t, err) + tests := []struct { + name string + commits interface{} + expected string + wantErr bool + }{ + { + name: "real response", + commits: response, + expected: "ec05bacb91d757b4b6b2a11a0676471020e89fb5", + }, + { + name: "commits is nil", + commits: nil, + expected: "", + }, + { + name: "commits is empty map", + commits: map[string]interface{}{}, + expected: "", + }, + { + name: "commits is empty slice", + commits: []interface{}{}, + expected: "", + wantErr: true, + }, + { + name: "commits is bad type", + commits: "hello world!", + expected: "", + wantErr: true, + }, + { + name: "values is empty slice", + commits: map[string]interface{}{ + "values": []interface{}{}, + }, + expected: "", + }, + { + name: "values contains non slice", + commits: map[string]interface{}{ + "values": "value", + }, + wantErr: true, + }, + { + name: "values contains empty slice", + commits: map[string]interface{}{ + "values": []interface{}{}, + }, + expected: "", + }, + { + name: "values contains empty map", + commits: map[string]interface{}{ + "values": map[string]interface{}{}, + }, + wantErr: true, + }, + { + name: "values contains empty slice", + commits: map[string]interface{}{ + "values": []interface{}{}, + }, + expected: "", + }, + { + name: "values contains slice of map not containing hash", + commits: map[string]interface{}{ + "values": []interface{}{ + map[string]interface{}{"key": "value"}, + }, + }, + expected: "", + }, + { + name: "values contains slice of map containing hash", + commits: map[string]interface{}{ + "values": []interface{}{ + map[string]interface{}{ + "key": "value", + "hash": "CommitHash", + }, + map[string]interface{}{ + "other": "val2", + }, + }, + }, + expected: "CommitHash", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + response, err := extractCommitHashFromResponse(tt.commits) + if tt.wantErr { + assert.Error(t, err) + } else { + require.NoError(t, err) + assert.Equal(t, tt.expected, response) + } + }) + } +} + +func createBitbucketCloudHandler(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) + w.WriteHeader(expectedStatusCode) if r.RequestURI == "/workspaces" { workspacesResults := make(map[string]interface{}) workspacesResults["values"] = []bitbucket.Workspace{{Slug: username}} diff --git a/vcsclient/bitbucketserver.go b/vcsclient/bitbucketserver.go index 1f740d9f..ff86c004 100644 --- a/vcsclient/bitbucketserver.go +++ b/vcsclient/bitbucketserver.go @@ -40,7 +40,9 @@ func (client *BitbucketServerClient) TestConnection(ctx context.Context) error { if err != nil { return err } - _, err = bitbucketClient.GetUsers(make(map[string]interface{})) + + options := map[string]interface{}{"limit": 1} + _, err = bitbucketClient.GetUsers(options) return err } @@ -209,6 +211,29 @@ type projectsResponse struct { } `json:"values,omitempty"` } +func (client *BitbucketServerClient) GetLatestCommitHash(ctx context.Context, owner, repository, branch string) (string, error) { + bitbucketClient, err := client.buildBitbucketClient(ctx) + if err != nil { + return "", err + } + options := map[string]interface{}{"limit": 1} + if branch != "" { + options["until"] = branch + } + apiResponse, err := bitbucketClient.GetCommits(owner, repository, options) + if err != nil { + return "", err + } + commits, err := bitbucketv1.GetCommitsResponse(apiResponse) + if err != nil { + return "", err + } + if len(commits) > 0 { + return commits[0].ID, nil + } + return "", nil +} + // Get all projects for which the authenticated user has the PROJECT_VIEW permission func (client *BitbucketServerClient) listProjects(bitbucketClient *bitbucketv1.DefaultApiService) ([]string, error) { var apiResponse *bitbucketv1.APIResponse diff --git a/vcsclient/bitbucketserver_test.go b/vcsclient/bitbucketserver_test.go index f1a54789..d035eec2 100644 --- a/vcsclient/bitbucketserver_test.go +++ b/vcsclient/bitbucketserver_test.go @@ -19,39 +19,40 @@ import ( "github.com/stretchr/testify/assert" ) -func TestBitbucketServerConnection(t *testing.T) { +func TestBitbucketServer_Connection(t *testing.T) { ctx := context.Background() mockResponse := make(map[string][]bitbucketv1.User) - client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, true, mockResponse, "/api/1.0/admin/users", createBitbucketServerHandler) + client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, true, mockResponse, + "/api/1.0/admin/users?limit=1", createBitbucketServerHandler) defer cleanUp() err := client.TestConnection(ctx) assert.NoError(t, err) } -func TestBitbucketConnectionWhenContextCancelled(t *testing.T) { +func TestBitbucketServer_ConnectionWhenContextCancelled(t *testing.T) { ctx := context.Background() ctxWithCancel, cancel := context.WithCancel(ctx) cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.BitbucketServer, 0) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.BitbucketServer, 0) + defer cleanUp() err := client.TestConnection(ctxWithCancel) assert.ErrorIs(t, err, context.Canceled) } -func TestBitbucketConnectionWhenContextTimesOut(t *testing.T) { +func TestBitbucketServer_ConnectionWhenContextTimesOut(t *testing.T) { ctx := context.Background() ctxWithTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.BitbucketServer, 50*time.Millisecond) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.BitbucketServer, 50*time.Millisecond) + defer cleanUp() err := client.TestConnection(ctxWithTimeout) assert.ErrorIs(t, err, context.DeadlineExceeded) } -func TestBitbucketServerListRepositories(t *testing.T) { +func TestBitbucketServer_ListRepositories(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, false, nil, "", createBitbucketServerListRepositoriesHandler) defer cleanUp() @@ -61,7 +62,7 @@ func TestBitbucketServerListRepositories(t *testing.T) { assert.Equal(t, map[string][]string{"~" + username: {repo1}, username: {repo2}}, actualRepositories) } -func TestBitbucketServerListBranches(t *testing.T) { +func TestBitbucketServer_ListBranches(t *testing.T) { ctx := context.Background() mockResponse := map[string][]bitbucketv1.Branch{ "values": {{ID: branch1}, {ID: branch2}}, @@ -74,7 +75,7 @@ func TestBitbucketServerListBranches(t *testing.T) { assert.ElementsMatch(t, actualRepositories, []string{branch1, branch2}) } -func TestBitbucketServerCreateWebhook(t *testing.T) { +func TestBitbucketServer_CreateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int31() mockResponse := bitbucketv1.Webhook{ID: int(id)} @@ -88,7 +89,7 @@ func TestBitbucketServerCreateWebhook(t *testing.T) { assert.Equal(t, strconv.Itoa(int(id)), actualId) } -func TestBitbucketServerUpdateWebhook(t *testing.T) { +func TestBitbucketServer_UpdateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int31() stringId := strconv.Itoa(int(id)) @@ -101,7 +102,7 @@ func TestBitbucketServerUpdateWebhook(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketServerDeleteWebhook(t *testing.T) { +func TestBitbucketServer_DeleteWebhook(t *testing.T) { ctx := context.Background() id := rand.Int31() stringId := strconv.Itoa(int(id)) @@ -113,7 +114,7 @@ func TestBitbucketServerDeleteWebhook(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketServerSetCommitStatus(t *testing.T) { +func TestBitbucketServer_SetCommitStatus(t *testing.T) { ctx := context.Background() ref := "9caf1c431fb783b669f0f909bd018b40f2ea3808" client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, false, nil, fmt.Sprintf("/build-status/1.0/commits/%s", ref), createBitbucketServerHandler) @@ -124,7 +125,7 @@ func TestBitbucketServerSetCommitStatus(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketServerDownloadRepository(t *testing.T) { +func TestBitbucketServer_DownloadRepository(t *testing.T) { ctx := context.Background() dir, err := ioutil.TempDir("", "") assert.NoError(t, err) @@ -140,7 +141,7 @@ func TestBitbucketServerDownloadRepository(t *testing.T) { assert.NoError(t, err) } -func TestBitbucketServerCreatePullRequest(t *testing.T) { +func TestBitbucketServer_CreatePullRequest(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, true, nil, "/api/1.0/projects/jfrog/repos/repo-1/pull-requests", createBitbucketServerHandler) defer cleanUp() @@ -149,9 +150,50 @@ func TestBitbucketServerCreatePullRequest(t *testing.T) { assert.NoError(t, err) } -func createBitbucketServerHandler(t *testing.T, expectedUri string, response []byte) http.HandlerFunc { +func TestBitbucketServer_GetLatestCommitHash(t *testing.T) { + ctx := context.Background() + response, err := os.ReadFile(filepath.Join("testdata", "bitbucketserver", "commit_list_response.json")) + assert.NoError(t, err) + + // limit=1 appears twice because it is added twice by: github.com/gfleury/go-bitbucket-v1@v0.0. + //0-20210826163055-dff2223adeac/default_api.go:3848 + client, cleanUp := createServerAndClient(t, vcsutils.BitbucketServer, false, response, + fmt.Sprintf("/api/1.0/projects/%s/repos/%s/commits?limit=1&limit=1&until=master", owner, repo1), + createBitbucketServerHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.NoError(t, err) + assert.Equal(t, "6686432526c25d15310a1bb21c1d08b47b87d719", id) +} + +func TestBitbucketServer_GetLatestCommitHashNotFound(t *testing.T) { + ctx := context.Background() + response := []byte(`{ + "errors": [ + { + "context": null, + "exceptionName": "com.atlassian.bitbucket.project.NoSuchProjectException", + "message": "Project unknown does not exist." + } + ] + }`) + client, cleanUp := createServerAndClientReturningStatus(t, vcsutils.BitbucketServer, false, response, + fmt.Sprintf("/api/1.0/projects/%s/repos/%s/commits?limit=1&limit=1&until=master", owner, repo1), + http.StatusNotFound, createBitbucketServerHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.Error(t, err) + assert.Contains(t, err.Error(), "Status: 404 Not Found") + assert.Empty(t, id) +} + +func createBitbucketServerHandler(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) + w.WriteHeader(expectedStatusCode) _, err := w.Write(response) require.NoError(t, err) assert.Equal(t, expectedUri, r.RequestURI) @@ -159,7 +201,7 @@ func createBitbucketServerHandler(t *testing.T, expectedUri string, response []b } } -func createBitbucketServerListRepositoriesHandler(t *testing.T, _ string, _ []byte) http.HandlerFunc { +func createBitbucketServerListRepositoriesHandler(t *testing.T, _ string, _ []byte, expectedStatusCode int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { var responseObj interface{} if r.RequestURI == "/api/1.0/projects?start=0" { @@ -173,7 +215,7 @@ func createBitbucketServerListRepositoriesHandler(t *testing.T, _ string, _ []by } else { assert.Fail(t, "Unexpected request Uri "+r.RequestURI) } - w.WriteHeader(http.StatusOK) + w.WriteHeader(expectedStatusCode) response, err := json.Marshal(responseObj) require.NoError(t, err) _, err = w.Write(response) diff --git a/vcsclient/common_test.go b/vcsclient/common_test.go index 7fb09e74..b2223498 100644 --- a/vcsclient/common_test.go +++ b/vcsclient/common_test.go @@ -25,10 +25,15 @@ var ( branch2 = "branch-2" ) -type createHandlerFunc func(t *testing.T, expectedUri string, response []byte) http.HandlerFunc +type createHandlerFunc func(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc func createServerAndClient(t *testing.T, vcsProvider vcsutils.VcsProvider, basicAuth bool, response interface{}, expectedUri string, createHandlerFunc createHandlerFunc) (VcsClient, func()) { + return createServerAndClientReturningStatus(t, vcsProvider, basicAuth, response, expectedUri, http.StatusOK, createHandlerFunc) +} + +func createServerAndClientReturningStatus(t *testing.T, vcsProvider vcsutils.VcsProvider, basicAuth bool, response interface{}, + expectedUri string, expectedStatusCode int, createHandlerFunc createHandlerFunc) (VcsClient, func()) { var byteResponse []byte var ok bool if byteResponse, ok = response.([]byte); !ok { @@ -37,18 +42,22 @@ func createServerAndClient(t *testing.T, vcsProvider vcsutils.VcsProvider, basic byteResponse, err = json.Marshal(response) assert.NoError(t, err) } - server := httptest.NewServer(createHandlerFunc(t, expectedUri, byteResponse)) + server := httptest.NewServer(createHandlerFunc(t, expectedUri, byteResponse, expectedStatusCode)) + client := buildClient(t, vcsProvider, basicAuth, server) + return client, server.Close +} + +func buildClient(t *testing.T, vcsProvider vcsutils.VcsProvider, basicAuth bool, server *httptest.Server) VcsClient { clientBuilder := NewClientBuilder(vcsProvider).ApiEndpoint(server.URL).Token(token) if basicAuth { clientBuilder = clientBuilder.Username("frogger") } client, err := clientBuilder.Build() assert.NoError(t, err) - return client, server.Close + return client } -func createWaitingServerAndClient(t *testing.T, provider vcsutils.VcsProvider, - waitDuration time.Duration) (VcsClient, func()) { +func createWaitingServerAndClient(t *testing.T, provider vcsutils.VcsProvider, waitDuration time.Duration) (VcsClient, func()) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if waitDuration > 0 { time.Sleep(waitDuration) diff --git a/vcsclient/github.go b/vcsclient/github.go index cdf5c6ec..7ce0f98f 100644 --- a/vcsclient/github.go +++ b/vcsclient/github.go @@ -182,6 +182,33 @@ func (client *GitHubClient) CreatePullRequest(ctx context.Context, owner, reposi return err } +func (client *GitHubClient) GetLatestCommitHash(ctx context.Context, owner, repository, branch string) (string, error) { + ghClient, err := client.buildGithubClient(ctx) + if err != nil { + return "", err + } + listOptions := &github.CommitsListOptions{ + ListOptions: github.ListOptions{ + Page: 1, + PerPage: 1, + }, + } + if branch != "" { + listOptions.SHA = branch + } + commits, _, err := ghClient.Repositories.ListCommits(ctx, owner, repository, listOptions) + if err != nil { + return "", err + } + if len(commits) > 0 { + sha := commits[0].SHA + if sha != nil { + return *sha, nil + } + } + return "", nil +} + func createGitHubHook(token, payloadUrl string, webhookEvents ...vcsutils.WebhookEvent) *github.Hook { return &github.Hook{ Events: getGitHubWebhookEvents(webhookEvents...), diff --git a/vcsclient/github_test.go b/vcsclient/github_test.go index eb9f015c..b6df1aa1 100644 --- a/vcsclient/github_test.go +++ b/vcsclient/github_test.go @@ -8,6 +8,7 @@ import ( "math/rand" "net/http" "os" + "path/filepath" "strconv" "strings" "testing" @@ -18,7 +19,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestGitHubConnection(t *testing.T) { +func TestGitHubClient_Connection(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, "It's Not Easy Being Green", "/zen", createGitHubHandler) defer cleanUp() @@ -27,29 +28,29 @@ func TestGitHubConnection(t *testing.T) { assert.NoError(t, err) } -func TestGithubConnectionWhenContextCancelled(t *testing.T) { +func TestGitHubClient_ConnectionWhenContextCancelled(t *testing.T) { ctx := context.Background() ctxWithCancel, cancel := context.WithCancel(ctx) cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.GitHub, 0) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.GitHub, 0) + defer cleanUp() err := client.TestConnection(ctxWithCancel) assert.ErrorIs(t, err, context.Canceled) } -func TestGithubConnectionWhenContextTimesOut(t *testing.T) { +func TestGitHubClient_ConnectionWhenContextTimesOut(t *testing.T) { ctx := context.Background() ctxWithTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.GitHub, 50*time.Millisecond) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.GitHub, 50*time.Millisecond) + defer cleanUp() err := client.TestConnection(ctxWithTimeout) assert.ErrorIs(t, err, context.DeadlineExceeded) } -func TestGitHubListRepositories(t *testing.T) { +func TestGitHubClient_ListRepositories(t *testing.T) { ctx := context.Background() expectedRepo1 := github.Repository{Name: &repo1, Owner: &github.User{Login: &username}} expectedRepo2 := github.Repository{Name: &repo2, Owner: &github.User{Login: &username}} @@ -61,7 +62,7 @@ func TestGitHubListRepositories(t *testing.T) { assert.Equal(t, actualRepositories, map[string][]string{username: {repo1, repo2}}) } -func TestGitHubListBranches(t *testing.T) { +func TestGitHubClient_ListBranches(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, []github.Branch{{Name: &branch1}, {Name: &branch2}}, fmt.Sprintf("/repos/jfrog/%s/branches", repo1), createGitHubHandler) defer cleanUp() @@ -71,7 +72,7 @@ func TestGitHubListBranches(t *testing.T) { assert.ElementsMatch(t, actualBranches, []string{branch1, branch2}) } -func TestGitHubCreateWebhook(t *testing.T) { +func TestGitHubClient_CreateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int63() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, github.Hook{ID: &id}, fmt.Sprintf("/repos/jfrog/%s/hooks", repo1), createGitHubHandler) @@ -83,7 +84,7 @@ func TestGitHubCreateWebhook(t *testing.T) { assert.Equal(t, actualId, strconv.FormatInt(id, 10)) } -func TestGitHubUpdateWebhook(t *testing.T) { +func TestGitHubClient_UpdateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int63() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, github.Hook{ID: &id}, fmt.Sprintf("/repos/jfrog/%s/hooks/%s", repo1, strconv.FormatInt(id, 10)), createGitHubHandler) @@ -94,7 +95,7 @@ func TestGitHubUpdateWebhook(t *testing.T) { assert.NoError(t, err) } -func TestGitHubDeleteWebhook(t *testing.T) { +func TestGitHubClient_DeleteWebhook(t *testing.T) { ctx := context.Background() id := rand.Int63() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, github.Hook{ID: &id}, fmt.Sprintf("/repos/jfrog/%s/hooks/%s", repo1, strconv.FormatInt(id, 10)), createGitHubHandler) @@ -104,7 +105,7 @@ func TestGitHubDeleteWebhook(t *testing.T) { assert.NoError(t, err) } -func TestGitHubCreateCommitStatus(t *testing.T) { +func TestGitHubClient_CreateCommitStatus(t *testing.T) { ctx := context.Background() ref := "39e5418" client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, github.RepoStatus{}, fmt.Sprintf("/repos/jfrog/%s/statuses/%s", repo1, ref), createGitHubHandler) @@ -115,13 +116,15 @@ func TestGitHubCreateCommitStatus(t *testing.T) { assert.NoError(t, err) } -func TestGitHubDownloadRepository(t *testing.T) { +func TestGitHubClient_DownloadRepository(t *testing.T) { ctx := context.Background() dir, err := ioutil.TempDir("", "") assert.NoError(t, err) defer func() { _ = os.RemoveAll(dir) }() - client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, []byte("https://github.com/octocat/Hello-World/archive/refs/heads/master.tar.gz"), "/repos/jfrog/Hello-World/tarball/test", createGitHubHandler) + client, cleanUp := createServerAndClientReturningStatus(t, vcsutils.GitHub, false, + []byte("https://github.com/octocat/Hello-World/archive/refs/heads/master.tar.gz"), + "/repos/jfrog/Hello-World/tarball/test", http.StatusFound, createGitHubHandler) defer cleanUp() assert.NoError(t, err) @@ -133,7 +136,7 @@ func TestGitHubDownloadRepository(t *testing.T) { assert.Equal(t, "README", fileinfo[0].Name()) } -func TestGitHubCreatePullRequest(t *testing.T) { +func TestGitHubClient_CreatePullRequest(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, github.PullRequest{}, "/repos/jfrog/repo-1/pulls", createGitHubHandler) defer cleanUp() @@ -142,18 +145,49 @@ func TestGitHubCreatePullRequest(t *testing.T) { assert.NoError(t, err) } -func createGitHubHandler(t *testing.T, expectedUri string, response []byte) http.HandlerFunc { +func TestGitHubClient_GetLatestCommitHash(t *testing.T) { + ctx := context.Background() + response, err := os.ReadFile(filepath.Join("testdata", "github", "commit_list_response.json")) + assert.NoError(t, err) + + client, cleanUp := createServerAndClient(t, vcsutils.GitHub, false, response, + fmt.Sprintf("/repos/%s/%s/commits?page=1&per_page=1&sha=master", owner, repo1), createGitHubHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.NoError(t, err) + assert.Equal(t, "67a87728853d912182e508d4f92d81f916eec2c6", id) +} + +func TestGitHubClient_GetLatestCommitHashNotFound(t *testing.T) { + ctx := context.Background() + response := []byte(`{ + "documentation_url": "https://docs.github.com/rest/reference/repos#list-commits", + "message": "Not Found" + }`) + + client, cleanUp := createServerAndClientReturningStatus(t, vcsutils.GitHub, false, response, + fmt.Sprintf("/repos/%s/%s/commits?page=1&per_page=1&sha=master", owner, repo1), http.StatusNotFound, createGitHubHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + require.Error(t, err) + assert.Contains(t, err.Error(), "404 Not Found") + assert.Empty(t, id) +} + +func createGitHubHandler(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, expectedUri, r.RequestURI) + assert.Equal(t, "Bearer "+token, r.Header.Get("Authorization")) if strings.Contains(r.RequestURI, "tarball") { w.Header().Add("Location", string(response)) - w.WriteHeader(http.StatusFound) + w.WriteHeader(expectedStatusCode) return - } else { - w.WriteHeader(http.StatusOK) } + w.WriteHeader(expectedStatusCode) _, err := w.Write(response) require.NoError(t, err) - assert.Equal(t, expectedUri, r.RequestURI) - assert.Equal(t, "Bearer "+token, r.Header.Get("Authorization")) } } diff --git a/vcsclient/gitlab.go b/vcsclient/gitlab.go index a49c01f8..fb73f6bf 100644 --- a/vcsclient/gitlab.go +++ b/vcsclient/gitlab.go @@ -166,6 +166,27 @@ func (client *GitLabClient) CreatePullRequest(ctx context.Context, owner, reposi return err } +func (client *GitLabClient) GetLatestCommitHash(ctx context.Context, owner, repository, branch string) (string, error) { + listOptions := &gitlab.ListCommitsOptions{ + ListOptions: gitlab.ListOptions{ + Page: 1, + PerPage: 1, + }, + } + if branch != "" { + listOptions.RefName = &branch + } + + commits, _, err := client.glClient.Commits.ListCommits(getProjectId(owner, repository), listOptions, gitlab.WithContext(ctx)) + if err != nil { + return "", err + } + if len(commits) > 0 { + return commits[0].ID, nil + } + return "", nil +} + func getProjectId(owner, project string) string { return fmt.Sprintf("%s/%s", owner, project) } diff --git a/vcsclient/gitlab_test.go b/vcsclient/gitlab_test.go index ae36904d..ae062246 100644 --- a/vcsclient/gitlab_test.go +++ b/vcsclient/gitlab_test.go @@ -20,7 +20,7 @@ import ( "github.com/xanzy/go-gitlab" ) -func TestGitLabConnection(t *testing.T) { +func TestGitLabClient_Connection(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, []gitlab.Project{}, "/api/v4/projects", createGitLabHandler) defer cleanUp() @@ -29,30 +29,30 @@ func TestGitLabConnection(t *testing.T) { assert.NoError(t, err) } -func TestGitlabConnectionWhenContextCancelled(t *testing.T) { +func TestGitLabClient_ConnectionWhenContextCancelled(t *testing.T) { ctx := context.Background() ctxWithCancel, cancel := context.WithCancel(ctx) cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.GitLab, 0) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.GitLab, 0) + defer cleanUp() err := client.TestConnection(ctxWithCancel) assert.ErrorIs(t, err, context.Canceled) } -func TestGitlabConnectionWhenContextTimesOut(t *testing.T) { +func TestGitLabClient_ConnectionWhenContextTimesOut(t *testing.T) { ctx := context.Background() ctxWithTimeout, cancel := context.WithTimeout(ctx, 10*time.Millisecond) defer cancel() - client, closeServer := createWaitingServerAndClient(t, vcsutils.GitLab, 50*time.Millisecond) - defer closeServer() + client, cleanUp := createWaitingServerAndClient(t, vcsutils.GitLab, 50*time.Millisecond) + defer cleanUp() err := client.TestConnection(ctxWithTimeout) assert.ErrorIs(t, err, context.DeadlineExceeded) } -func TestGitLabListRepositories(t *testing.T) { +func TestGitLabClient_ListRepositories(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, []gitlab.Project{{Path: repo1}, {Path: repo2}}, "/api/v4/groups/frogger/projects?page=1", createGitLabHandler) defer cleanUp() @@ -62,7 +62,7 @@ func TestGitLabListRepositories(t *testing.T) { assert.Equal(t, actualRepositories, map[string][]string{username: {repo1, repo2}}) } -func TestGitLabListBranches(t *testing.T) { +func TestGitLabClient_ListBranches(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, []gitlab.Branch{{Name: branch1}, {Name: branch2}}, fmt.Sprintf("/api/v4/projects/%s/repository/branches", url.PathEscape(owner+"/"+repo1)), createGitLabHandler) defer cleanUp() @@ -72,7 +72,7 @@ func TestGitLabListBranches(t *testing.T) { assert.ElementsMatch(t, actualRepositories, []string{branch1, branch2}) } -func TestGitLabCreateWebhook(t *testing.T) { +func TestGitLabClient_CreateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, gitlab.ProjectHook{ID: id}, fmt.Sprintf("/api/v4/projects/%s/hooks", url.PathEscape(owner+"/"+repo1)), createGitLabHandler) @@ -85,7 +85,7 @@ func TestGitLabCreateWebhook(t *testing.T) { assert.Equal(t, actualId, strconv.Itoa(id)) } -func TestGitLabUpdateWebhook(t *testing.T) { +func TestGitLabClient_UpdateWebhook(t *testing.T) { ctx := context.Background() id := rand.Int() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, gitlab.ProjectHook{ID: id}, fmt.Sprintf("/api/v4/projects/%s/hooks/%d", url.PathEscape(owner+"/"+repo1), id), createGitLabHandler) @@ -96,7 +96,7 @@ func TestGitLabUpdateWebhook(t *testing.T) { assert.NoError(t, err) } -func TestGitLabDeleteWebhook(t *testing.T) { +func TestGitLabClient_DeleteWebhook(t *testing.T) { ctx := context.Background() id := rand.Int() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, gitlab.ProjectHook{ID: id}, fmt.Sprintf("/api/v4/projects/%s/hooks/%d", url.PathEscape(owner+"/"+repo1), id), createGitLabHandler) @@ -106,7 +106,7 @@ func TestGitLabDeleteWebhook(t *testing.T) { assert.NoError(t, err) } -func TestGitLabCreateCommitStatus(t *testing.T) { +func TestGitLabClient_CreateCommitStatus(t *testing.T) { ctx := context.Background() ref := "5fbf81b31ff7a3b06bd362d1891e2f01bdb2be69" client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, gitlab.CommitStatus{}, fmt.Sprintf("/api/v4/projects/%s/statuses/%s", url.PathEscape(owner+"/"+repo1), ref), createGitLabHandler) @@ -117,7 +117,7 @@ func TestGitLabCreateCommitStatus(t *testing.T) { assert.NoError(t, err) } -func TestGitLabDownloadRepository(t *testing.T) { +func TestGitLabClient_DownloadRepository(t *testing.T) { ctx := context.Background() dir, err := ioutil.TempDir("", "") assert.NoError(t, err) @@ -138,7 +138,7 @@ func TestGitLabDownloadRepository(t *testing.T) { assert.Equal(t, "README.md", fileinfo[0].Name()) } -func TestGitLabCreatePullRequest(t *testing.T) { +func TestGitLabClient_CreatePullRequest(t *testing.T) { ctx := context.Background() client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, &gitlab.MergeRequest{}, fmt.Sprintf("/api/v4/projects/%s/merge_requests", url.PathEscape(owner+"/"+repo1)), createGitLabHandler) defer cleanUp() @@ -147,19 +147,55 @@ func TestGitLabCreatePullRequest(t *testing.T) { assert.NoError(t, err) } -func createGitLabHandler(t *testing.T, expectedUri string, response []byte) http.HandlerFunc { +func TestGitLabClient_GetLatestCommitHash(t *testing.T) { + ctx := context.Background() + response, err := os.ReadFile(filepath.Join("testdata", "gitlab", "commit_list_response.json")) + assert.NoError(t, err) + + client, cleanUp := createServerAndClient(t, vcsutils.GitLab, false, response, + fmt.Sprintf("/api/v4/projects/%s/repository/commits?page=1&per_page=1&ref_name=master", + url.PathEscape(owner+"/"+repo1)), createGitLabHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.NoError(t, err) + assert.Equal(t, "ed899a2f4b50b4370feeea94676502b42383c746", id) +} + +func TestGitLabClient_GetLatestCommitHashNotFound(t *testing.T) { + ctx := context.Background() + response := []byte(`{ + "message": "404 Project Not Found" +}`) + + client, cleanUp := createServerAndClientReturningStatus(t, vcsutils.GitLab, false, response, + fmt.Sprintf("/api/v4/projects/%s/repository/commits?page=1&per_page=1&ref_name=master", + url.PathEscape(owner+"/"+repo1)), http.StatusNotFound, createGitLabHandler) + defer cleanUp() + + id, err := client.GetLatestCommitHash(ctx, owner, repo1, "master") + + require.Error(t, err) + assert.Contains(t, err.Error(), "404 Project Not Found") + assert.Empty(t, id) +} + +func createGitLabHandler(t *testing.T, expectedUri string, response []byte, expectedStatusCode int) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) if r.RequestURI == "/api/v4/" { + w.WriteHeader(http.StatusOK) return } if r.RequestURI == "/api/v4/groups" { byteResponse, err := json.Marshal(&[]gitlab.Group{{Path: username}}) assert.NoError(t, err) + w.WriteHeader(http.StatusOK) _, err = w.Write(byteResponse) assert.NoError(t, err) return } + w.WriteHeader(expectedStatusCode) _, err := w.Write(response) assert.NoError(t, err) assert.Equal(t, expectedUri, r.RequestURI) diff --git a/vcsclient/testdata/bitbucketcloud/commit_list_response.json b/vcsclient/testdata/bitbucketcloud/commit_list_response.json new file mode 100644 index 00000000..e901a480 --- /dev/null +++ b/vcsclient/testdata/bitbucketcloud/commit_list_response.json @@ -0,0 +1,1240 @@ +{ + "pagelen": 30, + "values": [ + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T19:47:03+00:00", + "hash": "ec05bacb91d757b4b6b2a11a0676471020e89fb5", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/ec05bacb91d757b4b6b2a11a0676471020e89fb5/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/ec05bacb91d757b4b6b2a11a0676471020e89fb5/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/ec05bacb91d757b4b6b2a11a0676471020e89fb5" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/ec05bacb91d757b4b6b2a11a0676471020e89fb5" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/ec05bacb91d757b4b6b2a11a0676471020e89fb5" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/ec05bacb91d757b4b6b2a11a0676471020e89fb5" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/ec05bacb91d757b4b6b2a11a0676471020e89fb5/statuses" + } + }, + "message": "Fix README.md: yaml\n", + "parents": [ + { + "hash": "774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

Fix README.md: yaml

", + "markup": "markdown", + "raw": "Fix README.md: yaml\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

Fix README.md: yaml

", + "markup": "markdown", + "raw": "Fix README.md: yaml\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T19:42:44+00:00", + "hash": "774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/774aa0fb252bccbc2a7e01060ef4d4be0b0eeaa9/statuses" + } + }, + "message": "Fix new lines in README\n", + "parents": [ + { + "hash": "1807e7d3f7a8f9a7cd3925d321a009f81da0d415", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

Fix new lines in README

", + "markup": "markdown", + "raw": "Fix new lines in README\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

Fix new lines in README

", + "markup": "markdown", + "raw": "Fix new lines in README\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T19:32:48+00:00", + "hash": "1807e7d3f7a8f9a7cd3925d321a009f81da0d415", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/1807e7d3f7a8f9a7cd3925d321a009f81da0d415/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/1807e7d3f7a8f9a7cd3925d321a009f81da0d415/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/1807e7d3f7a8f9a7cd3925d321a009f81da0d415" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/1807e7d3f7a8f9a7cd3925d321a009f81da0d415/statuses" + } + }, + "message": "Update README, remove tests\n", + "parents": [ + { + "hash": "def26c6128ebe11fac555fe58b59227e9655dc4d", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/def26c6128ebe11fac555fe58b59227e9655dc4d" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/def26c6128ebe11fac555fe58b59227e9655dc4d" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

Update README, remove tests

", + "markup": "markdown", + "raw": "Update README, remove tests\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

Update README, remove tests

", + "markup": "markdown", + "raw": "Update README, remove tests\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T18:46:43+00:00", + "hash": "def26c6128ebe11fac555fe58b59227e9655dc4d", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/def26c6128ebe11fac555fe58b59227e9655dc4d/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/def26c6128ebe11fac555fe58b59227e9655dc4d/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/def26c6128ebe11fac555fe58b59227e9655dc4d" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/def26c6128ebe11fac555fe58b59227e9655dc4d" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/def26c6128ebe11fac555fe58b59227e9655dc4d" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/def26c6128ebe11fac555fe58b59227e9655dc4d" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/def26c6128ebe11fac555fe58b59227e9655dc4d/statuses" + } + }, + "message": "test\n", + "parents": [ + { + "hash": "f1a1201e352814d77a79af9d757dfccc89279f96", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f1a1201e352814d77a79af9d757dfccc89279f96" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f1a1201e352814d77a79af9d757dfccc89279f96" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T18:43:28+00:00", + "hash": "f1a1201e352814d77a79af9d757dfccc89279f96", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f1a1201e352814d77a79af9d757dfccc89279f96/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f1a1201e352814d77a79af9d757dfccc89279f96/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/f1a1201e352814d77a79af9d757dfccc89279f96" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f1a1201e352814d77a79af9d757dfccc89279f96" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/f1a1201e352814d77a79af9d757dfccc89279f96" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f1a1201e352814d77a79af9d757dfccc89279f96" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f1a1201e352814d77a79af9d757dfccc89279f96/statuses" + } + }, + "message": "test\n", + "parents": [ + { + "hash": "140f60dd0dcf50d93d6d35bc38317c523486c15f", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/140f60dd0dcf50d93d6d35bc38317c523486c15f" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/140f60dd0dcf50d93d6d35bc38317c523486c15f" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T18:41:48+00:00", + "hash": "140f60dd0dcf50d93d6d35bc38317c523486c15f", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/140f60dd0dcf50d93d6d35bc38317c523486c15f/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/140f60dd0dcf50d93d6d35bc38317c523486c15f/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/140f60dd0dcf50d93d6d35bc38317c523486c15f" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/140f60dd0dcf50d93d6d35bc38317c523486c15f" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/140f60dd0dcf50d93d6d35bc38317c523486c15f" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/140f60dd0dcf50d93d6d35bc38317c523486c15f" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/140f60dd0dcf50d93d6d35bc38317c523486c15f/statuses" + } + }, + "message": "test\n", + "parents": [ + { + "hash": "f8618cbc9ef831d33867f8fd3de6b368060f49d2", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

test

", + "markup": "markdown", + "raw": "test\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T17:56:56+00:00", + "hash": "f8618cbc9ef831d33867f8fd3de6b368060f49d2", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f8618cbc9ef831d33867f8fd3de6b368060f49d2/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f8618cbc9ef831d33867f8fd3de6b368060f49d2/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f8618cbc9ef831d33867f8fd3de6b368060f49d2" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f8618cbc9ef831d33867f8fd3de6b368060f49d2/statuses" + } + }, + "message": "JFrogDev to jfrog\n", + "parents": [ + { + "hash": "cd07380cb2debd41554568ae99b9e561d6eb3546", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/cd07380cb2debd41554568ae99b9e561d6eb3546" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/cd07380cb2debd41554568ae99b9e561d6eb3546" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

JFrogDev to jfrog

", + "markup": "markdown", + "raw": "JFrogDev to jfrog\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

JFrogDev to jfrog

", + "markup": "markdown", + "raw": "JFrogDev to jfrog\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T17:34:37+00:00", + "hash": "cd07380cb2debd41554568ae99b9e561d6eb3546", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/cd07380cb2debd41554568ae99b9e561d6eb3546/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/cd07380cb2debd41554568ae99b9e561d6eb3546/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/cd07380cb2debd41554568ae99b9e561d6eb3546" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/cd07380cb2debd41554568ae99b9e561d6eb3546" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/cd07380cb2debd41554568ae99b9e561d6eb3546" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/cd07380cb2debd41554568ae99b9e561d6eb3546" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/cd07380cb2debd41554568ae99b9e561d6eb3546/statuses" + } + }, + "message": "tags trigger\n", + "parents": [ + { + "hash": "566f2a7b82c0e4325e374b1fc2352891fc1b10e2", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T17:27:48+00:00", + "hash": "566f2a7b82c0e4325e374b1fc2352891fc1b10e2", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/566f2a7b82c0e4325e374b1fc2352891fc1b10e2/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/566f2a7b82c0e4325e374b1fc2352891fc1b10e2/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/566f2a7b82c0e4325e374b1fc2352891fc1b10e2" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/566f2a7b82c0e4325e374b1fc2352891fc1b10e2/statuses" + } + }, + "message": "tags trigger\n", + "parents": [ + { + "hash": "b41bbad45ce0bf8720521b8422718a29c32bbbf1", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T17:17:59+00:00", + "hash": "b41bbad45ce0bf8720521b8422718a29c32bbbf1", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/b41bbad45ce0bf8720521b8422718a29c32bbbf1/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/b41bbad45ce0bf8720521b8422718a29c32bbbf1/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/b41bbad45ce0bf8720521b8422718a29c32bbbf1" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/b41bbad45ce0bf8720521b8422718a29c32bbbf1/statuses" + } + }, + "message": "tags trigger\n", + "parents": [ + { + "hash": "c66caf690e257f8ec6477c528e455512c9b61dfd", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/c66caf690e257f8ec6477c528e455512c9b61dfd" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/c66caf690e257f8ec6477c528e455512c9b61dfd" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

tags trigger

", + "markup": "markdown", + "raw": "tags trigger\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T17:06:53+00:00", + "hash": "c66caf690e257f8ec6477c528e455512c9b61dfd", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/c66caf690e257f8ec6477c528e455512c9b61dfd/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/c66caf690e257f8ec6477c528e455512c9b61dfd/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/c66caf690e257f8ec6477c528e455512c9b61dfd" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/c66caf690e257f8ec6477c528e455512c9b61dfd" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/c66caf690e257f8ec6477c528e455512c9b61dfd" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/c66caf690e257f8ec6477c528e455512c9b61dfd" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/c66caf690e257f8ec6477c528e455512c9b61dfd/statuses" + } + }, + "message": "add release pipeline\n", + "parents": [ + { + "hash": "f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

add release pipeline

", + "markup": "markdown", + "raw": "add release pipeline\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

add release pipeline

", + "markup": "markdown", + "raw": "add release pipeline\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T16:54:09+00:00", + "hash": "f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/f62ea5359e7af59880b4a5e23e0ce6c1b32b5d3c/statuses" + } + }, + "message": "Update image name\n", + "parents": [ + { + "hash": "847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3", + "links": { + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + } + }, + "type": "commit" + } + ], + "rendered": { + "message": { + "html": "

Update image name

", + "markup": "markdown", + "raw": "Update image name\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

Update image name

", + "markup": "markdown", + "raw": "Update image name\n", + "type": "rendered" + }, + "type": "commit" + }, + { + "author": { + "raw": "user ", + "type": "author", + "user": { + "account_id": "5d74edb2098f0b0daa7630de", + "display_name": "user", + "links": { + "avatar": { + "href": "https://secure.gravatar.com/avatar/477cdb6499e58f28541b52307a3c5bbc?d=https%3A%2F%2Favatar-management--avatars.us-west-2.prod.public.atl-paas.net%2Finitials%2FRN-1.png" + }, + "html": { + "href": "https://bitbucket.org/%7B1658dec0-6727-44ba-b680-15f84e664746%7D/" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/users/%7B1658dec0-6727-44ba-b680-15f84e664746%7D" + } + }, + "nickname": "user", + "type": "user", + "uuid": "{1658dec0-6727-44ba-b680-15f84e664746}" + } + }, + "date": "2020-06-01T16:20:16+00:00", + "hash": "847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3", + "links": { + "approve": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3/approve" + }, + "comments": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3/comments" + }, + "diff": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/diff/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli/commits/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + }, + "patch": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/patch/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3" + }, + "statuses": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli/commit/847b70c1c72c59aa7e4c40bbcefdbb44d8b32ae3/statuses" + } + }, + "message": "Initial commit\n", + "parents": [], + "rendered": { + "message": { + "html": "

Initial commit

", + "markup": "markdown", + "raw": "Initial commit\n", + "type": "rendered" + } + }, + "repository": { + "full_name": "user2/setup-jfrog-cli", + "links": { + "avatar": { + "href": "https://bytebucket.org/ravatar/%7Bd5f8a3f0-0cf1-43e9-8451-5379417d6c7c%7D?ts=default" + }, + "html": { + "href": "https://bitbucket.org/user2/setup-jfrog-cli" + }, + "self": { + "href": "https://api.bitbucket.org/2.0/repositories/user2/setup-jfrog-cli" + } + }, + "name": "setup-jfrog-cli", + "type": "repository", + "uuid": "{d5f8a3f0-0cf1-43e9-8451-5379417d6c7c}" + }, + "summary": { + "html": "

Initial commit

", + "markup": "markdown", + "raw": "Initial commit\n", + "type": "rendered" + }, + "type": "commit" + } + ] +} \ No newline at end of file diff --git a/vcsclient/testdata/bitbucketserver/commit_list_response.json b/vcsclient/testdata/bitbucketserver/commit_list_response.json new file mode 100644 index 00000000..d6082d92 --- /dev/null +++ b/vcsclient/testdata/bitbucketserver/commit_list_response.json @@ -0,0 +1,63 @@ +{ + "isLastPage": false, + "limit": 1, + "nextPageStart": 1, + "size": 1, + "start": 0, + "values": [ + { + "author": { + "active": true, + "displayName": "user", + "emailAddress": "user@example.com", + "id": 1315, + "links": { + "self": [ + { + "href": "https://git.example/users/username" + } + ] + }, + "name": "user", + "slug": "user", + "type": "NORMAL" + }, + "authorTimestamp": 1634630691000, + "committer": { + "active": true, + "displayName": "user", + "emailAddress": "user@example.com", + "id": 1315, + "links": { + "self": [ + { + "href": "https://git.example/users/username" + } + ] + }, + "name": "user", + "slug": "user", + "type": "NORMAL" + }, + "committerTimestamp": 1634630691000, + "displayId": "6686432526c", + "id": "6686432526c25d15310a1bb21c1d08b47b87d719", + "message": "Pull request", + "parents": [ + { + "displayId": "3407fa714e4", + "id": "3407fa714e47aec09dfe52c12165679d6d04eeaa" + }, + { + "displayId": "09ecf6eadff", + "id": "09ecf6eadffa1be8f9f61899c30cc590d0c4bfc8" + } + ], + "properties": { + "jira-key": [ + "JIRA-123" + ] + } + } + ] +} \ No newline at end of file diff --git a/vcsclient/testdata/github/commit_list_response.json b/vcsclient/testdata/github/commit_list_response.json new file mode 100644 index 00000000..4a467454 --- /dev/null +++ b/vcsclient/testdata/github/commit_list_response.json @@ -0,0 +1,86 @@ +[ + { + "author": { + "avatar_url": "https://avatars.githubusercontent.com/u/11367982?v=4", + "events_url": "https://api.github.com/users/user/events{/privacy}", + "followers_url": "https://api.github.com/users/user/followers", + "following_url": "https://api.github.com/users/user/following{/other_user}", + "gists_url": "https://api.github.com/users/user/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/user", + "id": 11367982, + "login": "user", + "node_id": "MDQ6VXNlcjExMzY3OTgy", + "organizations_url": "https://api.github.com/users/user/orgs", + "received_events_url": "https://api.github.com/users/user/received_events", + "repos_url": "https://api.github.com/users/user/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/user/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/user/subscriptions", + "type": "User", + "url": "https://api.github.com/users/user" + }, + "comments_url": "https://api.github.com/repos/jfrog/froggit-go/commits/67a87728853d912182e508d4f92d81f916eec2c6/comments", + "commit": { + "author": { + "date": "2021-10-06T06:44:58Z", + "email": "user@jfrog.com", + "name": "user" + }, + "comment_count": 0, + "committer": { + "date": "2021-10-06T06:54:51Z", + "email": "user@jfrog.com", + "name": "user" + }, + "message": "Merge remote-tracking branch 'upstream/dev'", + "tree": { + "sha": "bf881793041a5ed53caf7014cca5dd5f286edf17", + "url": "https://api.github.com/repos/jfrog/froggit-go/git/trees/bf881793041a5ed53caf7014cca5dd5f286edf17" + }, + "url": "https://api.github.com/repos/jfrog/froggit-go/git/commits/67a87728853d912182e508d4f92d81f916eec2c6", + "verification": { + "payload": null, + "reason": "unsigned", + "signature": null, + "verified": false + } + }, + "committer": { + "avatar_url": "https://avatars.githubusercontent.com/u/11367982?v=4", + "events_url": "https://api.github.com/users/user/events{/privacy}", + "followers_url": "https://api.github.com/users/user/followers", + "following_url": "https://api.github.com/users/user/following{/other_user}", + "gists_url": "https://api.github.com/users/user/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/user", + "id": 11367982, + "login": "user", + "node_id": "MDQ6VXNlcjExMzY3OTgy", + "organizations_url": "https://api.github.com/users/user/orgs", + "received_events_url": "https://api.github.com/users/user/received_events", + "repos_url": "https://api.github.com/users/user/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/user/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/user/subscriptions", + "type": "User", + "url": "https://api.github.com/users/user" + }, + "html_url": "https://github.com/jfrog/froggit-go/commit/67a87728853d912182e508d4f92d81f916eec2c6", + "node_id": "C_kwDOF_Bo3doAKDY3YTg3NzI4ODUzZDkxMjE4MmU1MDhkNGY5MmQ4MWY5MTZlZWMyYzY", + "parents": [ + { + "html_url": "https://github.com/jfrog/froggit-go/commit/9b3a3c0a1f0dd0023785f69967814c7654ca9b5e", + "sha": "9b3a3c0a1f0dd0023785f69967814c7654ca9b5e", + "url": "https://api.github.com/repos/jfrog/froggit-go/commits/9b3a3c0a1f0dd0023785f69967814c7654ca9b5e" + }, + { + "html_url": "https://github.com/jfrog/froggit-go/commit/0abf1a178c64f124a891903f08f13aaf31e8e493", + "sha": "0abf1a178c64f124a891903f08f13aaf31e8e493", + "url": "https://api.github.com/repos/jfrog/froggit-go/commits/0abf1a178c64f124a891903f08f13aaf31e8e493" + } + ], + "sha": "67a87728853d912182e508d4f92d81f916eec2c6", + "url": "https://api.github.com/repos/jfrog/froggit-go/commits/67a87728853d912182e508d4f92d81f916eec2c6" + } +] \ No newline at end of file diff --git a/vcsclient/testdata/gitlab/commit_list_response.json b/vcsclient/testdata/gitlab/commit_list_response.json new file mode 100644 index 00000000..e861317a --- /dev/null +++ b/vcsclient/testdata/gitlab/commit_list_response.json @@ -0,0 +1,34 @@ +[ + { + "id": "ed899a2f4b50b4370feeea94676502b42383c746", + "short_id": "ed899a2f4b5", + "title": "Replace sanitize with escape once", + "author_name": "Example User", + "author_email": "user@example.com", + "authored_date": "2012-09-20T11:50:22+03:00", + "committer_name": "Administrator", + "committer_email": "admin@example.com", + "committed_date": "2012-09-20T11:50:22+03:00", + "created_at": "2012-09-20T11:50:22+03:00", + "message": "Replace sanitize with escape once", + "parent_ids": [ + "6104942438c14ec7bd21c6cd5bd995272b3faff6" + ], + "web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746" + }, + { + "id": "6104942438c14ec7bd21c6cd5bd995272b3faff6", + "short_id": "6104942438c", + "title": "Sanitize for network graph", + "author_name": "randx", + "author_email": "user@example.com", + "committer_name": "ExampleName", + "committer_email": "user@example.com", + "created_at": "2012-09-20T09:06:12+03:00", + "message": "Sanitize for network graph", + "parent_ids": [ + "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba" + ], + "web_url": "https://gitlab.example.com/thedude/gitlab-foss/-/commit/ed899a2f4b50b4370feeea94676502b42383c746" + } +] \ No newline at end of file diff --git a/vcsclient/vcsclient.go b/vcsclient/vcsclient.go index cea45ab3..59212421 100644 --- a/vcsclient/vcsclient.go +++ b/vcsclient/vcsclient.go @@ -82,4 +82,10 @@ type VcsClient interface { // title - Pull request title // description - Pull request description CreatePullRequest(ctx context.Context, owner, repository, sourceBranch, targetBranch, title, description string) error + + // GetLatestCommitHash Get the most recent commit SHA-1 of a branch + // owner - User or organization + // repository - VCS repository name + // branch - The name of the branch + GetLatestCommitHash(ctx context.Context, owner, repository, branch string) (string, error) }