First pass at implementing gh repo license list
This commit is contained in:
parent
e63dc2216b
commit
fd8c4633e3
7 changed files with 391 additions and 13 deletions
|
|
@ -244,7 +244,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
|
|||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
hostname, _ := cfg.Authentication().DefaultHost()
|
||||
licenses, err := listLicenseTemplates(httpClient, hostname)
|
||||
licenses, err := shared.ListLicenseTemplates(httpClient, hostname)
|
||||
if err != nil {
|
||||
return nil, cobra.ShellCompDirectiveError
|
||||
}
|
||||
|
|
@ -830,7 +830,7 @@ func interactiveLicense(client *http.Client, hostname string, prompter iprompter
|
|||
return "", nil
|
||||
}
|
||||
|
||||
licenses, err := listLicenseTemplates(client, hostname)
|
||||
licenses, err := shared.ListLicenseTemplates(client, hostname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,17 +350,6 @@ func listGitIgnoreTemplates(httpClient *http.Client, hostname string) ([]string,
|
|||
return gitIgnoreTemplates, nil
|
||||
}
|
||||
|
||||
// listLicenseTemplates uses API v3 here because license template isn't supported by GraphQL yet.
|
||||
func listLicenseTemplates(httpClient *http.Client, hostname string) ([]api.License, error) {
|
||||
var licenseTemplates []api.License
|
||||
client := api.NewClientFromHTTP(httpClient)
|
||||
err := client.REST(hostname, "GET", "licenses", nil, &licenseTemplates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return licenseTemplates, nil
|
||||
}
|
||||
|
||||
// Returns the current username and any orgs that user is a member of.
|
||||
func userAndOrgs(httpClient *http.Client, hostname string) (string, []string, error) {
|
||||
client := api.NewClientFromHTTP(httpClient)
|
||||
|
|
|
|||
18
pkg/cmd/repo/license/license.go
Normal file
18
pkg/cmd/repo/license/license.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package license
|
||||
|
||||
import (
|
||||
cmdList "github.com/cli/cli/v2/pkg/cmd/repo/license/list"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdLicense(f *cmdutil.Factory) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "license <command>",
|
||||
Short: "View available repository license options",
|
||||
}
|
||||
|
||||
cmd.AddCommand(cmdList.NewCmdList(f, nil))
|
||||
|
||||
return cmd
|
||||
}
|
||||
96
pkg/cmd/repo/license/list/list.go
Normal file
96
pkg/cmd/repo/license/list/list.go
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/internal/tableprinter"
|
||||
"github.com/cli/cli/v2/pkg/cmd/repo/shared"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type LicenseRenderer interface {
|
||||
Render([]api.License, ListOptions) error
|
||||
}
|
||||
|
||||
type TableLicenseRenderer struct{}
|
||||
|
||||
var licenseFields = []string{
|
||||
"key",
|
||||
"name",
|
||||
}
|
||||
|
||||
type ListOptions struct {
|
||||
IO *iostreams.IOStreams
|
||||
HTTPClient func() (*http.Client, error)
|
||||
Exporter cmdutil.Exporter
|
||||
Config func() (gh.Config, error)
|
||||
}
|
||||
|
||||
func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Command {
|
||||
opts := &ListOptions{
|
||||
IO: f.IOStreams,
|
||||
HTTPClient: f.HttpClient,
|
||||
Config: f.Config,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "List available repository license options",
|
||||
Aliases: []string{"ls"},
|
||||
Args: cmdutil.ExactArgs(0, "gh repo license list takes no arguments"),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if runF != nil {
|
||||
return runF(opts)
|
||||
}
|
||||
return listRun(opts)
|
||||
},
|
||||
}
|
||||
// cmdutil.AddFormatFlags(cmd, &opts.Exporter)
|
||||
cmdutil.AddJSONFlags(cmd, &opts.Exporter, licenseFields)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func listRun(opts *ListOptions) error {
|
||||
client, err := opts.HTTPClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := opts.Config()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostname, _ := cfg.Authentication().DefaultHost()
|
||||
licenses, err := shared.ListLicenseTemplates(client, hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(licenses) == 0 {
|
||||
return cmdutil.NewNoResultsError("no licenses found")
|
||||
}
|
||||
|
||||
if opts.Exporter != nil {
|
||||
return opts.Exporter.Write(opts.IO, licenses)
|
||||
}
|
||||
|
||||
r := &TableLicenseRenderer{}
|
||||
return r.Render(licenses, opts)
|
||||
}
|
||||
|
||||
func (r *TableLicenseRenderer) Render(licenses []api.License, opts *ListOptions) error {
|
||||
t := tableprinter.New(opts.IO, tableprinter.WithHeader("KEY", "NAME"))
|
||||
for _, l := range licenses {
|
||||
t.AddField(l.Key)
|
||||
t.AddField(l.Name)
|
||||
t.EndRow()
|
||||
}
|
||||
|
||||
return t.Render()
|
||||
}
|
||||
259
pkg/cmd/repo/license/list/list_test.go
Normal file
259
pkg/cmd/repo/license/list/list_test.go
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
package list
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/api"
|
||||
"github.com/cli/cli/v2/internal/config"
|
||||
"github.com/cli/cli/v2/internal/gh"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewCmdList(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantErr bool
|
||||
tty bool
|
||||
}{
|
||||
{
|
||||
name: "no arguments",
|
||||
args: []string{},
|
||||
wantErr: false,
|
||||
tty: false,
|
||||
},
|
||||
{
|
||||
name: "too many arguments",
|
||||
args: []string{"foo", "bar"},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
ios.SetStdinTTY(tt.tty)
|
||||
ios.SetStdoutTTY(tt.tty)
|
||||
ios.SetStderrTTY(tt.tty)
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: ios,
|
||||
}
|
||||
cmd := NewCmdList(f, func(*ListOptions) error {
|
||||
return nil
|
||||
})
|
||||
cmd.SetArgs(tt.args)
|
||||
cmd.SetIn(&bytes.Buffer{})
|
||||
cmd.SetOut(&bytes.Buffer{})
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
|
||||
_, err := cmd.ExecuteC()
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableLicenseRenderer(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts ListOptions
|
||||
isTTY bool
|
||||
wantStdout string
|
||||
wantErr bool
|
||||
licenses []api.License
|
||||
}{
|
||||
{
|
||||
name: "licenses + tty",
|
||||
opts: ListOptions{},
|
||||
isTTY: true,
|
||||
wantStdout: heredoc.Doc(`
|
||||
KEY NAME
|
||||
mit MIT License
|
||||
lgpl-3.0 GNU Lesser General Public License v3.0
|
||||
`),
|
||||
wantErr: false,
|
||||
licenses: []api.License{
|
||||
{
|
||||
Key: "mit",
|
||||
Name: "MIT License",
|
||||
},
|
||||
{
|
||||
Key: "lgpl-3.0",
|
||||
Name: "GNU Lesser General Public License v3.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no licenses + tty",
|
||||
opts: ListOptions{},
|
||||
isTTY: true,
|
||||
wantStdout: heredoc.Doc(`
|
||||
KEY NAME
|
||||
`),
|
||||
wantErr: false,
|
||||
licenses: []api.License{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ios, _, stdout, _ := iostreams.Test()
|
||||
ios.SetStdoutTTY(tt.isTTY)
|
||||
ios.SetStdinTTY(tt.isTTY)
|
||||
ios.SetStderrTTY(tt.isTTY)
|
||||
tt.opts.IO = ios
|
||||
|
||||
r := &TableLicenseRenderer{}
|
||||
err := r.Render(tt.licenses, &tt.opts)
|
||||
|
||||
if !tt.wantErr {
|
||||
assert.NoError(t, err, "Expected no error while rendering table")
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.wantStdout, stdout.String(), "Rendered table differs from expected")
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRun(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts *ListOptions
|
||||
isTTY bool
|
||||
httpStubs func(t *testing.T, reg *httpmock.Registry)
|
||||
wantStdout string
|
||||
wantStderr string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{
|
||||
name: "license list tty",
|
||||
isTTY: true,
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "licenses"),
|
||||
httpmock.StringResponse(`[
|
||||
{
|
||||
"key": "mit",
|
||||
"name": "MIT License",
|
||||
"spdx_id": "MIT",
|
||||
"url": "https://api.github.com/licenses/mit",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "lgpl-3.0",
|
||||
"name": "GNU Lesser General Public License v3.0",
|
||||
"spdx_id": "LGPL-3.0",
|
||||
"url": "https://api.github.com/licenses/lgpl-3.0",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "mpl-2.0",
|
||||
"name": "Mozilla Public License 2.0",
|
||||
"spdx_id": "MPL-2.0",
|
||||
"url": "https://api.github.com/licenses/mpl-2.0",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "agpl-3.0",
|
||||
"name": "GNU Affero General Public License v3.0",
|
||||
"spdx_id": "AGPL-3.0",
|
||||
"url": "https://api.github.com/licenses/agpl-3.0",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "unlicense",
|
||||
"name": "The Unlicense",
|
||||
"spdx_id": "Unlicense",
|
||||
"url": "https://api.github.com/licenses/unlicense",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "apache-2.0",
|
||||
"name": "Apache License 2.0",
|
||||
"spdx_id": "Apache-2.0",
|
||||
"url": "https://api.github.com/licenses/apache-2.0",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
},
|
||||
{
|
||||
"key": "gpl-3.0",
|
||||
"name": "GNU General Public License v3.0",
|
||||
"spdx_id": "GPL-3.0",
|
||||
"url": "https://api.github.com/licenses/gpl-3.0",
|
||||
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||
}
|
||||
]`,
|
||||
))
|
||||
},
|
||||
wantStdout: heredoc.Doc(`
|
||||
KEY NAME
|
||||
mit MIT License
|
||||
lgpl-3.0 GNU Lesser General Public License v3.0
|
||||
mpl-2.0 Mozilla Public License 2.0
|
||||
agpl-3.0 GNU Affero General Public License v3.0
|
||||
unlicense The Unlicense
|
||||
apache-2.0 Apache License 2.0
|
||||
gpl-3.0 GNU General Public License v3.0
|
||||
`),
|
||||
wantStderr: "",
|
||||
opts: &ListOptions{},
|
||||
},
|
||||
{
|
||||
name: "license list no licenses tty",
|
||||
isTTY: true,
|
||||
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "licenses"),
|
||||
httpmock.StringResponse(`[]`),
|
||||
)
|
||||
},
|
||||
wantStdout: "",
|
||||
wantStderr: "",
|
||||
wantErr: true,
|
||||
errMsg: "no licenses found",
|
||||
opts: &ListOptions{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
reg := &httpmock.Registry{}
|
||||
if tt.httpStubs != nil {
|
||||
tt.httpStubs(t, reg)
|
||||
}
|
||||
tt.opts.Config = func() (gh.Config, error) {
|
||||
return config.NewBlankConfig(), nil
|
||||
}
|
||||
tt.opts.HTTPClient = func() (*http.Client, error) {
|
||||
return &http.Client{Transport: reg}, nil
|
||||
}
|
||||
ios, _, stdout, stderr := iostreams.Test()
|
||||
ios.SetStdoutTTY(tt.isTTY)
|
||||
ios.SetStdinTTY(tt.isTTY)
|
||||
ios.SetStderrTTY(tt.isTTY)
|
||||
tt.opts.IO = ios
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer reg.Verify(t)
|
||||
err := listRun(tt.opts)
|
||||
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.errMsg, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.wantStdout, stdout.String(), "Stdout differs from expected")
|
||||
assert.Equal(t, tt.wantStderr, stderr.String(), "Stderr differs from expected")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import (
|
|||
repoEditCmd "github.com/cli/cli/v2/pkg/cmd/repo/edit"
|
||||
repoForkCmd "github.com/cli/cli/v2/pkg/cmd/repo/fork"
|
||||
gardenCmd "github.com/cli/cli/v2/pkg/cmd/repo/garden"
|
||||
licenseCmd "github.com/cli/cli/v2/pkg/cmd/repo/license"
|
||||
repoListCmd "github.com/cli/cli/v2/pkg/cmd/repo/list"
|
||||
repoRenameCmd "github.com/cli/cli/v2/pkg/cmd/repo/rename"
|
||||
repoDefaultCmd "github.com/cli/cli/v2/pkg/cmd/repo/setdefault"
|
||||
|
|
@ -54,6 +55,7 @@ func NewCmdRepo(f *cmdutil.Factory) *cobra.Command {
|
|||
repoSyncCmd.NewCmdSync(f, nil),
|
||||
repoEditCmd.NewCmdEdit(f, nil),
|
||||
deployKeyCmd.NewCmdDeployKey(f),
|
||||
licenseCmd.NewCmdLicense(f),
|
||||
repoRenameCmd.NewCmdRename(f, nil),
|
||||
repoArchiveCmd.NewCmdArchive(f, nil),
|
||||
repoUnarchiveCmd.NewCmdUnarchive(f, nil),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package shared
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cli/cli/v2/api"
|
||||
)
|
||||
|
||||
var invalidCharactersRE = regexp.MustCompile(`[^\w._-]+`)
|
||||
|
|
@ -12,3 +15,14 @@ func NormalizeRepoName(repoName string) string {
|
|||
newName := invalidCharactersRE.ReplaceAllString(repoName, "-")
|
||||
return strings.TrimSuffix(newName, ".git")
|
||||
}
|
||||
|
||||
// ListLicenseTemplates uses API v3 here because license template isn't supported by GraphQL yet.
|
||||
func ListLicenseTemplates(httpClient *http.Client, hostname string) ([]api.License, error) {
|
||||
var licenseTemplates []api.License
|
||||
client := api.NewClientFromHTTP(httpClient)
|
||||
err := client.REST(hostname, "GET", "licenses", nil, &licenseTemplates)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return licenseTemplates, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue