Skip to content

Commit

Permalink
Merge pull request #5 from b4b4r07/refactor
Browse files Browse the repository at this point in the history
Refactor
  • Loading branch information
b4b4r07 authored Feb 7, 2020
2 parents 6bde95e + 0316944 commit ec3241b
Show file tree
Hide file tree
Showing 7 changed files with 392 additions and 344 deletions.
2 changes: 1 addition & 1 deletion action.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: 'github-labeler'
description: 'Declarative way to configure GitHub labels'
inputs:
manifest:
config:
description: 'Path to YAML file which defines GitHub labels'
required: false
default: '.github/labels.yml'
Expand Down
176 changes: 176 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package main

import (
"errors"
"fmt"
"io"
"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"
)

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

GitHub *App
Config Config
}

type Option struct {
DryRun bool `long:"dry-run" description:"Just dry run"`
Config string `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 App struct {
Labeler Labeler
logger *log.Logger
}

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)

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

gc := &App{
Labeler: githubClientImpl{client},
logger: log.New(os.Stdout, "labeler: ", log.Ldate|log.Ltime),
}

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

c.GitHub = gc
c.Config = cfg

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

actual := c.ActualConfig()
if cmp.Equal(actual, c.Config) {
// no need to sync
gc.logger.Printf("Claimed config and actual config is the same")
return nil
}

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

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.GitHub.GetLabel(owner, repo, label)
if err != nil {
return c.GitHub.CreateLabel(owner, repo, label)
}

if ghLabel.Description != label.Description || ghLabel.Color != label.Color {
return c.GitHub.EditLabel(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.GitHub.ListLabels(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.GitHub.DeleteLabel(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) ActualConfig() Config {
var cfg Config
for _, repo := range c.Config.Repos {
slugs := strings.Split(repo.Name, "/")
if len(slugs) != 2 {
// TODO: handle error
continue
}
labels, err := c.GitHub.ListLabels(slugs[0], slugs[1])
if err != nil {
// TODO: handle error
continue
}
var ls []string
for _, label := range labels {
ls = append(ls, label.Name)
}
repo.Labels = ls
cfg.Repos = append(cfg.Repos, repo)
cfg.Labels = append(cfg.Labels, labels...)
}
return cfg
}
49 changes: 49 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package main

import (
"fmt"
"io/ioutil"

yaml "gopkg.in/yaml.v2"
)

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

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

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

func (cfg Config) checkIfRepoHasLabel(repoName, labelName string) bool {
var labels []string
for _, repo := range cfg.Repos {
if repo.Name == repoName {
labels = repo.Labels
break
}
}
for _, label := range labels {
if label == labelName {
return true
}
}
return false
}
6 changes: 3 additions & 3 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/bin/bash

manifest=${INPUT_MANIFEST}
config=${INPUT_CONFIG}

import=${INPUT_IMPORT:-false}
if ${import}; then
github-labeler --import --config=${manifest}
github-labeler --import --config=${config}
exit ${?}
fi

github-labeler --config=${manifest}
github-labeler --config=${config}
63 changes: 63 additions & 0 deletions interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"context"

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

type Labeler interface {
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)
}

type githubClientImpl struct {
ghClient *github.Client
}

func (l githubClientImpl) GetLabel(ctx context.Context, owner string, repo string, name string) (*github.Label, *github.Response, error) {
return l.ghClient.Issues.GetLabel(ctx, owner, repo, name)
}

func (l githubClientImpl) EditLabel(ctx context.Context, owner string, repo string, name string, label *github.Label) (*github.Label, *github.Response, error) {
return l.ghClient.Issues.EditLabel(ctx, owner, repo, name, label)
}

func (l githubClientImpl) CreateLabel(ctx context.Context, owner string, repo string, label *github.Label) (*github.Label, *github.Response, error) {
return l.ghClient.Issues.CreateLabel(ctx, owner, repo, label)
}

func (l githubClientImpl) ListLabels(ctx context.Context, owner string, repo string, opt *github.ListOptions) ([]*github.Label, *github.Response, error) {
return l.ghClient.Issues.ListLabels(ctx, owner, repo, opt)
}

func (l githubClientImpl) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*github.Response, error) {
return l.ghClient.Issues.DeleteLabel(ctx, owner, repo, name)
}

type githubClientDryRun struct {
ghClient *github.Client
}

func (l githubClientDryRun) GetLabel(ctx context.Context, owner string, repo string, name string) (*github.Label, *github.Response, error) {
return l.ghClient.Issues.GetLabel(ctx, owner, repo, name)
}

func (l githubClientDryRun) EditLabel(ctx context.Context, owner string, repo string, name string, label *github.Label) (*github.Label, *github.Response, error) {
return nil, nil, nil
}

func (l githubClientDryRun) CreateLabel(ctx context.Context, owner string, repo string, label *github.Label) (*github.Label, *github.Response, error) {
return nil, nil, nil
}

func (l githubClientDryRun) ListLabels(ctx context.Context, owner string, repo string, opt *github.ListOptions) ([]*github.Label, *github.Response, error) {
return l.ghClient.Issues.ListLabels(ctx, owner, repo, opt)
}

func (l githubClientDryRun) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*github.Response, error) {
return nil, nil
}
Loading

0 comments on commit ec3241b

Please sign in to comment.