package github import ( "encoding/base32" "fmt" "io/ioutil" "net/http" "net/url" "strings" "github.com/drone/drone/plugin/remote/github/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 } // WARNING, HACK // for out-of-date github enterprise editions the primary // and verified fields won't exist. if !strings.HasPrefix(*user.HTMLURL, DefaultURL) && len(emails) != 0 && emails[0].Primary == nil { user.Email = emails[0].Email return user, nil } // else we should iterate through the list 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"] = "form" 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"] = "form" 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)) } // GetPayload is a helper function that will parse the JSON payload. It will // first check for a `payload` parameter in a POST, but can fallback to a // raw JSON body as well. func GetPayload(req *http.Request) []byte { var payload = req.FormValue("payload") if len(payload) == 0 { raw, _ := ioutil.ReadAll(req.Body) return raw } return []byte(payload) }