From 270ce52b7648d2852de91c5f3dd879d33a37cc20 Mon Sep 17 00:00:00 2001 From: Christopher Brickley Date: Sat, 27 Sep 2014 23:36:11 -0400 Subject: [PATCH] add gogs support --- plugin/remote/gogs/gogs.go | 183 +++++++++++++++++++++++++ plugin/remote/gogs/register.go | 23 ++++ server/app/scripts/app.js | 8 +- server/app/scripts/controllers/conf.js | 3 + server/app/scripts/filters/filters.js | 4 +- server/app/views/commit_detail.html | 13 +- server/app/views/config.html | 4 +- server/app/views/login.html | 9 +- server/app/views/login_gogs.html | 23 ++++ server/app/views/users_add.html | 3 +- server/main.go | 2 + shared/model/remote.go | 1 + 12 files changed, 265 insertions(+), 11 deletions(-) create mode 100644 plugin/remote/gogs/gogs.go create mode 100644 plugin/remote/gogs/register.go create mode 100644 server/app/views/login_gogs.html diff --git a/plugin/remote/gogs/gogs.go b/plugin/remote/gogs/gogs.go new file mode 100644 index 000000000..9ad32a209 --- /dev/null +++ b/plugin/remote/gogs/gogs.go @@ -0,0 +1,183 @@ +package gogs + +import ( + "fmt" + "io/ioutil" + "log" + "net/http" + "net/url" + "strings" + "time" + + "github.com/drone/drone/shared/model" + "github.com/gogits/go-gogs-client" +) + +type Gogs struct { + URL string + Secret string +} + +func New(url string, secret string) *Gogs { + return &Gogs{URL: url, Secret: secret} +} + +// Authorize handles Gogs authorization +func (r *Gogs) Authorize(res http.ResponseWriter, req *http.Request) (*model.Login, error) { + var username = req.FormValue("username") + var password = req.FormValue("password") + var client = gogs.NewClient(r.URL, "") + + // try to fetch drone token if it exists + var accessToken = "" + tokens, err := client.ListAccessTokens(username, password) + if err != nil { + return nil, err + } + for _, token := range tokens { + if token.Name == "drone" { + accessToken = token.Sha1 + break + } + } + + // if drone token not found, create it + if accessToken == "" { + token, err := client.CreateAccessToken(username, password, gogs.CreateAccessTokenOption{Name: "drone"}) + if err != nil { + return nil, err + } + accessToken = token.Sha1 + } + + // update client + client = gogs.NewClient(r.URL, accessToken) + + // fetch user information + user, err := client.GetUserInfo(username) + if err != nil { + return nil, err + } + + var login = new(model.Login) + login.Name = user.FullName + login.Email = user.Email + login.Access = accessToken + login.Login = username + return login, nil +} + +// GetKind returns the internal identifier of this remote Gogs instance +func (r *Gogs) GetKind() string { + return model.RemoteGogs +} + +// GetHost returns the hostname of this remote Gogs instance +func (r *Gogs) GetHost() string { + uri, _ := url.Parse(r.URL) + return uri.Host +} + +// GetRepos fetches all repositories that the specified +// user has access to in the remote system. +func (r *Gogs) GetRepos(user *model.User) ([]*model.Repo, error) { + var repos []*model.Repo + + var remote = r.GetKind() + var hostname = r.GetHost() + var client = gogs.NewClient(r.URL, user.Access) + + gogsRepos, err := client.ListMyRepos() + + if err != nil { + return nil, err + } + + for _, repo := range gogsRepos { + var repoName = strings.Split(repo.FullName, "/") + if len(repoName) < 2 { + log.Println("invalid repo full_name", repo.FullName) + continue + } + var owner = repoName[0] + var name = repoName[1] + + var repo = model.Repo{ + UserID: user.ID, + Remote: remote, + Host: hostname, + Owner: owner, + Name: name, + Private: repo.Private, + CloneURL: repo.CloneUrl, + GitURL: repo.CloneUrl, + SSHURL: repo.SshUrl, + URL: repo.HtmlUrl, + Role: &model.Perm{ + Admin: repo.Permissions.Admin, + Write: repo.Permissions.Push, + Read: repo.Permissions.Pull, + }, + } + + repos = append(repos, &repo) + } + + return repos, err +} + +// GetScript fetches the build script (.drone.yml) from the remote +// repository and returns a byte array +func (r *Gogs) GetScript(user *model.User, repo *model.Repo, hook *model.Hook) ([]byte, error) { + var client = gogs.NewClient(r.URL, user.Access) + return client.GetFile(repo.Owner, repo.Name, hook.Sha, ".drone.yml") +} + +// Activate activates a repository +func (r *Gogs) Activate(user *model.User, repo *model.Repo, link string) error { + var client = gogs.NewClient(r.URL, user.Access) + + var config = map[string]string{ + "url": link, + "secret": r.Secret, + "content_type": "json", + } + var hook = gogs.CreateHookOption{ + Type: "gogs", + Config: config, + Active: true, + } + + _, err := client.CreateRepoHook(repo.Owner, repo.Name, hook) + return err +} + +// ParseHook parses the post-commit hook from the Request body +// and returns the required data in a standard format. +func (r *Gogs) ParseHook(req *http.Request) (*model.Hook, error) { + defer req.Body.Close() + var payloadbytes, _ = ioutil.ReadAll(req.Body) + var payload, err = gogs.ParseHook(payloadbytes) + if err != nil { + return nil, err + } + + // verify the payload has the minimum amount of required data. + if payload.Repo == nil || payload.Commits == nil || len(payload.Commits) == 0 { + return nil, fmt.Errorf("Invalid Gogs post-commit Hook. Missing Repo or Commit data.") + } + + if payload.Secret != r.Secret { + return nil, fmt.Errorf("Payload secret does not match stored secret") + } + + return &model.Hook{ + Owner: payload.Repo.Owner.UserName, + Repo: payload.Repo.Name, + Sha: payload.Commits[0].Id, + Branch: payload.Branch(), + Author: payload.Commits[0].Author.UserName, + Timestamp: time.Now().UTC().String(), + Message: payload.Commits[0].Message, + }, nil +} diff --git a/plugin/remote/gogs/register.go b/plugin/remote/gogs/register.go new file mode 100644 index 000000000..592d729f3 --- /dev/null +++ b/plugin/remote/gogs/register.go @@ -0,0 +1,23 @@ +package gogs + +import ( + "github.com/drone/config" + "github.com/drone/drone/plugin/remote" +) + +var ( + gogsUrl = config.String("gogs-url", "") + gogsSecret = config.String("gogs-secret", "") +) + +// Registers the Gogs plugin using the default +// settings from the config file or environment +// variables. +func Register() { + if len(*gogsUrl) == 0 { + return + } + remote.Register( + New(*gogsUrl, *gogsSecret), + ) +} diff --git a/server/app/scripts/app.js b/server/app/scripts/app.js index be352eaff..4f1a670b0 100644 --- a/server/app/scripts/app.js +++ b/server/app/scripts/app.js @@ -52,6 +52,10 @@ app.config(['$routeProvider', '$locationProvider', '$httpProvider', function($ro templateUrl: '/static/views/login_gitlab.html', title: 'GitLab Login', }) + .when('/gogs', { + templateUrl: '/static/views/login_gogs.html', + title: 'Gogs Setup', + }) .when('/setup', { templateUrl: '/static/views/setup.html', controller: 'SetupController', @@ -234,6 +238,6 @@ app.controller("AccountReposController", function($scope, $http, $location, user return true; }; $scope.byRemote = function(entry){ - return $scope.remote == "" || $scope.remote == entry.remote; - }; + return $scope.remote == "" || $scope.remote == entry.remote; + }; }); diff --git a/server/app/scripts/controllers/conf.js b/server/app/scripts/controllers/conf.js index 6e42b7f89..4cc8202f6 100644 --- a/server/app/scripts/controllers/conf.js +++ b/server/app/scripts/controllers/conf.js @@ -30,6 +30,9 @@ angular.module('app').controller("ConfigController", function($scope, $http, rem case 'stash.atlassian.com': $scope.stash = remote; break; + case 'gogs': + $scope.gogs = remote; + break; } } }) diff --git a/server/app/scripts/filters/filters.js b/server/app/scripts/filters/filters.js index bdb4ec0bf..32c1c0886 100644 --- a/server/app/scripts/filters/filters.js +++ b/server/app/scripts/filters/filters.js @@ -144,6 +144,7 @@ case 'enterprise.github.com' : return 'GitHub Enterprise'; case 'bitbucket.org' : return 'Bitbucket'; case 'stash.atlassian.com' : return 'Atlassian Stash'; + case 'gogs' : return 'Gogs'; } } } @@ -160,6 +161,7 @@ case 'enterprise.github.com' : return 'fa-github-square'; case 'bitbucket.org' : return 'fa-bitbucket-square'; case 'stash.atlassian.com' : return 'fa-bitbucket-square'; + case 'gogs' : return 'fa-git-square'; } } } @@ -203,4 +205,4 @@ .filter('toDuration', toDuration) .filter('unique', unique); -})(); \ No newline at end of file +})(); diff --git a/server/app/views/commit_detail.html b/server/app/views/commit_detail.html index 639b73284..c53f9dfb3 100644 --- a/server/app/views/commit_detail.html +++ b/server/app/views/commit_detail.html @@ -16,8 +16,17 @@ + +
+ + commit + {{ commit.sha | shortHash}} + to {{ commit.branch }} branch + +
+ -
+
commit {{ commit.sha | shortHash}} to {{ commit.branch }} branch
- \ No newline at end of file + diff --git a/server/app/views/config.html b/server/app/views/config.html index 6e6d285c5..8784f5978 100644 --- a/server/app/views/config.html +++ b/server/app/views/config.html @@ -16,14 +16,14 @@ -
+
-
+
diff --git a/server/app/views/login.html b/server/app/views/login.html index 7f08f2714..4de2fd9dc 100644 --- a/server/app/views/login.html +++ b/server/app/views/login.html @@ -1,4 +1,4 @@ - + +
+
+
+ +
+
+ +
+
+ +
+
+
diff --git a/server/app/views/users_add.html b/server/app/views/users_add.html index 07243532f..8b1fc4f4f 100644 --- a/server/app/views/users_add.html +++ b/server/app/views/users_add.html @@ -25,6 +25,7 @@ + @@ -35,4 +36,4 @@
- \ No newline at end of file + diff --git a/server/main.go b/server/main.go index 66f439703..a260ba0f8 100644 --- a/server/main.go +++ b/server/main.go @@ -24,6 +24,7 @@ import ( "github.com/drone/drone/plugin/remote/bitbucket" "github.com/drone/drone/plugin/remote/github" "github.com/drone/drone/plugin/remote/gitlab" + "github.com/drone/drone/plugin/remote/gogs" "github.com/drone/drone/server/blobstore" "github.com/drone/drone/server/capability" "github.com/drone/drone/server/datastore" @@ -97,6 +98,7 @@ func main() { bitbucket.Register() github.Register() gitlab.Register() + gogs.Register() caps = map[string]bool{} caps[capability.Registration] = *open diff --git a/shared/model/remote.go b/shared/model/remote.go index 74f6ca2c5..ddb2feb15 100644 --- a/shared/model/remote.go +++ b/shared/model/remote.go @@ -6,6 +6,7 @@ const ( RemoteGithubEnterprise = "enterprise.github.com" RemoteBitbucket = "bitbucket.org" RemoteStash = "stash.atlassian.com" + RemoteGogs = "gogs" ) type Remote struct {