mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 11:51:02 +00:00
Add Bitbucket integration
This commit is contained in:
parent
070caa3833
commit
8f4f747c86
10 changed files with 426 additions and 13 deletions
|
@ -133,11 +133,18 @@ func setupHandlers() {
|
||||||
|
|
||||||
// handlers for setting up your GitHub repository
|
// handlers for setting up your GitHub repository
|
||||||
m.Post("/new/github.com", handler.UserHandler(handler.RepoCreateGithub))
|
m.Post("/new/github.com", handler.UserHandler(handler.RepoCreateGithub))
|
||||||
m.Get("/new/github.com", handler.UserHandler(handler.RepoAdd))
|
m.Get("/new/github.com", handler.UserHandler(handler.RepoAddGithub))
|
||||||
|
|
||||||
|
// handlers for setting up your Bitbucket repository
|
||||||
|
m.Post("/new/bitbucket.org", handler.UserHandler(handler.RepoCreateBitbucket))
|
||||||
|
m.Get("/new/bitbucket.org", handler.UserHandler(handler.RepoAddBitbucket))
|
||||||
|
|
||||||
// handlers for linking your GitHub account
|
// handlers for linking your GitHub account
|
||||||
m.Get("/auth/login/github", handler.UserHandler(handler.LinkGithub))
|
m.Get("/auth/login/github", handler.UserHandler(handler.LinkGithub))
|
||||||
|
|
||||||
|
// handlers for linking your Bitbucket account
|
||||||
|
m.Get("/auth/login/bitbucket", handler.UserHandler(handler.LinkBitbucket))
|
||||||
|
|
||||||
// handlers for dashboard pages
|
// handlers for dashboard pages
|
||||||
m.Get("/dashboard/team/:team", handler.UserHandler(handler.TeamShow))
|
m.Get("/dashboard/team/:team", handler.UserHandler(handler.TeamShow))
|
||||||
m.Get("/dashboard", handler.UserHandler(handler.UserShow))
|
m.Get("/dashboard", handler.UserHandler(handler.UserShow))
|
||||||
|
@ -176,7 +183,10 @@ func setupHandlers() {
|
||||||
m.Get("/account/admin/users", handler.AdminHandler(handler.AdminUserList))
|
m.Get("/account/admin/users", handler.AdminHandler(handler.AdminUserList))
|
||||||
|
|
||||||
// handlers for GitHub post-commit hooks
|
// handlers for GitHub post-commit hooks
|
||||||
m.Post("/hook/github.com", handler.ErrorHandler(hookHandler.Hook))
|
m.Post("/hook/github.com", handler.ErrorHandler(hookHandler.HookGithub))
|
||||||
|
|
||||||
|
// handlers for Bitbucket post-commit hooks
|
||||||
|
m.Post("/hook/bitbucket.org", handler.ErrorHandler(hookHandler.HookBitbucket))
|
||||||
|
|
||||||
// handlers for first-time installation
|
// handlers for first-time installation
|
||||||
m.Get("/install", handler.ErrorHandler(handler.Install))
|
m.Get("/install", handler.ErrorHandler(handler.Install))
|
||||||
|
|
|
@ -8,6 +8,8 @@ import (
|
||||||
. "github.com/drone/drone/pkg/model"
|
. "github.com/drone/drone/pkg/model"
|
||||||
"github.com/drone/go-github/github"
|
"github.com/drone/go-github/github"
|
||||||
"github.com/drone/go-github/oauth2"
|
"github.com/drone/go-github/oauth2"
|
||||||
|
"github.com/drone/go-bitbucket/bitbucket"
|
||||||
|
"github.com/drone/go-bitbucket/oauth1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create the User session.
|
// Create the User session.
|
||||||
|
@ -94,3 +96,78 @@ func LinkGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
http.Redirect(w, r, "/new/github.com", http.StatusSeeOther)
|
http.Redirect(w, r, "/new/github.com", http.StatusSeeOther)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LinkBitbucket(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
|
|
||||||
|
// get settings from database
|
||||||
|
settings := database.SettingsMust()
|
||||||
|
|
||||||
|
// bitbucket oauth1 consumer
|
||||||
|
var consumer = oauth1.Consumer{
|
||||||
|
RequestTokenURL: "https://bitbucket.org/api/1.0/oauth/request_token/",
|
||||||
|
AuthorizationURL: "https://bitbucket.org/!api/1.0/oauth/authenticate",
|
||||||
|
AccessTokenURL: "https://bitbucket.org/api/1.0/oauth/access_token/",
|
||||||
|
CallbackURL: settings.URL().String() + "/auth/login/bitbucket",
|
||||||
|
ConsumerKey: settings.BitbucketKey,
|
||||||
|
ConsumerSecret: settings.BitbucketSecret,
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the oauth verifier
|
||||||
|
verifier := r.FormValue("oauth_verifier")
|
||||||
|
if len(verifier) == 0 {
|
||||||
|
// Generate a Request Token
|
||||||
|
requestToken, err := consumer.RequestToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the request token as a signed cookie
|
||||||
|
SetCookie(w, r, "bitbucket_token", requestToken.Encode())
|
||||||
|
|
||||||
|
url, _ := consumer.AuthorizeRedirect(requestToken)
|
||||||
|
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove bitbucket token data once before redirecting
|
||||||
|
// back to the application.
|
||||||
|
defer DelCookie(w, r, "bitbucket_token")
|
||||||
|
|
||||||
|
// get the tokens from the request
|
||||||
|
requestTokenStr := GetCookie(r, "bitbucket_token")
|
||||||
|
requestToken, err := oauth1.ParseRequestTokenStr(requestTokenStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// exchange for an access token
|
||||||
|
accessToken, err := consumer.AuthorizeToken(requestToken, verifier)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the Bitbucket client
|
||||||
|
client := bitbucket.New(
|
||||||
|
settings.BitbucketKey,
|
||||||
|
settings.BitbucketSecret,
|
||||||
|
accessToken.Token(),
|
||||||
|
accessToken.Secret(),
|
||||||
|
)
|
||||||
|
|
||||||
|
// get the currently authenticated Bitbucket User
|
||||||
|
user, err := client.Users.Current()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the user account
|
||||||
|
u.BitbucketLogin = user.User.Username
|
||||||
|
u.BitbucketToken = accessToken.Token()
|
||||||
|
u.BitbucketSecret = accessToken.Secret()
|
||||||
|
if err := database.SaveUser(u); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
http.Redirect(w, r, "/new/bitbucket.org", http.StatusSeeOther)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
. "github.com/drone/drone/pkg/model"
|
. "github.com/drone/drone/pkg/model"
|
||||||
"github.com/drone/drone/pkg/queue"
|
"github.com/drone/drone/pkg/queue"
|
||||||
"github.com/drone/go-github/github"
|
"github.com/drone/go-github/github"
|
||||||
|
"github.com/drone/go-bitbucket/bitbucket"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HookHandler struct {
|
type HookHandler struct {
|
||||||
|
@ -23,9 +24,9 @@ func NewHookHandler(queue *queue.Queue) *HookHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes a generic POST-RECEIVE hook and
|
// Processes a generic POST-RECEIVE GitHub hook and
|
||||||
// attempts to trigger a build.
|
// attempts to trigger a build.
|
||||||
func (h *HookHandler) Hook(w http.ResponseWriter, r *http.Request) error {
|
func (h *HookHandler) HookGithub(w http.ResponseWriter, r *http.Request) error {
|
||||||
// handle github ping
|
// handle github ping
|
||||||
if r.Header.Get("X-Github-Event") == "ping" {
|
if r.Header.Get("X-Github-Event") == "ping" {
|
||||||
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
|
@ -34,7 +35,7 @@ func (h *HookHandler) Hook(w http.ResponseWriter, r *http.Request) error {
|
||||||
// if this is a pull request route
|
// if this is a pull request route
|
||||||
// to a different handler
|
// to a different handler
|
||||||
if r.Header.Get("X-Github-Event") == "pull_request" {
|
if r.Header.Get("X-Github-Event") == "pull_request" {
|
||||||
h.PullRequestHook(w, r)
|
h.PullRequestHookGithub(w, r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +176,7 @@ func (h *HookHandler) Hook(w http.ResponseWriter, r *http.Request) error {
|
||||||
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HookHandler) PullRequestHook(w http.ResponseWriter, r *http.Request) {
|
func (h *HookHandler) PullRequestHookGithub(w http.ResponseWriter, r *http.Request) {
|
||||||
// get the payload of the message
|
// get the payload of the message
|
||||||
// this should contain a json representation of the
|
// this should contain a json representation of the
|
||||||
// repository and commit details
|
// repository and commit details
|
||||||
|
@ -291,6 +292,99 @@ func (h *HookHandler) PullRequestHook(w http.ResponseWriter, r *http.Request) {
|
||||||
RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Processes a generic POST-RECEIVE Bitbucket hook and
|
||||||
|
// attempts to trigger a build.
|
||||||
|
func (h *HookHandler) HookBitbucket(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
// get the payload from the request
|
||||||
|
payload := r.FormValue("payload")
|
||||||
|
|
||||||
|
// parse the post-commit hook
|
||||||
|
hook, err := bitbucket.ParseHook([]byte(payload))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the repo from the URL
|
||||||
|
repoId := r.FormValue("id")
|
||||||
|
|
||||||
|
// get the repo from the database, return error if not found
|
||||||
|
repo, err := database.GetRepoSlug(repoId)
|
||||||
|
if err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user that owns the repository
|
||||||
|
user, err := database.GetUser(repo.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that the commit doesn't already exist.
|
||||||
|
// We should never build the same commit twice.
|
||||||
|
_, err = database.GetCommitHash(hook.Commits[len(hook.Commits)-1].Hash, repo.ID)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||||
|
}
|
||||||
|
|
||||||
|
commit := &Commit{}
|
||||||
|
commit.RepoID = repo.ID
|
||||||
|
commit.Branch = hook.Commits[len(hook.Commits)-1].Branch
|
||||||
|
commit.Hash = hook.Commits[len(hook.Commits)-1].Hash
|
||||||
|
commit.Status = "Pending"
|
||||||
|
commit.Created = time.Now().UTC()
|
||||||
|
commit.Message = hook.Commits[len(hook.Commits)-1].Message
|
||||||
|
commit.Timestamp = time.Now().UTC().String()
|
||||||
|
commit.SetAuthor(hook.Commits[len(hook.Commits)-1].Author)
|
||||||
|
|
||||||
|
// get the github settings from the database
|
||||||
|
settings := database.SettingsMust()
|
||||||
|
|
||||||
|
// create the Bitbucket client
|
||||||
|
client := bitbucket.New(
|
||||||
|
settings.BitbucketKey,
|
||||||
|
settings.BitbucketSecret,
|
||||||
|
user.BitbucketToken,
|
||||||
|
user.BitbucketSecret,
|
||||||
|
)
|
||||||
|
|
||||||
|
// get the yaml from the database
|
||||||
|
raw, err := client.Sources.Find(repo.Owner, repo.Name, commit.Hash, ".drone.yml")
|
||||||
|
if err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse the build script
|
||||||
|
buildscript, err := script.ParseBuild([]byte(raw.Data), repo.Params)
|
||||||
|
if err != nil {
|
||||||
|
msg := "Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
|
||||||
|
if err := saveFailedBuild(commit, msg); err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the commit to the database
|
||||||
|
if err := database.SaveCommit(commit); err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the build to the database
|
||||||
|
build := &Build{}
|
||||||
|
build.Slug = "1" // TODO
|
||||||
|
build.CommitID = commit.ID
|
||||||
|
build.Created = time.Now().UTC()
|
||||||
|
build.Status = "Pending"
|
||||||
|
if err := database.SaveBuild(build); err != nil {
|
||||||
|
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the build to the queue
|
||||||
|
h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})
|
||||||
|
|
||||||
|
// OK!
|
||||||
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// Helper method for saving a failed build or commit in the case where it never starts to build.
|
// Helper method for saving a failed build or commit in the case where it never starts to build.
|
||||||
// This can happen if the yaml is bad or doesn't exist.
|
// This can happen if the yaml is bad or doesn't exist.
|
||||||
func saveFailedBuild(commit *Commit, msg string) error {
|
func saveFailedBuild(commit *Commit, msg string) error {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/drone/drone/pkg/database"
|
"github.com/drone/drone/pkg/database"
|
||||||
. "github.com/drone/drone/pkg/model"
|
. "github.com/drone/drone/pkg/model"
|
||||||
"github.com/drone/go-github/github"
|
"github.com/drone/go-github/github"
|
||||||
|
"github.com/drone/go-bitbucket/bitbucket"
|
||||||
|
|
||||||
"launchpad.net/goyaml"
|
"launchpad.net/goyaml"
|
||||||
)
|
)
|
||||||
|
@ -52,7 +53,7 @@ func RepoDashboard(w http.ResponseWriter, r *http.Request, u *User, repo *Repo)
|
||||||
return RenderTemplate(w, "repo_dashboard.html", &data)
|
return RenderTemplate(w, "repo_dashboard.html", &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RepoAdd(w http.ResponseWriter, r *http.Request, u *User) error {
|
func RepoAddGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
settings := database.SettingsMust()
|
settings := database.SettingsMust()
|
||||||
teams, err := database.ListTeams(u.ID)
|
teams, err := database.ListTeams(u.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -73,6 +74,27 @@ func RepoAdd(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
return RenderTemplate(w, "github_add.html", &data)
|
return RenderTemplate(w, "github_add.html", &data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RepoAddBitbucket(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
|
settings := database.SettingsMust()
|
||||||
|
teams, err := database.ListTeams(u.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := struct {
|
||||||
|
User *User
|
||||||
|
Teams []*Team
|
||||||
|
Settings *Settings
|
||||||
|
}{u, teams, settings}
|
||||||
|
// if the user hasn't linked their Bitbucket account
|
||||||
|
// render a different template
|
||||||
|
if len(u.BitbucketToken) == 0 {
|
||||||
|
return RenderTemplate(w, "bitbucket_link.html", &data)
|
||||||
|
}
|
||||||
|
// otherwise display the template for adding
|
||||||
|
// a new Bitbucket repository.
|
||||||
|
return RenderTemplate(w, "bitbucket_add.html", &data)
|
||||||
|
}
|
||||||
|
|
||||||
func RepoCreateGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
func RepoCreateGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
teamName := r.FormValue("team")
|
teamName := r.FormValue("team")
|
||||||
owner := r.FormValue("owner")
|
owner := r.FormValue("owner")
|
||||||
|
@ -146,6 +168,84 @@ func RepoCreateGithub(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RepoCreateBitbucket(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||||
|
teamName := r.FormValue("team")
|
||||||
|
owner := r.FormValue("owner")
|
||||||
|
name := r.FormValue("name")
|
||||||
|
|
||||||
|
// get the bitbucket settings from the database
|
||||||
|
settings := database.SettingsMust()
|
||||||
|
|
||||||
|
// create the Bitbucket client
|
||||||
|
client := bitbucket.New(
|
||||||
|
settings.BitbucketKey,
|
||||||
|
settings.BitbucketSecret,
|
||||||
|
u.BitbucketToken,
|
||||||
|
u.BitbucketSecret,
|
||||||
|
)
|
||||||
|
|
||||||
|
bitbucketRepo, err := client.Repos.Find(owner, name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find Bitbucket repository %s/%s.", owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo, err := NewBitbucketRepo(owner, name, bitbucketRepo.Private)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.UserID = u.ID
|
||||||
|
repo.Private = bitbucketRepo.Private
|
||||||
|
|
||||||
|
// if the user chose to assign to a team account
|
||||||
|
// we need to retrieve the team, verify the user
|
||||||
|
// has access, and then set the team id.
|
||||||
|
if len(teamName) > 0 {
|
||||||
|
team, err := database.GetTeamSlug(teamName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to find Team %s.", teamName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// user must be an admin member of the team
|
||||||
|
if ok, _ := database.IsMemberAdmin(u.ID, team.ID); !ok {
|
||||||
|
return fmt.Errorf("Invalid permission to access Team %s.", teamName)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo.TeamID = team.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the repository is private we'll need
|
||||||
|
// to upload a bitbucket key to the repository
|
||||||
|
if repo.Private {
|
||||||
|
// name the key
|
||||||
|
keyName := fmt.Sprintf("%s@%s", repo.Owner, settings.Domain)
|
||||||
|
|
||||||
|
// create the bitbucket key, or update if one already exists
|
||||||
|
_, err := client.RepoKeys.CreateUpdate(owner, name, repo.PublicKey, keyName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Unable to add Public Key to your Bitbucket repository: %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a hook so that we get notified when code
|
||||||
|
// is pushed to the repository and can execute a build.
|
||||||
|
link := fmt.Sprintf("%s://%s/hook/bitbucket.org?id=%s", settings.Scheme, settings.Domain, repo.Slug)
|
||||||
|
|
||||||
|
// add the hook
|
||||||
|
if _, err := client.Brokers.CreateUpdate(owner, name, link, bitbucket.BrokerTypePost); err != nil {
|
||||||
|
return fmt.Errorf("Unable to add Hook to your Bitbucket repository. %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save to the database
|
||||||
|
if err := database.SaveRepo(repo); err != nil {
|
||||||
|
return fmt.Errorf("Error saving repository to the database. %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// Repository Settings
|
// Repository Settings
|
||||||
func RepoSettingsForm(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
|
func RepoSettingsForm(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
|
||||||
|
|
||||||
|
|
|
@ -50,12 +50,12 @@
|
||||||
<input class="form-control form-control-large" type="text" name="GitHubApiUrl" value="{{.Settings.GitHubApiUrl}}" />
|
<input class="form-control form-control-large" type="text" name="GitHubApiUrl" value="{{.Settings.GitHubApiUrl}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group hide">
|
<div class="form-group">
|
||||||
<div class="alert">Bitbucket OAuth Consumer Key and Secret.</div>
|
<div class="alert">Bitbucket OAuth Consumer Key and Secret.</div>
|
||||||
<label>Bitbucket Key and Secret:</label>
|
<label>Bitbucket Key and Secret:</label>
|
||||||
<div>
|
<div>
|
||||||
<input class="form-control form-control-large" type="text" name="BitbucketKey" value="" />
|
<input class="form-control form-control-large" type="text" name="BitbucketKey" value="{{.Settings.BitbucketKey}}" />
|
||||||
<input class="form-control form-control-large" type="password" name="BitbucketSecret" value="" />
|
<input class="form-control form-control-large" type="password" name="BitbucketSecret" value="{{.Settings.BitbucketSecret}}" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -119,4 +119,4 @@
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
99
pkg/template/pages/bitbucket_add.html
Normal file
99
pkg/template/pages/bitbucket_add.html
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
{{ define "title" }}Bitbucket · Add Repository{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="subhead">
|
||||||
|
<div class="container">
|
||||||
|
<h1>
|
||||||
|
<span>Repository Setup</span>
|
||||||
|
<small>Bitbucket</small>
|
||||||
|
</h1>
|
||||||
|
</div><!-- ./container -->
|
||||||
|
</div><!-- ./subhead -->
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<ul class="nav nav-pills nav-stacked">
|
||||||
|
<li><a href="/new/github.com">GitHub</a></li>
|
||||||
|
<li class="active"><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||||
|
</ul>
|
||||||
|
</div><!-- ./col-xs-3 -->
|
||||||
|
|
||||||
|
<div class="col-xs-9" role="main">
|
||||||
|
<div class="alert">
|
||||||
|
Enter your repository details
|
||||||
|
<a class="btn btn-default pull-right" href="/auth/login/bitbucket" style="font-size: 18px;background:#f4f4f4;">Re-Link Account</a>
|
||||||
|
</div>
|
||||||
|
<form class="form-repo" method="POST" action="/new/bitbucket.org">
|
||||||
|
<div class="field-group">
|
||||||
|
<div>
|
||||||
|
<label>Bitbucket Owner</label>
|
||||||
|
<div>
|
||||||
|
<input class="form-control form-control-large" type="text" name="owner" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field-separator">/</div>
|
||||||
|
<div class="field-group">
|
||||||
|
<div>
|
||||||
|
<label>Repository Name</label>
|
||||||
|
<div>
|
||||||
|
<input class="form-control form-control-large" type="text" name="name" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br/>
|
||||||
|
<div class="alert">Select your Drone account</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="team" checked="True" value="">
|
||||||
|
<img src="{{ .User.Image }}?s=32">
|
||||||
|
<span>Me</span>
|
||||||
|
</li>
|
||||||
|
{{ range .Teams }}
|
||||||
|
<li>
|
||||||
|
<input type="radio" name="team" value="{{ .Slug }}">
|
||||||
|
<img src="{{ .Image }}?s=32">
|
||||||
|
<span>{{ .Name }}</span>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
<div class="alert alert-success hide" id="successAlert"></div>
|
||||||
|
<div class="alert alert-error hide" id="failureAlert"></div>
|
||||||
|
<div class="form-actions">
|
||||||
|
<input class="btn btn-primary" id="submitButton" type="submit" value="Add" data-loading-text="Saving ..">
|
||||||
|
<a class="btn btn-default" href="/dashboard">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div><!-- ./col-xs-9 -->
|
||||||
|
</div><!-- ./row -->
|
||||||
|
</div><!-- ./container -->
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "script" }}
|
||||||
|
<script>
|
||||||
|
document.forms[0].onsubmit = function(event) {
|
||||||
|
$("#successAlert").hide();
|
||||||
|
$("#failureAlert").hide();
|
||||||
|
$('#submitButton').button('loading')
|
||||||
|
|
||||||
|
var form = event.target
|
||||||
|
var formData = new FormData(form);
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', form.action);
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (this.status == 200) {
|
||||||
|
var name = $("input[name=name]").val()
|
||||||
|
var owner = $("input[name=owner]").val()
|
||||||
|
window.location.pathname = "/bitbucket.org/"+owner+"/"+name
|
||||||
|
} else {
|
||||||
|
$("#failureAlert").text("Unable to setup the Repository");
|
||||||
|
$("#failureAlert").show().removeClass("hide");
|
||||||
|
$('#submitButton').button('reset')
|
||||||
|
};
|
||||||
|
};
|
||||||
|
xhr.send(formData);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
31
pkg/template/pages/bitbucket_link.html
Normal file
31
pkg/template/pages/bitbucket_link.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{{ define "title" }}Bitbucket · Add Repository{{ end }}
|
||||||
|
|
||||||
|
{{ define "content" }}
|
||||||
|
<div class="subhead">
|
||||||
|
<div class="container">
|
||||||
|
<h1>
|
||||||
|
<span>Repository Setup</span>
|
||||||
|
<small>Bitbucket</small>
|
||||||
|
</h1>
|
||||||
|
</div><!-- ./container -->
|
||||||
|
</div><!-- ./subhead -->
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xs-3">
|
||||||
|
<ul class="nav nav-pills nav-stacked">
|
||||||
|
<li><a href="/new/github.com">GitHub</a></li>
|
||||||
|
<li class="active"><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||||
|
</ul>
|
||||||
|
</div><!-- ./col-xs-3 -->
|
||||||
|
|
||||||
|
<div class="col-xs-9" role="main">
|
||||||
|
<div class="alert">Link Your Bitbucket Account
|
||||||
|
<a class="btn btn-primary pull-right" href="/auth/login/bitbucket" style="font-size: 18px;">Link Now</a>
|
||||||
|
</div>
|
||||||
|
</div><!-- ./col-xs-9 -->
|
||||||
|
</div><!-- ./row -->
|
||||||
|
</div><!-- ./container -->
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ define "script" }}{{ end }}
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
||||||
<li><a href="/new/bitbucket.org">Bitbucket <small>(coming soon)</small></a></li>
|
<li><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- ./col-xs-3 -->
|
</div><!-- ./col-xs-3 -->
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
<ul class="nav nav-pills nav-stacked">
|
<ul class="nav nav-pills nav-stacked">
|
||||||
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
||||||
<li><a href="/new/bitbucket.org">Bitbucket <small>(coming soon)</small></a></li>
|
<li><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- ./col-xs-3 -->
|
</div><!-- ./col-xs-3 -->
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,8 @@ func init() {
|
||||||
"admin_settings.html",
|
"admin_settings.html",
|
||||||
"github_add.html",
|
"github_add.html",
|
||||||
"github_link.html",
|
"github_link.html",
|
||||||
|
"bitbucket_add.html",
|
||||||
|
"bitbucket_link.html",
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the base template as a string
|
// extract the base template as a string
|
||||||
|
|
Loading…
Reference in a new issue