Merge pull request #237 from fudanchii/gitlab
Gitlab integration support
This commit is contained in:
commit
0c9a765956
23 changed files with 937 additions and 35 deletions
6
Makefile
6
Makefile
|
@ -22,7 +22,7 @@ PKGS := $(addprefix github.com/drone/drone/pkg/,$(PKGS))
|
|||
|
||||
all: embed build
|
||||
|
||||
deps:
|
||||
deps: go-gitlab-client
|
||||
[ -d $$GOPATH/src/code.google.com/p/go ] || hg clone -u default https://code.google.com/p/go $$GOPATH/src/code.google.com/p/go
|
||||
[ -d $$GOPATH/src/github.com/dotcloud/docker ] || git clone git://github.com/dotcloud/docker $$GOPATH/src/github.com/dotcloud/docker
|
||||
go get code.google.com/p/go.crypto/bcrypt
|
||||
|
@ -97,3 +97,7 @@ dpkg:
|
|||
|
||||
run:
|
||||
bin/droned --port=":8080" --datasource="drone.sqlite"
|
||||
|
||||
go-gitlab-client:
|
||||
rm -rf $$GOPATH/src/github.com/plouc/go-gitlab-client
|
||||
git clone -b raw-request https://github.com/fudanchii/go-gitlab-client $$GOPATH/src/github.com/plouc/go-gitlab-client
|
||||
|
|
|
@ -133,6 +133,7 @@ func setupHandlers() {
|
|||
queue := queue.Start(workers, queueRunner)
|
||||
|
||||
hookHandler := handler.NewHookHandler(queue)
|
||||
gitlab := handler.NewGitlabHandler(queue)
|
||||
|
||||
m := pat.New()
|
||||
m.Get("/login", handler.ErrorHandler(handler.Login))
|
||||
|
@ -152,16 +153,24 @@ func setupHandlers() {
|
|||
m.Post("/new/github.com", handler.UserHandler(handler.RepoCreateGithub))
|
||||
m.Get("/new/github.com", handler.UserHandler(handler.RepoAddGithub))
|
||||
|
||||
// handlers for linking your GitHub account
|
||||
m.Get("/auth/login/github", handler.UserHandler(handler.LinkGithub))
|
||||
|
||||
// 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
|
||||
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 setting up your GitLab repository
|
||||
m.Post("/new/gitlab", handler.UserHandler(gitlab.Create))
|
||||
m.Get("/new/gitlab", handler.UserHandler(gitlab.Add))
|
||||
|
||||
// handler for linking GitLab account
|
||||
m.Post("/link/gitlab", handler.UserHandler(gitlab.Link))
|
||||
m.Get("/link/gitlab", handler.UserHandler(gitlab.ReLink))
|
||||
|
||||
// handlers for dashboard pages
|
||||
m.Get("/dashboard/team/:team", handler.UserHandler(handler.TeamShow))
|
||||
m.Get("/dashboard", handler.UserHandler(handler.UserShow))
|
||||
|
@ -205,6 +214,9 @@ func setupHandlers() {
|
|||
// handlers for Bitbucket post-commit hooks
|
||||
m.Post("/hook/bitbucket.org", handler.ErrorHandler(hookHandler.HookBitbucket))
|
||||
|
||||
// handlers for GitLab post-commit hooks
|
||||
m.Post("/hook/gitlab", handler.ErrorHandler(gitlab.Hook))
|
||||
|
||||
// handlers for first-time installation
|
||||
m.Get("/install", handler.ErrorHandler(handler.Install))
|
||||
m.Post("/install", handler.ErrorHandler(handler.InstallPost))
|
||||
|
|
43
pkg/database/migrate/20140328201430_add_gitlab_columns.go
Normal file
43
pkg/database/migrate/20140328201430_add_gitlab_columns.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package migrate
|
||||
|
||||
type rev20140328201430 struct{}
|
||||
|
||||
var AddGitlabColumns = &rev20140328201430{}
|
||||
|
||||
func (r *rev20140328201430) Revision() int64 {
|
||||
return 20140328201430
|
||||
}
|
||||
|
||||
func (r *rev20140328201430) Up(mg *MigrationDriver) error {
|
||||
// Migration steps here
|
||||
if _, err := mg.AddColumn("settings", mg.T.String("gitlab_domain")); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := mg.AddColumn("settings", mg.T.String("gitlab_apiurl")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := mg.Tx.Exec(`update settings set gitlab_domain=?`, "gitlab.com"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := mg.Tx.Exec(`update settings set gitlab_apiurl=?`, "https://gitlab.com"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := mg.AddColumn("users", mg.T.String("gitlab_token")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := mg.Tx.Exec(`update users set gitlab_token=?`, "")
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *rev20140328201430) Down(mg *MigrationDriver) error {
|
||||
// Revert migration steps here
|
||||
if _, err := mg.DropColumns("users", "gitlab_token"); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := mg.DropColumns("settings", "gitlab_domain", "gitlab_apiurl")
|
||||
return err
|
||||
}
|
|
@ -12,6 +12,7 @@ func (m *Migration) All() *Migration {
|
|||
m.Add(RenamePrivelegedToPrivileged)
|
||||
m.Add(GitHubEnterpriseSupport)
|
||||
m.Add(AddOpenInvitationColumn)
|
||||
m.Add(AddGitlabColumns)
|
||||
|
||||
// m.Add(...)
|
||||
// ...
|
||||
|
|
|
@ -11,8 +11,8 @@ const settingsTable = "settings"
|
|||
// SQL Queries to retrieve the system settings
|
||||
const settingsStmt = `
|
||||
SELECT id, github_key, github_secret, github_domain, github_apiurl, bitbucket_key, bitbucket_secret,
|
||||
smtp_server, smtp_port, smtp_address, smtp_username, smtp_password, hostname, scheme, open_invitations
|
||||
FROM settings WHERE id = 1
|
||||
gitlab_domain, gitlab_apiurl, smtp_server, smtp_port, smtp_address, smtp_username, smtp_password,
|
||||
hostname, scheme, open_invitations FROM settings WHERE id = 1
|
||||
`
|
||||
|
||||
//var (
|
||||
|
|
|
@ -32,26 +32,29 @@ func Setup() {
|
|||
|
||||
// create dummy user data
|
||||
user1 := User{
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Brad Rydzewski",
|
||||
Email: "brad.rydzewski@gmail.com",
|
||||
Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87",
|
||||
Token: "123",
|
||||
Admin: true}
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Brad Rydzewski",
|
||||
Email: "brad.rydzewski@gmail.com",
|
||||
Gravatar: "8c58a0be77ee441bb8f8595b7f1b4e87",
|
||||
Token: "123",
|
||||
GitlabToken: "123",
|
||||
Admin: true}
|
||||
user2 := User{
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Thomas Burke",
|
||||
Email: "cavepig@gmail.com",
|
||||
Gravatar: "c62f7126273f7fa786274274a5dec8ce",
|
||||
Token: "456",
|
||||
Admin: false}
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Thomas Burke",
|
||||
Email: "cavepig@gmail.com",
|
||||
Gravatar: "c62f7126273f7fa786274274a5dec8ce",
|
||||
Token: "456",
|
||||
GitlabToken: "456",
|
||||
Admin: false}
|
||||
user3 := User{
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Carlos Morales",
|
||||
Email: "ytsejammer@gmail.com",
|
||||
Gravatar: "c2180a539620d90d68eaeb848364f1c2",
|
||||
Token: "789",
|
||||
Admin: false}
|
||||
Password: "$2a$10$b8d63QsTL38vx7lj0HEHfOdbu1PCAg6Gfca74UavkXooIBx9YxopS",
|
||||
Name: "Carlos Morales",
|
||||
Email: "ytsejammer@gmail.com",
|
||||
Gravatar: "c2180a539620d90d68eaeb848364f1c2",
|
||||
Token: "789",
|
||||
GitlabToken: "789",
|
||||
Admin: false}
|
||||
|
||||
database.SaveUser(&user1)
|
||||
database.SaveUser(&user2)
|
||||
|
|
|
@ -13,21 +13,24 @@ const userTable = "users"
|
|||
// SQL Queries to retrieve a user by their unique database key
|
||||
const userFindIdStmt = `
|
||||
SELECT id, email, password, token, name, gravatar, created, updated, admin,
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret,
|
||||
gitlab_token
|
||||
FROM users WHERE id = ?
|
||||
`
|
||||
|
||||
// SQL Queries to retrieve a user by their email address
|
||||
const userFindEmailStmt = `
|
||||
SELECT id, email, password, token, name, gravatar, created, updated, admin,
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret,
|
||||
gitlab_token
|
||||
FROM users WHERE email = ?
|
||||
`
|
||||
|
||||
// SQL Queries to retrieve a list of all users
|
||||
const userStmt = `
|
||||
SELECT id, email, password, token, name, gravatar, created, updated, admin,
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret
|
||||
github_login, github_token, bitbucket_login, bitbucket_token, bitbucket_secret,
|
||||
gitlab_token
|
||||
FROM users
|
||||
ORDER BY name ASC
|
||||
`
|
||||
|
|
|
@ -3,6 +3,7 @@ package handler
|
|||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -170,6 +171,14 @@ func AdminSettingsUpdate(w http.ResponseWriter, r *http.Request, u *User) error
|
|||
settings.GitHubDomain = r.FormValue("GitHubDomain")
|
||||
settings.GitHubApiUrl = r.FormValue("GitHubApiUrl")
|
||||
|
||||
// update gitlab settings
|
||||
settings.GitlabApiUrl = r.FormValue("GitlabApiUrl")
|
||||
glUrl, err := url.Parse(settings.GitlabApiUrl)
|
||||
if err != nil {
|
||||
return RenderError(w, err, http.StatusBadRequest)
|
||||
}
|
||||
settings.GitlabDomain = glUrl.Host
|
||||
|
||||
// update smtp settings
|
||||
settings.SmtpServer = r.FormValue("SmtpServer")
|
||||
settings.SmtpPort = r.FormValue("SmtpPort")
|
||||
|
@ -252,6 +261,8 @@ func InstallPost(w http.ResponseWriter, r *http.Request) error {
|
|||
settings.Scheme = r.FormValue("Scheme")
|
||||
settings.GitHubApiUrl = "https://api.github.com"
|
||||
settings.GitHubDomain = "github.com"
|
||||
settings.GitlabApiUrl = "https://gitlab.com"
|
||||
settings.GitlabDomain = "gitlab.com"
|
||||
database.SaveSettings(&settings)
|
||||
|
||||
// add the user to the session object
|
||||
|
|
|
@ -6,10 +6,10 @@ import (
|
|||
|
||||
"github.com/drone/drone/pkg/database"
|
||||
. "github.com/drone/drone/pkg/model"
|
||||
"github.com/drone/go-github/github"
|
||||
"github.com/drone/go-github/oauth2"
|
||||
"github.com/drone/go-bitbucket/bitbucket"
|
||||
"github.com/drone/go-bitbucket/oauth1"
|
||||
"github.com/drone/go-github/github"
|
||||
"github.com/drone/go-github/oauth2"
|
||||
)
|
||||
|
||||
// Create the User session.
|
||||
|
|
333
pkg/handler/gitlab.go
Normal file
333
pkg/handler/gitlab.go
Normal file
|
@ -0,0 +1,333 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/pkg/build/script"
|
||||
"github.com/drone/drone/pkg/database"
|
||||
. "github.com/drone/drone/pkg/model"
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
"github.com/plouc/go-gitlab-client"
|
||||
)
|
||||
|
||||
type GitlabHandler struct {
|
||||
queue *queue.Queue
|
||||
apiPath string
|
||||
}
|
||||
|
||||
func NewGitlabHandler(queue *queue.Queue) *GitlabHandler {
|
||||
return &GitlabHandler{
|
||||
queue: queue,
|
||||
apiPath: "/api/v3",
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) Add(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 GitLab account
|
||||
// render a different template
|
||||
if len(u.GitlabToken) == 0 {
|
||||
return RenderTemplate(w, "gitlab_link.html", &data)
|
||||
}
|
||||
// otherwise display the template for adding
|
||||
// a new GitLab repository.
|
||||
return RenderTemplate(w, "gitlab_add.html", &data)
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) Link(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||
token := strings.TrimSpace(r.FormValue("token"))
|
||||
|
||||
if len(u.GitlabToken) == 0 || token != u.GitlabToken && len(token) > 0 {
|
||||
u.GitlabToken = token
|
||||
settings := database.SettingsMust()
|
||||
gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)
|
||||
_, err := gl.CurrentUser()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Private Token is not valid: %q", err)
|
||||
}
|
||||
if err := database.SaveUser(u); err != nil {
|
||||
return RenderError(w, err, http.StatusBadRequest)
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/new/gitlab", http.StatusSeeOther)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) ReLink(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||
data := struct {
|
||||
User *User
|
||||
}{u}
|
||||
return RenderTemplate(w, "gitlab_link.html", &data)
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) Create(w http.ResponseWriter, r *http.Request, u *User) error {
|
||||
teamName := r.FormValue("team")
|
||||
owner := r.FormValue("owner")
|
||||
name := r.FormValue("name")
|
||||
|
||||
repo, err := g.newGitlabRepo(u, owner, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) newGitlabRepo(u *User, owner, name string) (*Repo, error) {
|
||||
settings := database.SettingsMust()
|
||||
gl := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, u.GitlabToken)
|
||||
|
||||
project, err := gl.Project(ns(owner, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var cloneUrl string
|
||||
if project.Public {
|
||||
cloneUrl = project.HttpRepoUrl
|
||||
} else {
|
||||
cloneUrl = project.SshRepoUrl
|
||||
}
|
||||
|
||||
repo, err := NewRepo(settings.GitlabDomain, owner, name, ScmGit, cloneUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo.UserID = u.ID
|
||||
repo.Private = !project.Public
|
||||
if repo.Private {
|
||||
// name the key
|
||||
keyName := fmt.Sprintf("%s@%s", repo.Owner, settings.Domain)
|
||||
|
||||
// TODO: (fudanchii) check if we already opted to use UserKey
|
||||
|
||||
// create the github key, or update if one already exists
|
||||
if err := gl.AddProjectDeployKey(ns(owner, name), keyName, repo.PublicKey); err != nil {
|
||||
return nil, fmt.Errorf("Unable to add Public Key to your GitLab repository.")
|
||||
}
|
||||
}
|
||||
|
||||
link := fmt.Sprintf("%s://%s/hook/gitlab?id=%s", settings.Scheme, settings.Domain, repo.Slug)
|
||||
if err := gl.AddProjectHook(ns(owner, name), link, true, false, true); err != nil {
|
||||
return nil, fmt.Errorf("Unable to add Hook to your GitLab repository.")
|
||||
}
|
||||
|
||||
return repo, err
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
|
||||
rID := r.FormValue("id")
|
||||
repo, err := database.GetRepoSlug(rID)
|
||||
if err != nil {
|
||||
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
}
|
||||
|
||||
user, err := database.GetUser(repo.UserID)
|
||||
if err != nil {
|
||||
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
}
|
||||
|
||||
payload, _ := ioutil.ReadAll(r.Body)
|
||||
parsed, err := gogitlab.ParseHook(payload)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if parsed.ObjectKind == "merge_request" {
|
||||
fmt.Println(string(payload))
|
||||
if err := g.PullRequestHook(parsed, repo, user); err != nil {
|
||||
return err
|
||||
}
|
||||
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||
}
|
||||
|
||||
if len(parsed.After) == 0 {
|
||||
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
}
|
||||
|
||||
_, err = database.GetCommitHash(parsed.After, repo.ID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
fmt.Println("commit already exists")
|
||||
return RenderText(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||
}
|
||||
|
||||
commit := &Commit{}
|
||||
commit.RepoID = repo.ID
|
||||
commit.Branch = parsed.Branch()
|
||||
commit.Hash = parsed.After
|
||||
commit.Status = "Pending"
|
||||
commit.Created = time.Now().UTC()
|
||||
|
||||
head := parsed.Head()
|
||||
commit.Message = head.Message
|
||||
commit.Timestamp = head.Timestamp
|
||||
if head.Author != nil {
|
||||
commit.SetAuthor(head.Author.Email)
|
||||
} else {
|
||||
commit.SetAuthor(parsed.UserName)
|
||||
}
|
||||
|
||||
// get the github settings from the database
|
||||
settings := database.SettingsMust()
|
||||
|
||||
// get the drone.yml file from GitHub
|
||||
client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)
|
||||
|
||||
content, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml")
|
||||
if err != nil {
|
||||
msg := "No .drone.yml was found in this repository. You need to add one.\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)
|
||||
}
|
||||
|
||||
// parse the build script
|
||||
buildscript, err := script.ParseBuild(content, 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)
|
||||
}
|
||||
|
||||
g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})
|
||||
|
||||
// OK!
|
||||
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, user *User) error {
|
||||
obj := p.ObjectAttributes
|
||||
|
||||
// Gitlab may trigger multiple hooks upon updating merge requests status
|
||||
// only build when it was just opened and the merge hasn't been checked yet.
|
||||
if !(obj.State == "opened" && obj.MergeStatus == "unchecked") {
|
||||
fmt.Println("Ignore GitLab Merge Requests")
|
||||
return nil
|
||||
}
|
||||
|
||||
settings := database.SettingsMust()
|
||||
|
||||
client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)
|
||||
|
||||
// GitLab merge-requests hook doesn't include repository data.
|
||||
// Have to fetch it manually
|
||||
src, err := client.RepoBranch(strconv.Itoa(obj.SourceProjectId), obj.SourceBranch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = database.GetCommitHash(src.Commit.Id, repo.ID)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
fmt.Println("commit already exists")
|
||||
return err
|
||||
}
|
||||
|
||||
commit := &Commit{}
|
||||
commit.RepoID = repo.ID
|
||||
commit.Branch = src.Name
|
||||
commit.Hash = src.Commit.Id
|
||||
commit.Status = "Pending"
|
||||
commit.Created = time.Now().UTC()
|
||||
commit.PullRequest = strconv.Itoa(obj.IId)
|
||||
|
||||
commit.Message = src.Commit.Message
|
||||
commit.Timestamp = src.Commit.AuthoredDateRaw
|
||||
commit.SetAuthor(src.Commit.Author.Email)
|
||||
|
||||
content, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml")
|
||||
if err != nil {
|
||||
msg := "No .drone.yml was found in this repository. You need to add one.\n"
|
||||
if err := saveFailedBuild(commit, msg); err != nil {
|
||||
return fmt.Errorf("Failed to save build: %q", err)
|
||||
}
|
||||
return fmt.Errorf("Error to fetch build script: %q", err)
|
||||
}
|
||||
|
||||
// parse the build script
|
||||
buildscript, err := script.ParseBuild(content, 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 fmt.Errorf("Failed to save build: %q", err)
|
||||
}
|
||||
return fmt.Errorf("Failed to parse build script: %q", err)
|
||||
}
|
||||
|
||||
// save the commit to the database
|
||||
if err := database.SaveCommit(commit); err != nil {
|
||||
return fmt.Errorf("Failed to save commit: %q", err)
|
||||
}
|
||||
|
||||
// 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 fmt.Errorf("Failed to save build: %q", err)
|
||||
}
|
||||
|
||||
g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ns namespaces user and repo.
|
||||
// Returns user%2Frepo
|
||||
func ns(user, repo string) string {
|
||||
return fmt.Sprintf("%s%%2F%s", user, repo)
|
||||
}
|
|
@ -10,8 +10,8 @@ import (
|
|||
"github.com/drone/drone/pkg/database"
|
||||
. "github.com/drone/drone/pkg/model"
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
"github.com/drone/go-github/github"
|
||||
"github.com/drone/go-bitbucket/bitbucket"
|
||||
"github.com/drone/go-github/github"
|
||||
)
|
||||
|
||||
type HookHandler struct {
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"github.com/drone/drone/pkg/channel"
|
||||
"github.com/drone/drone/pkg/database"
|
||||
. "github.com/drone/drone/pkg/model"
|
||||
"github.com/drone/go-github/github"
|
||||
"github.com/drone/go-bitbucket/bitbucket"
|
||||
"github.com/drone/go-github/github"
|
||||
|
||||
"launchpad.net/goyaml"
|
||||
)
|
||||
|
|
328
pkg/handler/testing/gitlab_test.go
Normal file
328
pkg/handler/testing/gitlab_test.go
Normal file
|
@ -0,0 +1,328 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/pkg/database"
|
||||
"github.com/drone/drone/pkg/handler"
|
||||
"github.com/drone/drone/pkg/model"
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
|
||||
dbtest "github.com/drone/drone/pkg/database/testing"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
// Tests the ability to create GitHub repositories.
|
||||
func Test_GitLabCreate(t *testing.T) {
|
||||
// seed the database with values
|
||||
SetupGitlabFixtures()
|
||||
defer TeardownGitlabFixtures()
|
||||
|
||||
q := &queue.Queue{}
|
||||
gl := handler.NewGitlabHandler(q)
|
||||
|
||||
// mock request
|
||||
req := http.Request{}
|
||||
req.Form = url.Values{}
|
||||
|
||||
// get user that will add repositories
|
||||
user, _ := database.GetUser(1)
|
||||
settings := database.SettingsMust()
|
||||
|
||||
Convey("Given request to setup gitlab repo", t, func() {
|
||||
|
||||
Convey("When repository is public", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "public")
|
||||
req.Form.Set("team", "")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
repo, _ := database.GetRepoSlug(settings.GitlabDomain + "/example/public")
|
||||
|
||||
Convey("The repository is created", func() {
|
||||
So(err, ShouldBeNil)
|
||||
So(repo, ShouldNotBeNil)
|
||||
So(repo.ID, ShouldNotEqual, 0)
|
||||
So(repo.Owner, ShouldEqual, "example")
|
||||
So(repo.Name, ShouldEqual, "public")
|
||||
So(repo.Host, ShouldEqual, settings.GitlabDomain)
|
||||
So(repo.TeamID, ShouldEqual, 0)
|
||||
So(repo.UserID, ShouldEqual, user.ID)
|
||||
So(repo.Private, ShouldEqual, false)
|
||||
So(repo.SCM, ShouldEqual, "git")
|
||||
})
|
||||
Convey("The repository is public", func() {
|
||||
So(repo.Private, ShouldEqual, false)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When repository is private", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "private")
|
||||
req.Form.Set("team", "")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
repo, _ := database.GetRepoSlug(settings.GitlabDomain + "/example/private")
|
||||
|
||||
Convey("The repository is created", func() {
|
||||
So(err, ShouldBeNil)
|
||||
So(repo, ShouldNotBeNil)
|
||||
So(repo.ID, ShouldNotEqual, 0)
|
||||
})
|
||||
Convey("The repository is private", func() {
|
||||
So(repo.Private, ShouldEqual, true)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When repository is not found", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "notfound")
|
||||
req.Form.Set("team", "")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
|
||||
Convey("The result is an error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldStartWith, "*Gitlab.buildAndExecRequestRaw")
|
||||
})
|
||||
|
||||
Convey("The repository is not created", func() {
|
||||
_, err := database.GetRepoSlug("example/notfound")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, sql.ErrNoRows)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When repository hook is not writable", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "hookerr")
|
||||
req.Form.Set("team", "")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
|
||||
Convey("The result is an error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Unable to add Hook to your GitLab repository.")
|
||||
})
|
||||
|
||||
Convey("The repository is not created", func() {
|
||||
_, err := database.GetRepoSlug("example/hookerr")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, sql.ErrNoRows)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When repository ssh key is not writable", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "keyerr")
|
||||
req.Form.Set("team", "")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
|
||||
Convey("The result is an error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Unable to add Public Key to your GitLab repository.")
|
||||
})
|
||||
|
||||
Convey("The repository is not created", func() {
|
||||
_, err := database.GetRepoSlug("example/keyerr")
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err, ShouldEqual, sql.ErrNoRows)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a team is provided", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "team")
|
||||
req.Form.Set("team", "drone")
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
// invoke handler
|
||||
err := gl.Create(res, &req, user)
|
||||
team, _ := database.GetTeamSlug("drone")
|
||||
repo, _ := database.GetRepoSlug(settings.GitlabDomain + "/example/team")
|
||||
|
||||
Convey("The repository is created", func() {
|
||||
So(err, ShouldBeNil)
|
||||
So(repo, ShouldNotBeNil)
|
||||
So(repo.ID, ShouldNotEqual, 0)
|
||||
})
|
||||
|
||||
Convey("The team should be set", func() {
|
||||
So(repo.TeamID, ShouldEqual, team.ID)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a team is not found", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "public")
|
||||
req.Form.Set("team", "faketeam")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
|
||||
Convey("The result is an error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Unable to find Team faketeam.")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a team is forbidden", func() {
|
||||
req.Form.Set("owner", "example")
|
||||
req.Form.Set("name", "public")
|
||||
req.Form.Set("team", "golang")
|
||||
res := httptest.NewRecorder()
|
||||
err := gl.Create(res, &req, user)
|
||||
|
||||
Convey("The result is an error", func() {
|
||||
So(err, ShouldNotBeNil)
|
||||
So(err.Error(), ShouldEqual, "Invalid permission to access Team golang.")
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// this code should be refactored and centralized, but for now
|
||||
// it is just proof-of-concepting a testing strategy, so we'll
|
||||
// revisit later.
|
||||
|
||||
// server is a test HTTP server used to provide mock API responses.
|
||||
var glServer *httptest.Server
|
||||
|
||||
func SetupGitlabFixtures() {
|
||||
dbtest.Setup()
|
||||
|
||||
// test server
|
||||
mux := http.NewServeMux()
|
||||
glServer = httptest.NewServer(mux)
|
||||
url, _ := url.Parse(glServer.URL)
|
||||
|
||||
// set database to use a localhost url for GitHub
|
||||
settings := model.Settings{}
|
||||
settings.GitlabApiUrl = url.String() // normall would be "https://api.github.com"
|
||||
settings.GitlabDomain = url.Host // normally would be "github.com"
|
||||
settings.Scheme = url.Scheme
|
||||
settings.Domain = "localhost"
|
||||
database.SaveSettings(&settings)
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a public repository and successfully
|
||||
// create a commit hook.
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/public", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"name": "public",
|
||||
"path_with_namespace": "example/public",
|
||||
"public": true
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/public/hooks", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{
|
||||
"url": "https://example.com/example/public/hooks/1",
|
||||
"id": 1
|
||||
}`)
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a private repository and successfully
|
||||
// create a commit hook and ssh deploy key
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/private", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"name": "private",
|
||||
"path_with_namespace": "example/private",
|
||||
"public": false
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/private/hooks", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{
|
||||
"url": "https://example.com/example/private/hooks/1",
|
||||
"id": 1
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/private/keys", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{
|
||||
"id": 1,
|
||||
"key": "ssh-rsa AAA...",
|
||||
"url": "https://api.github.com/user/keys/1",
|
||||
"title": "octocat@octomac"
|
||||
}`)
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a not found when accessing a github repository.
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/notfound", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.NotFound(w, r)
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a public repository and successfully
|
||||
// create a commit hook.
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/hookerr", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"name": "hookerr",
|
||||
"path_with_namespace": "example/hookerr",
|
||||
"public": true
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/hookerr/hooks", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a private repository and successfully
|
||||
// create a commit hook and ssh deploy key
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/keyerr", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"name": "keyerr",
|
||||
"path_with_namespace": "example/keyerr",
|
||||
"public": false
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/keyerr/hooks", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{
|
||||
"url": "https://api.github.com/api/v3/projects/example/keyerr/hooks/1",
|
||||
"id": 1
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/keyerr/keys", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
})
|
||||
|
||||
// -----------------------------------------------------------------------------------
|
||||
// fixture to return a public repository and successfully to
|
||||
// test adding a team.
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/team", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprint(w, `{
|
||||
"name": "team",
|
||||
"path_with_namespace": "example/team",
|
||||
"public": true
|
||||
}`)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/api/v3/projects/example/team/hooks", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, `{
|
||||
"url": "https://api.github.com/api/v3/projects/example/team/hooks/1",
|
||||
"id": 1
|
||||
}`)
|
||||
})
|
||||
}
|
||||
|
||||
func TeardownGitlabFixtures() {
|
||||
dbtest.Teardown()
|
||||
glServer.Close()
|
||||
}
|
|
@ -32,6 +32,10 @@ type Settings struct {
|
|||
BitbucketKey string `meddler:"bitbucket_key"`
|
||||
BitbucketSecret string `meddler:"bitbucket_secret"`
|
||||
|
||||
// GitLab Domain
|
||||
GitlabDomain string `meddler:"gitlab_domain"`
|
||||
GitlabApiUrl string `meddler:"gitlab_apiurl"`
|
||||
|
||||
// Domain of the server, eg drone.io
|
||||
Domain string `meddler:"hostname"`
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ type User struct {
|
|||
BitbucketLogin string `meddler:"bitbucket_login" json:"-"`
|
||||
BitbucketToken string `meddler:"bitbucket_token" json:"-"`
|
||||
BitbucketSecret string `meddler:"bitbucket_secret" json:"-"`
|
||||
|
||||
GitlabToken string `meddler:"gitlab_token" json:"-"`
|
||||
}
|
||||
|
||||
// Creates a new User from the given Name and Email.
|
||||
|
|
|
@ -50,6 +50,13 @@
|
|||
<input class="form-control form-control-large" type="text" name="GitHubApiUrl" value="{{.Settings.GitHubApiUrl}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="alert">GitLab Settings</div>
|
||||
<label>Base URL for GitLab: <small>(e.g. https://gitlab.com)</small></label>
|
||||
<div>
|
||||
<input class="form-control form-control-xlarge" type="text" name="GitlabApiUrl" value="{{.Settings.GitlabApiUrl}}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="alert">Bitbucket OAuth Consumer Key and Secret.</div>
|
||||
<label>Bitbucket Key and Secret:</label>
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
<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>
|
||||
<li class="active"><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li><a href="/new/gitlab">GitLab</a></li>
|
||||
</ul>
|
||||
</div><!-- ./col-xs-3 -->
|
||||
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
<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>
|
||||
<li class="active"><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li><a href="/new/gitlab">GitLab</a></li>
|
||||
</ul>
|
||||
</div><!-- ./col-xs-3 -->
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
||||
<li><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li><a href="/new/gitlab">GitLab</a></li>
|
||||
</ul>
|
||||
</div><!-- ./col-xs-3 -->
|
||||
|
||||
|
@ -98,4 +99,4 @@
|
|||
return false;
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="active"><a href="/new/github.com">GitHub</a></li>
|
||||
<li><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li><a href="/new/gitlab">GitLab</a></li>
|
||||
</ul>
|
||||
</div><!-- ./col-xs-3 -->
|
||||
|
||||
|
@ -28,4 +29,4 @@
|
|||
</div><!-- ./container -->
|
||||
{{ end }}
|
||||
|
||||
{{ define "script" }}{{ end }}
|
||||
{{ define "script" }}{{ end }}
|
||||
|
|
102
pkg/template/pages/gitlab_add.html
Normal file
102
pkg/template/pages/gitlab_add.html
Normal file
|
@ -0,0 +1,102 @@
|
|||
{{ define "title" }}GitLab · Add Repository{{ end }}
|
||||
|
||||
{{ define "content" }}
|
||||
<div class="subhead">
|
||||
<div class="container">
|
||||
<h1>
|
||||
<span>Repository Setup</span>
|
||||
<small>GitLab</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><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li class="active"><a href="/new/gitlab">GitLab</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="/link/gitlab" style="font-size: 18px;background:#f4f4f4;">Re-Link Account</a>
|
||||
</div>
|
||||
<form class="form-repo" method="POST" action="/new/gitlab">
|
||||
<input type="hidden" name="domain" autocomplete="off" value="{{.Settings.GitlabDomain}}">
|
||||
<div class="field-group">
|
||||
<div>
|
||||
<label>GitLab 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()
|
||||
var domain = $("input[name=domain]").val()
|
||||
window.location.pathname = "/" + domain + "/"+owner+"/"+name
|
||||
} else {
|
||||
$("#failureAlert").text("Unable to setup the Repository");
|
||||
$("#failureAlert").show().removeClass("hide");
|
||||
$('#submitButton').button('reset')
|
||||
};
|
||||
};
|
||||
xhr.send(formData);
|
||||
return false;
|
||||
}
|
||||
</script>
|
||||
{{ end }}
|
43
pkg/template/pages/gitlab_link.html
Normal file
43
pkg/template/pages/gitlab_link.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
{{ define "title" }}GitLab · Add Repository{{ end }}
|
||||
|
||||
{{ define "content" }}
|
||||
<div class="subhead">
|
||||
<div class="container">
|
||||
<h1>
|
||||
<span>Repository Setup</span>
|
||||
<small>GitLab</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><a href="/new/bitbucket.org">Bitbucket</a></li>
|
||||
<li class="active"><a href="/new/gitlab">GitLab</a></li>
|
||||
</ul>
|
||||
</div><!-- ./col-xs-3 -->
|
||||
|
||||
<div class="col-xs-9" role="main">
|
||||
<div class="alert">Link Your GitLab Account</div>
|
||||
<form class="form-repo" method="POST" action="/link/gitlab">
|
||||
<div class="field-group">
|
||||
<div><label>GitLab Private Token</label>
|
||||
<div><input class="form-control form-control-large" type="text" name="token" autocomplete="off" value="{{.User.GitlabToken}}"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<input class="btn btn-primary" id="submitButton" type="submit" value="Link" 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" }}{{ end }}
|
|
@ -82,6 +82,8 @@ func init() {
|
|||
"admin_settings.html",
|
||||
"github_add.html",
|
||||
"github_link.html",
|
||||
"gitlab_add.html",
|
||||
"gitlab_link.html",
|
||||
"bitbucket_add.html",
|
||||
"bitbucket_link.html",
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue