From 3abd5ab72fda571e69fffd97bd4e0033dd5f495c Mon Sep 17 00:00:00 2001 From: micnncim Date: Sun, 23 May 2021 12:02:14 +0900 Subject: [PATCH] Support sync with multiple repositories (#58) --- README.md | 6 +++-- cmd/action-label-syncer/main.go | 47 +++++++++++++++++++++------------ go.mod | 1 + go.sum | 23 +++++++++++----- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 61af033..02d67a6 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ You can add `jobs..steps.with.prune: false` in order to preserver all ex ## Sync labels on another repository -It is also possible to specify a repository as an input to the action. This is useful if you want to store your labels somewhere centrally and modify multiple repository labels. +It is also possible to specify a repository or repositories as an input to the action. This is useful if you want to store your labels somewhere centrally and modify multiple repository labels. **Note: The default `GITHUB_TOKEN` will not have permissions to operate on other repositories so you must specify a personal access token in your secrets.** @@ -85,7 +85,9 @@ jobs: - uses: micnncim/action-label-syncer@v1 with: manifest: path/to/manifest/labels.yml - repository: myother/repository + repository: | + owner/repository-1 + owner/repository-2 token: ${{ secrets.PERSONAL_TOKEN }} ``` diff --git a/cmd/action-label-syncer/main.go b/cmd/action-label-syncer/main.go index a4119bc..a871228 100644 --- a/cmd/action-label-syncer/main.go +++ b/cmd/action-label-syncer/main.go @@ -17,19 +17,31 @@ package main import ( "context" "fmt" + "log" "os" "strconv" "strings" "github.com/micnncim/action-label-syncer/pkg/github" + "go.uber.org/multierr" ) func main() { + if err := run(context.Background()); err != nil { + log.Fatal(err) + } +} + +func run(ctx context.Context) error { manifest := os.Getenv("INPUT_MANIFEST") labels, err := github.FromManifestToLabels(manifest) if err != nil { - fmt.Fprintf(os.Stderr, "unable to load manifest: %v\n", err) - os.Exit(1) + return fmt.Errorf("unable to load manifest: %w", err) + } + + prune, err := strconv.ParseBool(os.Getenv("INPUT_PRUNE")) + if err != nil { + return fmt.Errorf("unable to parse prune: %w", err) } token := os.Getenv("INPUT_TOKEN") @@ -42,22 +54,23 @@ func main() { if len(repository) == 0 { repository = os.Getenv("GITHUB_REPOSITORY") } - slugs := strings.Split(repository, "/") - if len(slugs) != 2 { - fmt.Fprintf(os.Stderr, "invalid repository: %v\n", repository) - os.Exit(1) - } - owner, repo := slugs[0], slugs[1] - prune, err := strconv.ParseBool(os.Getenv("INPUT_PRUNE")) - if err != nil { - fmt.Fprintf(os.Stderr, "unable to parse prune: %v\n", err) - os.Exit(1) - } + // Doesn't run concurrently to avoid GitHub API rate limit. + for _, r := range strings.Split(repository, "\n") { + if len(r) == 0 { + continue + } + + s := strings.Split(r, "/") + if len(s) != 2 { + err = multierr.Append(err, fmt.Errorf("invalid repository: %s", repository)) + } + owner, repo := s[0], s[1] - ctx := context.Background() - if err := client.SyncLabels(ctx, owner, repo, labels, prune); err != nil { - fmt.Fprintf(os.Stderr, "unable to sync labels: %v\n", err) - os.Exit(1) + if err := client.SyncLabels(ctx, owner, repo, labels, prune); err != nil { + err = multierr.Append(err, fmt.Errorf("unable to sync labels: %w", err)) + } } + + return err } diff --git a/go.mod b/go.mod index b66a155..61ac80c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect + go.uber.org/multierr v1.7.0 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index dbd8e43..8d904fb 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,23 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= 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= @@ -18,10 +31,8 @@ google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO50 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=