diff --git a/AUTHORS.md b/AUTHORS.md index 0b319bb..54e659f 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,5 +1,5 @@ # Authors -Ce Gao +[@gaocegege](https://github.com/gaocegege) ###### Auto generated by [gaocegege/maintainer](https://github.com/gaocegege/maintainer) on 2017-05-22 diff --git a/cmd/changelog.go b/cmd/changelog.go index 09599c8..ce52c74 100644 --- a/cmd/changelog.go +++ b/cmd/changelog.go @@ -28,10 +28,6 @@ const ( changelogGeneratorCmd string = "github_changelog_generator" ) -var ( - tokenValue *string -) - // changelogCmd represents the changelog command var changelogCmd = &cobra.Command{ Use: "changelog", @@ -51,10 +47,6 @@ In the future, maintainer will support install this dependency automatically.`, func init() { RootCmd.AddCommand(changelogCmd) - - tokenValue = changelogCmd.PersistentFlags().String(config.Token, "", "The token in GitHub."+ - "To make more than 50 requests per hour the GitHub token is required."+ - "You can generate it at: https://github.com/settings/tokens/new.") } func changelogRun() error { diff --git a/cmd/contributor.go b/cmd/contributor.go index b660458..c4e57a0 100644 --- a/cmd/contributor.go +++ b/cmd/contributor.go @@ -15,15 +15,20 @@ package cmd import ( + "context" + "errors" "log" - "os/exec" - "strings" - "sort" + "golang.org/x/oauth2" + + "fmt" "github.com/gaocegege/maintainer/config" + "github.com/gaocegege/maintainer/repo" "github.com/gaocegege/maintainer/util" + "github.com/google/go-github/github" "github.com/spf13/cobra" + "github.com/spf13/viper" ) const ( @@ -37,33 +42,14 @@ const ( ) var ( + contributorPattern = []string{ + "(?P.*) <(?P.*@.*)>", + } + errNameOrEmailNotExists = errors.New("Couldn't get the name or email of one contributor") + order *string ) -// Contributor is the type for contributor. -type Contributor struct { - Name string - Commit int -} - -// ContributorSlice is the type for slice of contributors. -type ContributorSlice []*Contributor - -// Len is part of sort.Interface. -func (d ContributorSlice) Len() int { - return len(d) -} - -// Swap is part of sort.Interface. -func (d ContributorSlice) Swap(i, j int) { - d[i], d[j] = d[j], d[i] -} - -// Less is part of sort.Interface. We use count as the value to sort by -func (d ContributorSlice) Less(i, j int) bool { - return d[i].Commit < d[j].Commit -} - // contributorCmd represents the contributor command var contributorCmd = &cobra.Command{ Use: "contributor", @@ -82,33 +68,55 @@ passion to contribute.`, func init() { RootCmd.AddCommand(contributorCmd) - order = contributorCmd.PersistentFlags().String(config.Order, orderTime, "The order to compose Authors.md."+ - "(time, commit)") + order = contributorCmd.PersistentFlags().String(config.Order, orderCommit, "The order to compose Authors.md."+ + "(commit)") } // contributorRun runs the real logic to generate AUTHORS.md. func contributorRun() error { - // git log --format='%aN <%aE>'. - gitLogCmd := exec.Command(gitCmd, gitLogArgs, gitFormatArgs) - output, err := gitLogCmd.Output() + repo, err := repo.NewRepository() if err != nil { - return err + log.Panicf("Error when read the information from local repository: %s\n", err) } - // Parse output and remove duplicates. - outputStr := string(output) - outputStr = outputStr[:len(outputStr)-1] - contributors := strings.Split(outputStr, "\n") - dict := make(map[string]int) - for _, contributor := range contributors { - contributor = strings.Trim(contributor, "'") - if _, ok := dict[contributor]; ok != true { - dict[contributor] = 1 - } else { - dict[contributor] = dict[contributor] + 1 + token := viper.GetString(config.Token) + // Override token in CLI. + if *tokenValue != "" { + log.Println("Found token in flag, override it.") + token = *tokenValue + } + ctx := context.Background() + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + contributors := []*github.Contributor{} + i := 1 + for { + contributorsBuf, _, err := client.Repositories.ListContributors(repo.Owner, repo.Name, &github.ListContributorsOptions{ + // See https://developer.github.com/v3/repos/#list-contributors + // Anon: "true", + ListOptions: github.ListOptions{ + Page: i, + PerPage: 100, + }, + }) + if err != nil { + return err } + if len(contributorsBuf) == 0 { + break + } + contributors = append(contributors, contributorsBuf...) + i = i + 1 + } + + if err := composeByOrder(contributors); err != nil { + return err } - return composeOrder(&dict) + return nil } // authorHeader returns the header to be written into AUTHORS.md. @@ -116,27 +124,11 @@ func authorHeader() string { return "# Authors\n\n" } -func composeOrder(data *map[string]int) error { - contributors := make(ContributorSlice, 0, len(*data)) - for k, v := range *data { - contributors = append(contributors, &Contributor{ - Name: k, - Commit: v, - }) - } - - switch *order { - case orderCommit: - sort.Sort(sort.Reverse(contributors)) - } +func composeByOrder(contributors []*github.Contributor) error { return writeToFile(contributors) } -func orderByCommit(contributors ContributorSlice) error { - return nil -} - -func writeToFile(contributors ContributorSlice) error { +func writeToFile(contributors []*github.Contributor) error { // Output results to AUTHORS.md. f, err := util.OpenFile(authorFile) if err != nil { @@ -145,8 +137,8 @@ func writeToFile(contributors ContributorSlice) error { if _, err := f.WriteString(authorHeader()); err != nil { return err } - for _, k := range contributors { - if _, err := f.WriteString(k.Name); err != nil { + for _, contributor := range contributors { + if _, err := f.WriteString(fmt.Sprintf("[@%s](%s)", *contributor.Login, *contributor.HTMLURL)); err != nil { return err } if _, err := f.WriteString("\n\n"); err != nil { diff --git a/cmd/root.go b/cmd/root.go index 5f0ada9..000ebe1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -20,6 +20,7 @@ import ( "log" + "github.com/gaocegege/maintainer/config" "github.com/gaocegege/maintainer/repo" "github.com/spf13/cobra" @@ -27,7 +28,8 @@ import ( ) var ( - cfgFile string + cfgFile string + tokenValue *string // Repo stores the information in the local repository. Repo *repo.Repository ) @@ -50,6 +52,9 @@ func Execute() { func init() { cobra.OnInitialize(initConfig) + tokenValue = RootCmd.PersistentFlags().String(config.Token, "", "The token in GitHub."+ + "To make more than 50 requests per hour the GitHub token is required."+ + "You can generate it at: https://github.com/settings/tokens/new.") RootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.maintainer.yaml)") } diff --git a/repo/repo.go b/repo/repo.go index 27abea7..a922f10 100644 --- a/repo/repo.go +++ b/repo/repo.go @@ -18,7 +18,8 @@ import ( "errors" "fmt" "os/exec" - "regexp" + + "github.com/gaocegege/maintainer/util" ) const ( @@ -89,7 +90,7 @@ func getNameAndRepoName() (string, string, error) { // getNameAndRepoName gets the name and project from local repository. func getNameAndRepoNameFromRemote(remoteStr string) (string, string, error) { for _, regEx := range gitRemotePattern { - paramsMap := getParams(regEx, remoteStr) + paramsMap := util.GetParams(regEx, remoteStr) name, ok1 := paramsMap["user"] project, ok2 := paramsMap["project"] if ok1 != true || ok2 != true { @@ -99,19 +100,3 @@ func getNameAndRepoNameFromRemote(remoteStr string) (string, string, error) { } return "", "", errNameOrProjectNotExists } - -// getParams get the params from regexp. -// See http://stackoverflow.com/questions/30483652/how-to-get-capturing-group-functionality-in-golang-regular-expressions. -func getParams(regEx, url string) (paramsMap map[string]string) { - - var compRegEx = regexp.MustCompile(regEx) - match := compRegEx.FindStringSubmatch(url) - - paramsMap = make(map[string]string) - for i, name := range compRegEx.SubexpNames() { - if i > 0 && i <= len(match) { - paramsMap[name] = match[i] - } - } - return -} diff --git a/util/regexutil.go b/util/regexutil.go new file mode 100644 index 0000000..b9fff96 --- /dev/null +++ b/util/regexutil.go @@ -0,0 +1,19 @@ +package util + +import "regexp" + +// GetParams get the params from regexp. +// See http://stackoverflow.com/questions/30483652/how-to-get-capturing-group-functionality-in-golang-regular-expressions. +func GetParams(regEx, url string) (paramsMap map[string]string) { + + var compRegEx = regexp.MustCompile(regEx) + match := compRegEx.FindStringSubmatch(url) + + paramsMap = make(map[string]string) + for i, name := range compRegEx.SubexpNames() { + if i > 0 && i <= len(match) { + paramsMap[name] = match[i] + } + } + return +}