mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2026-03-23 02:15:55 +01:00
added github remote
This commit is contained in:
384
remote/github/github.go
Normal file
384
remote/github/github.go
Normal file
@@ -0,0 +1,384 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/drone/drone/common"
|
||||
"github.com/drone/drone/settings"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultAPI = "https://api.github.com/"
|
||||
DefaultURL = "https://github.com"
|
||||
DefaultScope = "repo,repo:status,user:email"
|
||||
)
|
||||
|
||||
type GitHub struct {
|
||||
URL string
|
||||
API string
|
||||
Client string
|
||||
Secret string
|
||||
PrivateMode bool
|
||||
SkipVerify bool
|
||||
Orgs []string
|
||||
Open bool
|
||||
|
||||
cache *lru.Cache
|
||||
}
|
||||
|
||||
func New(service *settings.Service) *GitHub {
|
||||
var github = GitHub{
|
||||
API: DefaultAPI,
|
||||
URL: DefaultURL,
|
||||
Client: service.OAuth.Client,
|
||||
Secret: service.OAuth.Secret,
|
||||
PrivateMode: service.PrivateMode,
|
||||
SkipVerify: service.SkipVerify,
|
||||
Orgs: service.Orgs,
|
||||
Open: service.Open,
|
||||
}
|
||||
var err error
|
||||
github.cache, err = lru.New(1028)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// if GitHub enterprise then ensure we're using the
|
||||
// appropriate URLs
|
||||
if !strings.HasPrefix(service.Base, DefaultURL) && len(service.Base) != 0 {
|
||||
github.URL = service.Base
|
||||
github.API = service.Base + "/api/v3/"
|
||||
}
|
||||
// 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
|
||||
}
|
||||
|
||||
func (g *GitHub) Login(token, secret string) (*common.User, error) {
|
||||
client := NewClient(g.API, token, g.SkipVerify)
|
||||
login, err := GetUserEmail(client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user := common.User{}
|
||||
user.Login = *login.Login
|
||||
user.Email = *login.Email
|
||||
user.Name = *login.Name
|
||||
user.Token = token
|
||||
user.Secret = secret
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// Repo fetches the named repository from the remote system.
|
||||
func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
repo_, err := GetRepo(client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &common.Repo{}
|
||||
repo.Owner = owner
|
||||
repo.Name = name
|
||||
repo.Language = *repo_.Language
|
||||
repo.FullName = *repo_.FullName
|
||||
repo.Link = *repo_.HTMLURL
|
||||
repo.Private = *repo_.Private
|
||||
repo.Clone = *repo_.CloneURL
|
||||
|
||||
if g.PrivateMode {
|
||||
repo.Private = true
|
||||
}
|
||||
return repo, err
|
||||
}
|
||||
|
||||
// Perm fetches the named repository from the remote system.
|
||||
func (g *GitHub) Perm(u *common.User, owner, name string) (*common.Perm, error) {
|
||||
key := fmt.Sprintf("%s/%s/%s", u.Login, owner, name)
|
||||
val, ok := g.cache.Get(key)
|
||||
if ok {
|
||||
return val.(*common.Perm), nil
|
||||
}
|
||||
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
repo, err := GetRepo(client, owner, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m := &common.Perm{}
|
||||
m.Login = u.Login
|
||||
m.Admin = (*repo.Permissions)["admin"]
|
||||
m.Push = (*repo.Permissions)["push"]
|
||||
m.Pull = (*repo.Permissions)["pull"]
|
||||
g.cache.Add(key, m)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Script fetches the build script (.drone.yml) from the remote
|
||||
// repository and returns in string format.
|
||||
func (g *GitHub) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
var sha string
|
||||
if b.Commit != nil {
|
||||
sha = b.Commit.Sha
|
||||
} else {
|
||||
sha = b.PullRequest.Source.Sha
|
||||
}
|
||||
return GetFile(client, r.Owner, r.Name, ".drone.yml", sha)
|
||||
}
|
||||
|
||||
// Activate activates a repository by creating the post-commit hook and
|
||||
// adding the SSH deploy key, if applicable.
|
||||
func (g *GitHub) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
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 r.Private || g.PrivateMode {
|
||||
_, err = CreateUpdateKey(client, r.Owner, r.Name, title, k.Public)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
_, err = CreateUpdateHook(client, r.Owner, r.Name, link)
|
||||
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 *GitHub) Deactivate(u *common.User, r *common.Repo, link string) error {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
title, err := GetKeyTitle(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove the deploy-key if it is installed remote.
|
||||
if r.Private || g.PrivateMode {
|
||||
if err := DeleteKey(client, r.Owner, r.Name, title); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return DeleteHook(client, r.Owner, r.Name, link)
|
||||
}
|
||||
|
||||
func (g *GitHub) Status(u *common.User, r *common.Repo, b *common.Build, link string) error {
|
||||
client := NewClient(g.API, u.Token, g.SkipVerify)
|
||||
var ref string
|
||||
if b.Commit != nil {
|
||||
ref = b.Commit.Ref
|
||||
} else {
|
||||
ref = b.PullRequest.Source.Ref
|
||||
}
|
||||
status := getStatus(b.State)
|
||||
desc := getDesc(b.State)
|
||||
data := github.RepoStatus{
|
||||
Context: github.String("Drone"),
|
||||
State: github.String(status),
|
||||
Description: github.String(desc),
|
||||
TargetURL: github.String(link),
|
||||
}
|
||||
_, _, err := client.Repositories.CreateStatus(r.Owner, r.Name, ref, &data)
|
||||
return err
|
||||
}
|
||||
|
||||
// Hook parses the post-commit hook from the Request body
|
||||
// and returns the required data in a standard format.
|
||||
func (g *GitHub) Hook(r *http.Request) (*common.Hook, error) {
|
||||
switch r.Header.Get("X-Github-Event") {
|
||||
case "pull_request":
|
||||
return g.pullRequest(r)
|
||||
case "push":
|
||||
return g.push(r)
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// push parses a hook with event type `push` and returns
|
||||
// the commit data.
|
||||
func (g *GitHub) push(r *http.Request) (*common.Hook, error) {
|
||||
payload := GetPayload(r)
|
||||
hook := &pushHook{}
|
||||
err := json.Unmarshal(payload, hook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repo := &common.Repo{}
|
||||
repo.Owner = hook.Repo.Owner.Login
|
||||
repo.Name = hook.Repo.Name
|
||||
repo.Language = hook.Repo.Language
|
||||
repo.FullName = hook.Repo.FullName
|
||||
repo.Link = hook.Repo.HTMLURL
|
||||
repo.Private = hook.Repo.Private
|
||||
repo.Clone = hook.Repo.CloneURL
|
||||
|
||||
commit := &common.Commit{}
|
||||
commit.Sha = hook.Head.ID
|
||||
commit.Ref = hook.Ref
|
||||
commit.Message = hook.Head.Message
|
||||
commit.Timestamp = hook.Head.Timestamp
|
||||
|
||||
commit.Author = &common.Author{}
|
||||
commit.Author.Name = hook.Head.Author.Name
|
||||
commit.Author.Email = hook.Head.Author.Email
|
||||
commit.Author.Login = hook.Head.Author.Username
|
||||
|
||||
// we should ignore github pages
|
||||
if commit.Ref == "refs/heads/gh-pages" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &common.Hook{Repo: repo, Commit: commit}, nil
|
||||
}
|
||||
|
||||
// pullRequest parses a hook with event type `pullRequest`
|
||||
// and returns the commit data.
|
||||
func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) {
|
||||
payload := GetPayload(r)
|
||||
hook := &struct {
|
||||
Action string `json:"action"`
|
||||
PullRequest *github.PullRequest `json:"pull_request"`
|
||||
Repo *github.Repository `json:"repository"`
|
||||
}{}
|
||||
err := json.Unmarshal(payload, hook)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ignore these
|
||||
if hook.Action != "opened" && hook.Action != "synchronize" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
repo := &common.Repo{}
|
||||
repo.Owner = *hook.Repo.Owner.Login
|
||||
repo.Name = *hook.Repo.Name
|
||||
repo.Language = *hook.Repo.Language
|
||||
repo.FullName = *hook.Repo.FullName
|
||||
repo.Link = *hook.Repo.HTMLURL
|
||||
repo.Private = *hook.Repo.Private
|
||||
repo.Clone = *hook.Repo.CloneURL
|
||||
|
||||
pr := &common.PullRequest{}
|
||||
pr.Number = *hook.PullRequest.Number
|
||||
pr.Title = *hook.PullRequest.Title
|
||||
|
||||
pr.Source = &common.Commit{}
|
||||
pr.Source.Sha = *hook.PullRequest.Head.SHA
|
||||
pr.Source.Ref = *hook.PullRequest.Head.Ref
|
||||
pr.Source.Author = &common.Author{}
|
||||
pr.Source.Author.Login = *hook.PullRequest.User.Login
|
||||
|
||||
pr.Source.Remote = &common.Remote{}
|
||||
pr.Source.Remote.Clone = *hook.PullRequest.Head.Repo.CloneURL
|
||||
pr.Source.Remote.Name = *hook.PullRequest.Head.Repo.Name
|
||||
pr.Source.Remote.FullName = *hook.PullRequest.Head.Repo.FullName
|
||||
|
||||
pr.Target = &common.Commit{}
|
||||
pr.Target.Sha = *hook.PullRequest.Base.SHA
|
||||
pr.Target.Ref = *hook.PullRequest.Base.Ref
|
||||
|
||||
return &common.Hook{Repo: repo, PullRequest: pr}, nil
|
||||
}
|
||||
|
||||
type pushHook struct {
|
||||
Ref string `json:"ref"`
|
||||
|
||||
Head struct {
|
||||
ID string `json:"id"`
|
||||
Message string `json:"message"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
|
||||
Author struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
} `json:"author"`
|
||||
|
||||
Committer struct {
|
||||
Name string `json:"name"`
|
||||
Email string `json:"name"`
|
||||
Username string `json:"username"`
|
||||
} `json:"committer"`
|
||||
} `json:"head_commit"`
|
||||
|
||||
Repo struct {
|
||||
Owner struct {
|
||||
Login string `json:"login"`
|
||||
} `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
FullName string `json:"full_name"`
|
||||
Language string `json:"language"`
|
||||
Private bool `json:"private"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
} `json:"repository"`
|
||||
}
|
||||
|
||||
const (
|
||||
StatusPending = "pending"
|
||||
StatusSuccess = "success"
|
||||
StatusFailure = "failure"
|
||||
StatusError = "error"
|
||||
)
|
||||
|
||||
const (
|
||||
DescPending = "this build is pending"
|
||||
DescSuccess = "the build was successful"
|
||||
DescFailure = "the build failed"
|
||||
DescError = "oops, something went wrong"
|
||||
)
|
||||
|
||||
// getStatus is a helper functin that converts a Drone
|
||||
// status to a GitHub status.
|
||||
func getStatus(status string) string {
|
||||
switch status {
|
||||
case common.StatePending, common.StateRunning:
|
||||
return StatusPending
|
||||
case common.StateSuccess:
|
||||
return StatusSuccess
|
||||
case common.StateFailure:
|
||||
return StatusFailure
|
||||
case common.StateError, common.StateKilled:
|
||||
return StatusError
|
||||
default:
|
||||
return StatusError
|
||||
}
|
||||
}
|
||||
|
||||
// getDesc is a helper function that generates a description
|
||||
// message for the build based on the status.
|
||||
func getDesc(status string) string {
|
||||
switch status {
|
||||
case common.StatePending, common.StateRunning:
|
||||
return DescPending
|
||||
case common.StateSuccess:
|
||||
return DescSuccess
|
||||
case common.StateFailure:
|
||||
return DescFailure
|
||||
case common.StateError, common.StateKilled:
|
||||
return DescError
|
||||
default:
|
||||
return DescError
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user