mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-10-02 08:22:01 +00:00
Merge pull request #1690 from josmo/bbserver-cleanup
Cleanup for the Stash remote implementation
This commit is contained in:
commit
e0b7b31a5d
7 changed files with 475 additions and 466 deletions
|
@ -5,21 +5,26 @@ package bitbucketserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote"
|
||||||
|
"github.com/drone/drone/remote/bitbucketserver/internal"
|
||||||
|
"github.com/mrjones/oauth"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/drone/drone/model"
|
|
||||||
"github.com/drone/drone/remote"
|
|
||||||
"github.com/mrjones/oauth"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
requestTokenURL = "%s/plugins/servlet/oauth/request-token"
|
||||||
|
authorizeTokenURL = "%s/plugins/servlet/oauth/authorize"
|
||||||
|
accessTokenURL = "%s/plugins/servlet/oauth/access-token"
|
||||||
|
)
|
||||||
|
|
||||||
// Opts defines configuration options.
|
// Opts defines configuration options.
|
||||||
type Opts struct {
|
type Opts struct {
|
||||||
URL string // Stash server url.
|
URL string // Stash server url.
|
||||||
|
@ -27,218 +32,128 @@ type Opts struct {
|
||||||
Password string // Git machine account password.
|
Password string // Git machine account password.
|
||||||
ConsumerKey string // Oauth1 consumer key.
|
ConsumerKey string // Oauth1 consumer key.
|
||||||
ConsumerRSA string // Oauth1 consumer key file.
|
ConsumerRSA string // Oauth1 consumer key file.
|
||||||
|
|
||||||
SkipVerify bool // Skip ssl verification.
|
SkipVerify bool // Skip ssl verification.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
URL string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
SkipVerify bool
|
||||||
|
Consumer *oauth.Consumer
|
||||||
|
}
|
||||||
|
|
||||||
// New returns a Remote implementation that integrates with Bitbucket Server,
|
// New returns a Remote implementation that integrates with Bitbucket Server,
|
||||||
// the on-premise edition of Bitbucket Cloud, formerly known as Stash.
|
// the on-premise edition of Bitbucket Cloud, formerly known as Stash.
|
||||||
func New(opts Opts) (remote.Remote, error) {
|
func New(opts Opts) (remote.Remote, error) {
|
||||||
bb := &client{
|
config := &Config{
|
||||||
URL: opts.URL,
|
URL: opts.URL,
|
||||||
ConsumerKey: opts.ConsumerKey,
|
Username: opts.Username,
|
||||||
ConsumerRSA: opts.ConsumerRSA,
|
Password: opts.Password,
|
||||||
GitUserName: opts.Username,
|
SkipVerify: opts.SkipVerify,
|
||||||
GitPassword: opts.Password,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case bb.GitUserName == "":
|
case opts.Username == "":
|
||||||
return nil, fmt.Errorf("Must have a git machine account username")
|
return nil, fmt.Errorf("Must have a git machine account username")
|
||||||
case bb.GitPassword == "":
|
case opts.Password == "":
|
||||||
return nil, fmt.Errorf("Must have a git machine account password")
|
return nil, fmt.Errorf("Must have a git machine account password")
|
||||||
case bb.ConsumerKey == "":
|
case opts.ConsumerKey == "":
|
||||||
return nil, fmt.Errorf("Must have a oauth1 consumer key")
|
return nil, fmt.Errorf("Must have a oauth1 consumer key")
|
||||||
case bb.ConsumerRSA == "":
|
case opts.ConsumerRSA == "":
|
||||||
return nil, fmt.Errorf("Must have a oauth1 consumer key file")
|
return nil, fmt.Errorf("Must have a oauth1 consumer key file")
|
||||||
}
|
}
|
||||||
|
|
||||||
keyfile, err := ioutil.ReadFile(bb.ConsumerRSA)
|
keyFile, err := ioutil.ReadFile(opts.ConsumerRSA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
block, _ := pem.Decode(keyfile)
|
block, _ := pem.Decode(keyFile)
|
||||||
bb.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
PrivateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
config.Consumer = CreateConsumer(opts.URL, opts.ConsumerKey, PrivateKey)
|
||||||
// TODO de-referencing is a bit weird and may not behave as expected, and could
|
return config, nil
|
||||||
// have race conditions. Instead store the parsed key (I already did this above)
|
|
||||||
// and then pass the parsed private key when creating the Bitbucket client.
|
|
||||||
bb.Consumer = *NewClient(bb.ConsumerRSA, bb.ConsumerKey, bb.URL)
|
|
||||||
return bb, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
func (c *Config) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
||||||
URL string
|
|
||||||
ConsumerKey string
|
|
||||||
GitUserName string
|
|
||||||
GitPassword string
|
|
||||||
ConsumerRSA string
|
|
||||||
PrivateKey *rsa.PrivateKey
|
|
||||||
Consumer oauth.Consumer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) Login(res http.ResponseWriter, req *http.Request) (*model.User, error) {
|
|
||||||
requestToken, url, err := c.Consumer.GetRequestTokenAndUrl("oob")
|
requestToken, url, err := c.Consumer.GetRequestTokenAndUrl("oob")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var code = req.FormValue("oauth_verifier")
|
var code = req.FormValue("oauth_verifier")
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
http.Redirect(res, req, url, http.StatusSeeOther)
|
http.Redirect(res, req, url, http.StatusSeeOther)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
requestToken.Token = req.FormValue("oauth_token")
|
requestToken.Token = req.FormValue("oauth_token")
|
||||||
accessToken, err := c.Consumer.AuthorizeToken(requestToken, code)
|
accessToken, err := c.Consumer.AuthorizeToken(requestToken, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := c.Consumer.MakeHttpClient(accessToken)
|
client := internal.NewClientWithToken(c.URL, c.Consumer, accessToken.Token)
|
||||||
|
|
||||||
|
user, err := client.FindCurrentUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := client.Get(fmt.Sprintf("%s/plugins/servlet/applinks/whoami", c.URL))
|
return convertUser(user, accessToken), nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
bits, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
login := string(bits)
|
|
||||||
|
|
||||||
// TODO errors should never be ignored like this
|
|
||||||
response1, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/users/%s", c.URL, login))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer response1.Body.Close()
|
|
||||||
|
|
||||||
contents, err := ioutil.ReadAll(response1.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var user User
|
|
||||||
err = json.Unmarshal(contents, &user)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &model.User{
|
|
||||||
Login: login,
|
|
||||||
Email: user.EmailAddress,
|
|
||||||
Token: accessToken.Token,
|
|
||||||
Avatar: avatarLink(user.EmailAddress),
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth is not supported by the Stash driver.
|
// Auth is not supported by the Stash driver.
|
||||||
func (*client) Auth(token, secret string) (string, error) {
|
func (*Config) Auth(token, secret string) (string, error) {
|
||||||
return "", fmt.Errorf("Not Implemented")
|
return "", fmt.Errorf("Not Implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Teams is not supported by the Stash driver.
|
// Teams is not supported by the Stash driver.
|
||||||
func (*client) Teams(u *model.User) ([]*model.Team, error) {
|
func (*Config) Teams(u *model.User) ([]*model.Team, error) {
|
||||||
var teams []*model.Team
|
var teams []*model.Team
|
||||||
return teams, nil
|
return teams, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
func (c *Config) Repo(u *model.User, owner, name string) (*model.Repo, error) {
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
repo, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepo(owner, name)
|
||||||
repo, err := c.FindRepo(client, owner, name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return repo, nil
|
return convertRepo(repo), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Repos(u *model.User) ([]*model.RepoLite, error) {
|
func (c *Config) Repos(u *model.User) ([]*model.RepoLite, error) {
|
||||||
|
repos, err := internal.NewClientWithToken(c.URL, c.Consumer, u.Token).FindRepos()
|
||||||
var repos = []*model.RepoLite{}
|
|
||||||
|
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
|
||||||
|
|
||||||
response, err := client.Get(fmt.Sprintf("%s/rest/api/1.0/repos?limit=10000", c.URL))
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
contents, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var repoResponse Repos
|
var all []*model.RepoLite
|
||||||
err = json.Unmarshal(contents, &repoResponse)
|
for _, repo := range repos {
|
||||||
if err != nil {
|
all = append(all, convertRepoLite(repo))
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, repo := range repoResponse.Values {
|
return all, nil
|
||||||
repos = append(repos, &model.RepoLite{
|
|
||||||
Name: repo.Slug,
|
|
||||||
FullName: repo.Project.Key + "/" + repo.Slug,
|
|
||||||
Owner: repo.Project.Key,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return repos, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Perm(u *model.User, owner, repo string) (*model.Perm, error) {
|
func (c *Config) Perm(u *model.User, owner, repo string) (*model.Perm, error) {
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token)
|
||||||
perms := new(model.Perm)
|
|
||||||
|
|
||||||
// If you don't have access return none right away
|
return client.FindRepoPerms(owner, repo)
|
||||||
_, err := c.FindRepo(client, owner, repo)
|
|
||||||
if err != nil {
|
|
||||||
return perms, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must have admin to be able to list hooks. If have access the enable perms
|
|
||||||
_, err = client.Get(fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s", c.URL, owner, repo, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook"))
|
|
||||||
if err == nil {
|
|
||||||
perms.Push = true
|
|
||||||
perms.Admin = true
|
|
||||||
}
|
|
||||||
perms.Pull = true
|
|
||||||
return perms, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
func (c *Config) File(u *model.User, r *model.Repo, b *model.Build, f string) ([]byte, error) {
|
||||||
log.Info(fmt.Sprintf("Staring file for bitbucketServer login: %s repo: %s buildevent: %s string: %s", u.Login, r.Name, b.Event, f))
|
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token)
|
||||||
|
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
return client.FindFileForRepo(r.Owner, r.Name, f, b.Ref)
|
||||||
fileURL := fmt.Sprintf("%s/projects/%s/repos/%s/browse/%s?raw", c.URL, r.Owner, r.Name, f)
|
|
||||||
log.Info(fileURL)
|
|
||||||
response, err := client.Get(fileURL)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
if response.StatusCode == 404 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
responseBytes, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return responseBytes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status is not supported by the Gogs driver.
|
// Status is not supported by the bitbucketserver driver.
|
||||||
func (*client) Status(*model.User, *model.Repo, *model.Build, string) error {
|
func (*Config) Status(*model.User, *model.Repo, *model.Build, string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
|
func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||||
u, err := url.Parse(c.URL)
|
u, err := url.Parse(c.URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -252,132 +167,40 @@ func (c *client) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||||
}
|
}
|
||||||
return &model.Netrc{
|
return &model.Netrc{
|
||||||
Machine: host,
|
Machine: host,
|
||||||
Login: c.GitUserName,
|
Login: c.Username,
|
||||||
Password: c.GitPassword,
|
Password: c.Password,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Activate(u *model.User, r *model.Repo, link string) error {
|
func (c *Config) Activate(u *model.User, r *model.Repo, link string) error {
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token)
|
||||||
hook, err := c.CreateHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
|
|
||||||
if err != nil {
|
return client.CreateHook(r.Owner, r.Name, link)
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info(hook)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Deactivate(u *model.User, r *model.Repo, link string) error {
|
func (c *Config) Deactivate(u *model.User, r *model.Repo, link string) error {
|
||||||
client := NewClientWithToken(&c.Consumer, u.Token)
|
client := internal.NewClientWithToken(c.URL, c.Consumer, u.Token)
|
||||||
err := c.DeleteHook(client, r.Owner, r.Name, "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook", link)
|
return client.DeleteHook(r.Owner, r.Name, link)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
func (c *Config) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
|
||||||
hook := new(postHook)
|
return parseHook(r, c.URL)
|
||||||
if err := json.NewDecoder(r.Body).Decode(hook); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
build := &model.Build{
|
|
||||||
Event: model.EventPush,
|
|
||||||
Ref: hook.RefChanges[0].RefID, // TODO check for index Values
|
|
||||||
Author: hook.Changesets.Values[0].ToCommit.Author.EmailAddress, // TODO check for index Values
|
|
||||||
Commit: hook.RefChanges[0].ToHash, // TODO check for index value
|
|
||||||
Avatar: avatarLink(hook.Changesets.Values[0].ToCommit.Author.EmailAddress),
|
|
||||||
}
|
|
||||||
|
|
||||||
repo := &model.Repo{
|
|
||||||
Name: hook.Repository.Slug,
|
|
||||||
Owner: hook.Repository.Project.Key,
|
|
||||||
FullName: fmt.Sprintf("%s/%s", hook.Repository.Project.Key, hook.Repository.Slug),
|
|
||||||
Branch: "master",
|
|
||||||
Kind: model.RepoGit,
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo, build, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HookDetail struct {
|
func CreateConsumer(URL string, ConsumerKey string, PrivateKey *rsa.PrivateKey) *oauth.Consumer {
|
||||||
Key string `json:"key"`
|
consumer := oauth.NewRSAConsumer(
|
||||||
Name string `json:"name"`
|
ConsumerKey,
|
||||||
Type string `json:"type"`
|
PrivateKey,
|
||||||
Description string `json:"description"`
|
oauth.ServiceProvider{
|
||||||
Version string `json:"version"`
|
RequestTokenUrl: fmt.Sprintf(requestTokenURL, URL),
|
||||||
ConfigFormKey string `json:"configFormKey"`
|
AuthorizeTokenUrl: fmt.Sprintf(authorizeTokenURL, URL),
|
||||||
}
|
AccessTokenUrl: fmt.Sprintf(accessTokenURL, URL),
|
||||||
|
HttpMethod: "POST",
|
||||||
type Hook struct {
|
})
|
||||||
Enabled bool `json:"enabled"`
|
consumer.HttpClient = &http.Client{
|
||||||
Details *HookDetail `json:"details"`
|
Transport: &http.Transport{
|
||||||
}
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
},
|
||||||
// Enable hook for named repository
|
}
|
||||||
func (bs *client) CreateHook(client *http.Client, project, slug, hook_key, link string) (*Hook, error) {
|
return consumer
|
||||||
|
|
||||||
// Set hook
|
|
||||||
hookBytes := []byte(fmt.Sprintf(`{"hook-url-0":"%s"}`, link))
|
|
||||||
|
|
||||||
// Enable hook
|
|
||||||
enablePath := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled",
|
|
||||||
project, slug, hook_key)
|
|
||||||
|
|
||||||
doPut(client, bs.URL+enablePath, hookBytes)
|
|
||||||
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable hook for named repository
|
|
||||||
func (bs *client) DeleteHook(client *http.Client, project, slug, hook_key, link string) error {
|
|
||||||
enablePath := fmt.Sprintf("/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled",
|
|
||||||
project, slug, hook_key)
|
|
||||||
doDelete(client, bs.URL+enablePath)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *client) FindRepo(client *http.Client, owner string, name string) (*model.Repo, error) {
|
|
||||||
|
|
||||||
urlString := fmt.Sprintf("%s/rest/api/1.0/projects/%s/repos/%s", c.URL, owner, name)
|
|
||||||
|
|
||||||
response, err := client.Get(urlString)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
defer response.Body.Close()
|
|
||||||
contents, err := ioutil.ReadAll(response.Body)
|
|
||||||
bsRepo := BSRepo{}
|
|
||||||
err = json.Unmarshal(contents, &bsRepo)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repo := &model.Repo{
|
|
||||||
Name: bsRepo.Slug,
|
|
||||||
Owner: bsRepo.Project.Key,
|
|
||||||
Branch: "master",
|
|
||||||
Kind: model.RepoGit,
|
|
||||||
IsPrivate: true, // TODO(josmo) possibly set this as a setting - must always be private to use netrc
|
|
||||||
FullName: fmt.Sprintf("%s/%s", bsRepo.Project.Key, bsRepo.Slug),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, item := range bsRepo.Links.Clone {
|
|
||||||
if item.Name == "http" {
|
|
||||||
uri, err := url.Parse(item.Href)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
uri.User = nil
|
|
||||||
repo.Clone = uri.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, item := range bsRepo.Links.Self {
|
|
||||||
if item.Href != "" {
|
|
||||||
repo.Link = item.Href
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return repo, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
package bitbucketserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"github.com/mrjones/oauth"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewClient(ConsumerRSA string, ConsumerKey string, URL string) *oauth.Consumer {
|
|
||||||
//TODO: make this configurable
|
|
||||||
privateKeyFileContents, err := ioutil.ReadFile(ConsumerRSA)
|
|
||||||
log.Info("Tried to read the key")
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
block, _ := pem.Decode([]byte(privateKeyFileContents))
|
|
||||||
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := oauth.NewRSAConsumer(
|
|
||||||
ConsumerKey,
|
|
||||||
privateKey,
|
|
||||||
oauth.ServiceProvider{
|
|
||||||
RequestTokenUrl: URL + "/plugins/servlet/oauth/request-token",
|
|
||||||
AuthorizeTokenUrl: URL + "/plugins/servlet/oauth/authorize",
|
|
||||||
AccessTokenUrl: URL + "/plugins/servlet/oauth/access-token",
|
|
||||||
HttpMethod: "POST",
|
|
||||||
})
|
|
||||||
c.HttpClient = &http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientWithToken(Consumer *oauth.Consumer, AccessToken string) *http.Client {
|
|
||||||
var token oauth.AccessToken
|
|
||||||
token.Token = AccessToken
|
|
||||||
client, err := Consumer.MakeHttpClient(&token)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
return client
|
|
||||||
}
|
|
110
remote/bitbucketserver/convert.go
Normal file
110
remote/bitbucketserver/convert.go
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
package bitbucketserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote/bitbucketserver/internal"
|
||||||
|
"github.com/mrjones/oauth"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// convertRepo is a helper function used to convert a Bitbucket server repository
|
||||||
|
// structure to the common Drone repository structure.
|
||||||
|
func convertRepo(from *internal.Repo) *model.Repo {
|
||||||
|
|
||||||
|
repo := model.Repo{
|
||||||
|
Name: from.Slug,
|
||||||
|
Owner: from.Project.Key,
|
||||||
|
Branch: "master",
|
||||||
|
Kind: model.RepoGit,
|
||||||
|
IsPrivate: true, // Since we have to use Netrc it has to always be private :/
|
||||||
|
FullName: fmt.Sprintf("%s/%s", from.Project.Key, from.Slug),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range from.Links.Clone {
|
||||||
|
if item.Name == "http" {
|
||||||
|
uri, err := url.Parse(item.Href)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
uri.User = nil
|
||||||
|
repo.Clone = uri.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, item := range from.Links.Self {
|
||||||
|
if item.Href != "" {
|
||||||
|
repo.Link = item.Href
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debug(fmt.Printf("Repo: %+v\n", repo))
|
||||||
|
return &repo
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertRepoLite is a helper function used to convert a Bitbucket repository
|
||||||
|
// structure to the simplified Drone repository structure.
|
||||||
|
func convertRepoLite(from *internal.Repo) *model.RepoLite {
|
||||||
|
return &model.RepoLite{
|
||||||
|
Owner: from.Project.Key,
|
||||||
|
Name: from.Slug,
|
||||||
|
FullName: from.Project.Key + "/" + from.Slug,
|
||||||
|
//TODO: find the avatar for the repo
|
||||||
|
//Avatar: might need another ws call?
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertPushHook is a helper function used to convert a Bitbucket push
|
||||||
|
// hook to the Drone build struct holding commit information.
|
||||||
|
func convertPushHook(hook *internal.PostHook, baseURL string) *model.Build {
|
||||||
|
//get the ref parts to see if it's a tags or heads
|
||||||
|
refParts := strings.Split(hook.RefChanges[0].RefID, "/")
|
||||||
|
name := refParts[2]
|
||||||
|
commitType := refParts[1]
|
||||||
|
|
||||||
|
build := &model.Build{
|
||||||
|
Commit: hook.RefChanges[0].ToHash, // TODO check for index value
|
||||||
|
//Link: TODO find link
|
||||||
|
Branch: name,
|
||||||
|
Message: hook.Changesets.Values[0].ToCommit.Message, //TODO check for index Values
|
||||||
|
Avatar: avatarLink(hook.Changesets.Values[0].ToCommit.Author.EmailAddress),
|
||||||
|
Author: fmt.Sprintf("%s <%s>", hook.Changesets.Values[0].ToCommit.Author.Name, hook.Changesets.Values[0].ToCommit.Author.EmailAddress),
|
||||||
|
Email: hook.Changesets.Values[0].ToCommit.Author.EmailAddress,
|
||||||
|
Timestamp: time.Now().UTC().Unix(),
|
||||||
|
Ref: hook.RefChanges[0].RefID, // TODO check for index Values
|
||||||
|
Link: fmt.Sprintf("%s/projects/%s/repos/%s/commits/%s", baseURL, hook.Repository.Project.Key, hook.Repository.Slug, hook.RefChanges[0].ToHash),
|
||||||
|
}
|
||||||
|
switch commitType {
|
||||||
|
case "tags":
|
||||||
|
build.Event = model.EventTag
|
||||||
|
default:
|
||||||
|
build.Event = model.EventPush
|
||||||
|
}
|
||||||
|
|
||||||
|
return build
|
||||||
|
}
|
||||||
|
|
||||||
|
// convertUser is a helper function used to convert a Bitbucket user account
|
||||||
|
// structure to the Drone User structure.
|
||||||
|
func convertUser(from *internal.User, token *oauth.AccessToken) *model.User {
|
||||||
|
return &model.User{
|
||||||
|
Login: from.Slug,
|
||||||
|
Token: token.Token,
|
||||||
|
Email: from.EmailAddress,
|
||||||
|
Avatar: avatarLink(from.EmailAddress),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func avatarLink(email string) string {
|
||||||
|
hasher := md5.New()
|
||||||
|
hasher.Write([]byte(strings.ToLower(email)))
|
||||||
|
emailHash := fmt.Sprintf("%v", hex.EncodeToString(hasher.Sum(nil)))
|
||||||
|
avatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s.jpg", emailHash)
|
||||||
|
log.Debug(avatarURL)
|
||||||
|
return avatarURL
|
||||||
|
}
|
|
@ -1,64 +0,0 @@
|
||||||
package bitbucketserver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
log "github.com/Sirupsen/logrus"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func avatarLink(email string) (url string) {
|
|
||||||
hasher := md5.New()
|
|
||||||
hasher.Write([]byte(strings.ToLower(email)))
|
|
||||||
emailHash := fmt.Sprintf("%v", hex.EncodeToString(hasher.Sum(nil)))
|
|
||||||
avatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s.jpg", emailHash)
|
|
||||||
log.Info(avatarURL)
|
|
||||||
return avatarURL
|
|
||||||
}
|
|
||||||
|
|
||||||
func doPut(client *http.Client, url string, body []byte) {
|
|
||||||
request, err := http.NewRequest("PUT", url, bytes.NewBuffer(body))
|
|
||||||
request.Header.Add("Content-Type", "application/json")
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
} else {
|
|
||||||
defer response.Body.Close()
|
|
||||||
contents, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
fmt.Println("The calculated length is:", len(string(contents)), "for the url:", url)
|
|
||||||
fmt.Println(" ", response.StatusCode)
|
|
||||||
hdr := response.Header
|
|
||||||
for key, value := range hdr {
|
|
||||||
fmt.Println(" ", key, ":", value)
|
|
||||||
}
|
|
||||||
fmt.Println(string(contents))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func doDelete(client *http.Client, url string) {
|
|
||||||
request, err := http.NewRequest("DELETE", url, nil)
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
} else {
|
|
||||||
defer response.Body.Close()
|
|
||||||
contents, err := ioutil.ReadAll(response.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
fmt.Println("The calculated length is:", len(string(contents)), "for the url:", url)
|
|
||||||
fmt.Println(" ", response.StatusCode)
|
|
||||||
hdr := response.Header
|
|
||||||
for key, value := range hdr {
|
|
||||||
fmt.Println(" ", key, ":", value)
|
|
||||||
}
|
|
||||||
fmt.Println(string(contents))
|
|
||||||
}
|
|
||||||
}
|
|
181
remote/bitbucketserver/internal/client.go
Normal file
181
remote/bitbucketserver/internal/client.go
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
log "github.com/Sirupsen/logrus"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/mrjones/oauth"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
currentUserId = "%s/plugins/servlet/applinks/whoami"
|
||||||
|
pathUser = "%s/rest/api/1.0/users/%s"
|
||||||
|
pathRepo = "%s/rest/api/1.0/projects/%s/repos/%s"
|
||||||
|
pathRepos = "%s/rest/api/1.0/repos?limit=%s"
|
||||||
|
pathHook = "%s/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s"
|
||||||
|
pathSource = "%s/projects/%s/repos/%s/browse/%s?at=%s&raw"
|
||||||
|
hookName = "com.atlassian.stash.plugin.stash-web-post-receive-hooks-plugin:postReceiveHook"
|
||||||
|
pathHookEnabled = "%s/rest/api/1.0/projects/%s/repos/%s/settings/hooks/%s/enabled"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
client *http.Client
|
||||||
|
base string
|
||||||
|
accessToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientWithToken(url string, consumer *oauth.Consumer, AccessToken string) *Client {
|
||||||
|
var token oauth.AccessToken
|
||||||
|
token.Token = AccessToken
|
||||||
|
client, err := consumer.MakeHttpClient(&token)
|
||||||
|
log.Debug(fmt.Printf("Create client: %+v %s\n", token, url))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return &Client{client, url, AccessToken}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FindCurrentUser() (*User, error) {
|
||||||
|
CurrentUserIdResponse, err := c.client.Get(fmt.Sprintf(currentUserId, c.base))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer CurrentUserIdResponse.Body.Close()
|
||||||
|
bits, err := ioutil.ReadAll(CurrentUserIdResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
login := string(bits)
|
||||||
|
|
||||||
|
CurrentUserResponse, err := c.client.Get(fmt.Sprintf(pathUser, c.base, login))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer CurrentUserResponse.Body.Close()
|
||||||
|
|
||||||
|
contents, err := ioutil.ReadAll(CurrentUserResponse.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var user User
|
||||||
|
err = json.Unmarshal(contents, &user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &user, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FindRepo(owner string, name string) (*Repo, error) {
|
||||||
|
urlString := fmt.Sprintf(pathRepo, c.base, owner, name)
|
||||||
|
response, err := c.client.Get(urlString)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
contents, err := ioutil.ReadAll(response.Body)
|
||||||
|
repo := Repo{}
|
||||||
|
err = json.Unmarshal(contents, &repo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &repo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FindRepos() ([]*Repo, error) {
|
||||||
|
requestUrl := fmt.Sprintf(pathRepos, c.base, "1000")
|
||||||
|
log.Debug(fmt.Printf("request :%s", requestUrl))
|
||||||
|
response, err := c.client.Get(requestUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
contents, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var repoResponse Repos
|
||||||
|
err = json.Unmarshal(contents, &repoResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(fmt.Printf("repoResponse: %+v\n", repoResponse))
|
||||||
|
|
||||||
|
return repoResponse.Values, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FindRepoPerms(owner string, repo string) (*model.Perm, error) {
|
||||||
|
perms := new(model.Perm)
|
||||||
|
// If you don't have access return none right away
|
||||||
|
_, err := c.FindRepo(owner, repo)
|
||||||
|
if err != nil {
|
||||||
|
return perms, err
|
||||||
|
}
|
||||||
|
// Must have admin to be able to list hooks. If have access the enable perms
|
||||||
|
_, err = c.client.Get(fmt.Sprintf(pathHook, c.base, owner, repo, hookName))
|
||||||
|
if err == nil {
|
||||||
|
perms.Push = true
|
||||||
|
perms.Admin = true
|
||||||
|
}
|
||||||
|
perms.Pull = true
|
||||||
|
log.Debug(fmt.Printf("Perms: %+v\n", perms))
|
||||||
|
return perms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) FindFileForRepo(owner string, repo string, fileName string, ref string) ([]byte, error) {
|
||||||
|
response, err := c.client.Get(fmt.Sprintf(pathSource, c.base, owner, repo, fileName, ref))
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
if response.StatusCode == 404 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
responseBytes, err := ioutil.ReadAll(response.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return responseBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) CreateHook(owner string, name string, callBackLink string) error {
|
||||||
|
// Set hook
|
||||||
|
//TODO: Check existing and add up to 5
|
||||||
|
hookBytes := []byte(fmt.Sprintf(`{"hook-url-0":"%s"}`, callBackLink))
|
||||||
|
return c.doPut(fmt.Sprintf(pathHookEnabled, c.base, owner, name, hookName), hookBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) DeleteHook(owner string, name string, link string) error {
|
||||||
|
//TODO: eventially should only delete the link callback
|
||||||
|
return c.doDelete(fmt.Sprintf(pathHookEnabled, c.base, owner, name, hookName))
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to help create the hook
|
||||||
|
func (c *Client) doPut(url string, body []byte) error {
|
||||||
|
request, err := http.NewRequest("PUT", url, bytes.NewBuffer(body))
|
||||||
|
request.Header.Add("Content-Type", "application/json")
|
||||||
|
response, err := c.client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to do delete on the hook
|
||||||
|
func (c *Client) doDelete(url string) error {
|
||||||
|
request, err := http.NewRequest("DELETE", url, nil)
|
||||||
|
response, err := c.client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,76 @@
|
||||||
package bitbucketserver
|
package internal
|
||||||
|
|
||||||
type postHook struct {
|
type User struct {
|
||||||
|
Active bool `json:"active"`
|
||||||
|
DisplayName string `json:"displayName"`
|
||||||
|
EmailAddress string `json:"emailAddress"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links struct {
|
||||||
|
Self []struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
} `json:"self"`
|
||||||
|
} `json:"links"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repo struct {
|
||||||
|
Forkable bool `json:"forkable"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Links struct {
|
||||||
|
Clone []struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"clone"`
|
||||||
|
Self []struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
} `json:"self"`
|
||||||
|
} `json:"links"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Project struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
Links struct {
|
||||||
|
Self []struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
} `json:"self"`
|
||||||
|
} `json:"links"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Public bool `json:"public"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
} `json:"project"`
|
||||||
|
Public bool `json:"public"`
|
||||||
|
ScmID string `json:"scmId"`
|
||||||
|
Slug string `json:"slug"`
|
||||||
|
State string `json:"state"`
|
||||||
|
StatusMessage string `json:"statusMessage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repos struct {
|
||||||
|
IsLastPage bool `json:"isLastPage"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
Start int `json:"start"`
|
||||||
|
Values []*Repo `json:"values"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Hook struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
Details *HookDetail `json:"details"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HookDetail struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
ConfigFormKey string `json:"configFormKey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostHook struct {
|
||||||
Changesets struct {
|
Changesets struct {
|
||||||
Filter interface{} `json:"filter"`
|
Filter interface{} `json:"filter"`
|
||||||
IsLastPage bool `json:"isLastPage"`
|
IsLastPage bool `json:"isLastPage"`
|
||||||
|
@ -83,90 +153,3 @@ type postHook struct {
|
||||||
StatusMessage string `json:"statusMessage"`
|
StatusMessage string `json:"statusMessage"`
|
||||||
} `json:"repository"`
|
} `json:"repository"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Repos struct {
|
|
||||||
IsLastPage bool `json:"isLastPage"`
|
|
||||||
Limit int `json:"limit"`
|
|
||||||
Size int `json:"size"`
|
|
||||||
Start int `json:"start"`
|
|
||||||
Values []struct {
|
|
||||||
Forkable bool `json:"forkable"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Links struct {
|
|
||||||
Clone []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"clone"`
|
|
||||||
Self []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"self"`
|
|
||||||
} `json:"links"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Project struct {
|
|
||||||
Description string `json:"description"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
Links struct {
|
|
||||||
Self []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"self"`
|
|
||||||
} `json:"links"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Public bool `json:"public"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
} `json:"project"`
|
|
||||||
Public bool `json:"public"`
|
|
||||||
ScmID string `json:"scmId"`
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
State string `json:"state"`
|
|
||||||
StatusMessage string `json:"statusMessage"`
|
|
||||||
} `json:"values"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Active bool `json:"active"`
|
|
||||||
DisplayName string `json:"displayName"`
|
|
||||||
EmailAddress string `json:"emailAddress"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Links struct {
|
|
||||||
Self []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"self"`
|
|
||||||
} `json:"links"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BSRepo struct {
|
|
||||||
Forkable bool `json:"forkable"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Links struct {
|
|
||||||
Clone []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
} `json:"clone"`
|
|
||||||
Self []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"self"`
|
|
||||||
} `json:"links"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Project struct {
|
|
||||||
Description string `json:"description"`
|
|
||||||
ID int `json:"id"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
Links struct {
|
|
||||||
Self []struct {
|
|
||||||
Href string `json:"href"`
|
|
||||||
} `json:"self"`
|
|
||||||
} `json:"links"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Public bool `json:"public"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
} `json:"project"`
|
|
||||||
Public bool `json:"public"`
|
|
||||||
ScmID string `json:"scmId"`
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
State string `json:"state"`
|
|
||||||
StatusMessage string `json:"statusMessage"`
|
|
||||||
}
|
|
28
remote/bitbucketserver/parse.go
Normal file
28
remote/bitbucketserver/parse.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package bitbucketserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/drone/drone/model"
|
||||||
|
"github.com/drone/drone/remote/bitbucketserver/internal"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
// parseHook parses a Bitbucket hook from an http.Request request and returns
|
||||||
|
// Repo and Build detail. TODO: find a way to support PR hooks
|
||||||
|
func parseHook(r *http.Request, baseURL string) (*model.Repo, *model.Build, error) {
|
||||||
|
hook := new(internal.PostHook)
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(hook); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
build := convertPushHook(hook, baseURL)
|
||||||
|
repo := &model.Repo{
|
||||||
|
Name: hook.Repository.Slug,
|
||||||
|
Owner: hook.Repository.Project.Key,
|
||||||
|
FullName: fmt.Sprintf("%s/%s", hook.Repository.Project.Key, hook.Repository.Slug),
|
||||||
|
Branch: "master",
|
||||||
|
Kind: model.RepoGit,
|
||||||
|
}
|
||||||
|
|
||||||
|
return repo, build, nil
|
||||||
|
}
|
Loading…
Reference in a new issue