woodpecker/server/forge/bitbucket/convert.go

235 lines
7 KiB
Go
Raw Normal View History

// Copyright 2022 Woodpecker Authors
2018-02-19 22:24:10 +00:00
// Copyright 2018 Drone.IO Inc.
2018-03-21 13:02:17 +00:00
//
2018-02-19 22:24:10 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2018-03-21 13:02:17 +00:00
//
2018-02-19 22:24:10 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2018-03-21 13:02:17 +00:00
//
2018-02-19 22:24:10 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bitbucket
import (
"fmt"
2015-10-05 01:34:06 +00:00
"net/url"
2017-03-18 08:49:27 +00:00
"regexp"
"strings"
"golang.org/x/oauth2"
"go.woodpecker-ci.org/woodpecker/v2/server/forge/bitbucket/internal"
"go.woodpecker-ci.org/woodpecker/v2/server/model"
)
const (
statusPending = "INPROGRESS" // cspell:disable-line
statusSuccess = "SUCCESSFUL"
statusFailure = "FAILED"
)
// convertStatus is a helper function used to convert a Woodpecker status to a
// Bitbucket commit status.
func convertStatus(status model.StatusValue) string {
switch status {
2017-03-18 08:49:27 +00:00
case model.StatusPending, model.StatusRunning, model.StatusBlocked:
return statusPending
case model.StatusSuccess:
return statusSuccess
default:
return statusFailure
}
}
2016-04-29 19:39:56 +00:00
// convertRepo is a helper function used to convert a Bitbucket repository
// structure to the common Woodpecker repository structure.
func convertRepo(from *internal.Repo, perm *internal.RepoPerm) *model.Repo {
repo := model.Repo{
ForgeRemoteID: model.ForgeRemoteID(from.UUID),
Clone: cloneLink(from),
CloneSSH: sshCloneLink(from),
Owner: strings.Split(from.FullName, "/")[0],
Name: strings.Split(from.FullName, "/")[1],
FullName: from.FullName,
ForgeURL: from.Links.HTML.Href,
IsSCMPrivate: from.IsPrivate,
Avatar: from.Owner.Links.Avatar.Href,
SCMKind: model.SCMKind(from.Scm),
Branch: from.MainBranch.Name,
Perm: convertPerm(perm),
PREnabled: true,
}
if repo.SCMKind == model.RepoHg {
repo.Branch = "default"
}
return &repo
}
func convertPerm(from *internal.RepoPerm) *model.Perm {
perms := new(model.Perm)
switch from.Permission {
case "admin":
perms.Admin = true
fallthrough
case "write":
perms.Push = true
fallthrough
default:
perms.Pull = true
}
return perms
}
2016-04-29 19:39:56 +00:00
// cloneLink is a helper function that tries to extract the clone url from the
// repository object.
func cloneLink(repo *internal.Repo) string {
var clone string
2016-04-29 19:39:56 +00:00
// above we manually constructed the repository clone url. below we will
// iterate through the list of clone links and attempt to instead use the
// clone url provided by bitbucket.
for _, link := range repo.Links.Clone {
if link.Name == "https" {
clone = link.Href
}
}
2016-04-29 19:39:56 +00:00
// if no repository name is provided, we use the Html link. this excludes the
// .git suffix, but will still clone the repo.
if len(clone) == 0 {
clone = repo.Links.HTML.Href
}
2016-04-29 19:39:56 +00:00
// if bitbucket tries to automatically populate the user in the url we must
// strip it out.
cloneURL, err := url.Parse(clone)
if err == nil {
cloneURL.User = nil
clone = cloneURL.String()
}
return clone
}
// cloneLink is a helper function that tries to extract the clone url from the
// repository object.
func sshCloneLink(repo *internal.Repo) string {
for _, link := range repo.Links.Clone {
if link.Name == "ssh" {
return link.Href
}
}
return ""
}
2016-04-29 19:39:56 +00:00
// convertUser is a helper function used to convert a Bitbucket user account
// structure to the Woodpecker User structure.
2016-04-29 19:39:56 +00:00
func convertUser(from *internal.Account, token *oauth2.Token) *model.User {
return &model.User{
Login: from.Login,
Token: token.AccessToken,
Secret: token.RefreshToken,
Expiry: token.Expiry.UTC().Unix(),
Avatar: from.Links.Avatar.Href,
ForgeRemoteID: model.ForgeRemoteID(fmt.Sprint(from.UUID)),
2016-04-29 19:39:56 +00:00
}
}
// convertWorkspaceList is a helper function used to convert a Bitbucket team list
// structure to the Woodpecker Team structure.
func convertWorkspaceList(from []*internal.Workspace) []*model.Team {
2016-04-29 19:39:56 +00:00
var teams []*model.Team
for _, workspace := range from {
teams = append(teams, convertWorkspace(workspace))
2016-04-29 19:39:56 +00:00
}
return teams
}
// convertWorkspace is a helper function used to convert a Bitbucket team account
// structure to the Woodpecker Team structure.
func convertWorkspace(from *internal.Workspace) *model.Team {
2016-04-29 19:39:56 +00:00
return &model.Team{
Login: from.Slug,
2016-04-29 19:39:56 +00:00
Avatar: from.Links.Avatar.Href,
}
}
// convertPullHook is a helper function used to convert a Bitbucket pull request
// hook to the Woodpecker pipeline struct holding commit information.
func convertPullHook(from *internal.PullRequestHook) *model.Pipeline {
event := model.EventPull
if from.PullRequest.State == stateClosed || from.PullRequest.State == stateDeclined {
event = model.EventPullClosed
}
Change Bitbucket PR hook to point the source branch, commit & ref (#3965) ## Description This is the first fix for: https://github.com/woodpecker-ci/woodpecker/issues/3932 Change the Pull Request hook parser to return the source commit, branch, and ref instead of the destination. Right now, the workflow pulls the destination configuration and code. It should pull the source configuration and code to verify that the configuration and code work as expected before merging the changes. In case of the close event, the hook parser returns the destination branch, ref and merge commit. Usually, the contributor automatically deletes the source branch after merging the changes to the destination branch. Using the source values will cause the workflow to fail. After the changes, Woodpecker will correctly download the workflow from the source branch (Pull Request commit), but it will fail to clone the repository. This issue is related to the commit format returned by the Bitbucket webhook. This inconsistency has already been reported: https://jira.atlassian.com/browse/BCLOUD-21201. The webhook returns a short SHA. The problem is that the `git fetch` command requires the full SHA. A workaround for this issue is to use the ref to fetch the code: ```yaml clone: git: image: woodpeckerci/plugin-git settings: ref: ${CI_COMMIT_REF} ``` This is not ideal, because the Pull Request head won't always match the workflow commit, but it solves 80% of the event use cases (e.g. trigger a pull request workflow on change). This workaround won't work when re-running a previous workflow pointing to another commit, it will pull the last commit, not the previous one. ## Solutions The solution proposed by the community is to retrieve the full SHA from the Bitbucket API using the short one. This solution has drawbacks: - The Bitbucket API rate limit is 1000 req/h. This solution will reduce the maximum number of workflow runs per hour. - It requires a braking change in the forges interface because the ´Hook(...)´ method does not have an instance of the HTTP Client. We propose to allow the git plugin to fetch the source code from a URL. The Bitbucket returns a link pointing to the commit. This proposal only requires a small change to the git plugin: - Add a new optional parameter (e.g. CommitLink) - Add a clause to the following conditional: https://github.com/woodpecker-ci/plugin-git/blob/7ac9615f409b539486b8841bd5ef01ae16bbc434/plugin.go#L79C1-L88C3 ```go if p.Pipeline.CommitLink != "" {...} ``` Git commands: ```shell $ git fetch --no-tags --depth=1 --filter=tree:0 https://bitbucket.org/workspace/repo/commits/692972aabfec $ git reset --hard -q 692972aabfec # It works with the short SHA ``` Woodpecker will set CommitLink to a blank string for the other forges, but Bitbuckket will use the one returned by the webhook.
2024-07-23 14:58:38 +00:00
pipeline := &model.Pipeline{
Event: event,
Change Bitbucket PR hook to point the source branch, commit & ref (#3965) ## Description This is the first fix for: https://github.com/woodpecker-ci/woodpecker/issues/3932 Change the Pull Request hook parser to return the source commit, branch, and ref instead of the destination. Right now, the workflow pulls the destination configuration and code. It should pull the source configuration and code to verify that the configuration and code work as expected before merging the changes. In case of the close event, the hook parser returns the destination branch, ref and merge commit. Usually, the contributor automatically deletes the source branch after merging the changes to the destination branch. Using the source values will cause the workflow to fail. After the changes, Woodpecker will correctly download the workflow from the source branch (Pull Request commit), but it will fail to clone the repository. This issue is related to the commit format returned by the Bitbucket webhook. This inconsistency has already been reported: https://jira.atlassian.com/browse/BCLOUD-21201. The webhook returns a short SHA. The problem is that the `git fetch` command requires the full SHA. A workaround for this issue is to use the ref to fetch the code: ```yaml clone: git: image: woodpeckerci/plugin-git settings: ref: ${CI_COMMIT_REF} ``` This is not ideal, because the Pull Request head won't always match the workflow commit, but it solves 80% of the event use cases (e.g. trigger a pull request workflow on change). This workaround won't work when re-running a previous workflow pointing to another commit, it will pull the last commit, not the previous one. ## Solutions The solution proposed by the community is to retrieve the full SHA from the Bitbucket API using the short one. This solution has drawbacks: - The Bitbucket API rate limit is 1000 req/h. This solution will reduce the maximum number of workflow runs per hour. - It requires a braking change in the forges interface because the ´Hook(...)´ method does not have an instance of the HTTP Client. We propose to allow the git plugin to fetch the source code from a URL. The Bitbucket returns a link pointing to the commit. This proposal only requires a small change to the git plugin: - Add a new optional parameter (e.g. CommitLink) - Add a clause to the following conditional: https://github.com/woodpecker-ci/plugin-git/blob/7ac9615f409b539486b8841bd5ef01ae16bbc434/plugin.go#L79C1-L88C3 ```go if p.Pipeline.CommitLink != "" {...} ``` Git commands: ```shell $ git fetch --no-tags --depth=1 --filter=tree:0 https://bitbucket.org/workspace/repo/commits/692972aabfec $ git reset --hard -q 692972aabfec # It works with the short SHA ``` Woodpecker will set CommitLink to a blank string for the other forges, but Bitbuckket will use the one returned by the webhook.
2024-07-23 14:58:38 +00:00
Commit: from.PullRequest.Source.Commit.Hash,
2024-08-02 19:42:04 +00:00
Ref: fmt.Sprintf("refs/pull-requests/%d/from", from.PullRequest.ID),
2016-11-19 22:44:22 +00:00
Refspec: fmt.Sprintf("%s:%s",
from.PullRequest.Source.Branch.Name,
from.PullRequest.Dest.Branch.Name,
),
ForgeURL: from.PullRequest.Links.HTML.Href,
Change Bitbucket PR hook to point the source branch, commit & ref (#3965) ## Description This is the first fix for: https://github.com/woodpecker-ci/woodpecker/issues/3932 Change the Pull Request hook parser to return the source commit, branch, and ref instead of the destination. Right now, the workflow pulls the destination configuration and code. It should pull the source configuration and code to verify that the configuration and code work as expected before merging the changes. In case of the close event, the hook parser returns the destination branch, ref and merge commit. Usually, the contributor automatically deletes the source branch after merging the changes to the destination branch. Using the source values will cause the workflow to fail. After the changes, Woodpecker will correctly download the workflow from the source branch (Pull Request commit), but it will fail to clone the repository. This issue is related to the commit format returned by the Bitbucket webhook. This inconsistency has already been reported: https://jira.atlassian.com/browse/BCLOUD-21201. The webhook returns a short SHA. The problem is that the `git fetch` command requires the full SHA. A workaround for this issue is to use the ref to fetch the code: ```yaml clone: git: image: woodpeckerci/plugin-git settings: ref: ${CI_COMMIT_REF} ``` This is not ideal, because the Pull Request head won't always match the workflow commit, but it solves 80% of the event use cases (e.g. trigger a pull request workflow on change). This workaround won't work when re-running a previous workflow pointing to another commit, it will pull the last commit, not the previous one. ## Solutions The solution proposed by the community is to retrieve the full SHA from the Bitbucket API using the short one. This solution has drawbacks: - The Bitbucket API rate limit is 1000 req/h. This solution will reduce the maximum number of workflow runs per hour. - It requires a braking change in the forges interface because the ´Hook(...)´ method does not have an instance of the HTTP Client. We propose to allow the git plugin to fetch the source code from a URL. The Bitbucket returns a link pointing to the commit. This proposal only requires a small change to the git plugin: - Add a new optional parameter (e.g. CommitLink) - Add a clause to the following conditional: https://github.com/woodpecker-ci/plugin-git/blob/7ac9615f409b539486b8841bd5ef01ae16bbc434/plugin.go#L79C1-L88C3 ```go if p.Pipeline.CommitLink != "" {...} ``` Git commands: ```shell $ git fetch --no-tags --depth=1 --filter=tree:0 https://bitbucket.org/workspace/repo/commits/692972aabfec $ git reset --hard -q 692972aabfec # It works with the short SHA ``` Woodpecker will set CommitLink to a blank string for the other forges, but Bitbuckket will use the one returned by the webhook.
2024-07-23 14:58:38 +00:00
Branch: from.PullRequest.Source.Branch.Name,
Message: from.PullRequest.Title,
Avatar: from.Actor.Links.Avatar.Href,
Author: from.Actor.Login,
2017-03-18 08:49:27 +00:00
Sender: from.Actor.Login,
Timestamp: from.PullRequest.Updated.UTC().Unix(),
}
Change Bitbucket PR hook to point the source branch, commit & ref (#3965) ## Description This is the first fix for: https://github.com/woodpecker-ci/woodpecker/issues/3932 Change the Pull Request hook parser to return the source commit, branch, and ref instead of the destination. Right now, the workflow pulls the destination configuration and code. It should pull the source configuration and code to verify that the configuration and code work as expected before merging the changes. In case of the close event, the hook parser returns the destination branch, ref and merge commit. Usually, the contributor automatically deletes the source branch after merging the changes to the destination branch. Using the source values will cause the workflow to fail. After the changes, Woodpecker will correctly download the workflow from the source branch (Pull Request commit), but it will fail to clone the repository. This issue is related to the commit format returned by the Bitbucket webhook. This inconsistency has already been reported: https://jira.atlassian.com/browse/BCLOUD-21201. The webhook returns a short SHA. The problem is that the `git fetch` command requires the full SHA. A workaround for this issue is to use the ref to fetch the code: ```yaml clone: git: image: woodpeckerci/plugin-git settings: ref: ${CI_COMMIT_REF} ``` This is not ideal, because the Pull Request head won't always match the workflow commit, but it solves 80% of the event use cases (e.g. trigger a pull request workflow on change). This workaround won't work when re-running a previous workflow pointing to another commit, it will pull the last commit, not the previous one. ## Solutions The solution proposed by the community is to retrieve the full SHA from the Bitbucket API using the short one. This solution has drawbacks: - The Bitbucket API rate limit is 1000 req/h. This solution will reduce the maximum number of workflow runs per hour. - It requires a braking change in the forges interface because the ´Hook(...)´ method does not have an instance of the HTTP Client. We propose to allow the git plugin to fetch the source code from a URL. The Bitbucket returns a link pointing to the commit. This proposal only requires a small change to the git plugin: - Add a new optional parameter (e.g. CommitLink) - Add a clause to the following conditional: https://github.com/woodpecker-ci/plugin-git/blob/7ac9615f409b539486b8841bd5ef01ae16bbc434/plugin.go#L79C1-L88C3 ```go if p.Pipeline.CommitLink != "" {...} ``` Git commands: ```shell $ git fetch --no-tags --depth=1 --filter=tree:0 https://bitbucket.org/workspace/repo/commits/692972aabfec $ git reset --hard -q 692972aabfec # It works with the short SHA ``` Woodpecker will set CommitLink to a blank string for the other forges, but Bitbuckket will use the one returned by the webhook.
2024-07-23 14:58:38 +00:00
if from.PullRequest.State == stateClosed {
pipeline.Commit = from.PullRequest.MergeCommit.Hash
pipeline.Ref = fmt.Sprintf("refs/heads/%s", from.PullRequest.Dest.Branch.Name)
pipeline.Branch = from.PullRequest.Dest.Branch.Name
}
return pipeline
}
// convertPushHook is a helper function used to convert a Bitbucket push
// hook to the Woodpecker pipeline struct holding commit information.
func convertPushHook(hook *internal.PushHook, change *internal.Change) *model.Pipeline {
pipeline := &model.Pipeline{
Commit: change.New.Target.Hash,
ForgeURL: change.New.Target.Links.HTML.Href,
Branch: change.New.Name,
Message: change.New.Target.Message,
Avatar: hook.Actor.Links.Avatar.Href,
Author: hook.Actor.Login,
2017-03-18 08:49:27 +00:00
Sender: hook.Actor.Login,
Timestamp: change.New.Target.Date.UTC().Unix(),
}
switch change.New.Type {
case "tag", "annotated_tag", "bookmark":
pipeline.Event = model.EventTag
pipeline.Ref = fmt.Sprintf("refs/tags/%s", change.New.Name)
default:
pipeline.Event = model.EventPush
pipeline.Ref = fmt.Sprintf("refs/heads/%s", change.New.Name)
}
if len(change.New.Target.Author.Raw) != 0 {
pipeline.Email = extractEmail(change.New.Target.Author.Raw)
}
return pipeline
}
// regex for git author fields (r.g. "name <name@mail.tld>").
2017-01-20 09:52:17 +00:00
var reGitMail = regexp.MustCompile("<(.*)>")
// extracts the email from a git commit author string.
func extractEmail(gitAuthor string) (author string) {
matches := reGitMail.FindAllStringSubmatch(gitAuthor, -1)
2017-03-18 08:49:27 +00:00
if len(matches) == 1 {
author = matches[0][1]
}
return
}