├── .github └── workflows │ └── release.yml ├── .goreleaser.yml ├── README.md ├── client.go ├── cmd └── label-exporter │ └── main.go ├── exporter.go ├── go.mod ├── go.sum └── label.go /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" 6 | jobs: 7 | 8 | release: 9 | runs-on: ubuntu-latest 10 | steps: 11 | 12 | - name: Check out 13 | uses: actions/checkout@v2 14 | 15 | - name: Setup Go 16 | uses: actions/setup-go@v1 17 | with: 18 | go-version: 1.13 19 | 20 | - name: Run GoReleaser 21 | uses: goreleaser/goreleaser-action@v1 22 | with: 23 | version: latest 24 | args: release --rm-dist 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | 28 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | builds: 2 | - main: ./cmd/label-exporter 3 | env: 4 | - CGO_ENABLED=0 5 | - GO111MODULE=on 6 | goos: 7 | - linux 8 | - darwin 9 | goarch: 10 | - 386 11 | - amd64 12 | - arm 13 | - arm64 14 | before: 15 | hooks: 16 | - go mod download 17 | checksum: 18 | name_template: 'checksums.txt' 19 | snapshot: 20 | name_template: "{{ .Tag }}-next" 21 | changelog: 22 | sort: asc 23 | filters: 24 | exclude: 25 | - '^Fix' 26 | - '^Refactor' 27 | 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # label-exporter 2 | 3 | Export GitHub labels in the form of YAML, JSON and table. 4 | 5 | ## Installation 6 | 7 | Create a personal access token with `public_repo` scope at 8 | https://github.com/settings/tokens/new and: 9 | 10 | ```console 11 | $ export GITHUB_TOKEN=enter_the_token_here 12 | ``` 13 | 14 | Then either download a binary release from 15 | https://github.com/micnncim/label-exporter/releases or: 16 | 17 | ```console 18 | $ go get github.com/micnncim/label-exporter/cmd/label-exporter 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```console 24 | $ label-exporter --help 25 | usage: label-exporter [] 26 | 27 | Flags: 28 | --help Show context-sensitive help (also try --help-long and --help-man). 29 | -y, --yaml Use the YAML format. 30 | -j, --json Use the JSON format. 31 | -t, --table Use the table format. 32 | 33 | Args: 34 | Owner of the repository. 35 | Repository whose wanted labels. 36 | ``` 37 | 38 | ## Example 39 | 40 | ```console 41 | $ label-exporter micnncim label-exporter --yaml 42 | - color: d73a4a 43 | description: Something isn't working 44 | name: bug 45 | - color: 0075ca 46 | description: Improvements or additions to documentation 47 | name: documentation 48 | - color: cfd3d7 49 | description: This issue or pull request already exists 50 | name: duplicate 51 | ``` 52 | 53 | ```console 54 | $ label-exporter micnncim label-exporter --json | jq 55 | [ 56 | { 57 | "name": "bug", 58 | "description": "Something isn't working", 59 | "color": "d73a4a" 60 | }, 61 | { 62 | "name": "documentation", 63 | "description": "Improvements or additions to documentation", 64 | "color": "0075ca" 65 | }, 66 | { 67 | "name": "duplicate", 68 | "description": "This issue or pull request already exists", 69 | "color": "cfd3d7" 70 | } 71 | ] 72 | ``` 73 | 74 | ```console 75 | $ label-exporter micnncim label-exporter --table 76 | +------------------+--------------------------------+--------+ 77 | | NAME | DESCRIPTION | COLOR | 78 | +------------------+--------------------------------+--------+ 79 | | bug | Something isn't working | d73a4a | 80 | | documentation | Improvements or additions to | 0075ca | 81 | | | documentation | | 82 | | duplicate | This issue or pull request | cfd3d7 | 83 | | | already exists | | 84 | +------------------+--------------------------------+--------+ 85 | ``` 86 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package exporter 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "os" 7 | 8 | "github.com/google/go-github/v28/github" 9 | "golang.org/x/oauth2" 10 | ) 11 | 12 | type githubClient struct { 13 | client *github.Client 14 | } 15 | 16 | func NewClient() (*githubClient, error) { 17 | token := os.Getenv("GITHUB_TOKEN") 18 | if token == "" { 19 | return nil, errors.New("missing GITHUB_TOKEN") 20 | } 21 | cli := newClient(token) 22 | return &githubClient{ 23 | client: cli, 24 | }, nil 25 | } 26 | 27 | func newClient(token string) *github.Client { 28 | ts := oauth2.StaticTokenSource(&oauth2.Token{ 29 | AccessToken: token, 30 | }) 31 | tc := oauth2.NewClient(context.Background(), ts) 32 | return github.NewClient(tc) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/label-exporter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | 8 | "gopkg.in/alecthomas/kingpin.v2" 9 | 10 | exporter "github.com/micnncim/label-exporter" 11 | ) 12 | 13 | var ( 14 | owner = kingpin.Arg("owner", "Owner of the repository.").Required().String() 15 | repo = kingpin.Arg("repo", "Repository whose wanted labels.").Required().String() 16 | yaml = kingpin.Flag("yaml", "Use the YAML format.").Short('y').Bool() 17 | json = kingpin.Flag("json", "Use the JSON format.").Short('j').Bool() 18 | table = kingpin.Flag("table", "Use the table format.").Short('t').Bool() 19 | ) 20 | 21 | func main() { 22 | kingpin.Parse() 23 | 24 | client, err := exporter.NewClient() 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | labels, err := client.ListLabels(context.Background(), *owner, *repo) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | 33 | if *yaml { 34 | b, err := exporter.LabelsToYAML(labels) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | fmt.Println(string(b)) 39 | return 40 | } 41 | 42 | if *json { 43 | b, err := exporter.LabelsToJSON(labels) 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | fmt.Println(string(b)) 48 | return 49 | } 50 | 51 | if *table { 52 | b, err := exporter.LabelsToTable(labels) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | fmt.Println(string(b)) 57 | return 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /exporter.go: -------------------------------------------------------------------------------- 1 | package exporter 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | 7 | "github.com/olekukonko/tablewriter" 8 | "sigs.k8s.io/yaml" 9 | ) 10 | 11 | func LabelsToJSON(labels []*Label) ([]byte, error) { 12 | return json.Marshal(labels) 13 | } 14 | 15 | func LabelsToYAML(labels []*Label) ([]byte, error) { 16 | return yaml.Marshal(labels) 17 | } 18 | 19 | func LabelsToTable(labels []*Label) ([]byte, error) { 20 | labelRows := make([][]string, 0, len(labels)) 21 | for _, l := range labels { 22 | labelRows = append(labelRows, []string{l.Name, l.Description, l.Color}) 23 | } 24 | 25 | b := &bytes.Buffer{} 26 | t := tablewriter.NewWriter(b) 27 | t.SetHeader([]string{"Name", "Description", "Color"}) 28 | t.AppendBulk(labelRows) 29 | t.Render() 30 | 31 | return b.Bytes(), nil 32 | } 33 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/micnncim/label-exporter 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect 7 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 // indirect 8 | github.com/google/go-github/v28 v28.0.0 9 | github.com/mattn/go-runewidth v0.0.4 // indirect 10 | github.com/olekukonko/tablewriter v0.0.1 11 | github.com/stretchr/testify v1.4.0 // indirect 12 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be 13 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 14 | sigs.k8s.io/yaml v1.1.0 15 | ) 16 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= 2 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 3 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E= 4 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 5 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 6 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 8 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 9 | github.com/google/go-github/v28 v28.0.0 h1:+UjHI4+1W/vsXR4jJBWt0ZA74XHbvt5yBAvsf1M3bgM= 10 | github.com/google/go-github/v28 v28.0.0/go.mod h1:+5GboIspo7F0NG2qsvfYh7en6F3EK37uyqv+c35AR3s= 11 | github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= 12 | github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= 13 | github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 14 | github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 19 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 20 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 21 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 22 | golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= 23 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 24 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= 25 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 26 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= 27 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 28 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 29 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 30 | google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= 31 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 32 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 33 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 35 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 36 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 37 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 38 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 39 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 40 | -------------------------------------------------------------------------------- /label.go: -------------------------------------------------------------------------------- 1 | package exporter 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/google/go-github/v28/github" 7 | ) 8 | 9 | type Label struct { 10 | Name string `json:"name"` 11 | Description string `json:"description"` 12 | Color string `json:"color"` 13 | } 14 | 15 | func (c *githubClient) ListLabels(ctx context.Context, owner, repo string) ([]*Label, error) { 16 | opt := &github.ListOptions{PerPage: 10} 17 | var labels []*Label 18 | for { 19 | ghLabels, resp, err := c.client.Issues.ListLabels(ctx, owner, repo, opt) 20 | if err != nil { 21 | return nil, err 22 | } 23 | for _, l := range ghLabels { 24 | labels = append(labels, &Label{ 25 | Name: l.GetName(), 26 | Description: l.GetDescription(), 27 | Color: l.GetColor(), 28 | }) 29 | } 30 | if resp.NextPage == 0 { 31 | break 32 | } 33 | opt.Page = resp.NextPage 34 | } 35 | return labels, nil 36 | } 37 | --------------------------------------------------------------------------------