Skip to content

Instantly share code, notes, and snippets.

@radeksimko
Created June 26, 2015 09:21
Show Gist options
  • Select an option

  • Save radeksimko/ba515be2f007af659cd1 to your computer and use it in GitHub Desktop.

Select an option

Save radeksimko/ba515be2f007af659cd1 to your computer and use it in GitHub Desktop.
Get number of contributions & followers for all members of a GH organization. Run as `go run github_contributions.go ORG_NAME`
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strconv"
"strings"
"github.com/google/go-github/github"
"golang.org/x/net/html"
"golang.org/x/oauth2"
"launchpad.net/xmlpath"
)
var GithubToken = os.Getenv("GITHUB_TOKEN")
var OrgName = os.Args[1]
const GithubProfileUrl = "https://github.com/%s"
const GithubContributionsXpath = `//span[@class="contrib-number"]`
func main() {
log.SetOutput(os.Stderr)
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: GithubToken},
)
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)
err := GetMembers(client, 0, processMember)
if err != nil {
fmt.Println(err.Error())
}
}
func GetMembers(client *github.Client, pageNum int,
callback func(*github.User, *github.Client) error) error {
members, resp, err := client.Organizations.ListMembers(OrgName,
&github.ListMembersOptions{
ListOptions: github.ListOptions{
Page: pageNum,
},
})
log.Printf("Remaining requests: %d", resp.Remaining)
if err != nil {
return err
}
for _, member := range members {
err = callback(&member, client)
if err != nil {
return err
}
}
if resp.NextPage != pageNum {
pageNum += 1
return GetMembers(client, pageNum, callback)
} else {
fmt.Printf("%#v", resp)
}
return nil
}
func processMember(rawUser *github.User, client *github.Client) error {
user, resp, err := client.Users.Get(*rawUser.Login)
log.Printf("Remaining requests: %d", resp.Remaining)
if err != nil {
return err
}
fmt.Printf("%s\t", *user.Login)
fmt.Printf("%d\t", *user.Followers)
contributions, err := GetTotalContributions(*user.Login)
if err != nil {
return err
}
fmt.Printf("%d\t", contributions)
if user.Hireable != nil {
fmt.Printf("%t", *user.Hireable)
} else {
fmt.Printf("%t", false)
}
fmt.Println("")
return nil
}
func GetTotalContributions(username string) (int, error) {
url := fmt.Sprintf(GithubProfileUrl, username)
resp, err := http.Get(url)
if err != nil {
return -1, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fixedHtml, err := fixBrokenHtml(string(body))
if err != nil {
return -1, err
}
reader := strings.NewReader(fixedHtml)
xmlroot, err := xmlpath.ParseHTML(reader)
if err != nil {
return -1, err
}
path := xmlpath.MustCompile(GithubContributionsXpath)
if value, ok := path.String(xmlroot); ok {
v := strings.TrimSuffix(value, " total")
i, err := strconv.ParseInt(v, 0, 0)
if err != nil {
return -1, err
}
return int(i), nil
}
return -1, fmt.Errorf("Failed to parse contributions for %q", username)
}
func fixBrokenHtml(brokenHtml string) (string, error) {
reader := strings.NewReader(brokenHtml)
root, err := html.Parse(reader)
if err != nil {
return "", err
}
var b bytes.Buffer
html.Render(&b, root)
return b.String(), nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment