package github import ( "crypto/tls" "encoding/base32" "fmt" "io/ioutil" "net/http" "net/url" "strings" "github.com/drone/drone/shared/oauth2" "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, skipVerify bool) *github.Client { t := &oauth2.Transport{ Token: &oauth2.Token{AccessToken: token}, } // this is for GitHub enterprise users that are using // self-signed certificates. if skipVerify { t.Transport = &http.Transport{ Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } } 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 } } // WARNING, HACK // for out-of-date github enterprise editions the primary // and verified fields won't exist. if !strings.HasPrefix(*user.URL, DefaultAPI) && len(emails) != 0 { user.Email = emails[0].Email return user, nil } return nil, fmt.Errorf("No verified Email address for GitHub account") } // GetRepo is a helper function that returns a named repo func GetRepo(client *github.Client, owner, repo string) (*github.Repository, error) { r, _, err := client.Repositories.Get(owner, repo) return r, err } // 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 } // GetOrgs is a helper function that returns a list of // all orgs that a user belongs to. func GetOrgs(client *github.Client) ([]github.Organization, error) { var orgs []github.Organization var opts = github.ListOptions{} opts.Page = 1 for opts.Page > 0 { list, resp, err := client.Organizations.List("", &opts) if err != nil { return nil, err } orgs = append(orgs, list...) // increment the next page to retrieve opts.Page = resp.NextPage } return orgs, nil } // 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 { hookurl, ok := hook.Config["url"].(string) if !ok { continue } if strings.HasPrefix(hookurl, url) { return &hook, nil } } return nil, nil } func DeleteHook(client *github.Client, owner, name, url string) error { hook, err := GetHook(client, owner, name, url) if err != nil { return err } if hook == nil { return nil } _, err = client.Repositories.DeleteHook(owner, name, *hook.ID) return err } // 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", "deployment"} 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) } // 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 { defer req.Body.Close() raw, _ := ioutil.ReadAll(req.Body) return raw } return []byte(payload) }