refactored plugin/remote and adapted @bugBugagazavr Gitlab code

This commit is contained in:
Brad Rydzewski
2014-09-02 00:18:17 -07:00
parent 71dfaa40fb
commit 38379992bf
31 changed files with 1525 additions and 993 deletions

View File

@@ -1,166 +0,0 @@
package github
import (
"fmt"
"net/url"
"github.com/drone/drone/plugin/remote"
"github.com/drone/go-github/github"
)
type Client struct {
config *Github
access string // user access token
}
// GetUser fetches the user by ID (login name).
func (c *Client) GetUser(login string) (*remote.User, error) {
return nil, nil
}
// GetRepos fetches all repositories that the specified
// user has access to in the remote system.
func (c *Client) GetRepos(owner string) ([]*remote.Repo, error) {
// create the github client
client := github.New(c.access)
client.ApiUrl = c.config.API
// retrieve a list of all github repositories
repos, err := client.Repos.ListAll()
if err != nil {
return nil, err
}
// store results in common format
result := []*remote.Repo{}
// parse the hostname from the github url
githuburl, err := url.Parse(c.config.URL)
if err != nil {
return nil, err
}
// loop throught the list and convert to the standard repo format
for _, repo := range repos {
// if the repository is private we should use the ssh
// url to clone, else we should use the git url
if repo.Private {
repo.CloneUrl = repo.SshUrl
}
result = append(result, &remote.Repo{
ID: repo.ID,
Host: githuburl.Host,
Owner: repo.Owner.Login,
Name: repo.Name,
Kind: "git",
Clone: repo.CloneUrl,
Git: repo.GitUrl,
SSH: repo.SshUrl,
Private: repo.Private,
Push: repo.Permissions.Push,
Pull: repo.Permissions.Pull,
Admin: repo.Permissions.Admin,
})
}
return result, nil
}
// GetScript fetches the build script (.drone.yml) from the remote
// repository using the GitHub API and returns the raw file in string format.
func (c *Client) GetScript(hook *remote.Hook) (out string, err error) {
// create the github client
client := github.New(c.access)
client.ApiUrl = c.config.API
// retrieve the .drone.yml file from GitHub
content, err := client.Contents.FindRef(hook.Owner, hook.Repo, ".drone.yml", hook.Sha)
if err != nil {
return
}
// decode the content
raw, err := content.DecodeContent()
if err != nil {
return
}
return string(raw), nil
}
// SetStatus
func (c *Client) SetStatus(owner, name, sha, status string) error {
// create the github client
client := github.New(c.access)
client.ApiUrl = c.config.API
// convert from drone status to github status
var message string
switch status {
case "Success":
status = "success"
message = "The build succeeded on drone.io"
case "Failure":
status = "failure"
message = "The build failed on drone.io"
case "Started", "Pending":
status = "pending"
message = "The build is pending on drone.io"
default:
status = "error"
message = "The build errored on drone.io"
}
// format the build URL
// TODO we really need the branch here
// TODO we really need the drone.io hostname as well
url := fmt.Sprintf("http://beta.drone.io/%s/%s/%s/%s", owner, name, "master", sha)
// update the status
return client.Repos.CreateStatus(owner, name, status, url, message, sha)
}
// SetActive will configure a post-commit and pull-request hook
// with the remote GitHub repository using the GitHub API.
//
// It will also, optionally, add a public RSA key. This is primarily
// used for private repositories, which typically must use the Git+SSH
// protocol to clone the repository.
func (c *Client) SetActive(owner, name, hook, key string) error {
// create the github client
client := github.New(c.access)
client.ApiUrl = c.config.API
// parse the hostname from the hook, and use this
// to name the ssh key
hookurl, err := url.Parse(hook)
if err != nil {
return err
}
// fetch the repository so that we can see if it
// is public or private.
repo, err := client.Repos.Find(owner, name)
if err != nil {
return err
}
// if the repository is private we'll need
// to upload a github key to the repository
if repo.Private {
// name the key
keyname := "drone@" + hookurl.Host
_, err := client.RepoKeys.CreateUpdate(owner, name, key, keyname)
if err != nil {
return err
}
}
// add the hook
if _, err := client.Hooks.CreateUpdate(owner, name, hook); err != nil {
return err
}
return nil
}

View File

@@ -1,7 +1,6 @@
package github
import (
"encoding/base32"
"fmt"
"net/http"
"net/url"
@@ -9,74 +8,225 @@ import (
"strings"
"time"
"github.com/drone/drone/plugin/remote"
"code.google.com/p/goauth2/oauth"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/model"
"github.com/drone/go-github/github"
"github.com/drone/go-github/oauth2"
"github.com/gorilla/securecookie"
)
var (
scope = "repo,repo:status,user:email"
const (
DefaultAPI = "https://api.github.com/"
DefaultURL = "https://github.com"
DefaultScope = "repo,repo:status,user:email"
)
type Github struct {
URL string `json:"url"` // https://github.com
API string `json:"api"` // https://api.github.com
Client string `json:"client"`
Secret string `json:"secret"`
Enabled bool `json:"enabled"`
type GitHub struct {
URL string
API string
Client string
Secret string
}
// GetName returns the name of this remote system.
func (g *Github) GetName() string {
switch g.URL {
case "https://github.com":
return "github.com"
default:
return "enterprise.github.com"
func New(url, api, client, secret string) *GitHub {
var github = GitHub{
URL: url,
API: api,
Client: client,
Secret: secret,
}
// the API must have a trailing slash
if !strings.HasSuffix(github.API, "/") {
github.API += "/"
}
// the URL must NOT have a trailing slash
if strings.HasSuffix(github.URL, "/") {
github.URL = github.URL[:len(github.URL)-1]
}
return &github
}
// GetHost returns the url.Host of this remote system.
func (g *Github) GetHost() (host string) {
u, err := url.Parse(g.URL)
func NewDefault(client, secret string) *GitHub {
return New(DefaultURL, DefaultAPI, client, secret)
}
// Authorize handles GitHub API Authorization.
func (r *GitHub) Authorize(res http.ResponseWriter, req *http.Request) (*model.Login, error) {
var config = &oauth.Config{
ClientId: r.Client,
ClientSecret: r.Secret,
Scope: DefaultScope,
AuthURL: fmt.Sprintf("%s/login/oauth/authorize", r.URL),
TokenURL: fmt.Sprintf("%s/login/oauth/access_token", r.URL),
RedirectURL: fmt.Sprintf("%s/login/%s", httputil.GetURL(req), r.GetKind()),
}
// get the OAuth code
var code = req.FormValue("code")
var state = req.FormValue("state")
if len(code) == 0 {
var random = GetRandom()
httputil.SetCookie(res, req, "github_state", random)
http.Redirect(res, req, config.AuthCodeURL(random), http.StatusSeeOther)
return nil, nil
}
cookieState := httputil.GetCookie(req, "github_state")
httputil.DelCookie(res, req, "github_state")
if cookieState != state {
return nil, fmt.Errorf("Error matching state in OAuth2 redirect")
}
var trans = &oauth.Transport{Config: config}
var token, err = trans.Exchange(code)
if err != nil {
return
return nil, err
}
return u.Host
var client = NewClient(r.API, token.AccessToken)
var useremail, errr = GetUserEmail(client)
if errr != nil {
return nil, errr
}
var login = new(model.Login)
login.ID = int64(*useremail.ID)
login.Access = token.AccessToken
login.Login = *useremail.Login
login.Name = *useremail.Name
login.Email = *useremail.Email
return login, nil
}
// GetHook parses the post-commit hook from the Request body
// GetKind returns the internal identifier of this remote GitHub instane.
func (r *GitHub) GetKind() string {
if r.IsEnterprise() {
return model.RemoteGithubEnterprise
} else {
return model.RemoteGithub
}
}
// GetHost returns the hostname of this remote GitHub instance.
func (r *GitHub) GetHost() string {
uri, _ := url.Parse(r.URL)
return uri.Host
}
// IsEnterprise returns true if the remote system is an
// instance of GitHub Enterprise Edition.
func (r *GitHub) IsEnterprise() bool {
return r.URL != DefaultURL
}
// GetRepos fetches all repositories that the specified
// user has access to in the remote system.
func (r *GitHub) GetRepos(user *model.User) ([]*model.Repo, error) {
var repos []*model.Repo
var client = NewClient(r.API, user.Access)
var list, err = GetAllRepos(client)
if err != nil {
return nil, err
}
var remote = r.GetKind()
var hostname = r.GetHost()
var enterprise = r.IsEnterprise()
for _, item := range list {
var repo = model.Repo{
UserID: user.ID,
Remote: remote,
Host: hostname,
Owner: *item.Owner.Login,
Name: *item.Name,
Private: *item.Private,
URL: *item.URL,
CloneURL: *item.GitURL,
GitURL: *item.GitURL,
SSHURL: *item.SSHURL,
Role: &model.Perm{},
}
if enterprise || repo.Private {
repo.CloneURL = *item.SSHURL
}
// if no permissions we should skip the repository
// entirely, since this should never happen
if item.Permissions == nil {
continue
}
repo.Role.Admin = (*item.Permissions)["admin"]
repo.Role.Write = (*item.Permissions)["push"]
repo.Role.Read = (*item.Permissions)["pull"]
repos = append(repos, &repo)
}
return repos, err
}
// GetScript fetches the build script (.drone.yml) from the remote
// repository and returns in string format.
func (r *GitHub) GetScript(user *model.User, repo *model.Repo, hook *model.Hook) ([]byte, error) {
var client = NewClient(r.API, user.Access)
return GetFile(client, repo.Owner, repo.Name, ".drone.yml", hook.Sha)
}
// Activate activates a repository by adding a Post-commit hook and
// a Public Deploy key, if applicable.
func (r *GitHub) Activate(user *model.User, repo *model.Repo, link string) error {
var client = NewClient(r.API, user.Access)
var title, err = GetKeyTitle(link)
if err != nil {
return err
}
// if the CloneURL is using the SSHURL then we know that
// we need to add an SSH key to GitHub.
if repo.SSHURL == repo.CloneURL {
_, err = CreateUpdateKey(client, repo.Owner, repo.Name, title, repo.PublicKey)
if err != nil {
return err
}
}
_, err = CreateUpdateHook(client, repo.Owner, repo.Name, link)
return err
}
// ParseHook parses the post-commit hook from the Request body
// and returns the required data in a standard format.
func (g *Github) GetHook(r *http.Request) (*remote.Hook, error) {
func (r *GitHub) ParseHook(req *http.Request) (*model.Hook, error) {
// handle github ping
if r.Header.Get("X-Github-Event") == "ping" {
if req.Header.Get("X-Github-Event") == "ping" {
return nil, nil
}
// handle github pull request hook differently
if r.Header.Get("X-Github-Event") == "pull_request" {
return g.GetPullRequestHook(r)
if req.Header.Get("X-Github-Event") == "pull_request" {
return r.ParsePullRequestHook(req)
}
// get the payload of the message
payload := r.FormValue("payload")
var payload = req.FormValue("payload")
// parse the github Hook payload
data, err := github.ParseHook([]byte(payload))
var data, err = github.ParseHook([]byte(payload))
if err != nil {
return nil, nil
}
// make sure this is being triggered because of a commit
// and not something like a tag deletion or whatever
if data.IsTag() || data.IsGithubPages() ||
data.IsHead() == false || data.IsDeleted() {
if data.IsTag() ||
data.IsGithubPages() ||
data.IsHead() == false ||
data.IsDeleted() {
return nil, nil
}
hook := remote.Hook{}
var hook = new(model.Hook)
hook.Repo = data.Repo.Name
hook.Owner = data.Repo.Owner.Login
hook.Sha = data.Head.Id
@@ -99,15 +249,17 @@ func (g *Github) GetHook(r *http.Request) (*remote.Hook, error) {
hook.Author = data.Commits[0].Author.Email
}
return &hook, nil
return hook, nil
}
func (g *Github) GetPullRequestHook(r *http.Request) (*remote.Hook, error) {
payload := r.FormValue("payload")
// ParsePullRequestHook parses the pull request hook from the Request body
// and returns the required data in a standard format.
func (r *GitHub) ParsePullRequestHook(req *http.Request) (*model.Hook, error) {
var payload = req.FormValue("payload")
// parse the payload to retrieve the pull-request
// hook meta-data.
data, err := github.ParsePullRequestHook([]byte(payload))
var data, err = github.ParsePullRequestHook([]byte(payload))
if err != nil {
return nil, err
}
@@ -119,7 +271,7 @@ func (g *Github) GetPullRequestHook(r *http.Request) (*remote.Hook, error) {
// TODO we should also store the pull request branch (ie from x to y)
// we can find it here: data.PullRequest.Head.Ref
hook := remote.Hook{
var hook = model.Hook{
Owner: data.Repo.Owner.Login,
Repo: data.Repo.Name,
Sha: data.PullRequest.Head.Sha,
@@ -137,78 +289,3 @@ func (g *Github) GetPullRequestHook(r *http.Request) (*remote.Hook, error) {
return &hook, nil
}
// GetLogin handles authentication to third party, remote services
// and returns the required user data in a standard format.
func (g *Github) GetLogin(w http.ResponseWriter, r *http.Request) (*remote.Login, error) {
// create the oauth2 client
oauth := oauth2.Client{
RedirectURL: fmt.Sprintf("%s://%s/login/%s", httputil.GetScheme(r), httputil.GetHost(r), g.GetName()),
AccessTokenURL: fmt.Sprintf("%s/login/oauth/access_token", g.URL),
AuthorizationURL: fmt.Sprintf("%s/login/oauth/authorize", g.URL),
ClientId: g.Client,
ClientSecret: g.Secret,
}
// get the OAuth code
code := r.FormValue("code")
state := r.FormValue("state")
if len(code) == 0 {
var random = base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32))
httputil.SetCookie(w, r, "github_state", string(random))
// redirect the user to login
redirect := oauth.AuthorizeRedirect(scope, random)
http.Redirect(w, r, redirect, http.StatusSeeOther)
return nil, nil
}
cookieState := httputil.GetCookie(r, "github_state")
httputil.DelCookie(w, r, "github_state")
if cookieState != state {
return nil, fmt.Errorf("Error matching state in OAuth2 redirect")
}
// exchange code for an auth token
token, err := oauth.GrantToken(code)
if err != nil {
return nil, fmt.Errorf("Error granting GitHub authorization token. %s", err)
}
// create the client
client := github.New(token.AccessToken)
client.ApiUrl = g.API
// get the user information
user, err := client.Users.Current()
if err != nil {
return nil, fmt.Errorf("Error retrieving currently authenticated GitHub user. %s", err)
}
// put the user data in the common format
login := remote.Login{
ID: user.ID,
Login: user.Login,
Access: token.AccessToken,
Name: user.Name,
}
// get the users primary email address
email, err := client.Emails.FindPrimary()
if err == nil {
login.Email = email.Email
}
return &login, nil
}
// GetClient returns a new Github remote client.
func (g *Github) GetClient(access, secret string) remote.Client {
return &Client{g, access}
}
// IsMatch returns true if the hostname matches the
// hostname of this remote client.
func (g *Github) IsMatch(hostname string) bool {
return strings.HasSuffix(hostname, g.URL)
}

View File

@@ -0,0 +1,94 @@
package github
import (
"testing"
"github.com/drone/drone/plugin/remote/github/testdata"
"github.com/drone/drone/shared/model"
"github.com/franela/goblin"
)
func Test_Github(t *testing.T) {
// setup a dummy github server
var server = testdata.NewServer()
defer server.Close()
var github = GitHub{
URL: server.URL,
API: server.URL,
}
var user = model.User{
Access: "e3b0c44298fc1c149afbf4c8996fb",
}
var repo = model.Repo{
Owner: "octocat",
Name: "Hello-World",
}
var hook = model.Hook{
Sha: "6dcb09b5b57875f334f61aebed695e2e4193db5e",
}
g := goblin.Goblin(t)
g.Describe("GitHub Plugin", func() {
g.It("Should identify github vs github enterprise", func() {
var ghc = &GitHub{URL: "https://github.com"}
var ghe = &GitHub{URL: "https://github.drone.io"}
g.Assert(ghc.IsEnterprise()).IsFalse()
g.Assert(ghe.IsEnterprise()).IsTrue()
g.Assert(ghc.GetKind()).Equal(model.RemoteGithub)
g.Assert(ghe.GetKind()).Equal(model.RemoteGithubEnterprise)
})
g.It("Should parse the hostname", func() {
var ghc = &GitHub{URL: "https://github.com"}
var ghe = &GitHub{URL: "https://github.drone.io:80"}
g.Assert(ghc.GetHost()).Equal("github.com")
g.Assert(ghe.GetHost()).Equal("github.drone.io:80")
})
g.It("Should get the repo list", func() {
var repos, err = github.GetRepos(&user)
g.Assert(err == nil).IsTrue()
g.Assert(len(repos)).Equal(4)
g.Assert(repos[0].Name).Equal("Hello-World")
g.Assert(repos[0].Owner).Equal("octocat")
g.Assert(repos[0].Host).Equal(github.GetHost())
g.Assert(repos[0].Remote).Equal(github.GetKind())
g.Assert(repos[0].Private).Equal(true)
g.Assert(repos[0].CloneURL).Equal("git@github.com:octocat/Hello-World.git")
g.Assert(repos[0].SSHURL).Equal("git@github.com:octocat/Hello-World.git")
g.Assert(repos[0].GitURL).Equal("git://github.com/octocat/Hello-World.git")
g.Assert(repos[0].Role.Admin).Equal(true)
g.Assert(repos[0].Role.Read).Equal(true)
g.Assert(repos[0].Role.Write).Equal(true)
})
g.It("Should get the build script", func() {
var script, err = github.GetScript(&user, &repo, &hook)
g.Assert(err == nil).IsTrue()
g.Assert(string(script)).Equal("image: go")
})
g.It("Should activate a public repo", func() {
repo.Private = false
repo.CloneURL = "git://github.com/octocat/Hello-World.git"
repo.SSHURL = "git@github.com:octocat/Hello-World.git"
var err = github.Activate(&user, &repo, "http://example.com")
g.Assert(err == nil).IsTrue()
})
g.It("Should activate a private repo", func() {
repo.Name = "Hola-Mundo"
repo.Private = true
repo.CloneURL = "git@github.com:octocat/Hola-Mundo.git"
repo.SSHURL = "git@github.com:octocat/Hola-Mundo.git"
var err = github.Activate(&user, &repo, "http://example.com")
g.Assert(err == nil).IsTrue()
})
g.It("Should parse a commit hook")
g.It("Should parse a pull request hook")
})
}

View File

@@ -0,0 +1,238 @@
package github
import (
"encoding/base32"
"fmt"
"net/url"
"code.google.com/p/goauth2/oauth"
"github.com/google/go-github/github"
"github.com/gorilla/securecookie"
)
// NewClient is a helper function that returns a new GitHub
// client using the provided OAuth token.
func NewClient(uri, token string) *github.Client {
t := &oauth.Transport{
Token: &oauth.Token{AccessToken: token},
}
c := github.NewClient(t.Client())
c.BaseURL, _ = url.Parse(uri)
return c
}
// GetUserEmail is a heper function that retrieves the currently
// authenticated user from GitHub + Email address.
func GetUserEmail(client *github.Client) (*github.User, error) {
user, _, err := client.Users.Get("")
if err != nil {
return nil, err
}
emails, _, err := client.Users.ListEmails(nil)
if err != nil {
return nil, err
}
for _, email := range emails {
if *email.Primary && *email.Verified {
user.Email = email.Email
return user, nil
}
}
return nil, fmt.Errorf("No verified Email address for GitHub account")
}
// GetAllRepos is a helper function that returns an aggregated list
// of all user and organization repositories.
func GetAllRepos(client *github.Client) ([]github.Repository, error) {
orgs, err := GetOrgs(client)
if err != nil {
return nil, err
}
repos, err := GetUserRepos(client)
if err != nil {
return nil, err
}
for _, org := range orgs {
list, err := GetOrgRepos(client, *org.Login)
if err != nil {
return nil, err
}
repos = append(repos, list...)
}
return repos, nil
}
// GetUserRepos is a helper function that returns a list of
// all user repositories. Paginated results are aggregated into
// a single list.
func GetUserRepos(client *github.Client) ([]github.Repository, error) {
var repos []github.Repository
var opts = github.RepositoryListOptions{}
opts.PerPage = 100
opts.Page = 1
// loop through user repository list
for opts.Page > 0 {
list, resp, err := client.Repositories.List("", &opts)
if err != nil {
return nil, err
}
repos = append(repos, list...)
// increment the next page to retrieve
opts.Page = resp.NextPage
}
return repos, nil
}
// GetOrgRepos is a helper function that returns a list of
// all org repositories. Paginated results are aggregated into
// a single list.
func GetOrgRepos(client *github.Client, org string) ([]github.Repository, error) {
var repos []github.Repository
var opts = github.RepositoryListByOrgOptions{}
opts.PerPage = 100
opts.Page = 1
// loop through user repository list
for opts.Page > 0 {
list, resp, err := client.Repositories.ListByOrg(org, &opts)
if err != nil {
return nil, err
}
repos = append(repos, list...)
// increment the next page to retrieve
opts.Page = resp.NextPage
}
return repos, nil
}
// GetOrgs is a helper function that returns a list of
// all org repositories.
func GetOrgs(client *github.Client) ([]github.Organization, error) {
orgs, _, err := client.Organizations.List("", nil)
return orgs, err
}
// GetHook is a heper function that retrieves a hook by
// hostname. To do this, it will retrieve a list of all hooks
// and iterate through the list.
func GetHook(client *github.Client, owner, name, url string) (*github.Hook, error) {
hooks, _, err := client.Repositories.ListHooks(owner, name, nil)
if err != nil {
return nil, err
}
for _, hook := range hooks {
if hook.Config["url"] == url {
return &hook, nil
}
}
return nil, nil
}
// CreateHook is a heper function that creates a post-commit hook
// for the specified repository.
func CreateHook(client *github.Client, owner, name, url string) (*github.Hook, error) {
var hook = new(github.Hook)
hook.Name = github.String("web")
hook.Events = []string{"push", "pull_request"}
hook.Config = map[string]interface{}{}
hook.Config["url"] = url
hook.Config["content_type"] = "json"
created, _, err := client.Repositories.CreateHook(owner, name, hook)
return created, err
}
// CreateUpdateHook is a heper function that creates a post-commit hook
// for the specified repository if it does not already exist, otherwise
// it updates the existing hook
func CreateUpdateHook(client *github.Client, owner, name, url string) (*github.Hook, error) {
var hook, _ = GetHook(client, owner, name, url)
if hook != nil {
hook.Name = github.String("web")
hook.Events = []string{"push", "pull_request"}
hook.Config = map[string]interface{}{}
hook.Config["url"] = url
hook.Config["content_type"] = "json"
var updated, _, err = client.Repositories.EditHook(owner, name, *hook.ID, hook)
return updated, err
}
return CreateHook(client, owner, name, url)
}
// GetKey is a heper function that retrieves a public Key by
// title. To do this, it will retrieve a list of all keys
// and iterate through the list.
func GetKey(client *github.Client, owner, name, title string) (*github.Key, error) {
keys, _, err := client.Repositories.ListKeys(owner, name, nil)
if err != nil {
return nil, err
}
for _, key := range keys {
if *key.Title == title {
return &key, nil
}
}
return nil, nil
}
// GetKeyTitle is a helper function that generates a title for the
// RSA public key based on the username and domain name.
func GetKeyTitle(rawurl string) (string, error) {
var uri, err = url.Parse(rawurl)
if err != nil {
return "", err
}
return fmt.Sprintf("drone@%s", uri.Host), nil
}
// CreateKey is a heper function that creates a deploy key
// for the specified repository.
func CreateKey(client *github.Client, owner, name, title, key string) (*github.Key, error) {
var k = new(github.Key)
k.Title = github.String(title)
k.Key = github.String(key)
created, _, err := client.Repositories.CreateKey(owner, name, k)
return created, err
}
// CreateUpdateKey is a heper function that creates a deployment key
// for the specified repository if it does not already exist, otherwise
// it updates the existing key
func CreateUpdateKey(client *github.Client, owner, name, title, key string) (*github.Key, error) {
var k, _ = GetKey(client, owner, name, title)
if k != nil {
k.Title = github.String(title)
k.Key = github.String(key)
var updated, _, err = client.Repositories.EditKey(owner, name, *k.ID, k)
return updated, err
}
return CreateKey(client, owner, name, title, key)
}
// GetFile is a heper function that retrieves a file from
// GitHub and returns its contents in byte array format.
func GetFile(client *github.Client, owner, name, path, ref string) ([]byte, error) {
var opts = new(github.RepositoryContentGetOptions)
opts.Ref = ref
content, _, _, err := client.Repositories.GetContents(owner, name, path, opts)
if err != nil {
return nil, err
}
return content.Decode()
}
// GetRandom is a helper function that generates a 32-bit random
// key, base32 encoded as a string value.
func GetRandom() string {
return base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32))
}

View File

@@ -0,0 +1,34 @@
package github
import (
"testing"
"github.com/drone/drone/plugin/remote/github/testdata"
"github.com/franela/goblin"
)
func Test_Helper(t *testing.T) {
// setup a dummy github server
var server = testdata.NewServer()
defer server.Close()
g := goblin.Goblin(t)
g.Describe("GitHub Helper Functions", func() {
g.It("Should Get a User")
g.It("Should Get a User Primary Email")
g.It("Should Get a User + Primary Email")
g.It("Should Get a list of Orgs")
g.It("Should Get a list of User Repos")
g.It("Should Get a list of Org Repos")
g.It("Should Get a list of All Repos")
g.It("Should Get a Repo Key")
g.It("Should Get a Repo Hook")
g.It("Should Create a Repo Key")
g.It("Should Create a Repo Hook")
g.It("Should Create or Update a Repo Key")
g.It("Should Create or Update a Repo Hook")
g.It("Should Get a Repo File")
})
}

View File

@@ -1,21 +0,0 @@
package github
import (
"github.com/drone/drone/plugin/remote"
"github.com/drone/drone/shared/model"
)
func init() {
remote.Register(model.RemoteGithub, plugin)
remote.Register(model.RemoteGithubEnterprise, plugin)
}
func plugin(remote *model.Remote) remote.Remote {
return &Github{
URL: remote.URL,
API: remote.API,
Client: remote.Client,
Secret: remote.Secret,
Enabled: remote.Open,
}
}

View File

@@ -0,0 +1,41 @@
package github
import (
"os"
"github.com/drone/drone/plugin/remote"
)
func init() {
init_github()
init_github_enterprise()
}
// registers the GitHub (github.com) plugin
func init_github() {
var cli = os.Getenv("GITHUB_CLIENT")
var sec = os.Getenv("GITHUB_SECRET")
if len(cli) == 0 ||
len(sec) == 0 {
return
}
var github = NewDefault(cli, sec)
remote.Register(github)
}
// registers the GitHub Enterprise plugin
func init_github_enterprise() {
var url = os.Getenv("GITHUB_ENTERPRISE_URL")
var api = os.Getenv("GITHUB_ENTERPRISE_API")
var cli = os.Getenv("GITHUB_ENTERPRISE_CLIENT")
var sec = os.Getenv("GITHUB_ENTERPRISE_SECRET")
if len(url) == 0 ||
len(api) == 0 ||
len(cli) == 0 ||
len(sec) == 0 {
return
}
var github = New(url, api, cli, sec)
remote.Register(github)
}

View File

@@ -0,0 +1,148 @@
package testdata
import (
"net/http"
"net/http/httptest"
)
// setup a mock server for testing purposes.
func NewServer() *httptest.Server {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
// handle requests and serve mock data
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// evaluate the path to serve a dummy data file
switch r.URL.Path {
case "/user/repos":
w.Write(userReposPayload)
return
case "/user/orgs":
w.Write(userOrgsPayload)
return
case "/orgs/github/repos":
w.Write(userReposPayload)
return
case "/repos/octocat/Hello-World/contents/.drone.yml":
w.Write(droneYamlPayload)
return
case "/repos/octocat/Hello-World/hooks":
switch r.Method {
case "POST":
w.Write(createHookPayload)
return
}
case "/repos/octocat/Hola-Mundo/hooks":
switch r.Method {
case "POST":
w.Write(createHookPayload)
return
}
case "/repos/octocat/Hola-Mundo/keys":
switch r.Method {
case "POST":
w.Write(createKeyPayload)
return
}
}
// else return a 404
http.NotFound(w, r)
})
// return the server to the client which
// will need to know the base URL path
return server
}
// sample repository list
var userReposPayload = []byte(`
[
{
"owner": {
"login": "octocat",
"id": 1
},
"id": 1296269,
"name": "Hello-World",
"full_name": "octocat/Hello-World",
"private": true,
"url": "https://api.github.com/repos/octocat/Hello-World",
"html_url": "https://github.com/octocat/Hello-World",
"clone_url": "https://github.com/octocat/Hello-World.git",
"git_url": "git://github.com/octocat/Hello-World.git",
"ssh_url": "git@github.com:octocat/Hello-World.git",
"permissions": {
"admin": true,
"push": true,
"pull": true
}
},
{
"owner": {
"login": "octocat",
"id": 1
},
"id": 9626921,
"name": "Hola-Mundo",
"full_name": "octocat/Hola-Mundo",
"private": false,
"url": "https://api.github.com/repos/octocat/Hola-Mundo",
"html_url": "https://github.com/octocat/Hola-Mundo",
"clone_url": "https://github.com/octocat/Hola-Mundo.git",
"git_url": "git://github.com/octocat/Hola-Mundo.git",
"ssh_url": "git@github.com:octocat/Hola-Mundo.git",
"permissions": {
"admin": false,
"push": false,
"pull": true
}
}
]
`)
var emptySetPayload = []byte(`[]`)
var emptyObjPayload = []byte(`{}`)
// sample org list response
var userOrgsPayload = []byte(`
[
{ "login": "github", "id": 1 }
]
`)
// sample content response for .drone.yml request
var droneYamlPayload = []byte(`
{
"type": "file",
"encoding": "base64",
"name": ".drone.yml",
"path": ".drone.yml",
"content": "aW1hZ2U6IGdv"
}
`)
// sample create hook response
var createHookPayload = []byte(`
{
"id": 1,
"name": "web",
"events": [ "push", "pull_request" ],
"active": true,
"config": {
"url": "http://example.com",
"content_type": "json"
}
}
`)
// sample create hook response
var createKeyPayload = []byte(`
{
"id": 1,
"key": "ssh-rsa AAA...",
"url": "https://api.github.com/user/keys/1",
"title": "octocat@octomac"
}
`)