From 40e09a1f6cbacf080324f0183437d6f7d91bdd5f Mon Sep 17 00:00:00 2001 From: Salman Aljammaz Date: Wed, 16 Oct 2013 18:10:07 +0100 Subject: [PATCH] script to generate contributors page from git shortlog. http://camlistore.org/issue/15 Change-Id: I670e19424772530b190326ba125fdf1122ff29af --- website/contriburls.json | 9 ++ website/scripts/gencontrib/gencontrib.go | 174 +++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 website/contriburls.json create mode 100644 website/scripts/gencontrib/gencontrib.go diff --git a/website/contriburls.json b/website/contriburls.json new file mode 100644 index 000000000..d91cb494c --- /dev/null +++ b/website/contriburls.json @@ -0,0 +1,9 @@ +{ + "brad@danga.com": "http://bradfitz.com/", + "bslatkin@gmail.com": "http://www.onebigfluke.com/", + "adg@golang.org": "http://nf.id.au/", + "dan@erat.org": "http://www.erat.org/", + "martine@danga.com": "http://neugierig.org/", + "agl@golang.org": "http://www.imperialviolet.org/", + "s@0x65.net": "http://0x65.net/" +} diff --git a/website/scripts/gencontrib/gencontrib.go b/website/scripts/gencontrib/gencontrib.go new file mode 100644 index 000000000..1541e3687 --- /dev/null +++ b/website/scripts/gencontrib/gencontrib.go @@ -0,0 +1,174 @@ +// The gencontrib binary generates an HTML list of git repository contributers. +package main + +import ( + "bufio" + "encoding/json" + "flag" + "fmt" + "html/template" + "io" + "log" + "os" + "os/exec" + "sort" + "strconv" + "strings" +) + +var contribStr = `

Contributors

+ +

Camlistore contributors include:

+ + + +

Want to help? See contributing.

+` + +var urlsFile = flag.String("urls", "", "email → url map file") + +func addURLs(idx map[string]*author) { + if *urlsFile == "" { + return + } + + f, err := os.Open(*urlsFile) + if err != nil { + log.Fatal("couldn't open urls file:", *urlsFile) + } + + dec := json.NewDecoder(f) + var mapping map[string]interface{} + err = dec.Decode(&mapping) + if err != nil { + log.Fatal("couldn't parse urls file:", err) + } + + for email, url := range mapping { + a := idx[email] + if a != nil { + a.URL = url.(string) + } else { + log.Printf("email %v is not a commiter", email) + } + } +} + +type author struct { + Names []string + Emails []string + Commits int + URL string +} + +func (a *author) add(src *author) { + if src == nil { + return + } + a.Emails = append(a.Emails, src.Emails...) + a.Names = append(a.Names, src.Names...) + a.Commits += src.Commits +} + +type Authors []*author + +func (s Authors) Len() int { return len(s) } +func (s Authors) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s Authors) Less(i, j int) bool { return s[i].Commits > s[j].Commits } + +func parseLine(l string) (name, email string, commits int, err error) { + t := strings.Split(strings.TrimSpace(l), " ") + if len(t) < 2 { + err = fmt.Errorf("line too short") + return + } + i := strings.LastIndex(t[1], " ") + email = strings.Trim(t[1][i+1:], "<>") + name = t[1][:i] + commits, err = strconv.Atoi(t[0]) + return +} + +func shortlog() io.Reader { + gitlog := exec.Command("git", "log") + gitlogOut, err := gitlog.StdoutPipe() + if err != nil { + log.Fatal(err) + } + gitlog.Start() + if err != nil { + log.Fatal("couldn't run git log:", err) + } + + shortlog := exec.Command("git", "shortlog", "-sen") + shortlog.Stdin = gitlogOut + shortlogOut, err := shortlog.StdoutPipe() + if err != nil { + log.Fatal(err) + } + shortlog.Start() + if err != nil { + log.Fatal("couldn't run git shortlog:", err) + } + + return shortlogOut +} + +func main() { + flag.Parse() + contribHtml, err := template.New("contrib").Parse(contribStr) + if err != nil { + log.Fatal("couldn't parse template") + } + + byName := make(map[string]*author) + byEmail := make(map[string]*author) + authorMap := make(map[*author]bool) + + sl := shortlog() + + scn := bufio.NewScanner(sl) + for scn.Scan() { + name, email, commits, err := parseLine(scn.Text()) + if err != nil { + log.Fatalf("couldn't parse line \"%v\": %v", scn.Text(), err) + } + + a := &author{ + Emails: []string{email}, + Names: []string{name}, + Commits: commits, + } + + a.add(byName[name]) + a.add(byEmail[email]) + for _, n := range a.Names { + delete(authorMap, byName[n]) + byName[n] = a + } + for _, e := range a.Emails { + delete(authorMap, byEmail[e]) + byEmail[e] = a + } + authorMap[a] = true + } + if scn.Err() != nil { + log.Fatal(err) + } + + addURLs(byEmail) + + authors := Authors{} + for a, _ := range authorMap { + authors = append(authors, a) + } + + sort.Sort(authors) + + if err := contribHtml.Execute(os.Stdout, authors); err != nil { + log.Fatalf("executing template: %v", err) + } +}