mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-26 11:51:02 +00:00
add gogs support
This commit is contained in:
parent
789435d06d
commit
270ce52b76
12 changed files with 265 additions and 11 deletions
183
plugin/remote/gogs/gogs.go
Normal file
183
plugin/remote/gogs/gogs.go
Normal file
|
@ -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
|
||||||
|
}
|
23
plugin/remote/gogs/register.go
Normal file
23
plugin/remote/gogs/register.go
Normal file
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
|
@ -52,6 +52,10 @@ app.config(['$routeProvider', '$locationProvider', '$httpProvider', function($ro
|
||||||
templateUrl: '/static/views/login_gitlab.html',
|
templateUrl: '/static/views/login_gitlab.html',
|
||||||
title: 'GitLab Login',
|
title: 'GitLab Login',
|
||||||
})
|
})
|
||||||
|
.when('/gogs', {
|
||||||
|
templateUrl: '/static/views/login_gogs.html',
|
||||||
|
title: 'Gogs Setup',
|
||||||
|
})
|
||||||
.when('/setup', {
|
.when('/setup', {
|
||||||
templateUrl: '/static/views/setup.html',
|
templateUrl: '/static/views/setup.html',
|
||||||
controller: 'SetupController',
|
controller: 'SetupController',
|
||||||
|
@ -234,6 +238,6 @@ app.controller("AccountReposController", function($scope, $http, $location, user
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
$scope.byRemote = function(entry){
|
$scope.byRemote = function(entry){
|
||||||
return $scope.remote == "" || $scope.remote == entry.remote;
|
return $scope.remote == "" || $scope.remote == entry.remote;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -30,6 +30,9 @@ angular.module('app').controller("ConfigController", function($scope, $http, rem
|
||||||
case 'stash.atlassian.com':
|
case 'stash.atlassian.com':
|
||||||
$scope.stash = remote;
|
$scope.stash = remote;
|
||||||
break;
|
break;
|
||||||
|
case 'gogs':
|
||||||
|
$scope.gogs = remote;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -144,6 +144,7 @@
|
||||||
case 'enterprise.github.com' : return 'GitHub Enterprise';
|
case 'enterprise.github.com' : return 'GitHub Enterprise';
|
||||||
case 'bitbucket.org' : return 'Bitbucket';
|
case 'bitbucket.org' : return 'Bitbucket';
|
||||||
case 'stash.atlassian.com' : return 'Atlassian Stash';
|
case 'stash.atlassian.com' : return 'Atlassian Stash';
|
||||||
|
case 'gogs' : return 'Gogs';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,6 +161,7 @@
|
||||||
case 'enterprise.github.com' : return 'fa-github-square';
|
case 'enterprise.github.com' : return 'fa-github-square';
|
||||||
case 'bitbucket.org' : return 'fa-bitbucket-square';
|
case 'bitbucket.org' : return 'fa-bitbucket-square';
|
||||||
case 'stash.atlassian.com' : 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('toDuration', toDuration)
|
||||||
.filter('unique', unique);
|
.filter('unique', unique);
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -16,8 +16,17 @@
|
||||||
</strong>
|
</strong>
|
||||||
</dd>
|
</dd>
|
||||||
<!-- /BITBUCKET -->
|
<!-- /BITBUCKET -->
|
||||||
|
<!-- GOGS -->
|
||||||
|
<dd class="large" ng-if="repo.remote == 'gogs' ">
|
||||||
|
<strong>
|
||||||
|
commit
|
||||||
|
<a href="{{ repo.url }}/commit/{{ commit.sha }}" >{{ commit.sha | shortHash}}</a>
|
||||||
|
to <a href="{{ repo.url }}/src/{{ commit.branch }}">{{ commit.branch }}</a> branch
|
||||||
|
</strong>
|
||||||
|
</dd>
|
||||||
|
<!-- /GOGS -->
|
||||||
<!-- STASH -->
|
<!-- STASH -->
|
||||||
<dd class="large" ng-if="repo.remote != 'gitlab.com' && repo.remote != 'github.com' && repo.remote != 'enterprise.github.com' && repo.remote != 'bitbucket.org' ">
|
<dd class="large" ng-if="repo.remote != 'gitlab.com' && repo.remote != 'github.com' && repo.remote != 'enterprise.github.com' && repo.remote != 'bitbucket.org' && repo.remote != 'gogs' ">
|
||||||
<strong>commit <u>{{ commit.sha | shortHash}}</u> to <u>{{ commit.branch }}</u> branch</strong>
|
<strong>commit <u>{{ commit.sha | shortHash}}</u> to <u>{{ commit.branch }}</u> branch</strong>
|
||||||
</dd>
|
</dd>
|
||||||
<!-- /STASH -->
|
<!-- /STASH -->
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="remote.type != 'github.com' && remote.type != 'bitbucket.org' ">
|
<div ng-if="remote.type != 'github.com' && remote.type != 'bitbucket.org' && remote.type != 'gogs' ">
|
||||||
<label>API URL</label>
|
<label>API URL</label>
|
||||||
<div ng-switch="remote.type">
|
<div ng-switch="remote.type">
|
||||||
<input ng-switch-default ng-model="remote.api" type="text" placeholder="https://www.foo.com/api" />
|
<input ng-switch-default ng-model="remote.api" type="text" placeholder="https://www.foo.com/api" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div ng-if="remote.type != 'gitlab.com'">
|
<div ng-if="remote.type != 'gitlab.com' && remote.type != 'gogs' ">
|
||||||
<label>OAuth Client</label>
|
<label>OAuth Client</label>
|
||||||
<div>
|
<div>
|
||||||
<input type="text" ng-model="remote.client" />
|
<input type="text" ng-model="remote.client" />
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!--
|
<!--
|
||||||
minor modifications to the style that only apply to this view
|
minor modifications to the style that only apply to this view
|
||||||
-->
|
-->
|
||||||
<style>
|
<style>
|
||||||
|
@ -11,12 +11,15 @@ minor modifications to the style that only apply to this view
|
||||||
<article id="loginpage">
|
<article id="loginpage">
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1" ng-if="state == 1 && remotes.length != 0" ng-repeat="remote in remotes">
|
<div class="pure-u-1" ng-if="state == 1 && remotes.length != 0" ng-repeat="remote in remotes">
|
||||||
<a ng-href="/api/auth/{{ remote.type }}" target="_self" ng-if="remote.type != 'gitlab.com' ">
|
<a ng-href="/api/auth/{{ remote.type }}" target="_self" ng-if="remote.type != 'gitlab.com' && remote.type != 'gogs' ">
|
||||||
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||||
</a>
|
</a>
|
||||||
<a ng-href="/gitlab" ng-if="remote.type == 'gitlab.com' ">
|
<a ng-href="/gitlab" ng-if="remote.type == 'gitlab.com' ">
|
||||||
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||||
</a>
|
</a>
|
||||||
|
<a ng-href="/gogs" ng-if="remote.type == 'gogs' ">
|
||||||
|
<i class="fa {{ remote.type | remoteIcon }}"></i> {{ remote.type | remoteName }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-u-1" ng-if="state == 1 && remotes.length == 0">
|
<div class="pure-u-1" ng-if="state == 1 && remotes.length == 0">
|
||||||
|
@ -25,4 +28,4 @@ minor modifications to the style that only apply to this view
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
23
server/app/views/login_gogs.html
Normal file
23
server/app/views/login_gogs.html
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<!--
|
||||||
|
minor modifications to the style that only apply to this view
|
||||||
|
-->
|
||||||
|
<style>
|
||||||
|
#container { padding-top: 155px; }
|
||||||
|
#header { height: 150px; }
|
||||||
|
#header .user { display:none; }
|
||||||
|
#header .brand { margin-top:55px ; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<article id="loginpage">
|
||||||
|
<form class="pure-g" method="POST" action="/api/auth/gogs">
|
||||||
|
<div class="pure-u-1">
|
||||||
|
<input type="text" name="username" placeholder="Username" />
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1">
|
||||||
|
<input type="password" name="password" placeholder="Password" />
|
||||||
|
</div>
|
||||||
|
<div class="pure-u-1">
|
||||||
|
<input type="submit" value="Gogs Login" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</article>
|
|
@ -25,6 +25,7 @@
|
||||||
<option value="gitlab.com">GitLab</option>
|
<option value="gitlab.com">GitLab</option>
|
||||||
<option value="bitbucket.org">Bitbucket</option>
|
<option value="bitbucket.org">Bitbucket</option>
|
||||||
<option value="stash.atlassian.com">Stash</option>
|
<option value="stash.atlassian.com">Stash</option>
|
||||||
|
<option value="gogs">Gogs</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<label for="username">Username</label>
|
<label for="username">Username</label>
|
||||||
|
@ -35,4 +36,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"github.com/drone/drone/plugin/remote/bitbucket"
|
"github.com/drone/drone/plugin/remote/bitbucket"
|
||||||
"github.com/drone/drone/plugin/remote/github"
|
"github.com/drone/drone/plugin/remote/github"
|
||||||
"github.com/drone/drone/plugin/remote/gitlab"
|
"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/blobstore"
|
||||||
"github.com/drone/drone/server/capability"
|
"github.com/drone/drone/server/capability"
|
||||||
"github.com/drone/drone/server/datastore"
|
"github.com/drone/drone/server/datastore"
|
||||||
|
@ -97,6 +98,7 @@ func main() {
|
||||||
bitbucket.Register()
|
bitbucket.Register()
|
||||||
github.Register()
|
github.Register()
|
||||||
gitlab.Register()
|
gitlab.Register()
|
||||||
|
gogs.Register()
|
||||||
|
|
||||||
caps = map[string]bool{}
|
caps = map[string]bool{}
|
||||||
caps[capability.Registration] = *open
|
caps[capability.Registration] = *open
|
||||||
|
|
|
@ -6,6 +6,7 @@ const (
|
||||||
RemoteGithubEnterprise = "enterprise.github.com"
|
RemoteGithubEnterprise = "enterprise.github.com"
|
||||||
RemoteBitbucket = "bitbucket.org"
|
RemoteBitbucket = "bitbucket.org"
|
||||||
RemoteStash = "stash.atlassian.com"
|
RemoteStash = "stash.atlassian.com"
|
||||||
|
RemoteGogs = "gogs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Remote struct {
|
type Remote struct {
|
||||||
|
|
Loading…
Reference in a new issue