github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/website/scripts/gencontrib/gencontrib.go (about)

     1  // The gencontrib binary generates an HTML list of git repository contributers.
     2  package main
     3  
     4  import (
     5  	"bufio"
     6  	"encoding/json"
     7  	"flag"
     8  	"fmt"
     9  	"html/template"
    10  	"io"
    11  	"log"
    12  	"os"
    13  	"os/exec"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  var contribStr = `<h1>Contributors</h1>
    20  
    21  <p>Camlistore contributors include:</p>
    22  
    23  <ul>
    24  {{range .}}<li>{{if .URL}}<a href="{{.URL}}">{{index .Names 0}}</a>{{else}}{{index .Names 0}}{{end}}</li>
    25  {{end}}
    26  </ul>
    27  
    28  <p>Want to help?  See <a href="/docs/contributing">contributing</a>.</p>
    29  `
    30  
    31  var urlsFile = flag.String("urls", "", "email → url map file")
    32  
    33  func addURLs(idx map[string]*author) {
    34  	if *urlsFile == "" {
    35  		return
    36  	}
    37  
    38  	f, err := os.Open(*urlsFile)
    39  	if err != nil {
    40  		log.Fatal("couldn't open urls file:", *urlsFile)
    41  	}
    42  
    43  	dec := json.NewDecoder(f)
    44  	var mapping map[string]interface{}
    45  	err = dec.Decode(&mapping)
    46  	if err != nil {
    47  		log.Fatal("couldn't parse urls file:", err)
    48  	}
    49  
    50  	for email, url := range mapping {
    51  		a := idx[email]
    52  		if a != nil {
    53  			a.URL = url.(string)
    54  		} else {
    55  			log.Printf("email %v is not a commiter", email)
    56  		}
    57  	}
    58  }
    59  
    60  type author struct {
    61  	Names   []string
    62  	Emails  []string
    63  	Commits int
    64  	URL     string
    65  }
    66  
    67  func (a *author) add(src *author) {
    68  	if src == nil {
    69  		return
    70  	}
    71  	a.Emails = append(a.Emails, src.Emails...)
    72  	a.Names = append(a.Names, src.Names...)
    73  	a.Commits += src.Commits
    74  }
    75  
    76  type Authors []*author
    77  
    78  func (s Authors) Len() int           { return len(s) }
    79  func (s Authors) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    80  func (s Authors) Less(i, j int) bool { return s[i].Commits > s[j].Commits }
    81  
    82  func parseLine(l string) (name, email string, commits int, err error) {
    83  	t := strings.Split(strings.TrimSpace(l), "	")
    84  	if len(t) < 2 {
    85  		err = fmt.Errorf("line too short")
    86  		return
    87  	}
    88  	i := strings.LastIndex(t[1], " ")
    89  	email = strings.Trim(t[1][i+1:], "<>")
    90  	name = t[1][:i]
    91  	commits, err = strconv.Atoi(t[0])
    92  	return
    93  }
    94  
    95  func shortlog() io.Reader {
    96  	gitlog := exec.Command("git", "log")
    97  	gitlogOut, err := gitlog.StdoutPipe()
    98  	if err != nil {
    99  		log.Fatal(err)
   100  	}
   101  	gitlog.Start()
   102  	if err != nil {
   103  		log.Fatal("couldn't run git log:", err)
   104  	}
   105  
   106  	shortlog := exec.Command("git", "shortlog", "-sen")
   107  	shortlog.Stdin = gitlogOut
   108  	shortlogOut, err := shortlog.StdoutPipe()
   109  	if err != nil {
   110  		log.Fatal(err)
   111  	}
   112  	shortlog.Start()
   113  	if err != nil {
   114  		log.Fatal("couldn't run git shortlog:", err)
   115  	}
   116  
   117  	return shortlogOut
   118  }
   119  
   120  func main() {
   121  	flag.Parse()
   122  	contribHtml, err := template.New("contrib").Parse(contribStr)
   123  	if err != nil {
   124  		log.Fatal("couldn't parse template")
   125  	}
   126  
   127  	byName := make(map[string]*author)
   128  	byEmail := make(map[string]*author)
   129  	authorMap := make(map[*author]bool)
   130  
   131  	sl := shortlog()
   132  
   133  	scn := bufio.NewScanner(sl)
   134  	for scn.Scan() {
   135  		name, email, commits, err := parseLine(scn.Text())
   136  		if err != nil {
   137  			log.Fatalf("couldn't parse line \"%v\": %v", scn.Text(), err)
   138  		}
   139  
   140  		a := &author{
   141  			Emails:  []string{email},
   142  			Names:   []string{name},
   143  			Commits: commits,
   144  		}
   145  
   146  		a.add(byName[name])
   147  		a.add(byEmail[email])
   148  		for _, n := range a.Names {
   149  			delete(authorMap, byName[n])
   150  			byName[n] = a
   151  		}
   152  		for _, e := range a.Emails {
   153  			delete(authorMap, byEmail[e])
   154  			byEmail[e] = a
   155  		}
   156  		authorMap[a] = true
   157  	}
   158  	if scn.Err() != nil {
   159  		log.Fatal(err)
   160  	}
   161  
   162  	addURLs(byEmail)
   163  
   164  	authors := Authors{}
   165  	for a, _ := range authorMap {
   166  		authors = append(authors, a)
   167  	}
   168  
   169  	sort.Sort(authors)
   170  
   171  	if err := contribHtml.Execute(os.Stdout, authors); err != nil {
   172  		log.Fatalf("executing template: %v", err)
   173  	}
   174  }