Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Import flag #4

Merged
merged 2 commits into from
Feb 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.12
require (
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/jessevdk/go-flags v1.4.0
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
gopkg.in/yaml.v2 v2.2.8
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4r
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down
145 changes: 103 additions & 42 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package main
import (
"context"
"errors"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
Expand All @@ -16,6 +16,7 @@ import (
"golang.org/x/sync/errgroup"

"github.com/google/go-github/github"
"github.com/jessevdk/go-flags"
)

// These variables are set in Goreleaser
Expand All @@ -35,7 +36,7 @@ type Label struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Color string `yaml:"color"`
PreviousName string `yaml:"previous_name"`
PreviousName string `yaml:"previous_name,omitempty"`
}

// Labels represents a collection of Label
Expand All @@ -50,6 +51,22 @@ type Repo struct {
// Repos represents a collection of Repo
type Repos []Repo

type CLI struct {
Stdout io.Writer
Stderr io.Writer
Option Option

Client *githubClient
Config Manifest
}

type Option struct {
DryRun bool `long:"dry-run" description:"Just dry run"`
Config string `short:"c" long:"config" description:"Path to YAML file that labels are defined" default:"labels.yaml"`
Import bool `long:"import" description:"Path to import labels existing on GitHub"`
Version bool `long:"version" description:"Show version"`
}

func loadManifest(path string) (Manifest, error) {
var m Manifest
buf, err := ioutil.ReadFile(path)
Expand Down Expand Up @@ -201,32 +218,32 @@ func (g *LabelService) Delete(owner, repo string, label Label) error {
}

// applyLabels creates/edits labels described in YAML
func (l Labeler) applyLabels(owner, repo string, label Label) error {
ghLabel, err := l.github.Label.Get(owner, repo, label)
func (c *CLI) applyLabels(owner, repo string, label Label) error {
ghLabel, err := c.Client.Label.Get(owner, repo, label)
if err != nil {
return l.github.Label.Create(owner, repo, label)
return c.Client.Label.Create(owner, repo, label)
}

if ghLabel.Description != label.Description || ghLabel.Color != label.Color {
return l.github.Label.Edit(owner, repo, label)
return c.Client.Label.Edit(owner, repo, label)
}

return nil
}

// deleteLabels deletes the label not described in YAML but exists on GitHub
func (l Labeler) deleteLabels(owner, repo string) error {
labels, err := l.github.Label.List(owner, repo)
func (c *CLI) deleteLabels(owner, repo string) error {
labels, err := c.Client.Label.List(owner, repo)
if err != nil {
return err
}

for _, label := range labels {
if l.manifest.checkIfRepoHasLabel(owner+"/"+repo, label.Name) {
if c.Config.checkIfRepoHasLabel(owner+"/"+repo, label.Name) {
// no need to delete
continue
}
err := l.github.Label.Delete(owner, repo, label)
err := c.Client.Label.Delete(owner, repo, label)
if err != nil {
return err
}
Expand All @@ -236,28 +253,62 @@ func (l Labeler) deleteLabels(owner, repo string) error {
}

// Sync syncs labels based on YAML
func (l Labeler) Sync(repo Repo) error {
func (c *CLI) Sync(repo Repo) error {
slugs := strings.Split(repo.Name, "/")
if len(slugs) != 2 {
return fmt.Errorf("repository name %q is invalid", repo.Name)
}
for _, labelName := range repo.Labels {
label, err := l.manifest.getDefinedLabel(labelName)
label, err := c.Config.getDefinedLabel(labelName)
if err != nil {
return err
}
err = l.applyLabels(slugs[0], slugs[1], label)
err = c.applyLabels(slugs[0], slugs[1], label)
if err != nil {
return err
}
}
return l.deleteLabels(slugs[0], slugs[1])
return c.deleteLabels(slugs[0], slugs[1])
}

func main() {
os.Exit(run(os.Args[1:]))
}

func run(args []string) int {
// clilog.Env = "GOMI_LOG"
// clilog.SetOutput()
// defer log.Printf("[INFO] finish main function")
//
// log.Printf("[INFO] Version: %s (%s)", Version, Revision)
// log.Printf("[INFO] gomiPath: %s", gomiPath)
// log.Printf("[INFO] inventoryPath: %s", inventoryPath)
// log.Printf("[INFO] Args: %#v", args)

var opt Option
args, err := flags.ParseArgs(&opt, args)
if err != nil {
return 2
}

cli := CLI{
Stdout: os.Stdout,
Stderr: os.Stderr,
Option: opt,
}

if err := cli.Run(args); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
}

return 0
}

func newLabeler(configPath string, dryRun bool) (Labeler, error) {
func (c *CLI) Run(args []string) error {
token := os.Getenv("GITHUB_TOKEN")
if token == "" {
return Labeler{}, errors.New("GITHUB_TOKEN is missing")
return errors.New("GITHUB_TOKEN is missing")
}

ts := oauth2.StaticTokenSource(&oauth2.Token{
Expand All @@ -266,51 +317,61 @@ func newLabeler(configPath string, dryRun bool) (Labeler, error) {
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)

m, err := loadManifest(configPath)
m, err := loadManifest(c.Option.Config)
if err != nil {
return Labeler{}, err
return err
}

gc := &githubClient{
Client: client,
dryRun: dryRun,
dryRun: c.Option.DryRun,
logger: log.New(os.Stdout, "labeler: ", log.Ldate|log.Ltime),
}
if dryRun {

if c.Option.DryRun {
gc.logger.SetPrefix("labeler (dry-run): ")
// gc.logger = log.New(os.Stdout, "labeler (dry-run): ", 0)
}

gc.common.client = gc
gc.Label = (*LabelService)(&gc.common)
return Labeler{
github: gc,
manifest: m,
}, nil
}

func main() {
var (
manifest = flag.String("manifest", "labels.yaml", "YAML file to be described about labels and repos")
dryRun = flag.Bool("dry-run", false, "dry run flag")
)
flag.Parse()
c.Client = gc
c.Config = m

labeler, err := newLabeler(*manifest, *dryRun)
if err != nil {
fmt.Fprintf(os.Stderr, "[ERROR] %v\n", err.Error())
os.Exit(1)
if c.Option.Import {
if len(c.Config.Repos) == 0 {
return fmt.Errorf("no repos found in %s", c.Option.Config)
}
var m Manifest
for _, repo := range c.Config.Repos {
e := strings.Split(repo.Name, "/")
if len(e) != 2 {
// TODO: handle error
continue
}
labels, err := c.Client.Label.List(e[0], e[1])
if err != nil {
// TODO: handle error
continue
}
m.Repos = append(m.Repos, repo)
m.Labels = append(m.Labels, labels...)
}
f, err := os.Create(c.Option.Config)
if err != nil {
return err
}
defer f.Close()
return yaml.NewEncoder(f).Encode(&m)
}

eg := errgroup.Group{}
for _, repo := range labeler.manifest.Repos {
for _, repo := range c.Config.Repos {
repo := repo
eg.Go(func() error {
return labeler.Sync(repo)
return c.Sync(repo)
})
}

if err := eg.Wait(); err != nil {
fmt.Fprintf(os.Stderr, "[ERROR] %v\n", err.Error())
os.Exit(1)
}
return eg.Wait()
}