-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from b4b4r07/refactor
Refactor
- Loading branch information
Showing
7 changed files
with
392 additions
and
344 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.