Skip to content

Commit

Permalink
adding full support for Gogs including private repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
bradrydzewski committed Oct 22, 2015
1 parent 22fc0e3 commit d376730
Show file tree
Hide file tree
Showing 18 changed files with 659 additions and 36 deletions.
4 changes: 4 additions & 0 deletions controller/pages.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ func ShowLogin(c *gin.Context) {
c.HTML(200, "login.html", gin.H{"Error": c.Query("error")})
}

func ShowLoginForm(c *gin.Context) {
c.HTML(200, "login_form.html", gin.H{})
}

func ShowUser(c *gin.Context) {
user := session.User(c)
token, _ := token.New(
Expand Down
1 change: 1 addition & 0 deletions docs/setup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [GitHub](github.md)
* [GitLab](gitlab.md)
* [Bitbucket](bitbucket.md)
* [Gogs](gogs.md)
* Database
* [SQLite](sqlite.md)
* [MySQL](mysql.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/setup/gitlab.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Gitlab

> GitLab support is experimental and unstable due to high variation in GitLab configurations and versions. We highly recommend using [Gogs](https://github.com/gogits/gogs) as an alternative to GitLab.
Drone comes with built-in support for GitLab version 8.0 and higher. To enable Gitlab you should configure the Gitlab driver using the following environment variables:

```bash
Expand Down
30 changes: 30 additions & 0 deletions docs/setup/gogs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Gogs

Drone comes with built-in support for Gogs version `0.6` and higher. To enable Gogs you should configure the Gogs driver using the following environment variables:

```bash
REMOTE_DRIVER=gogs
REMOTE_CONFIG=https://gogs.hooli.com?open=false
```

## Gogs configuration

The following is the standard URI connection scheme:

```
scheme://host[:port][?options]
```

The components of this string are:

* `scheme` server protocol `http` or `https`.
* `host` server address to connect to. The default value is github.com if not specified.
* `:port` optional. The default value is :80 if not specified.
* `?options` connection specific options.

## Gogs options

This section lists all connection options used in the connection string format. Connection options are pairs in the following form: `name=value`. The value is always case sensitive. Separate options with the ampersand (i.e. &) character:

* `open=false` allows users to self-register. Defaults to false for security reasons.
* `skip_verify=false` skip ca verification if self-signed certificate. Defaults to false for security reasons.
2 changes: 2 additions & 0 deletions docs/setup/mysql.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ This is an example connection string:
root:pa55word@tcp(localhost:3306)/drone?parseTime=true
```

Please note that `parseTime` is a **required** paramter.

## MySQL options

See the official [driver documentation](https://github.com/go-sql-driver/mysql#parameters) for a full list of driver options. Note that the `parseTime=true` is required.
Expand Down
34 changes: 0 additions & 34 deletions docs/setup/setup-agents.md

This file was deleted.

233 changes: 233 additions & 0 deletions remote/gogs/gogs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package gogs

import (
"fmt"
"net/http"
"net/url"
"strconv"

"github.com/drone/drone/model"
"github.com/drone/drone/shared/envconfig"
"github.com/gogits/go-gogs-client"

log "github.com/Sirupsen/logrus"
)

type Gogs struct {
URL string
Open bool
PrivateMode bool
SkipVerify bool
}

func Load(env envconfig.Env) *Gogs {
config := env.String("REMOTE_CONFIG", "")

// parse the remote DSN configuration string
url_, err := url.Parse(config)
if err != nil {
log.Fatalln("unable to parse remote dsn. %s", err)
}
params := url_.Query()
url_.Path = ""
url_.RawQuery = ""

// create the Githbub remote using parameters from
// the parsed DSN configuration string.
gogs := Gogs{}
gogs.URL = url_.String()
gogs.PrivateMode, _ = strconv.ParseBool(params.Get("private_mode"))
gogs.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
gogs.Open, _ = strconv.ParseBool(params.Get("open"))

return &gogs
}

// Login authenticates the session and returns the
// remote user details.
func (g *Gogs) Login(res http.ResponseWriter, req *http.Request) (*model.User, bool, error) {
var (
username = req.FormValue("username")
password = req.FormValue("password")
)

// if the username or password doesn't exist we re-direct
// the user to the login screen.
if len(username) == 0 || len(password) == 0 {
http.Redirect(res, req, "/login/form", http.StatusSeeOther)
return nil, false, nil
}

client := gogs.NewClient(g.URL, "")

// try to fetch drone token if it exists
var accessToken string
tokens, err := client.ListAccessTokens(username, password)
if err != nil {
return nil, false, 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, false, err
}
accessToken = token.Sha1
}

client = gogs.NewClient(g.URL, accessToken)
userInfo, err := client.GetUserInfo(username)
if err != nil {
return nil, false, err
}

user := model.User{}
user.Token = accessToken
user.Login = userInfo.UserName
user.Email = userInfo.Email
user.Avatar = userInfo.AvatarUrl
return &user, g.Open, nil
}

// Auth authenticates the session and returns the remote user
// login for the given token and secret
func (g *Gogs) Auth(token, secret string) (string, error) {
return "", fmt.Errorf("Method not supported")
}

// Repo fetches the named repository from the remote system.
func (g *Gogs) Repo(u *model.User, owner, name string) (*model.Repo, error) {
client := gogs.NewClient(g.URL, u.Token)
repos_, err := client.ListMyRepos()
if err != nil {
return nil, err
}

fullName := owner + "/" + name
for _, repo := range repos_ {
if repo.FullName == fullName {
return toRepo(repo), nil
}
}

return nil, fmt.Errorf("Not Found")
}

// Repos fetches a list of repos from the remote system.
func (g *Gogs) Repos(u *model.User) ([]*model.RepoLite, error) {
repos := []*model.RepoLite{}

client := gogs.NewClient(g.URL, u.Token)
repos_, err := client.ListMyRepos()
if err != nil {
return repos, err
}

for _, repo := range repos_ {
repos = append(repos, toRepoLite(repo))
}

return repos, err
}

// Perm fetches the named repository permissions from
// the remote system for the specified user.
func (g *Gogs) Perm(u *model.User, owner, name string) (*model.Perm, error) {
client := gogs.NewClient(g.URL, u.Token)
repos_, err := client.ListMyRepos()
if err != nil {
return nil, err
}

fullName := owner + "/" + name
for _, repo := range repos_ {
if repo.FullName == fullName {
return toPerm(repo.Permissions), nil
}
}

return nil, fmt.Errorf("Not Found")

}

// Script fetches the build script (.drone.yml) from the remote
// repository and returns in string format.
func (g *Gogs) Script(u *model.User, r *model.Repo, b *model.Build) ([]byte, []byte, error) {
client := gogs.NewClient(g.URL, u.Token)
cfg, err := client.GetFile(r.Owner, r.Name, b.Commit, ".drone.yml")
sec, _ := client.GetFile(r.Owner, r.Name, b.Commit, ".drone.sec")
return cfg, sec, err
}

// Status sends the commit status to the remote system.
// An example would be the GitHub pull request status.
func (g *Gogs) Status(u *model.User, r *model.Repo, b *model.Build, link string) error {
return fmt.Errorf("Not Implemented")
}

// Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system.
func (g *Gogs) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
url_, err := url.Parse(g.URL)
if err != nil {
return nil, err
}
return &model.Netrc{
Login: u.Token,
Password: "x-oauth-basic",
Machine: url_.Host,
}, nil
}

// Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable.
func (g *Gogs) Activate(u *model.User, r *model.Repo, k *model.Key, link string) error {
config := map[string]string{
"url": link,
"secret": r.Hash,
"content_type": "json",
}
hook := gogs.CreateHookOption{
Type: "gogs",
Config: config,
Active: true,
}

client := gogs.NewClient(g.URL, u.Token)
_, err := client.CreateRepoHook(r.Owner, r.Name, hook)
return err
}

// Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key.
func (g *Gogs) Deactivate(u *model.User, r *model.Repo, link string) error {
return fmt.Errorf("Not Implemented")
}

// Hook parses the post-commit hook from the Request body
// and returns the required data in a standard format.
func (g *Gogs) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
var (
err error
repo *model.Repo
build *model.Build
)

switch r.Header.Get("X-Gogs-Event") {
case "push":
var push *PushHook
push, err = parsePush(r.Body)
if err == nil {
repo = repoFromPush(push)
build = buildFromPush(push)
}
}
return repo, build, err
}
Loading

0 comments on commit d376730

Please sign in to comment.