From d7b9074ce618d8aa23046ee1a842e851ec293e2a Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Thu, 27 May 2021 09:32:21 -0400 Subject: [PATCH 01/14] Allow label renaming Introduce the "alias" attribute for labels so that label-syncer can rename labels, rather than deleting/re-creating them. This preserves labels on existing issues and pull requests. Closes #59 --- README.md | 19 +++++++++++++++++++ pkg/github/github.go | 25 ++++++++++++++++++------- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 02d67a6..080dbcc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,25 @@ The default file path is `.github/labels.yml`, but you can specify any file path To create manifest of the current labels easily, using [label-exporter](https://github.com/micnncim/label-exporter) is recommended. +### Renaming labels + +If you want to rename a label, you can set an `alias` in the manifest. +For example, if you want to rename the label `bug` to `Type: bug`, you +would use a manifest like this: + +```yaml +- name: Type: bug + alias: bug + description: Something isn't working + color: d73a4a +``` + +Renaming labels makes it easier to adopt a new taxonomy if you have +issues and pull requests using the old label names. Since you're +renaming labels rather than deleting and creating new ones, existing +pull requests and issues will keep their labels, but will adopt the +new name. + ### Create Workflow An example workflow is here. diff --git a/pkg/github/github.go b/pkg/github/github.go index 361a450..348a5aa 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -32,6 +32,7 @@ type Client struct { type Label struct { Name string `yaml:"name"` + Alias string `yaml:"alias"` Description string `yaml:"description"` Color string `yaml:"color"` } @@ -59,8 +60,12 @@ func NewClient(token string) *Client { func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []Label, prune bool) error { labelMap := make(map[string]Label) + aliasMap := make(map[string]Label) for _, l := range labels { labelMap[l.Name] = l + if l.Alias != "" { + aliasMap[l.Alias] = l + } } currentLabels, err := c.getLabels(ctx, owner, repo) @@ -79,8 +84,9 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La for _, currentLabel := range currentLabels { currentLabel := currentLabel eg.Go(func() error { - _, ok := labelMap[currentLabel.Name] - if ok { + _, name_ok := labelMap[currentLabel.Name] + _, alias_ok := aliasMap[currentLabel.Name] + if (alias_ok && !name_ok) || name_ok { return nil } return c.deleteLabel(ctx, owner, repo, currentLabel.Name) @@ -96,12 +102,17 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La for _, l := range labels { l := l eg.Go(func() error { + labelName := l.Name currentLabel, ok := currentLabelMap[l.Name] if !ok { - return c.createLabel(ctx, owner, repo, l) + currentLabel, ok = currentLabelMap[l.Alias] + if !ok { + return c.createLabel(ctx, owner, repo, l) + } + labelName = l.Alias } - if currentLabel.Description != l.Description || currentLabel.Color != l.Color { - return c.updateLabel(ctx, owner, repo, l) + if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { + return c.updateLabel(ctx, owner, repo, labelName, l) } fmt.Printf("label: %+v not changed on %s/%s\n", l, owner, repo) return nil @@ -147,13 +158,13 @@ func (c *Client) getLabels(ctx context.Context, owner, repo string) ([]Label, er return labels, nil } -func (c *Client) updateLabel(ctx context.Context, owner, repo string, label Label) error { +func (c *Client) updateLabel(ctx context.Context, owner, repo, labelName string, label Label) error { l := &github.Label{ Name: &label.Name, Description: &label.Description, Color: &label.Color, } - _, _, err := c.githubClient.Issues.EditLabel(ctx, owner, repo, label.Name, l) + _, _, err := c.githubClient.Issues.EditLabel(ctx, owner, repo, labelName, l) fmt.Printf("label %+v updated on: %s/%s\n", label, owner, repo) return err } From 0c70614e9e9996c7b0bb0c88d47708c9aaf8e039 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 11:30:36 -0700 Subject: [PATCH 02/14] Add support for multiple aliases This is useful when syncing labels across many projects with one config. If some projects used different label names originally, the new versions may need multiple aliases. --- pkg/github/github.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/github/github.go b/pkg/github/github.go index 348a5aa..6372aa0 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -33,6 +33,7 @@ type Client struct { type Label struct { Name string `yaml:"name"` Alias string `yaml:"alias"` + Aliases []string `yaml:"aliases"` Description string `yaml:"description"` Color string `yaml:"color"` } @@ -66,6 +67,9 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La if l.Alias != "" { aliasMap[l.Alias] = l } + for _, alias := range l.Aliases { + aliasMap[alias] = l + } } currentLabels, err := c.getLabels(ctx, owner, repo) From 6e113fbea0156d4137a4608f580384cc2c95f639 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:23:43 -0700 Subject: [PATCH 03/14] Add import feature Now you can import labels from another file that is common to multiple projects. --- pkg/github/github.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index 6372aa0..72e80e4 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "io/ioutil" + "path/filepath" "github.com/google/go-github/github" "golang.org/x/oauth2" @@ -31,6 +32,8 @@ type Client struct { } type Label struct { + // If "import" is present, all other fields are ignored. + Import string `yaml:"import"` Name string `yaml:"name"` Alias string `yaml:"alias"` Aliases []string `yaml:"aliases"` @@ -43,9 +46,31 @@ func FromManifestToLabels(path string) ([]Label, error) { if err != nil { return nil, err } + var labels []Label err = yaml.Unmarshal(buf, &labels) - return labels, err + if err != nil { + return nil, err + } + + // Handle imports of labels from another file + var flatLabels []Label + for _, l := range labels { + if l.Import == "" { + flatLabels = append(flatLabels, l) + } else { + var importPath string + importPath = filepath.Join(filepath.Dir(path), l.Import) + var importedLabels []Label + importedLabels, err = FromManifestToLabels(importPath) + if err != nil { + return nil, err + } + flatLabels = append(flatLabels, importedLabels...) + } + } + + return flatLabels, err } func NewClient(token string) *Client { From d6f485e7663c104058ef02ded2d0144063a5af05 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:28:04 -0700 Subject: [PATCH 04/14] Disable noisy message about not changing labels --- pkg/github/github.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index 72e80e4..26e3f80 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -143,7 +143,7 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { return c.updateLabel(ctx, owner, repo, labelName, l) } - fmt.Printf("label: %+v not changed on %s/%s\n", l, owner, repo) + // fmt.Printf("label: %+v not changed on %s/%s\n", l, owner, repo) return nil }) } From 228213dff8b351fde7f018abfd9e8ac3f098cd9d Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:31:30 -0700 Subject: [PATCH 05/14] Error early if description is too long GitHub will reject these labels, so we should identify them early and provide a clear message. --- pkg/github/github.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/github/github.go b/pkg/github/github.go index 26e3f80..ce459b0 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -57,6 +57,9 @@ func FromManifestToLabels(path string) ([]Label, error) { var flatLabels []Label for _, l := range labels { if l.Import == "" { + if len(l.Description) > 100 { + return nil, fmt.Errorf("Description of \"%s\" exceeds 100 characters", l.Name) + } flatLabels = append(flatLabels, l) } else { var importPath string From 3091c643da5190f41941409d9810b5e135009962 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:45:35 -0700 Subject: [PATCH 06/14] Remove var statements I am learning go! --- pkg/github/github.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index ce459b0..21214fd 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -62,10 +62,8 @@ func FromManifestToLabels(path string) ([]Label, error) { } flatLabels = append(flatLabels, l) } else { - var importPath string - importPath = filepath.Join(filepath.Dir(path), l.Import) - var importedLabels []Label - importedLabels, err = FromManifestToLabels(importPath) + importPath := filepath.Join(filepath.Dir(path), l.Import) + importedLabels, err := FromManifestToLabels(importPath) if err != nil { return nil, err } From 2706c20c7f5904cabc36fb600ee3a9ca2715d6f1 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:47:37 -0700 Subject: [PATCH 07/14] Add dry-run flag For a large migration, it will be useful to have a dry-run to see what changes would be made before they are actually made --- cmd/action-label-syncer/main.go | 11 ++++++++++- pkg/github/github.go | 33 +++++++++++++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/cmd/action-label-syncer/main.go b/cmd/action-label-syncer/main.go index a871228..83ff5e2 100644 --- a/cmd/action-label-syncer/main.go +++ b/cmd/action-label-syncer/main.go @@ -44,6 +44,15 @@ func run(ctx context.Context) error { return fmt.Errorf("unable to parse prune: %w", err) } + dryRun := false + dryRunEnv := os.Getenv("INPUT_DRY_RUN") + if dryRunEnv != "" { + dryRun, err = strconv.ParseBool(os.Getenv("INPUT_DRY_RUN")) + if err != nil { + return fmt.Errorf("unable to parse dry-run: %w", err) + } + } + token := os.Getenv("INPUT_TOKEN") if len(token) == 0 { token = os.Getenv("GITHUB_TOKEN") @@ -67,7 +76,7 @@ func run(ctx context.Context) error { } owner, repo := s[0], s[1] - if err := client.SyncLabels(ctx, owner, repo, labels, prune); err != nil { + if err := client.SyncLabels(ctx, owner, repo, labels, prune, dryRun); err != nil { err = multierr.Append(err, fmt.Errorf("unable to sync labels: %w", err)) } } diff --git a/pkg/github/github.go b/pkg/github/github.go index 21214fd..67c1771 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -85,7 +85,11 @@ func NewClient(token string) *Client { } } -func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []Label, prune bool) error { +func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []Label, prune bool, dryRun bool) error { + if dryRun { + fmt.Printf("Dry run! No actual changes will be made.\n") + } + labelMap := make(map[string]Label) aliasMap := make(map[string]Label) for _, l := range labels { @@ -119,7 +123,7 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La if (alias_ok && !name_ok) || name_ok { return nil } - return c.deleteLabel(ctx, owner, repo, currentLabel.Name) + return c.deleteLabel(ctx, owner, repo, currentLabel.Name, dryRun) }) } @@ -137,12 +141,12 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La if !ok { currentLabel, ok = currentLabelMap[l.Alias] if !ok { - return c.createLabel(ctx, owner, repo, l) + return c.createLabel(ctx, owner, repo, l, dryRun) } labelName = l.Alias } if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { - return c.updateLabel(ctx, owner, repo, labelName, l) + return c.updateLabel(ctx, owner, repo, labelName, l, dryRun) } // fmt.Printf("label: %+v not changed on %s/%s\n", l, owner, repo) return nil @@ -152,14 +156,17 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La return eg.Wait() } -func (c *Client) createLabel(ctx context.Context, owner, repo string, label Label) error { +func (c *Client) createLabel(ctx context.Context, owner, repo string, label Label, dryRun bool) error { l := &github.Label{ Name: &label.Name, Description: &label.Description, Color: &label.Color, } - _, _, err := c.githubClient.Issues.CreateLabel(ctx, owner, repo, l) fmt.Printf("label: %+v created on: %s/%s\n", label, owner, repo) + if dryRun { + return nil + } + _, _, err := c.githubClient.Issues.CreateLabel(ctx, owner, repo, l) return err } @@ -188,19 +195,25 @@ func (c *Client) getLabels(ctx context.Context, owner, repo string) ([]Label, er return labels, nil } -func (c *Client) updateLabel(ctx context.Context, owner, repo, labelName string, label Label) error { +func (c *Client) updateLabel(ctx context.Context, owner, repo, labelName string, label Label, dryRun bool) error { l := &github.Label{ Name: &label.Name, Description: &label.Description, Color: &label.Color, } - _, _, err := c.githubClient.Issues.EditLabel(ctx, owner, repo, labelName, l) fmt.Printf("label %+v updated on: %s/%s\n", label, owner, repo) + if dryRun { + return nil + } + _, _, err := c.githubClient.Issues.EditLabel(ctx, owner, repo, labelName, l) return err } -func (c *Client) deleteLabel(ctx context.Context, owner, repo, name string) error { - _, err := c.githubClient.Issues.DeleteLabel(ctx, owner, repo, name) +func (c *Client) deleteLabel(ctx context.Context, owner, repo, name string, dryRun bool) error { fmt.Printf("label: %s deleted from: %s/%s\n", name, owner, repo) + if dryRun { + return nil + } + _, err := c.githubClient.Issues.DeleteLabel(ctx, owner, repo, name) return err } From 760a59537ce79566e5897540d1078f80f32d0d37 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 12:52:46 -0700 Subject: [PATCH 08/14] Reformat output --- pkg/github/github.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index 67c1771..b429325 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -148,7 +148,7 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { return c.updateLabel(ctx, owner, repo, labelName, l, dryRun) } - // fmt.Printf("label: %+v not changed on %s/%s\n", l, owner, repo) + //fmt.Printf("Not changed: \"%s\" on %s/%s\n", l.Name, owner, repo) return nil }) } @@ -162,7 +162,7 @@ func (c *Client) createLabel(ctx context.Context, owner, repo string, label Labe Description: &label.Description, Color: &label.Color, } - fmt.Printf("label: %+v created on: %s/%s\n", label, owner, repo) + fmt.Printf("Created: \"%s\" on %s/%s\n", label.Name, owner, repo) if dryRun { return nil } @@ -201,7 +201,11 @@ func (c *Client) updateLabel(ctx context.Context, owner, repo, labelName string, Description: &label.Description, Color: &label.Color, } - fmt.Printf("label %+v updated on: %s/%s\n", label, owner, repo) + if labelName != label.Name { + fmt.Printf("Renamed: \"%s\" => \"%s\" on %s/%s\n", labelName, label.Name, owner, repo) + } else { + fmt.Printf("Updated: \"%s\" on %s/%s\n", label.Name, owner, repo) + } if dryRun { return nil } @@ -210,7 +214,7 @@ func (c *Client) updateLabel(ctx context.Context, owner, repo, labelName string, } func (c *Client) deleteLabel(ctx context.Context, owner, repo, name string, dryRun bool) error { - fmt.Printf("label: %s deleted from: %s/%s\n", name, owner, repo) + fmt.Printf("Deleted: \"%s\" on %s/%s\n", name, owner, repo) if dryRun { return nil } From 8996568a4f90160ef11daab0052cc5046eb2f710 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 13:00:22 -0700 Subject: [PATCH 09/14] Fix silent errors from main --- cmd/action-label-syncer/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/action-label-syncer/main.go b/cmd/action-label-syncer/main.go index 83ff5e2..2e43285 100644 --- a/cmd/action-label-syncer/main.go +++ b/cmd/action-label-syncer/main.go @@ -77,7 +77,7 @@ func run(ctx context.Context) error { owner, repo := s[0], s[1] if err := client.SyncLabels(ctx, owner, repo, labels, prune, dryRun); err != nil { - err = multierr.Append(err, fmt.Errorf("unable to sync labels: %w", err)) + return fmt.Errorf("unable to sync labels: %w", err) } } From 43ff7c27f89aef8b0851d0da101e2a199327f87a Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Thu, 23 Sep 2021 13:24:59 -0700 Subject: [PATCH 10/14] Fix alias checks after adding support for multiple aliases --- pkg/github/github.go | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index b429325..fbca36e 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -53,15 +53,19 @@ func FromManifestToLabels(path string) ([]Label, error) { return nil, err } - // Handle imports of labels from another file var flatLabels []Label for _, l := range labels { if l.Import == "" { + // Data checks and normalization. if len(l.Description) > 100 { return nil, fmt.Errorf("Description of \"%s\" exceeds 100 characters", l.Name) } + if l.Alias != "" { + l.Aliases = append(l.Aliases, l.Alias) + } flatLabels = append(flatLabels, l) } else { + // Handle imports of labels from another file importPath := filepath.Join(filepath.Dir(path), l.Import) importedLabels, err := FromManifestToLabels(importPath) if err != nil { @@ -94,9 +98,6 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La aliasMap := make(map[string]Label) for _, l := range labels { labelMap[l.Name] = l - if l.Alias != "" { - aliasMap[l.Alias] = l - } for _, alias := range l.Aliases { aliasMap[alias] = l } @@ -120,7 +121,7 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La eg.Go(func() error { _, name_ok := labelMap[currentLabel.Name] _, alias_ok := aliasMap[currentLabel.Name] - if (alias_ok && !name_ok) || name_ok { + if alias_ok || name_ok { return nil } return c.deleteLabel(ctx, owner, repo, currentLabel.Name, dryRun) @@ -136,17 +137,21 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La for _, l := range labels { l := l eg.Go(func() error { - labelName := l.Name currentLabel, ok := currentLabelMap[l.Name] if !ok { - currentLabel, ok = currentLabelMap[l.Alias] - if !ok { - return c.createLabel(ctx, owner, repo, l, dryRun) + for _, alias := range l.Aliases { + currentLabel, ok = currentLabelMap[alias] + if ok { + break + } } - labelName = l.Alias + } + + if !ok { + return c.createLabel(ctx, owner, repo, l, dryRun) } if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { - return c.updateLabel(ctx, owner, repo, labelName, l, dryRun) + return c.updateLabel(ctx, owner, repo, currentLabel.Name, l, dryRun) } //fmt.Printf("Not changed: \"%s\" on %s/%s\n", l.Name, owner, repo) return nil From 705fd7bad2271c3221f11bc84b2e29e84f524707 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Fri, 24 Sep 2021 13:29:20 -0700 Subject: [PATCH 11/14] Serialize label changes Doing them in parallel does not make the process much faster, but it does make the logs more difficult to read. Just do it serially instead. --- pkg/github/github.go | 54 ++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/pkg/github/github.go b/pkg/github/github.go index fbca36e..7117fa8 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -22,7 +22,6 @@ import ( "github.com/google/go-github/github" "golang.org/x/oauth2" - "golang.org/x/sync/errgroup" "gopkg.in/yaml.v2" ) @@ -112,53 +111,50 @@ func (c *Client) SyncLabels(ctx context.Context, owner, repo string, labels []La currentLabelMap[l.Name] = l } - eg := errgroup.Group{} - // Delete labels. if prune { for _, currentLabel := range currentLabels { currentLabel := currentLabel - eg.Go(func() error { - _, name_ok := labelMap[currentLabel.Name] - _, alias_ok := aliasMap[currentLabel.Name] - if alias_ok || name_ok { - return nil + _, name_ok := labelMap[currentLabel.Name] + _, alias_ok := aliasMap[currentLabel.Name] + if !alias_ok && !name_ok { + err := c.deleteLabel(ctx, owner, repo, currentLabel.Name, dryRun) + if err != nil { + return err } - return c.deleteLabel(ctx, owner, repo, currentLabel.Name, dryRun) - }) - } - - if err := eg.Wait(); err != nil { - return err + } } } // Create and/or update labels. for _, l := range labels { l := l - eg.Go(func() error { - currentLabel, ok := currentLabelMap[l.Name] - if !ok { - for _, alias := range l.Aliases { - currentLabel, ok = currentLabelMap[alias] - if ok { - break - } + currentLabel, ok := currentLabelMap[l.Name] + if !ok { + for _, alias := range l.Aliases { + currentLabel, ok = currentLabelMap[alias] + if ok { + break } } + } - if !ok { - return c.createLabel(ctx, owner, repo, l, dryRun) + if !ok { + err := c.createLabel(ctx, owner, repo, l, dryRun) + if err != nil { + return err } - if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { - return c.updateLabel(ctx, owner, repo, currentLabel.Name, l, dryRun) + } else if currentLabel.Description != l.Description || currentLabel.Color != l.Color || currentLabel.Name != l.Name { + err := c.updateLabel(ctx, owner, repo, currentLabel.Name, l, dryRun) + if err != nil { + return err } + } else { //fmt.Printf("Not changed: \"%s\" on %s/%s\n", l.Name, owner, repo) - return nil - }) + } } - return eg.Wait() + return nil } func (c *Client) createLabel(ctx context.Context, owner, repo string, label Label, dryRun bool) error { From e6bb391945d5044d67d84b1a3ebe23a35aa31344 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Mon, 27 Sep 2021 14:53:49 -0700 Subject: [PATCH 12/14] Error early instead of trying to change a label with a question mark in the name The GitHub client likely needs to be fixed to properly escape label names when updating or deleting them. In the meantime, just error early and make the user rename those manually first. --- pkg/github/github.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/github/github.go b/pkg/github/github.go index 7117fa8..133b656 100644 --- a/pkg/github/github.go +++ b/pkg/github/github.go @@ -19,6 +19,7 @@ import ( "fmt" "io/ioutil" "path/filepath" + "strings" "github.com/google/go-github/github" "golang.org/x/oauth2" @@ -59,6 +60,9 @@ func FromManifestToLabels(path string) ([]Label, error) { if len(l.Description) > 100 { return nil, fmt.Errorf("Description of \"%s\" exceeds 100 characters", l.Name) } + if strings.Contains(l.Name, "?") { + return nil, fmt.Errorf("Label name cannot contain question marks: \"%s\"", l.Name) + } if l.Alias != "" { l.Aliases = append(l.Aliases, l.Alias) } From 3ab1bfc0268592cf90ac616afd1084600d3e3292 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Mon, 14 Feb 2022 17:04:37 -0800 Subject: [PATCH 13/14] Update README with multiple aliases and import statements --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 080dbcc..1afc048 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,37 @@ renaming labels rather than deleting and creating new ones, existing pull requests and issues will keep their labels, but will adopt the new name. +You can also set multiple aliases, which can be useful when reusing a +configuration across multiple repositories, each of which may have a different +existing label. For example: + +```yaml +- name: Type: bug + aliases: + - bug + - defect + - "Seriously, what was I thinking?" + description: Something isn't working + color: d73a4a +``` + +### Import statements + +To reuse some common configurations across repositories, add an item with just +one field called `import`, which contains a path to another yaml file. The +contents of the imported file will be treated as if they appeared in the main +file. For example: + +```yaml +# Common label definitions for all projects, such as "Type: bug". +- import: common.yaml + +# Labels specific to this project: +- name: Platform: iOS + description: Issues specific to iOS + color: d7ea4a +``` + ### Create Workflow An example workflow is here. From af1a9509f1b00127e03749ebe40f687217cb77d9 Mon Sep 17 00:00:00 2001 From: Joey Parrish Date: Mon, 14 Feb 2022 17:50:59 -0800 Subject: [PATCH 14/14] Add dry_run parameter to action.yml --- action.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/action.yml b/action.yml index 260fafe..dcee2f9 100644 --- a/action.yml +++ b/action.yml @@ -16,6 +16,10 @@ inputs: description: "Remove unmanaged labels from repository" required: false default: true + dry_run: + description: "Print what would be done, but do nothing" + required: false + default: false runs: using: "docker" image: "Dockerfile"