This commit is contained in:
Benson Wong 2018-02-05 13:16:30 -08:00
Родитель a8deb58009 1cc0d7ee63
Коммит e7c26d89b8
5 изменённых файлов: 145 добавлений и 106 удалений

6
Gopkg.lock сгенерированный
Просмотреть файл

@ -2,16 +2,16 @@
[[projects]] [[projects]]
branch = "master"
name = "github.com/golang/protobuf" name = "github.com/golang/protobuf"
packages = ["proto"] packages = ["proto"]
revision = "925541529c1fa6821df4e44ce2723319eb2be768" revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]] [[projects]]
branch = "master"
name = "github.com/google/go-github" name = "github.com/google/go-github"
packages = ["github"] packages = ["github"]
revision = "e48060a28fac52d0f1cb758bc8b87c07bac4a87d" revision = "e48060a28fac52d0f1cb758bc8b87c07bac4a87d"
version = "v15.0.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -23,7 +23,7 @@
branch = "master" branch = "master"
name = "golang.org/x/net" name = "golang.org/x/net"
packages = ["context","context/ctxhttp"] packages = ["context","context/ctxhttp"]
revision = "0ed95abb35c445290478a5348a7b38bb154135fd" revision = "2fb46b16b8dda405028c50f7c7f0f9dd1fa6bfb1"
[[projects]] [[projects]]
branch = "master" branch = "master"

Просмотреть файл

@ -4,11 +4,8 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil"
"strings" "strings"
"github.com/google/go-github/github"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -39,7 +36,7 @@ func checkAll(c *cli.Context) error {
checkLabels, checkLabels,
checkUnassigned, checkUnassigned,
checkUnlabled, checkUnlabled,
checkMilestones, checkProjects,
} }
for _, f := range funcs { for _, f := range funcs {
@ -98,6 +95,7 @@ func checkLabels(c *cli.Context) error {
standardLabels := map[string]string{ standardLabels := map[string]string{
"bug": "b60205", "bug": "b60205",
"security": "b60205", "security": "b60205",
"improvement": "0e8a16",
"documentation": "0e8a16", "documentation": "0e8a16",
"fix": "0e8a16", "fix": "0e8a16",
"new-feature": "0e8a16", "new-feature": "0e8a16",
@ -115,8 +113,8 @@ func checkLabels(c *cli.Context) error {
continue continue
} }
name := *label.Name name := label.GetName()
color := *label.Color color := label.GetColor()
if expectedColor, ok := standardLabels[name]; !ok { if expectedColor, ok := standardLabels[name]; !ok {
// not a standard label // not a standard label
@ -162,11 +160,11 @@ func checkUnassigned(c *cli.Context) error {
return err return err
} }
count := *results.Total count := results.GetTotal()
if count > 0 { if count > 0 {
fmt.Fprintf(outError, " - Error: %d unassigned P1 issues\n", count) fmt.Fprintf(outError, " - Error: %d unassigned P1 issues\n", count)
for _, issue := range results.Issues { for _, issue := range results.Issues {
fmt.Fprintf(outError, " #%-4d %s", *issue.Number, *issue.Title) fmt.Fprintf(outError, " #%-4d %s", issue.GetNumber(), issue.GetTitle())
} }
} else { } else {
fmt.Fprintf(outInfo, " - OK. All P1 issues assigned\n") fmt.Fprintf(outInfo, " - OK. All P1 issues assigned\n")
@ -188,11 +186,11 @@ func checkUnlabled(c *cli.Context) error {
return err return err
} }
unassigned := *results.Total unassigned := results.GetTotal()
if unassigned > 0 { if unassigned > 0 {
fmt.Fprintf(outError, " - Error: %d issues unlabeled\n", unassigned) fmt.Fprintf(outError, " - Error: %d issues unlabeled\n", unassigned)
for _, issue := range results.Issues { for _, issue := range results.Issues {
fmt.Fprintf(outError, " #%-4d %s\n", *issue.Number, *issue.Title) fmt.Fprintf(outError, " #%-4d %s\n", issue.GetNumber(), issue.GetTitle())
} }
} else { } else {
fmt.Fprintf(outInfo, " - OK. All issues are labeled\n") fmt.Fprintf(outInfo, " - OK. All issues are labeled\n")
@ -201,93 +199,75 @@ func checkUnlabled(c *cli.Context) error {
return nil return nil
} }
func checkMilestones(c *cli.Context) error { func checkProjects(c *cli.Context) error {
owner, repo, err := extractOwnerRepo(c.Args().Get(0)) owner, repo, err := extractOwnerRepo(c.Args().Get(0))
if err != nil { if err != nil {
return err return err
} }
outInfo, outError := getWriters(c) outInfo, outError := getWriters(c)
fmt.Fprintf(outInfo, "Checking Milestones\n") fmt.Fprintf(outInfo, "Checking Projects\n")
ctx := context.Background() ctx := context.Background()
milestones, _, err := ghClient.Issues.ListMilestones(ctx, owner, repo, nil)
if err != nil {
fmt.Fprintf(outError, " - Error: Feteching milestones, %s\n", err.Error())
return err
}
projects, _, err := ghClient.Repositories.ListProjects(ctx, owner, repo, nil) projects, _, err := ghClient.Repositories.ListProjects(ctx, owner, repo, nil)
if err != nil { if err != nil {
fmt.Fprintf(outError, " - Error: Fetching projects, %s\n", err.Error()) fmt.Fprintf(outError, " - Error: Fetching projects, %s\n", err.Error())
return err return err
} }
pMap := make(map[string]*github.Project) projError := false
for _, p := range projects { for _, project := range projects {
pMap[*p.Name] = p
}
errHappend := false if project.GetBody() == "" {
for _, milestone := range milestones { fmt.Fprintf(outError, " - WARNING: Project [%s] *SHOULD* have a description\n", project.GetName())
if project, found := pMap[*milestone.Title]; !found { }
fmt.Fprintf(outError, " - Error: %s does not have a matching project", *milestone.Title)
} else {
// check the project's columns
pCols, _, err := ghClient.Projects.ListProjectColumns(ctx, *project.ID, nil)
if err != nil {
fmt.Fprintf(outError, " - Error: Fetching project columns, %s\n", err.Error())
return err
}
flags := 0 // check the project's columns
for _, col := range pCols { pCols, _, err := ghClient.Projects.ListProjectColumns(ctx, project.GetID(), nil)
switch *col.Name { if err != nil {
case "Backlog": fmt.Fprintf(outError, " - Error: Fetching project columns, %s\n", err.Error())
flags |= 0x01 return err
case "In Progress": }
flags |= 0x02
case "Blocked":
flags |= 0x04
case "Completed":
flags |= 0x08
default:
fmt.Fprintf(outError, ` - Error: Project "%s" has unexpected column %s\n`,
*project.Name, *col.Name)
errHappend = true
}
}
if flags&0x01 == 0 { flags := 0
fmt.Fprintf(outError, ` - Error: Project "%s" missing "Backlog" column\n`, *project.Name) for _, col := range pCols {
errHappend = true switch col.GetName() {
} case "Backlog":
if flags&0x02 == 0 { flags |= 0x01
fmt.Fprintf(outError, ` - Error: Project "%s" missing "In Progress" column\n`, *project.Name) case "In Progress":
errHappend = true flags |= 0x02
} case "Blocked":
if flags&0x04 == 0 { flags |= 0x04
fmt.Fprintf(outError, ` - Error: Project "%s" missing "Blocked" column\n`, *project.Name) case "Completed":
errHappend = true flags |= 0x08
} default:
if flags&0x08 == 0 { fmt.Fprintf(outError, " - Error: Project [%s] has unexpected column %s\n",
fmt.Fprintf(outError, ` - Error: Project "%s" missing "Completed" column\n`, *project.Name) project.GetName(), col.GetName())
errHappend = true projError = true
} }
} }
if flags&0x01 == 0 {
fmt.Fprintf(outError, " - Error: Project [%s] missing [Backlog] column\n", project.GetName())
projError = true
}
if flags&0x02 == 0 {
fmt.Fprintf(outError, " - Error: Project [%s] missing [In Progress] column\n", project.GetName())
projError = true
}
if flags&0x04 == 0 {
fmt.Fprintf(outError, " - Error: Project [%s] missing [Blocked] column\n", project.GetName())
projError = true
}
if flags&0x08 == 0 {
fmt.Fprintf(outError, " - Error: Project [%s] missing [Completed] column\n", project.GetName())
projError = true
}
} }
if !errHappend { if !projError {
fmt.Fprintf(outInfo, " - OK. Milestones verified\n") fmt.Fprintf(outInfo, " - OK. Projects verified\n")
} }
return nil return nil
} }
func getWriters(c *cli.Context) (info, err io.Writer) {
if c.Bool("quiet") {
return ioutil.Discard, c.App.Writer
} else {
return c.App.Writer, c.App.Writer
}
}

Просмотреть файл

@ -7,6 +7,8 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil"
"strings" "strings"
"github.com/google/go-github/github" "github.com/google/go-github/github"
@ -24,11 +26,13 @@ var (
// preflight ensures necessary flags and sets the package vars: ghClient, owner and repo // preflight ensures necessary flags and sets the package vars: ghClient, owner and repo
func preflight(c *cli.Context) (err error) { func preflight(c *cli.Context) (err error) {
_, outError := getWriters(c)
// set package vars // set package vars
ghtoken = c.String("ghtoken") ghtoken = c.String("ghtoken")
if ghtoken == "" { if ghtoken == "" {
fmt.Fprintf(c.App.Writer, "Error: github access token required\n") fmt.Fprintf(outError, "Error: github access token required\n")
err = errMissingFlag err = errMissingFlag
} }
@ -46,6 +50,17 @@ func preflight(c *cli.Context) (err error) {
tc := oauth2.NewClient(ctx, ts) tc := oauth2.NewClient(ctx, ts)
ghClient = github.NewClient(tc) ghClient = github.NewClient(tc)
} }
// check that we got the repo value, which should be the last thing in
// the arg list
args := c.Args()
if len(args) > 0 {
_, _, err = extractOwnerRepo(args[len(args)-1])
if err != nil {
fmt.Fprintf(outError, "ERROR: %s\n", err.Error())
return err
}
}
return return
} }
@ -55,8 +70,16 @@ func extractOwnerRepo(arg string) (string, string, error) {
parts := strings.Split(arg, "/") parts := strings.Split(arg, "/")
l := len(parts) l := len(parts)
if l < 2 { if l < 2 {
return "", "", errors.New("Invalid repo path, expect: owner/reponame") return "", "", errors.New("Invalid or missing repo path, expect: owner/reponame")
} }
return parts[l-2], parts[l-1], nil return parts[l-2], parts[l-1], nil
} }
func getWriters(c *cli.Context) (info, err io.Writer) {
if c.Bool("quiet") {
return ioutil.Discard, c.App.Writer
} else {
return c.App.Writer, c.App.Writer
}
}

Просмотреть файл

@ -2,6 +2,7 @@ package repo
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"net/http" "net/http"
@ -15,7 +16,7 @@ func initRepo(c *cli.Context) error {
return err return err
} }
if err := createMilestone(c); err != nil { if err := createProject(c); err != nil {
return err return err
} }
@ -29,7 +30,9 @@ func initLabels(c *cli.Context) error {
return err return err
} }
fmt.Fprintf(c.App.Writer, "Initializing labels on %s/%s\n", owner, repo) outInfo, outError := getWriters(c)
fmt.Fprintf(outInfo, "Initializing labels on %s/%s\n", owner, repo)
// make Issue labels // make Issue labels
labels := map[string]string{ labels := map[string]string{
@ -39,6 +42,7 @@ func initLabels(c *cli.Context) error {
"P5": "ffa32c", "P5": "ffa32c",
"bug": "b60205", "bug": "b60205",
"security": "b60205", "security": "b60205",
"improvement": "0e8a16",
"documentation": "0e8a16", "documentation": "0e8a16",
"fix": "0e8a16", "fix": "0e8a16",
"new-feature": "0e8a16", "new-feature": "0e8a16",
@ -56,17 +60,17 @@ func initLabels(c *cli.Context) error {
label := &github.Label{Name: &name, Color: &color} label := &github.Label{Name: &name, Color: &color}
_, _, err := ghClient.Issues.CreateLabel(ctx, owner, repo, label) _, _, err := ghClient.Issues.CreateLabel(ctx, owner, repo, label)
if err != nil { if err != nil {
fmt.Fprintf(c.App.Writer, " - Error: Creating label %s\n", name) fmt.Fprintf(outError, " - Error: Creating label %s\n", name)
continue continue
} else { } else {
fmt.Fprintf(c.App.Writer, " - Created label %s\n", name) fmt.Fprintf(outError, " - Created label %s\n", name)
} }
} else if *label.Color != color { } else if *label.Color != color {
_, _, err := ghClient.Issues.EditLabel(ctx, owner, repo, name, &github.Label{Color: &color}) _, _, err := ghClient.Issues.EditLabel(ctx, owner, repo, name, &github.Label{Color: &color})
if err != nil { if err != nil {
fmt.Fprintf(c.App.Writer, " - Error: changing color for %s\n", name) fmt.Fprintf(outError, " - Error: changing color for %s\n", name)
} else { } else {
fmt.Fprintf(c.App.Writer, " - Changed color for label %s to %s\n", name, color) fmt.Fprintf(outInfo, " - Changed color for label %s to %s\n", name, color)
} }
} }
} }
@ -80,43 +84,68 @@ func createMilestone(c *cli.Context) error {
return err return err
} }
outInfo, outError := getWriters(c)
milestoneTitle := c.String("milestone") milestoneTitle := c.String("milestone")
fmt.Fprintf(c.App.Writer, "Creating Milestone/Project: %s\n", milestoneTitle) fmt.Fprintf(outInfo, "Creating Milestone: %s\n", milestoneTitle)
ctx := context.Background() ctx := context.Background()
milestones, _, err := ghClient.Issues.ListMilestones(ctx, owner, repo, nil) milestones, _, err := ghClient.Issues.ListMilestones(ctx, owner, repo, nil)
if err != nil { if err != nil {
fmt.Fprintf(c.App.Writer, " - Error: Could not list milestones\n") fmt.Fprintf(outError, " - Error: Could not list milestones\n")
return err return err
} }
// short circuit if milestone already exists // short circuit if milestone already exists
for _, m := range milestones { for _, m := range milestones {
if *m.Title == milestoneTitle { if *m.Title == milestoneTitle {
fmt.Fprintf(c.App.Writer, " - Error: Milestone %s already exists\n", milestoneTitle) fmt.Fprintf(outError, " - Error: Milestone [%s] already exists\n", milestoneTitle)
return nil return errors.New("Duplicate Milestone Name")
} }
} }
milestone := &github.Milestone{Title: &milestoneTitle} milestone := &github.Milestone{Title: &milestoneTitle}
if _, _, err := ghClient.Issues.CreateMilestone(ctx, owner, repo, milestone); err != nil { if _, _, err := ghClient.Issues.CreateMilestone(ctx, owner, repo, milestone); err != nil {
fmt.Fprintf(c.App.Writer, " - Error: Creating milestone %s: %s\n", milestoneTitle, err.Error()) fmt.Fprintf(outError, " - Error: Creating milestone %s: %s\n", milestoneTitle, err.Error())
return err return err
} }
// Make Project w/ Backlog, In Progress, Blocked and Completed return nil
// ... note: it will always create a new Project 1.0. Your repo will just have }
// multiple ones if you run this several times..
fmt.Fprintf(c.App.Writer, " - Creating milestone %s\n", milestoneTitle) func createProject(c *cli.Context) error {
project := &github.ProjectOptions{Name: milestoneTitle} owner, repo, err := extractOwnerRepo(c.Args().Get(0))
if err != nil {
return err
}
projectTitle := c.String("project")
outInfo, outError := getWriters(c)
ctx := context.Background()
projects, _, err := ghClient.Repositories.ListProjects(ctx, owner, repo, nil)
if err != nil {
fmt.Fprintf(outError, " - Error: Fetching projects, %s\n", err.Error())
return err
}
for _, project := range projects {
if project.GetName() == projectTitle {
fmt.Fprintf(outError, " - Error: Project [%s] already exists\n", projectTitle)
return errors.New("Duplicate Project Name")
}
}
fmt.Fprintf(outInfo, "Creating Project %s\n", projectTitle)
project := &github.ProjectOptions{Name: projectTitle}
if proj, _, err := ghClient.Repositories.CreateProject(ctx, owner, repo, project); err != nil { if proj, _, err := ghClient.Repositories.CreateProject(ctx, owner, repo, project); err != nil {
fmt.Fprintf(c.App.Writer, " - Error: Creating Project %s: %s\n", milestoneTitle, err.Error()) fmt.Fprintf(outError, " - Error: Creating Project %s: %s\n", projectTitle, err.Error())
return err return err
} else { } else {
fmt.Fprintf(c.App.Writer, " - Created project %s\n", milestoneTitle) fmt.Fprintf(outInfo, " - Created project %s\n", projectTitle)
for _, colName := range []string{"Backlog", "In Progress", "Blocked", "Completed"} { for _, colName := range []string{"Backlog", "In Progress", "Blocked", "Completed"} {
ops := &github.ProjectColumnOptions{Name: colName} ops := &github.ProjectColumnOptions{Name: colName}
if _, _, err := ghClient.Projects.CreateProjectColumn(ctx, *proj.ID, ops); err != nil { if _, _, err := ghClient.Projects.CreateProjectColumn(ctx, *proj.ID, ops); err != nil {
fmt.Fprintf(c.App.Writer, " - Error: creating Project Column [%s]: %s\n", colName, err.Error()) fmt.Fprintf(outError, " - Error: Creating Project Column [%s]: %s\n", colName, err.Error())
} }
} }
} }

Просмотреть файл

@ -18,11 +18,11 @@ func NewCommand() cli.Command {
Subcommands: cli.Commands{ Subcommands: cli.Commands{
cli.Command{ cli.Command{
Name: "init", Name: "init",
Usage: "initializes labels and a milestone for a repo", Usage: "initializes labels and a project for a repo",
Flags: []cli.Flag{cli.StringFlag{ Flags: []cli.Flag{cli.StringFlag{
Name: "milestone, m", Name: "project, p",
Value: "Version 1.0", Value: "Version 1.0",
Usage: "initial milestone", Usage: "initial project to create",
}}, }},
Action: initRepo, Action: initRepo,
}, },
@ -39,7 +39,14 @@ func NewCommand() cli.Command {
}}, }},
Action: createMilestone, Action: createMilestone,
}, },
cli.Command{
Name: "create-project",
Usage: "creates a project with standard columns",
Flags: []cli.Flag{cli.StringFlag{
Name: "p, project",
}},
Action: createProject,
},
cli.Command{ cli.Command{
Name: "check", Name: "check",
Usage: "checks to verify standards conformity", Usage: "checks to verify standards conformity",
@ -100,9 +107,9 @@ func NewCommand() cli.Command {
}, },
}, },
cli.Command{ cli.Command{
Name: "milestones", Name: "projects",
Usage: "verify milestones have a project to track them", Usage: "verify projects fit standards",
Action: checkMilestones, Action: checkProjects,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "quiet, q", Name: "quiet, q",