You've already forked woodpecker
mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-12-23 22:01:08 +02:00
Move Plugins to a /plugin Directory
This commit is contained in:
156
plugin/remote/github/client.go
Normal file
156
plugin/remote/github/client.go
Normal file
@@ -0,0 +1,156 @@
|
||||
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)
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
}
|
||||
201
plugin/remote/github/github.go
Normal file
201
plugin/remote/github/github.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/plugin/remote"
|
||||
"github.com/drone/drone/shared/util/httputil"
|
||||
"github.com/drone/go-github/github"
|
||||
"github.com/drone/go-github/oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
scope = "repo,repo:status,user:email"
|
||||
state = "FqB4EbagQ2o"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
|
||||
// GetHost returns the url.Host of this remote system.
|
||||
func (g *Github) GetHost() (host string) {
|
||||
u, err := url.Parse(g.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return u.Host
|
||||
}
|
||||
|
||||
// GetHook 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) {
|
||||
// handle github ping
|
||||
if r.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)
|
||||
}
|
||||
|
||||
// get the payload of the message
|
||||
payload := r.FormValue("payload")
|
||||
|
||||
// parse the github Hook payload
|
||||
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() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
hook := remote.Hook{}
|
||||
hook.Repo = data.Repo.Name
|
||||
hook.Owner = data.Repo.Owner.Login
|
||||
hook.Sha = data.Head.Id
|
||||
hook.Branch = data.Branch()
|
||||
|
||||
if len(hook.Owner) == 0 {
|
||||
hook.Owner = data.Repo.Owner.Name
|
||||
}
|
||||
|
||||
// extract the author and message from the commit
|
||||
// this is kind of experimental, since I don't know
|
||||
// what I'm doing here.
|
||||
if data.Head != nil && data.Head.Author != nil {
|
||||
hook.Message = data.Head.Message
|
||||
hook.Timestamp = data.Head.Timestamp
|
||||
hook.Author = data.Head.Author.Email
|
||||
} else if data.Commits != nil && len(data.Commits) > 0 && data.Commits[0].Author != nil {
|
||||
hook.Message = data.Commits[0].Message
|
||||
hook.Timestamp = data.Commits[0].Timestamp
|
||||
hook.Author = data.Commits[0].Author.Email
|
||||
}
|
||||
|
||||
return &hook, nil
|
||||
}
|
||||
|
||||
func (g *Github) GetPullRequestHook(r *http.Request) (*remote.Hook, error) {
|
||||
payload := r.FormValue("payload")
|
||||
|
||||
// parse the payload to retrieve the pull-request
|
||||
// hook meta-data.
|
||||
data, err := github.ParsePullRequestHook([]byte(payload))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ignore these
|
||||
if data.Action != "opened" && data.Action != "synchronize" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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{
|
||||
Owner: data.Repo.Owner.Login,
|
||||
Repo: data.Repo.Name,
|
||||
Sha: data.PullRequest.Head.Sha,
|
||||
Branch: data.PullRequest.Base.Ref,
|
||||
Author: data.PullRequest.User.Login,
|
||||
Gravatar: data.PullRequest.User.GravatarId,
|
||||
Timestamp: time.Now().UTC().String(),
|
||||
Message: data.PullRequest.Title,
|
||||
PullRequest: strconv.Itoa(data.Number),
|
||||
}
|
||||
|
||||
if len(hook.Owner) == 0 {
|
||||
hook.Owner = data.Repo.Owner.Name
|
||||
}
|
||||
|
||||
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/github.com", httputil.GetScheme(r), httputil.GetHost(r)),
|
||||
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")
|
||||
if len(code) == 0 {
|
||||
redirect := oauth.AuthorizeRedirect(scope, state)
|
||||
http.Redirect(w, r, redirect, http.StatusSeeOther)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// 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)
|
||||
}
|
||||
Reference in New Issue
Block a user