Skip to content

Commit

Permalink
Divide files
Browse files Browse the repository at this point in the history
  • Loading branch information
babarot committed Feb 7, 2020
1 parent 6bde95e commit 511781a
Show file tree
Hide file tree
Showing 5 changed files with 381 additions and 340 deletions.
156 changes: 156 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package main

import (
"errors"
"fmt"
"log"
"os"
"strings"

"github.com/google/go-cmp/cmp"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
"golang.org/x/sync/errgroup"
yaml "gopkg.in/yaml.v2"
)

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

ts := oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: token,
})
tc := oauth2.NewClient(oauth2.NoContext, ts)
client := github.NewClient(tc)

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

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

if c.Option.DryRun {
gc.logger.SetPrefix("labeler (dry-run): ")
}

gc.common.client = gc
gc.Label = (*LabelService)(&gc.common)

c.Client = gc
c.Config = m

if len(c.Config.Repos) == 0 {
return fmt.Errorf("no repos found in %s", c.Option.Config)
}

if c.Option.Import {
m := c.CurrentLabels()
f, err := os.Create(c.Option.Config)
if err != nil {
return err
}
defer f.Close()
return yaml.NewEncoder(f).Encode(&m)
}

if cmp.Equal(c.CurrentLabels(), c.Config) {
// no need to sync
return nil
}

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

return eg.Wait()
}

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

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

return nil
}

// deleteLabels deletes the label not described in YAML but exists on GitHub
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 c.Config.checkIfRepoHasLabel(owner+"/"+repo, label.Name) {
// no need to delete
continue
}
err := c.Client.Label.Delete(owner, repo, label)
if err != nil {
return err
}
}

return nil
}

// Sync syncs labels based on YAML
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 := c.Config.getDefinedLabel(labelName)
if err != nil {
return err
}
err = c.applyLabels(slugs[0], slugs[1], label)
if err != nil {
return err
}
}
return c.deleteLabels(slugs[0], slugs[1])
}

func (c *CLI) CurrentLabels() Manifest {
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
}
var ls []string
for _, label := range labels {
ls = append(ls, label.Name)
}
repo.Labels = ls
m.Repos = append(m.Repos, repo)
m.Labels = append(m.Labels, labels...)
}
return m
}
43 changes: 43 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"fmt"
"io/ioutil"

yaml "gopkg.in/yaml.v2"
)

func loadManifest(path string) (Manifest, error) {
var m Manifest
buf, err := ioutil.ReadFile(path)
if err != nil {
return m, err
}
err = yaml.Unmarshal(buf, &m)
return m, err
}

func (m Manifest) getDefinedLabel(name string) (Label, error) {
for _, label := range m.Labels {
if label.Name == name {
return label, nil
}
}
return Label{}, fmt.Errorf("%s: no such defined label in manifest YAML", name)
}

func (m Manifest) checkIfRepoHasLabel(repoName, labelName string) bool {
var labels []string
for _, repo := range m.Repos {
if repo.Name == repoName {
labels = repo.Labels
break
}
}
for _, label := range labels {
if label == labelName {
return true
}
}
return false
}
85 changes: 85 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package main

import (
"context"
"io"
"log"

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

// Manifest represents the YAML file described about labels and repos
type Manifest struct {
Labels Labels `yaml:"labels"`
Repos Repos `yaml:"repos"`
}

// Label represents GitHub label
type Label struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Color string `yaml:"color"`
PreviousName string `yaml:"previous_name,omitempty"`
}

// Labels represents a collection of Label
type Labels []Label

// Repo represents GitHub repository
type Repo struct {
Name string `yaml:"name"`
Labels []string `yaml:"labels"`
}

// 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:"Import existing labels if enabled"`
Version bool `long:"version" description:"Show version"`
}

type githubClient struct {
// github *github.Client
github *github.Client

dryRun bool
logger *log.Logger

common service

Label *LabelService
}

type Labeler interface {
// Get(owner, repo string, label Label) (Label, error)
// Create(owner, repo string, label Label) error
// Edit(owner, repo string, label Label) error
// List(owner, repo string) ([]Label, error)
// Delete(owner, repo string, label Label) error

GetLabel(ctx context.Context, owner string, repo string, name string) (*github.Label, *github.Response, error)
EditLabel(ctx context.Context, owner string, repo string, name string, label *github.Label) (*github.Label, *github.Response, error)
CreateLabel(ctx context.Context, owner string, repo string, label *github.Label) (*github.Label, *github.Response, error)
ListLabels(ctx context.Context, owner string, repo string, opt *github.ListOptions) ([]*github.Label, *github.Response, error)
DeleteLabel(ctx context.Context, owner string, repo string, name string) (*github.Response, error)
}

// LabelService handles communication with the label related
// methods of GitHub API
type LabelService service

type service struct {
client *githubClient
}
97 changes: 97 additions & 0 deletions label.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package main

import (
"context"

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

// Get gets GitHub labels
func (g *LabelService) Get(owner, repo string, label Label) (Label, error) {
ctx := context.Background()
ghLabel, _, err := g.client.github.Issues.GetLabel(ctx, owner, repo, label.Name)
if err != nil {
return Label{}, err
}
return Label{
Name: ghLabel.GetName(),
Description: ghLabel.GetDescription(),
Color: ghLabel.GetName(),
}, nil
}

// Create creates GitHub labels
func (g *LabelService) Create(owner, repo string, label Label) error {
ctx := context.Background()
ghLabel := &github.Label{
Name: github.String(label.Name),
Description: github.String(label.Description),
Color: github.String(label.Color),
}
if len(label.PreviousName) > 0 {
g.client.logger.Printf("rename %q in %s/%s to %q", label.PreviousName, owner, repo, label.Name)
if g.client.dryRun {
return nil
}
_, _, err := g.client.github.Issues.EditLabel(ctx, owner, repo, label.PreviousName, ghLabel)
return err
}
g.client.logger.Printf("create %q in %s/%s", label.Name, owner, repo)
if g.client.dryRun {
return nil
}
_, _, err := g.client.github.Issues.CreateLabel(ctx, owner, repo, ghLabel)
return err
}

// Edit edits GitHub labels
func (g *LabelService) Edit(owner, repo string, label Label) error {
ctx := context.Background()
ghLabel := &github.Label{
Name: github.String(label.Name),
Description: github.String(label.Description),
Color: github.String(label.Color),
}
g.client.logger.Printf("edit %q in %s/%s", label.Name, owner, repo)
if g.client.dryRun {
return nil
}
_, _, err := g.client.github.Issues.EditLabel(ctx, owner, repo, label.Name, ghLabel)
return err
}

// List lists GitHub labels
func (g *LabelService) List(owner, repo string) ([]Label, error) {
ctx := context.Background()
opt := &github.ListOptions{PerPage: 10}
var labels []Label
for {
ghLabels, resp, err := g.client.github.Issues.ListLabels(ctx, owner, repo, opt)
if err != nil {
return labels, err
}
for _, ghLabel := range ghLabels {
labels = append(labels, Label{
Name: ghLabel.GetName(),
Description: ghLabel.GetDescription(),
Color: ghLabel.GetColor(),
})
}
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return labels, nil
}

// Delete deletes GitHub labels
func (g *LabelService) Delete(owner, repo string, label Label) error {
ctx := context.Background()
g.client.logger.Printf("delete %q in %s/%s", label.Name, owner, repo)
if g.client.dryRun {
return nil
}
_, err := g.client.github.Issues.DeleteLabel(ctx, owner, repo, label.Name)
return err
}
Loading

0 comments on commit 511781a

Please sign in to comment.