Docs cover 1158 commands; tests cover 573; usage covers 520. Usage is the bottleneck — every Completed command needs all three. Closing the largest group would move 70% of the debt — the biggest single shift available.
Coverage counts the docs that exist; depth weighs what they actually say — from full behavioral prose down to a bare signature stub.
command: gh
short: Work seamlessly with GitHub from the command line.
options:
- option: help
value_type: bool
default_value: "false"
description: Show help for command
- option: version
value_type: bool
default_value: "false"
description: Show gh version
N/A
N/A
command: gh
short: Work seamlessly with GitHub from the command line.
options:
- option: help
value_type: bool
default_value: "false"
description: Show help for command
- option: version
value_type: bool
default_value: "false"
description: Show gh version
N/A
N/A
N/A
command: gh
short: Work seamlessly with GitHub from the command line.
options:
- option: help
value_type: bool
default_value: "false"
description: Show help for command
- option: version
value_type: bool
default_value: "false"
description: Show gh version
N/A
N/A
N/A
command: gh agent-task short: Working with agent tasks in the GitHub CLI is in preview and long: subject to change without notice. pname: gh plink: gh.yaml
func TestNewCmdAgentTask(t *testing.T) {
tests := []struct {
name string
tokenSource string
customConfig func() (gh.Config, error)
wantErr bool
wantErrContains string
wantStdout string
}{
{
name: "oauth token is accepted",
tokenSource: "oauth_token",
wantErr: false,
wantStdout: "",
},
{
name: "keyring oauth token is accepted",
tokenSource: "keyring",
wantErr: false,
wantStdout: "",
},
{
name: "env var token is rejected",
tokenSource: "GH_TOKEN",
wantErr: true,
wantErrContains: "requires an OAuth token",
},
{
name: "enterprise token alone is ignored and rejected",
tokenSource: "GH_ENTERPRISE_TOKEN",
func TestAliasAreSet(t *testing.T) {
f := &cmdutil.Factory{}
ios, _, _, _ := iostreams.Test()
f.IOStreams = ios
f.Config = func() (gh.Config, error) { return setupMockOAuthConfig(t, "oauth_token"), nil }
cmd := NewCmdAgentTask(f)
require.ElementsMatch(t, []string{"agent-tasks", "agent", "agents"}, cmd.Aliases)
}
N/A
command: gh agent-task create short: Create an agent task (preview)
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
args string
tty bool
wantOpts *CreateOptions
wantErr string
}{
{
name: "no args nor file returns no error (prompting path)",
tty: true,
wantOpts: &CreateOptions{
ProblemStatement: "",
ProblemStatementFile: "",
},
},
{
name: "arg only success",
args: "'task description from args'",
wantOpts: &CreateOptions{
ProblemStatement: "task description from args",
ProblemStatementFile: "",
},
},
{
name: "empty arg",
args: "''",
wantErr: "task description cannot be empty",
},
{
func Test_createRun(t *testing.T) {
tmpDir := t.TempDir()
taskDescFile := filepath.Join(tmpDir, "task-description.md")
emptyTaskDescFile := filepath.Join(tmpDir, "empty-task-description.md")
require.NoError(t, os.WriteFile(taskDescFile, []byte("task description from file"), 0600))
require.NoError(t, os.WriteFile(emptyTaskDescFile, []byte(" \n\n"), 0600))
sampleDateString := "2025-08-29T00:00:00Z"
sampleDate, err := time.Parse(time.RFC3339, sampleDateString)
require.NoError(t, err)
createdJobSuccess := capi.Job{
ID: "job123",
SessionID: "sess1",
Actor: &capi.JobActor{
ID: 1,
Login: "octocat",
},
CreatedAt: sampleDate,
UpdatedAt: sampleDate,
}
createdJobSuccessWithPR := capi.Job{
ID: "job123",
SessionID: "sess1",
Actor: &capi.JobActor{
ID: 1,
Login: "octocat",
},
CreatedAt: sampleDate,
UpdatedAt: sampleDate,
N/A
command: gh agent-task create short: Create an agent task (preview)
N/A
N/A
command: gh agent-task create short: Create an agent task (preview)
N/A
N/A
command: gh agent-task create short: Create an agent task (preview)
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
args string
tty bool
wantOpts *CreateOptions
wantErr string
}{
{
name: "no args nor file returns no error (prompting path)",
tty: true,
wantOpts: &CreateOptions{
ProblemStatement: "",
ProblemStatementFile: "",
},
},
{
name: "arg only success",
args: "'task description from args'",
wantOpts: &CreateOptions{
ProblemStatement: "task description from args",
ProblemStatementFile: "",
},
},
{
name: "empty arg",
args: "''",
wantErr: "task description cannot be empty",
},
{
N/A
command: gh agent-task create short: Create an agent task (preview)
N/A
N/A
N/A
command: gh agent-task create short: Create an agent task (preview)
func Test_createRun(t *testing.T) {
tmpDir := t.TempDir()
taskDescFile := filepath.Join(tmpDir, "task-description.md")
emptyTaskDescFile := filepath.Join(tmpDir, "empty-task-description.md")
require.NoError(t, os.WriteFile(taskDescFile, []byte("task description from file"), 0600))
require.NoError(t, os.WriteFile(emptyTaskDescFile, []byte(" \n\n"), 0600))
sampleDateString := "2025-08-29T00:00:00Z"
sampleDate, err := time.Parse(time.RFC3339, sampleDateString)
require.NoError(t, err)
createdJobSuccess := capi.Job{
ID: "job123",
SessionID: "sess1",
Actor: &capi.JobActor{
ID: 1,
Login: "octocat",
},
CreatedAt: sampleDate,
UpdatedAt: sampleDate,
}
createdJobSuccessWithPR := capi.Job{
ID: "job123",
SessionID: "sess1",
Actor: &capi.JobActor{
ID: 1,
Login: "octocat",
},
CreatedAt: sampleDate,
UpdatedAt: sampleDate,
N/A
N/A
command: gh agent-task list short: List agent tasks (preview)
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
args string
wantOpts ListOptions
wantErr string
}{
{
name: "no arguments",
wantOpts: ListOptions{
Limit: defaultLimit,
},
},
{
name: "custom limit",
args: "--limit 15",
wantOpts: ListOptions{
Limit: 15,
},
},
{
name: "invalid limit",
args: "--limit 0",
wantErr: "invalid limit: 0",
},
{
name: "negative limit",
args: "--limit -5",
wantErr: "invalid limit: -5",
},
func Test_listRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleDateString := sampleDate.Format(time.RFC3339)
tests := []struct {
name string
tty bool
capiStubs func(*testing.T, *capi.CapiClientMock)
limit int
web bool
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "viewer-scoped no sessions",
tty: true,
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) {
return nil, nil
}
},
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
},
{
name: "viewer-scoped respects --limit",
tty: true,
limit: 999,
N/A
command: gh agent-task list short: List agent tasks (preview)
N/A
N/A
N/A
command: gh agent-task list short: List agent tasks (preview)
N/A
N/A
N/A
command: gh agent-task list short: List agent tasks (preview)
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
args string
wantOpts ListOptions
wantErr string
}{
{
name: "no arguments",
wantOpts: ListOptions{
Limit: defaultLimit,
},
},
{
name: "custom limit",
args: "--limit 15",
wantOpts: ListOptions{
Limit: 15,
},
},
{
name: "invalid limit",
args: "--limit 0",
wantErr: "invalid limit: 0",
},
{
name: "negative limit",
args: "--limit -5",
wantErr: "invalid limit: -5",
},
func Test_listRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleDateString := sampleDate.Format(time.RFC3339)
tests := []struct {
name string
tty bool
capiStubs func(*testing.T, *capi.CapiClientMock)
limit int
web bool
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "viewer-scoped no sessions",
tty: true,
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) {
return nil, nil
}
},
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
},
{
name: "viewer-scoped respects --limit",
tty: true,
limit: 999,
N/A
N/A
command: gh agent-task list short: List agent tasks (preview)
N/A
N/A
N/A
command: gh agent-task list short: List agent tasks (preview)
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
args string
wantOpts ListOptions
wantErr string
}{
{
name: "no arguments",
wantOpts: ListOptions{
Limit: defaultLimit,
},
},
{
name: "custom limit",
args: "--limit 15",
wantOpts: ListOptions{
Limit: 15,
},
},
{
name: "invalid limit",
args: "--limit 0",
wantErr: "invalid limit: 0",
},
{
name: "negative limit",
args: "--limit -5",
wantErr: "invalid limit: -5",
},
func Test_listRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleDateString := sampleDate.Format(time.RFC3339)
tests := []struct {
name string
tty bool
capiStubs func(*testing.T, *capi.CapiClientMock)
limit int
web bool
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "viewer-scoped no sessions",
tty: true,
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) {
return nil, nil
}
},
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
},
{
name: "viewer-scoped respects --limit",
tty: true,
limit: 999,
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
tty bool
args string
wantOpts ViewOptions
wantBaseRepo ghrepo.Interface
wantErr string
}{
{
name: "no arg tty",
tty: true,
args: "",
wantOpts: ViewOptions{},
},
{
name: "session ID arg tty",
tty: true,
args: "00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "00000000-0000-0000-0000-000000000000",
SessionID: "00000000-0000-0000-0000-000000000000",
},
},
{
name: "PR agent-session URL arg tty",
tty: true,
args: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
func Test_viewRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleCompletedAt := sampleDate.Add(5 * time.Minute)
tests := []struct {
name string
tty bool
opts ViewOptions
promptStubs func(*testing.T, *prompter.MockPrompter)
capiStubs func(*testing.T, *capi.CapiClientMock)
logRendererStubs func(*testing.T, *shared.LogRendererMock)
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "with session id, not found (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "some-session-id",
SessionID: "some-session-id",
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetSessionFunc = func(_ context.Context, _ string) (*capi.Session, error) {
return nil, capi.ErrSessionNotFound
}
},
wantStderr: "session not found\n",
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
tty bool
args string
wantOpts ViewOptions
wantBaseRepo ghrepo.Interface
wantErr string
}{
{
name: "no arg tty",
tty: true,
args: "",
wantOpts: ViewOptions{},
},
{
name: "session ID arg tty",
tty: true,
args: "00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "00000000-0000-0000-0000-000000000000",
SessionID: "00000000-0000-0000-0000-000000000000",
},
},
{
name: "PR agent-session URL arg tty",
tty: true,
args: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
tty bool
args string
wantOpts ViewOptions
wantBaseRepo ghrepo.Interface
wantErr string
}{
{
name: "no arg tty",
tty: true,
args: "",
wantOpts: ViewOptions{},
},
{
name: "session ID arg tty",
tty: true,
args: "00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "00000000-0000-0000-0000-000000000000",
SessionID: "00000000-0000-0000-0000-000000000000",
},
},
{
name: "PR agent-session URL arg tty",
tty: true,
args: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
wantOpts: ViewOptions{
SelectorArg: "https://github.com/OWNER/REPO/pull/101/agent-sessions/00000000-0000-0000-0000-000000000000",
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh agent-task view
short: View an agent task session.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh agent-task
plink: gh_agent-task.yaml
options:
- option: follow
value_type: bool
default_value: "false"
description: Follow agent session logs
- option: jq
shorthand: q
N/A
N/A
command: gh alias short: Aliases can be used to make shortcuts for gh commands or to compose multiple commands. long: Run `gh help alias set` to learn more. pname: gh plink: gh.yaml
func TestExpandAlias(t *testing.T) {
tests := []struct {
name string
expansion string
args []string
wantExpanded []string
wantErr string
}{
{
name: "no expansion",
expansion: "pr status",
args: []string{},
wantExpanded: []string{"pr", "status"},
},
{
name: "adding arguments after expansion",
expansion: "pr checkout",
args: []string{"123"},
wantExpanded: []string{"pr", "checkout", "123"},
},
{
name: "not enough arguments for expansion",
expansion: `issue list --author="$1" --label="$2"`,
args: []string{},
wantErr: `not enough arguments for alias: issue list --author="$1" --label="$2"`,
},
{
name: "not enough arguments for expansion 2",
expansion: `issue list --author="$1" --label="$2"`,
args: []string{"vilmibm"},
func TestExpandShellAlias(t *testing.T) {
findShFunc := func() (string, error) {
return "/usr/bin/sh", nil
}
tests := []struct {
name string
expansion string
args []string
findSh func() (string, error)
wantExpanded []string
wantErr string
}{
{
name: "simple expansion",
expansion: "!git branch --show-current",
args: []string{},
findSh: findShFunc,
wantExpanded: []string{"/usr/bin/sh", "-c", "git branch --show-current"},
},
{
name: "adding arguments after expansion",
expansion: "!git branch checkout",
args: []string{"123"},
findSh: findShFunc,
wantExpanded: []string{"/usr/bin/sh", "-c", "git branch checkout", "--", "123"},
},
{
name: "unable to find sh",
expansion: "!git branch --show-current",
args: []string{},
N/A
N/A
command: gh alias delete
short: Delete set aliases
pname: gh alias
plink: gh_alias.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Delete all aliases
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output DeleteOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify an alias to delete or `--all`",
},
{
name: "specified alias",
input: "co",
output: DeleteOptions{
Name: "co",
},
},
{
name: "all flag",
input: "--all",
output: DeleteOptions{
All: true,
},
},
{
name: "specified alias and all flag",
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
config string
isTTY bool
opts *DeleteOptions
wantAliases map[string]string
wantStdout string
wantStderr string
wantErrMsg string
}{
{
name: "delete alias",
config: heredoc.Doc(`
aliases:
il: issue list
co: pr checkout
`),
isTTY: true,
opts: &DeleteOptions{
Name: "co",
All: false,
},
wantAliases: map[string]string{
"il": "issue list",
},
wantStderr: "✓ Deleted alias co; was pr checkout\n",
},
{
name: "delete all aliases",
N/A
command: gh alias delete
short: Delete set aliases
pname: gh alias
plink: gh_alias.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Delete all aliases
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output DeleteOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify an alias to delete or `--all`",
},
{
name: "specified alias",
input: "co",
output: DeleteOptions{
Name: "co",
},
},
{
name: "all flag",
input: "--all",
output: DeleteOptions{
All: true,
},
},
{
name: "specified alias and all flag",
N/A
command: gh alias import
short: Import aliases from the contents of a YAML file.
long: |-
Aliases should be defined as a map in YAML, where the keys represent aliases and
the values represent the corresponding expansions. An example file should look like
the following:
bugs: issue list --label=bug
igrep: '!gh issue list --label="$1" | grep "$2"'
features: |-
issue list
--label=enhancement
func TestNewCmdImport(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ImportOptions
wantsError string
}{
{
name: "no filename and stdin tty",
cli: "",
tty: true,
wants: ImportOptions{
Filename: "",
OverwriteExisting: false,
},
wantsError: "no filename passed and nothing on STDIN",
},
{
name: "no filename and stdin is not tty",
cli: "",
tty: false,
wants: ImportOptions{
Filename: "-",
OverwriteExisting: false,
},
},
{
name: "stdin arg",
cli: "-",
func TestImportRun(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "aliases.yml")
importFileMsg := fmt.Sprintf(`- Importing aliases from file %q`, tmpFile)
importStdinMsg := "- Importing aliases from standard input"
tests := []struct {
name string
opts *ImportOptions
stdin string
fileContents string
initConfig string
aliasCommands []*cobra.Command
wantConfig string
wantStderr string
}{
{
name: "with no existing aliases",
opts: &ImportOptions{
Filename: tmpFile,
OverwriteExisting: false,
},
fileContents: heredoc.Doc(`
co: pr checkout
igrep: '!gh issue list --label="$1" | grep "$2"'
`),
wantConfig: heredoc.Doc(`
aliases:
co: pr checkout
igrep: '!gh issue list --label="$1" | grep "$2"'
`),
N/A
command: gh alias import
short: Import aliases from the contents of a YAML file.
long: |-
Aliases should be defined as a map in YAML, where the keys represent aliases and
the values represent the corresponding expansions. An example file should look like
the following:
bugs: issue list --label=bug
igrep: '!gh issue list --label="$1" | grep "$2"'
features: |-
issue list
--label=enhancement
N/A
N/A
N/A
command: gh alias list short: This command prints out all of the aliases gh is configured to use. pname: gh alias plink: gh_alias.yaml
func TestAliasList(t *testing.T) {
tests := []struct {
name string
config string
isTTY bool
wantErr bool
wantStdout string
wantStderr string
}{
{
name: "empty",
config: "",
isTTY: true,
wantErr: true,
wantStdout: "",
wantStderr: "",
},
{
name: "some",
config: heredoc.Doc(`
aliases:
co: pr checkout
gc: "!gh gist create \"$@\" | pbcopy"
`),
isTTY: true,
wantStdout: "co: pr checkout\ngc: '!gh gist create \"$@\" | pbcopy'\n",
wantStderr: "",
},
{
name: "multiline",
N/A
command: gh alias set
short: Define a word that will expand to a full gh command when invoked.
long: |-
The expansion may specify additional arguments and flags. If the expansion includes
positional placeholders such as `$1`, extra arguments that follow the alias will be
inserted appropriately. Otherwise, extra arguments will be appended to the expanded
command.
Use `-` as expansion argument to read the expansion string from standard input. This
is useful to avoid quoting issues when defining expansions.
If the expansion starts with `!` or if `--shell` was given, the expansion is a shell
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
input string
output SetOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "accepts 2 arg(s), received 0",
},
{
name: "only one argument",
input: "name",
wantErr: true,
errMsg: "accepts 2 arg(s), received 1",
},
{
name: "name and expansion",
input: "alias-name alias-expansion",
output: SetOptions{
Name: "alias-name",
Expansion: "alias-expansion",
},
},
{
name: "shell flag",
func TestSetRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *SetOptions
stdin string
wantExpansion string
wantStdout string
wantStderr string
wantErrMsg string
}{
{
name: "creates alias tty",
tty: true,
opts: &SetOptions{
Name: "foo",
Expansion: "bar",
},
wantExpansion: "bar",
wantStderr: "- Creating alias for foo: bar\n✓ Added alias foo\n",
},
{
name: "creates alias",
opts: &SetOptions{
Name: "foo",
Expansion: "bar",
},
wantExpansion: "bar",
},
{
func TestGetExpansion(t *testing.T) {
tests := []struct {
name string
want string
expansionArg string
stdin string
}{
{
name: "co",
want: "pr checkout",
expansionArg: "pr checkout",
},
{
name: "co",
want: "pr checkout",
expansionArg: "pr checkout",
stdin: "api graphql -F name=\"$1\"",
},
{
name: "stdin",
expansionArg: "-",
want: "api graphql -F name=\"$1\"",
stdin: "api graphql -F name=\"$1\"",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, stdin, _, _ := iostreams.Test()
ios.SetStdinTTY(false)
N/A
command: gh alias set
short: Define a word that will expand to a full gh command when invoked.
long: |-
The expansion may specify additional arguments and flags. If the expansion includes
positional placeholders such as `$1`, extra arguments that follow the alias will be
inserted appropriately. Otherwise, extra arguments will be appended to the expanded
command.
Use `-` as expansion argument to read the expansion string from standard input. This
is useful to avoid quoting issues when defining expansions.
If the expansion starts with `!` or if `--shell` was given, the expansion is a shell
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
input string
output SetOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "accepts 2 arg(s), received 0",
},
{
name: "only one argument",
input: "name",
wantErr: true,
errMsg: "accepts 2 arg(s), received 1",
},
{
name: "name and expansion",
input: "alias-name alias-expansion",
output: SetOptions{
Name: "alias-name",
Expansion: "alias-expansion",
},
},
{
name: "shell flag",
func TestSetRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *SetOptions
stdin string
wantExpansion string
wantStdout string
wantStderr string
wantErrMsg string
}{
{
name: "creates alias tty",
tty: true,
opts: &SetOptions{
Name: "foo",
Expansion: "bar",
},
wantExpansion: "bar",
wantStderr: "- Creating alias for foo: bar\n✓ Added alias foo\n",
},
{
name: "creates alias",
opts: &SetOptions{
Name: "foo",
Expansion: "bar",
},
wantExpansion: "bar",
},
{
N/A
N/A
command: gh alias set
short: Define a word that will expand to a full gh command when invoked.
long: |-
The expansion may specify additional arguments and flags. If the expansion includes
positional placeholders such as `$1`, extra arguments that follow the alias will be
inserted appropriately. Otherwise, extra arguments will be appended to the expanded
command.
Use `-` as expansion argument to read the expansion string from standard input. This
is useful to avoid quoting issues when defining expansions.
If the expansion starts with `!` or if `--shell` was given, the expansion is a shell
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
input string
output SetOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "accepts 2 arg(s), received 0",
},
{
name: "only one argument",
input: "name",
wantErr: true,
errMsg: "accepts 2 arg(s), received 1",
},
{
name: "name and expansion",
input: "alias-name alias-expansion",
output: SetOptions{
Name: "alias-name",
Expansion: "alias-expansion",
},
},
{
name: "shell flag",
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
func Test_NewCmdApi_WindowsAbsPath(t *testing.T) {
if runtime.GOOS != "windows" {
t.SkipNow()
}
cmd := NewCmdApi(&cmdutil.Factory{}, func(opts *ApiOptions) error {
return nil
})
cmd.SetArgs([]string{`C:\users\repos`})
_, err := cmd.ExecuteC()
assert.EqualError(t, err, `invalid API endpoint: "C:\users\repos". Your shell might be rewriting URL paths as filesystem paths. To avoid this, omit the leading slash from the endpoint argument`)
}
func Test_apiRun(t *testing.T) {
tests := []struct {
name string
options ApiOptions
httpResponse *http.Response
err error
stdout string
stderr string
isatty bool
}{
{
name: "success",
httpResponse: &http.Response{
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`bam!`)),
},
err: nil,
stdout: `bam!`,
stderr: ``,
isatty: false,
},
{
name: "show response headers",
options: ApiOptions{
ShowResponseHeaders: true,
},
httpResponse: &http.Response{
Proto: "HTTP/1.1",
Status: "200 Okey-dokey",
StatusCode: 200,
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
N/A
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
command: gh api
short: Makes an authenticated HTTP request to the GitHub API and prints the response.
long: |-
The endpoint argument should either be a path of a GitHub API v3 endpoint, or
`graphql` to access the GitHub API v4.
Placeholder values `{owner}`, `{repo}`, and `{branch}` in the endpoint
func Test_NewCmdApi(t *testing.T) {
f := &cmdutil.Factory{}
tests := []struct {
name string
cli string
wants ApiOptions
wantsErr bool
}{
{
name: "no flags",
cli: "graphql",
wants: ApiOptions{
Hostname: "",
RequestMethod: "GET",
RequestMethodPassed: false,
RequestPath: "graphql",
RequestInputFile: "",
RawFields: []string(nil),
MagicFields: []string(nil),
RequestHeaders: []string(nil),
ShowResponseHeaders: false,
Paginate: false,
Silent: false,
CacheTTL: 0,
Template: "",
FilterOutput: "",
Verbose: false,
},
wantsErr: false,
N/A
N/A
command: gh attestation short: Download and verify artifact attestations. pname: gh plink: gh.yaml
func TestLoadBundlesFromJSONLinesFile(t *testing.T) {
t.Run("with original file", func(t *testing.T) {
path := "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl"
attestations, err := loadBundlesFromJSONLinesFile(path)
require.NoError(t, err)
require.Len(t, attestations, 2)
})
t.Run("with extra lines", func(t *testing.T) {
// Create a temporary file with extra lines
tempDir := t.TempDir()
tempFile := filepath.Join(tempDir, "test_with_extra_lines.jsonl")
originalContent, err := os.ReadFile("../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl")
require.NoError(t, err)
extraLines := []byte("\n\n")
newContent := append(originalContent, extraLines...)
err = os.WriteFile(tempFile, newContent, 0644)
require.NoError(t, err)
// Test the function with the new file
attestations, err := loadBundlesFromJSONLinesFile(tempFile)
require.NoError(t, err)
require.Len(t, attestations, 2, "Should still load 2 valid attestations")
})
}
t.Run("with original file", func(t *testing.T) {
path := "../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl"
attestations, err := loadBundlesFromJSONLinesFile(path)
require.NoError(t, err)
require.Len(t, attestations, 2)
})
t.Run("with extra lines", func(t *testing.T) {
// Create a temporary file with extra lines
tempDir := t.TempDir()
tempFile := filepath.Join(tempDir, "test_with_extra_lines.jsonl")
originalContent, err := os.ReadFile("../test/data/sigstore-js-2.1.0_with_2_bundles.jsonl")
require.NoError(t, err)
extraLines := []byte("\n\n")
newContent := append(originalContent, extraLines...)
err = os.WriteFile(tempFile, newContent, 0644)
require.NoError(t, err)
// Test the function with the new file
attestations, err := loadBundlesFromJSONLinesFile(tempFile)
require.NoError(t, err)
require.Len(t, attestations, 2, "Should still load 2 valid attestations")
})
N/A
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
func TestNewDownloadCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
store := &LiveStore{
outputPath: t.TempDir(),
}
testcases := []struct {
name string
cli string
wants Options
wantsErr bool
}{
{
name: "Invalid digest-alg flag",
cli: fmt.Sprintf("%s --owner sigstore --digest-alg sha384", artifactPath),
wants: Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha384",
func TestRunDownload(t *testing.T) {
tempDir := t.TempDir()
store := &LiveStore{
outputPath: tempDir,
}
baseOpts := Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha512",
Owner: "sigstore",
Store: store,
Limit: 30,
Logger: io.NewTestHandler(),
}
t.Run("fetch and store attestations successfully with owner", func(t *testing.T) {
err := runDownload(&baseOpts)
require.NoError(t, err)
artifact, err := artifact.NewDigestedArtifact(baseOpts.OCIClient, baseOpts.ArtifactPath, baseOpts.DigestAlgorithm)
require.NoError(t, err)
expectedFilePath := expectedFilePath(tempDir, artifact.DigestWithAlg())
require.FileExists(t, expectedFilePath)
actualLineCount, err := countLines(expectedFilePath)
require.NoError(t, err)
t.Run("fetch and store attestations successfully with owner", func(t *testing.T) {
err := runDownload(&baseOpts)
require.NoError(t, err)
artifact, err := artifact.NewDigestedArtifact(baseOpts.OCIClient, baseOpts.ArtifactPath, baseOpts.DigestAlgorithm)
require.NoError(t, err)
expectedFilePath := expectedFilePath(tempDir, artifact.DigestWithAlg())
require.FileExists(t, expectedFilePath)
actualLineCount, err := countLines(expectedFilePath)
require.NoError(t, err)
expectedLineCount := 2
require.Equal(t, expectedLineCount, actualLineCount)
})
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
func TestNewDownloadCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
store := &LiveStore{
outputPath: t.TempDir(),
}
testcases := []struct {
name string
cli string
wants Options
wantsErr bool
}{
{
name: "Invalid digest-alg flag",
cli: fmt.Sprintf("%s --owner sigstore --digest-alg sha384", artifactPath),
wants: Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha384",
N/A
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
N/A
N/A
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
func TestNewDownloadCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
store := &LiveStore{
outputPath: t.TempDir(),
}
testcases := []struct {
name string
cli string
wants Options
wantsErr bool
}{
{
name: "Invalid digest-alg flag",
cli: fmt.Sprintf("%s --owner sigstore --digest-alg sha384", artifactPath),
wants: Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha384",
N/A
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
func TestNewDownloadCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
store := &LiveStore{
outputPath: t.TempDir(),
}
testcases := []struct {
name string
cli string
wants Options
wantsErr bool
}{
{
name: "Invalid digest-alg flag",
cli: fmt.Sprintf("%s --owner sigstore --digest-alg sha384", artifactPath),
wants: Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha384",
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
N/A
N/A
N/A
command: gh attestation download
short: '### NOTE: This feature is currently in public preview, and subject to change.'
long: Download attestations associated with an artifact for offline use.
pname: gh attestation
plink: gh_attestation.yaml
options:
- option: digest-alg
shorthand: d
value_type: string
description: 'The algorithm used to compute a digest of the artifact: {sha256|sha512} (default "sha256")'
func TestNewDownloadCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
store := &LiveStore{
outputPath: t.TempDir(),
}
testcases := []struct {
name string
cli string
wants Options
wantsErr bool
}{
{
name: "Invalid digest-alg flag",
cli: fmt.Sprintf("%s --owner sigstore --digest-alg sha384", artifactPath),
wants: Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha384",
N/A
command: gh attestation trusted-root
short: Output contents for a trusted_root.jsonl file, likely for offline verification.
long: |-
When using `gh attestation verify`, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with `--custom-trusted-root`; this command
will help you fetch a `trusted_root.jsonl` file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use `--tuf-url` to specify the URL of a custom TUF
func TestNewTrustedRootCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{}, nil
},
HttpClient: func() (*http.Client, error) {
reg := &httpmock.Registry{}
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wantsErr bool
}{
{
name: "Happy path",
cli: "",
wantsErr: false,
},
{
name: "Happy path",
func TestNewTrustedRootWithTenancy(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
httpClientFunc := func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
}
cli := "--hostname foo-bar.ghe.com"
t.Run("Host with NO auth configured", func(t *testing.T) {
f := &cmdutil.Factory{
IOStreams: testIO,
Config: func() (gh.Config, error) {
return &ghmock.ConfigMock{
AuthenticationFunc: func() gh.AuthConfig {
return &stubAuthConfig{hasActiveToken: false}
},
func TestGetTrustedRoot(t *testing.T) {
mirror := "https://tuf-repo.github.com"
root := test.NormalizeRelativePath("../verification/embed/tuf-repo.github.com/root.json")
opts := &Options{
TufUrl: mirror,
TufRootPath: root,
}
reg := &httpmock.Registry{}
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
t.Run("failed to create TUF root", func(t *testing.T) {
err := getTrustedRoot(newTUFErrClient, opts, client)
require.Error(t, err)
require.ErrorContains(t, err, "failed to create TUF client")
})
t.Run("fails because the root cannot be found", func(t *testing.T) {
opts.TufRootPath = test.NormalizeRelativePath("./does/not/exist/root.json")
err := getTrustedRoot(tuf.New, opts, client)
require.Error(t, err)
require.ErrorContains(t, err, "failed to read root file")
})
}
N/A
command: gh attestation trusted-root
short: Output contents for a trusted_root.jsonl file, likely for offline verification.
long: |-
When using `gh attestation verify`, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with `--custom-trusted-root`; this command
will help you fetch a `trusted_root.jsonl` file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use `--tuf-url` to specify the URL of a custom TUF
N/A
N/A
N/A
command: gh attestation trusted-root
short: Output contents for a trusted_root.jsonl file, likely for offline verification.
long: |-
When using `gh attestation verify`, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with `--custom-trusted-root`; this command
will help you fetch a `trusted_root.jsonl` file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use `--tuf-url` to specify the URL of a custom TUF
N/A
N/A
command: gh attestation trusted-root
short: Output contents for a trusted_root.jsonl file, likely for offline verification.
long: |-
When using `gh attestation verify`, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with `--custom-trusted-root`; this command
will help you fetch a `trusted_root.jsonl` file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use `--tuf-url` to specify the URL of a custom TUF
N/A
N/A
command: gh attestation trusted-root
short: Output contents for a trusted_root.jsonl file, likely for offline verification.
long: |-
When using `gh attestation verify`, if your machine is on the internet,
this will happen automatically. But to do offline verification, you need to
supply a trusted root file with `--custom-trusted-root`; this command
will help you fetch a `trusted_root.jsonl` file for that purpose.
You can call this command without any flags to get a trusted root file covering
the Sigstore Public Good Instance as well as GitHub's Sigstore instance.
Otherwise you can use `--tuf-url` to specify the URL of a custom TUF
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
func TestVerifyCmdAuthChecks(t *testing.T) {
f := &cmdutil.Factory{}
t.Run("by default auth check is required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{}))
require.True(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check to be required")
})
t.Run("when --bundle flag is provided, auth check is not required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{"--bundle", "not-important"}))
require.False(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check not to be required due to --bundle flag")
})
}
t.Run("by default auth check is required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{}))
require.True(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check to be required")
})
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
func TestVerifyCmdAuthChecks(t *testing.T) {
f := &cmdutil.Factory{}
t.Run("by default auth check is required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{}))
require.True(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check to be required")
})
t.Run("when --bundle flag is provided, auth check is not required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{"--bundle", "not-important"}))
require.False(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check not to be required due to --bundle flag")
})
}
t.Run("by default auth check is required", func(t *testing.T) {
cmd := NewVerifyCmd(f, func(o *Options) error {
return nil
})
// IsAuthCheckEnabled assumes commands under test are subcommands
parent := &cobra.Command{Use: "root"}
parent.AddCommand(cmd)
require.NoError(t, cmd.ParseFlags([]string{}))
require.True(t, cmdutil.IsAuthCheckEnabled(cmd), "expected auth check to be required")
})
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestRunVerify(t *testing.T) {
logger := io.NewTestHandler()
publicGoodOpts := Options{
ArtifactPath: artifactPath,
BundlePath: bundlePath,
DigestAlgorithm: "sha512",
APIClient: api.NewTestClient(),
Logger: logger,
OCIClient: oci.MockClient{},
OIDCIssuer: verification.GitHubOIDCIssuer,
Owner: "sigstore",
PredicateType: verification.SLSAPredicateV1,
SANRegex: "^https://github.com/sigstore/",
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
}
t.Run("with valid artifact and bundle", func(t *testing.T) {
require.NoError(t, runVerify(&publicGoodOpts))
})
t.Run("with failing OCI artifact fetch", func(t *testing.T) {
opts := publicGoodOpts
opts.ArtifactPath = "oci://ghcr.io/github/test"
opts.OCIClient = oci.ReferenceFailClient{}
err := runVerify(&opts)
require.Error(t, err)
require.ErrorContains(t, err, "failed to parse reference")
})
t.Run("with valid artifact and bundle", func(t *testing.T) {
require.NoError(t, runVerify(&publicGoodOpts))
})
t.Run("with failing OCI artifact fetch", func(t *testing.T) {
opts := publicGoodOpts
opts.ArtifactPath = "oci://ghcr.io/github/test"
opts.OCIClient = oci.ReferenceFailClient{}
err := runVerify(&opts)
require.Error(t, err)
require.ErrorContains(t, err, "failed to parse reference")
})
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
func TestNewVerifyCmd(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
var testReg httpmock.Registry
var metaResp = api.MetaResponse{
Domains: api.Domain{
ArtifactAttestations: api.ArtifactAttestations{
TrustDomain: "foo",
},
},
}
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
httpmock.StatusJSONResponse(200, &metaResp))
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
reg := &testReg
client := &http.Client{}
httpmock.ReplaceTripper(client, reg)
return client, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
}
testcases := []struct {
name string
cli string
wants Options
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh attestation verify
short: Verify the integrity and provenance of an artifact using its associated
long: |-
cryptographically signed attestations.
## Understanding Verification
An attestation is a claim (i.e. a provenance statement) made by an actor
N/A
N/A
N/A
command: gh auth short: Authenticate gh and git with GitHub pname: gh plink: gh.yaml
N/A
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
func Test_loginRun_nontty(t *testing.T) {
tests := []struct {
name string
opts *LoginOptions
env map[string]string
httpStubs func(*httpmock.Registry)
cfgStubs func(*testing.T, gh.Config)
wantHosts string
wantErr string
wantStderr string
wantSecureToken string
}{
{
name: "insecure with token",
opts: &LoginOptions{
Hostname: "github.com",
Token: "abc123",
InsecureStorage: true,
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", ""), httpmock.ScopesResponder("repo,read:org"))
reg.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"monalisa"}}}`))
},
wantHosts: "github.com:\n users:\n monalisa:\n oauth_token: abc123\n oauth_token: abc123\n user: monalisa\n",
},
{
name: "insecure with token and https git-protocol",
opts: &LoginOptions{
func Test_loginRun_Survey(t *testing.T) {
stubHomeDir(t, t.TempDir())
tests := []struct {
name string
opts *LoginOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.PrompterMock)
runStubs func(*run.CommandStubber)
cfgStubs func(*testing.T, gh.Config)
wantHosts string
wantErrOut *regexp.Regexp
wantSecureToken string
}{
{
name: "hostname set",
opts: &LoginOptions{
Hostname: "rebecca.chambers",
Interactive: true,
InsecureStorage: true,
},
wantHosts: heredoc.Doc(`
rebecca.chambers:
users:
jillv:
oauth_token: def456
git_protocol: https
oauth_token: def456
user: jillv
`),
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
N/A
N/A
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
command: gh auth login
short: Authenticate with a GitHub host.
long: |-
The default hostname is `github.com`. This can be overridden using the `--hostname`
flag.
The default authentication mode is a web-based browser flow. After completion, an
authentication token will be stored securely in the system credential store.
If a credential store is not found or there is an issue using it gh will fallback
to writing the token to a plain text file. See `gh auth status` for its
stored location.
func Test_NewCmdLogin(t *testing.T) {
tests := []struct {
name string
cli string
stdin string
stdinTTY bool
defaultHost string
wants LoginOptions
wantsErr bool
}{
{
name: "nontty, with-token",
stdin: "abc123\n",
cli: "--with-token",
wants: LoginOptions{
Hostname: "github.com",
Token: "abc123",
},
},
{
name: "nontty, with-token, enterprise default host",
stdin: "abc123\n",
cli: "--with-token",
defaultHost: "git.example.com",
wants: LoginOptions{
Hostname: "git.example.com",
Token: "abc123",
},
},
{
N/A
command: gh auth logout
short: Remove authentication for a GitHub account.
long: |-
This command removes the stored authentication configuration
for an account. The authentication configuration is only
removed locally.
This command does not revoke authentication tokens.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
func Test_NewCmdLogout(t *testing.T) {
tests := []struct {
name string
cli string
wants LogoutOptions
tty bool
}{
{
name: "nontty no arguments",
cli: "",
wants: LogoutOptions{},
},
{
name: "tty no arguments",
tty: true,
cli: "",
wants: LogoutOptions{},
},
{
name: "tty with hostname",
tty: true,
cli: "--hostname github.com",
wants: LogoutOptions{
Hostname: "github.com",
},
},
{
name: "nontty with hostname",
cli: "--hostname github.com",
wants: LogoutOptions{
func Test_logoutRun_tty(t *testing.T) {
tests := []struct {
name string
opts *LogoutOptions
prompterStubs func(*prompter.PrompterMock)
cfgHosts []hostUsers
secureStorage bool
wantHosts string
assertToken tokenAssertion
wantErrOut *regexp.Regexp
wantErr string
}{
{
name: "logs out prompted user when multiple known hosts with one user each",
opts: &LogoutOptions{},
cfgHosts: []hostUsers{
{"ghe.io", []user{
{"monalisa-ghe", "abc123"},
}},
{"github.com", []user{
{"monalisa", "abc123"},
}},
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.SelectFunc = func(_, _ string, opts []string) (int, error) {
return prompter.IndexFor(opts, "monalisa (github.com)")
}
},
assertToken: hasNoToken("github.com"),
wantHosts: "ghe.io:\n users:\n monalisa-ghe:\n oauth_token: abc123\n git_protocol: ssh\n oauth_token: abc123\n user: monalisa-ghe\n",
func Test_logoutRun_nontty(t *testing.T) {
tests := []struct {
name string
opts *LogoutOptions
cfgHosts []hostUsers
secureStorage bool
wantHosts string
assertToken tokenAssertion
wantErrOut *regexp.Regexp
wantErr string
}{
{
name: "logs out specified user when one known host",
opts: &LogoutOptions{
Hostname: "github.com",
Username: "monalisa",
},
cfgHosts: []hostUsers{
{"github.com", []user{
{"monalisa", "abc123"},
}},
},
wantHosts: "{}\n",
assertToken: hasNoToken("github.com"),
wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`),
},
{
name: "logs out specified user when multiple known hosts",
opts: &LogoutOptions{
Hostname: "github.com",
N/A
command: gh auth logout
short: Remove authentication for a GitHub account.
long: |-
This command removes the stored authentication configuration
for an account. The authentication configuration is only
removed locally.
This command does not revoke authentication tokens.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
func Test_NewCmdLogout(t *testing.T) {
tests := []struct {
name string
cli string
wants LogoutOptions
tty bool
}{
{
name: "nontty no arguments",
cli: "",
wants: LogoutOptions{},
},
{
name: "tty no arguments",
tty: true,
cli: "",
wants: LogoutOptions{},
},
{
name: "tty with hostname",
tty: true,
cli: "--hostname github.com",
wants: LogoutOptions{
Hostname: "github.com",
},
},
{
name: "nontty with hostname",
cli: "--hostname github.com",
wants: LogoutOptions{
func Test_logoutRun_nontty(t *testing.T) {
tests := []struct {
name string
opts *LogoutOptions
cfgHosts []hostUsers
secureStorage bool
wantHosts string
assertToken tokenAssertion
wantErrOut *regexp.Regexp
wantErr string
}{
{
name: "logs out specified user when one known host",
opts: &LogoutOptions{
Hostname: "github.com",
Username: "monalisa",
},
cfgHosts: []hostUsers{
{"github.com", []user{
{"monalisa", "abc123"},
}},
},
wantHosts: "{}\n",
assertToken: hasNoToken("github.com"),
wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`),
},
{
name: "logs out specified user when multiple known hosts",
opts: &LogoutOptions{
Hostname: "github.com",
N/A
command: gh auth logout
short: Remove authentication for a GitHub account.
long: |-
This command removes the stored authentication configuration
for an account. The authentication configuration is only
removed locally.
This command does not revoke authentication tokens.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
func Test_NewCmdLogout(t *testing.T) {
tests := []struct {
name string
cli string
wants LogoutOptions
tty bool
}{
{
name: "nontty no arguments",
cli: "",
wants: LogoutOptions{},
},
{
name: "tty no arguments",
tty: true,
cli: "",
wants: LogoutOptions{},
},
{
name: "tty with hostname",
tty: true,
cli: "--hostname github.com",
wants: LogoutOptions{
Hostname: "github.com",
},
},
{
name: "nontty with hostname",
cli: "--hostname github.com",
wants: LogoutOptions{
func Test_logoutRun_nontty(t *testing.T) {
tests := []struct {
name string
opts *LogoutOptions
cfgHosts []hostUsers
secureStorage bool
wantHosts string
assertToken tokenAssertion
wantErrOut *regexp.Regexp
wantErr string
}{
{
name: "logs out specified user when one known host",
opts: &LogoutOptions{
Hostname: "github.com",
Username: "monalisa",
},
cfgHosts: []hostUsers{
{"github.com", []user{
{"monalisa", "abc123"},
}},
},
wantHosts: "{}\n",
assertToken: hasNoToken("github.com"),
wantErrOut: regexp.MustCompile(`Logged out of github.com account monalisa`),
},
{
name: "logs out specified user when multiple known hosts",
opts: &LogoutOptions{
Hostname: "github.com",
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
func Test_NewCmdRefresh(t *testing.T) {
tests := []struct {
name string
cli string
wants RefreshOptions
wantsErr bool
tty bool
neverPrompt bool
}{
{
name: "tty no arguments",
tty: true,
wants: RefreshOptions{
Hostname: "",
},
},
{
name: "tty clipboard",
tty: true,
cli: "-c",
wants: RefreshOptions{
Hostname: "",
Clipboard: true,
},
},
{
name: "nontty no arguments",
wantsErr: true,
},
{
func Test_refreshRun(t *testing.T) {
tests := []struct {
name string
opts *RefreshOptions
prompterStubs func(*prompter.PrompterMock)
cfgHosts []string
authOut authOut
oldScopes string
wantErr string
nontty bool
wantAuthArgs authArgs
}{
{
name: "no hosts configured",
opts: &RefreshOptions{},
wantErr: `not logged in to any hosts`,
},
{
name: "hostname given but not previously authenticated with it",
cfgHosts: []string{
"github.com",
"aline.cedrac",
},
opts: &RefreshOptions{
Hostname: "obed.morton",
},
wantErr: `not logged in to obed.morton`,
},
{
name: "hostname provided and is configured",
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
N/A
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
N/A
N/A
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
func Test_NewCmdRefresh(t *testing.T) {
tests := []struct {
name string
cli string
wants RefreshOptions
wantsErr bool
tty bool
neverPrompt bool
}{
{
name: "tty no arguments",
tty: true,
wants: RefreshOptions{
Hostname: "",
},
},
{
name: "tty clipboard",
tty: true,
cli: "-c",
wants: RefreshOptions{
Hostname: "",
Clipboard: true,
},
},
{
name: "nontty no arguments",
wantsErr: true,
},
{
N/A
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
func Test_NewCmdRefresh(t *testing.T) {
tests := []struct {
name string
cli string
wants RefreshOptions
wantsErr bool
tty bool
neverPrompt bool
}{
{
name: "tty no arguments",
tty: true,
wants: RefreshOptions{
Hostname: "",
},
},
{
name: "tty clipboard",
tty: true,
cli: "-c",
wants: RefreshOptions{
Hostname: "",
Clipboard: true,
},
},
{
name: "nontty no arguments",
wantsErr: true,
},
{
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
func Test_NewCmdRefresh(t *testing.T) {
tests := []struct {
name string
cli string
wants RefreshOptions
wantsErr bool
tty bool
neverPrompt bool
}{
{
name: "tty no arguments",
tty: true,
wants: RefreshOptions{
Hostname: "",
},
},
{
name: "tty clipboard",
tty: true,
cli: "-c",
wants: RefreshOptions{
Hostname: "",
Clipboard: true,
},
},
{
name: "nontty no arguments",
wantsErr: true,
},
{
N/A
command: gh auth refresh
short: Expand or fix the permission scopes for stored credentials for active account.
long: |-
The `--scopes` flag accepts a comma separated list of scopes you want
your gh credentials to have. If no scopes are provided, the command
maintains previously added scopes.
The `--remove-scopes` flag accepts a comma separated list of scopes you
want to remove from your gh credentials. Scope removal is idempotent.
The minimum set of scopes (`repo`, `read:org`, and `gist`) cannot be removed.
func Test_NewCmdRefresh(t *testing.T) {
tests := []struct {
name string
cli string
wants RefreshOptions
wantsErr bool
tty bool
neverPrompt bool
}{
{
name: "tty no arguments",
tty: true,
wants: RefreshOptions{
Hostname: "",
},
},
{
name: "tty clipboard",
tty: true,
cli: "-c",
wants: RefreshOptions{
Hostname: "",
Clipboard: true,
},
},
{
name: "nontty no arguments",
wantsErr: true,
},
{
N/A
command: gh auth setup-git
short: This command configures `git` to use GitHub CLI as a credential helper.
pname: gh auth
plink: gh_auth.yaml
options:
- option: force
shorthand: f
value_type: string
description: Force setup even if the host is not known. Must be used in conjunction with --hostname
- option: hostname
shorthand: h
value_type: string
func TestNewCmdSetupGit(t *testing.T) {
tests := []struct {
name string
cli string
wantsErr bool
errMsg string
}{
{
name: "--force without hostname",
cli: "--force",
wantsErr: true,
errMsg: "`--force` must be used in conjunction with `--hostname`",
},
{
name: "no error when --force used with hostname",
cli: "--force --hostname ghe.io",
wantsErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &cmdutil.Factory{}
argv, err := shlex.Split(tt.cli)
require.NoError(t, err)
cmd := NewCmdSetupGit(f, func(opts *SetupGitOptions) error {
return nil
})
func Test_setupGitRun(t *testing.T) {
tests := []struct {
name string
opts *SetupGitOptions
setupErr error
cfgStubs func(*testing.T, gh.Config)
expectedHostsSetup []string
expectedErr error
expectedErrOut string
}{
{
name: "opts.Config returns an error",
opts: &SetupGitOptions{
Config: func() (gh.Config, error) {
return nil, fmt.Errorf("oops")
},
},
expectedErr: errors.New("oops"),
},
{
name: "when an unknown hostname is provided without forcing, return an error",
opts: &SetupGitOptions{
Hostname: "ghe.io",
},
cfgStubs: func(t *testing.T, cfg gh.Config) {
login(t, cfg, "github.com", "test-user", "gho_ABCDEFG", "https", false)
},
expectedErr: errors.New("You are not logged into the GitHub host \"ghe.io\". Run gh auth login -h ghe.io to authenticate or provide `--force`"),
},
{
N/A
command: gh auth setup-git
short: This command configures `git` to use GitHub CLI as a credential helper.
pname: gh auth
plink: gh_auth.yaml
options:
- option: force
shorthand: f
value_type: string
description: Force setup even if the host is not known. Must be used in conjunction with --hostname
- option: hostname
shorthand: h
value_type: string
N/A
N/A
N/A
command: gh auth setup-git
short: This command configures `git` to use GitHub CLI as a credential helper.
pname: gh auth
plink: gh_auth.yaml
options:
- option: force
shorthand: f
value_type: string
description: Force setup even if the host is not known. Must be used in conjunction with --hostname
- option: hostname
shorthand: h
value_type: string
N/A
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
func Test_NewCmdStatus(t *testing.T) {
tests := []struct {
name string
cli string
wants StatusOptions
}{
{
name: "no arguments",
cli: "",
wants: StatusOptions{},
},
{
name: "hostname set",
cli: "--hostname ellie.williams",
wants: StatusOptions{
Hostname: "ellie.williams",
},
},
{
name: "show token",
cli: "--show-token",
wants: StatusOptions{
ShowToken: true,
},
},
{
name: "active",
cli: "--active",
wants: StatusOptions{
Active: true,
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdStatus, []string{
"hosts",
})
}
func Test_statusRun(t *testing.T) {
tests := []struct {
name string
opts StatusOptions
jsonFields []string
env map[string]string
httpStubs func(*httpmock.Registry)
cfgStubs func(*testing.T, gh.Config)
wantErr error
wantOut string
wantErrOut string
}{
{
name: "timeout error",
opts: StatusOptions{
Hostname: "github.com",
},
cfgStubs: func(t *testing.T, c gh.Config) {
login(t, c, "github.com", "monalisa", "abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", ""), func(req *http.Request) (*http.Response, error) {
// timeout error
return nil, context.DeadlineExceeded
})
},
wantErr: cmdutil.SilentError,
wantErrOut: heredoc.Doc(`
github.com
X Timeout trying to log in to github.com account monalisa (GH_CONFIG_DIR/hosts.yml)
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
func Test_NewCmdStatus(t *testing.T) {
tests := []struct {
name string
cli string
wants StatusOptions
}{
{
name: "no arguments",
cli: "",
wants: StatusOptions{},
},
{
name: "hostname set",
cli: "--hostname ellie.williams",
wants: StatusOptions{
Hostname: "ellie.williams",
},
},
{
name: "show token",
cli: "--show-token",
wants: StatusOptions{
ShowToken: true,
},
},
{
name: "active",
cli: "--active",
wants: StatusOptions{
Active: true,
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
func Test_NewCmdStatus(t *testing.T) {
tests := []struct {
name string
cli string
wants StatusOptions
}{
{
name: "no arguments",
cli: "",
wants: StatusOptions{},
},
{
name: "hostname set",
cli: "--hostname ellie.williams",
wants: StatusOptions{
Hostname: "ellie.williams",
},
},
{
name: "show token",
cli: "--show-token",
wants: StatusOptions{
ShowToken: true,
},
},
{
name: "active",
cli: "--active",
wants: StatusOptions{
Active: true,
func Test_statusRun(t *testing.T) {
tests := []struct {
name string
opts StatusOptions
jsonFields []string
env map[string]string
httpStubs func(*httpmock.Registry)
cfgStubs func(*testing.T, gh.Config)
wantErr error
wantOut string
wantErrOut string
}{
{
name: "timeout error",
opts: StatusOptions{
Hostname: "github.com",
},
cfgStubs: func(t *testing.T, c gh.Config) {
login(t, c, "github.com", "monalisa", "abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", ""), func(req *http.Request) (*http.Response, error) {
// timeout error
return nil, context.DeadlineExceeded
})
},
wantErr: cmdutil.SilentError,
wantErrOut: heredoc.Doc(`
github.com
X Timeout trying to log in to github.com account monalisa (GH_CONFIG_DIR/hosts.yml)
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
N/A
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
N/A
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
func Test_NewCmdStatus(t *testing.T) {
tests := []struct {
name string
cli string
wants StatusOptions
}{
{
name: "no arguments",
cli: "",
wants: StatusOptions{},
},
{
name: "hostname set",
cli: "--hostname ellie.williams",
wants: StatusOptions{
Hostname: "ellie.williams",
},
},
{
name: "show token",
cli: "--show-token",
wants: StatusOptions{
ShowToken: true,
},
},
{
name: "active",
cli: "--active",
wants: StatusOptions{
Active: true,
N/A
command: gh auth status
short: Display active account and authentication state on each known GitHub host.
long: |-
For each host, the authentication state of each known account is tested and any issues are included in the output.
Each host section will indicate the active account, which will be used when targeting that host.
If an account on any host (or only the one given via `--hostname`) has authentication issues,
N/A
N/A
N/A
command: gh auth switch
short: Switch the active account for a GitHub host.
long: |-
This command changes the authentication configuration that will
be used when running commands targeting the specified GitHub host.
If the specified host has two accounts, the active account will be switched
automatically. If there are more than two accounts, disambiguation will be
required either through the `--user` flag or an interactive prompt.
For a list of authenticated accounts you can run `gh auth status`.
pname: gh auth
func TestNewCmdSwitch(t *testing.T) {
tests := []struct {
name string
input string
expectedOpts SwitchOptions
expectedErrMsg string
}{
{
name: "no flags",
input: "",
expectedOpts: SwitchOptions{},
},
{
name: "hostname flag",
input: "--hostname github.com",
expectedOpts: SwitchOptions{
Hostname: "github.com",
},
},
{
name: "user flag",
input: "--user monalisa",
expectedOpts: SwitchOptions{
Username: "monalisa",
},
},
{
name: "positional args is an error",
input: "some-positional-arg",
expectedErrMsg: "accepts 0 arg(s), received 1",
func TestSwitchRun(t *testing.T) {
type user struct {
name string
token string
}
type hostUsers struct {
host string
users []user
}
type successfulExpectation struct {
switchedHost string
activeUser string
activeToken string
hostsCfg string
stderr string
}
type failedExpectation struct {
err error
stderr string
}
userWithMissingToken := "user-that-is-broken-by-the-test"
tests := []struct {
name string
opts SwitchOptions
cfgHosts []hostUsers
N/A
command: gh auth switch
short: Switch the active account for a GitHub host.
long: |-
This command changes the authentication configuration that will
be used when running commands targeting the specified GitHub host.
If the specified host has two accounts, the active account will be switched
automatically. If there are more than two accounts, disambiguation will be
required either through the `--user` flag or an interactive prompt.
For a list of authenticated accounts you can run `gh auth status`.
pname: gh auth
func TestNewCmdSwitch(t *testing.T) {
tests := []struct {
name string
input string
expectedOpts SwitchOptions
expectedErrMsg string
}{
{
name: "no flags",
input: "",
expectedOpts: SwitchOptions{},
},
{
name: "hostname flag",
input: "--hostname github.com",
expectedOpts: SwitchOptions{
Hostname: "github.com",
},
},
{
name: "user flag",
input: "--user monalisa",
expectedOpts: SwitchOptions{
Username: "monalisa",
},
},
{
name: "positional args is an error",
input: "some-positional-arg",
expectedErrMsg: "accepts 0 arg(s), received 1",
func TestSwitchRun(t *testing.T) {
type user struct {
name string
token string
}
type hostUsers struct {
host string
users []user
}
type successfulExpectation struct {
switchedHost string
activeUser string
activeToken string
hostsCfg string
stderr string
}
type failedExpectation struct {
err error
stderr string
}
userWithMissingToken := "user-that-is-broken-by-the-test"
tests := []struct {
name string
opts SwitchOptions
cfgHosts []hostUsers
N/A
command: gh auth switch
short: Switch the active account for a GitHub host.
long: |-
This command changes the authentication configuration that will
be used when running commands targeting the specified GitHub host.
If the specified host has two accounts, the active account will be switched
automatically. If there are more than two accounts, disambiguation will be
required either through the `--user` flag or an interactive prompt.
For a list of authenticated accounts you can run `gh auth status`.
pname: gh auth
func TestNewCmdSwitch(t *testing.T) {
tests := []struct {
name string
input string
expectedOpts SwitchOptions
expectedErrMsg string
}{
{
name: "no flags",
input: "",
expectedOpts: SwitchOptions{},
},
{
name: "hostname flag",
input: "--hostname github.com",
expectedOpts: SwitchOptions{
Hostname: "github.com",
},
},
{
name: "user flag",
input: "--user monalisa",
expectedOpts: SwitchOptions{
Username: "monalisa",
},
},
{
name: "positional args is an error",
input: "some-positional-arg",
expectedErrMsg: "accepts 0 arg(s), received 1",
func TestSwitchRun(t *testing.T) {
type user struct {
name string
token string
}
type hostUsers struct {
host string
users []user
}
type successfulExpectation struct {
switchedHost string
activeUser string
activeToken string
hostsCfg string
stderr string
}
type failedExpectation struct {
err error
stderr string
}
userWithMissingToken := "user-that-is-broken-by-the-test"
tests := []struct {
name string
opts SwitchOptions
cfgHosts []hostUsers
N/A
command: gh auth token
short: This command outputs the authentication token for an account on a given GitHub host.
long: |-
Without the `--hostname` flag, the default host is chosen.
Without the `--user` flag, the active account for the host is chosen.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
shorthand: h
value_type: string
func TestNewCmdToken(t *testing.T) {
tests := []struct {
name string
input string
output TokenOptions
wantErr bool
wantErrMsg string
}{
{
name: "no flags",
input: "",
output: TokenOptions{},
},
{
name: "with hostname",
input: "--hostname github.mycompany.com",
output: TokenOptions{Hostname: "github.mycompany.com"},
},
{
name: "with user",
input: "--user test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand user",
input: "-u test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand hostname",
func TestTokenRun(t *testing.T) {
tests := []struct {
name string
opts TokenOptions
env map[string]string
cfgStubs func(*testing.T, gh.Config)
wantStdout string
wantErr bool
wantErrMsg string
}{
{
name: "token",
opts: TokenOptions{},
cfgStubs: func(t *testing.T, cfg gh.Config) {
login(t, cfg, "github.com", "test-user", "gho_ABCDEFG", "https", false)
},
wantStdout: "gho_ABCDEFG\n",
},
{
name: "token by hostname",
opts: TokenOptions{
Hostname: "github.mycompany.com",
},
cfgStubs: func(t *testing.T, cfg gh.Config) {
login(t, cfg, "github.com", "test-user", "gho_ABCDEFG", "https", false)
login(t, cfg, "github.mycompany.com", "test-user", "gho_1234567", "https", false)
},
wantStdout: "gho_1234567\n",
},
{
func TestTokenRunSecureStorage(t *testing.T) {
tests := []struct {
name string
opts TokenOptions
cfgStubs func(*testing.T, gh.Config)
wantStdout string
wantErr bool
wantErrMsg string
}{
{
name: "token",
opts: TokenOptions{},
cfgStubs: func(t *testing.T, cfg gh.Config) {
login(t, cfg, "github.com", "test-user", "gho_ABCDEFG", "https", true)
},
wantStdout: "gho_ABCDEFG\n",
},
{
name: "token by hostname",
opts: TokenOptions{
Hostname: "mycompany.com",
},
cfgStubs: func(t *testing.T, cfg gh.Config) {
login(t, cfg, "mycompany.com", "test-user", "gho_1234567", "https", true)
},
wantStdout: "gho_1234567\n",
},
{
name: "no token",
opts: TokenOptions{},
N/A
command: gh auth token
short: This command outputs the authentication token for an account on a given GitHub host.
long: |-
Without the `--hostname` flag, the default host is chosen.
Without the `--user` flag, the active account for the host is chosen.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
shorthand: h
value_type: string
func TestNewCmdToken(t *testing.T) {
tests := []struct {
name string
input string
output TokenOptions
wantErr bool
wantErrMsg string
}{
{
name: "no flags",
input: "",
output: TokenOptions{},
},
{
name: "with hostname",
input: "--hostname github.mycompany.com",
output: TokenOptions{Hostname: "github.mycompany.com"},
},
{
name: "with user",
input: "--user test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand user",
input: "-u test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand hostname",
N/A
N/A
command: gh auth token
short: This command outputs the authentication token for an account on a given GitHub host.
long: |-
Without the `--hostname` flag, the default host is chosen.
Without the `--user` flag, the active account for the host is chosen.
pname: gh auth
plink: gh_auth.yaml
options:
- option: hostname
shorthand: h
value_type: string
func TestNewCmdToken(t *testing.T) {
tests := []struct {
name string
input string
output TokenOptions
wantErr bool
wantErrMsg string
}{
{
name: "no flags",
input: "",
output: TokenOptions{},
},
{
name: "with hostname",
input: "--hostname github.mycompany.com",
output: TokenOptions{Hostname: "github.mycompany.com"},
},
{
name: "with user",
input: "--user test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand user",
input: "-u test-user",
output: TokenOptions{Username: "test-user"},
},
{
name: "with shorthand hostname",
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
func Test_runBrowse(t *testing.T) {
s := string(os.PathSeparator)
t.Setenv("GIT_DIR", "../../../git/fixtures/simple.git")
tests := []struct {
name string
opts BrowseOptions
httpStub func(*httpmock.Registry)
baseRepo ghrepo.Interface
defaultBranch string
expectedURL string
wantsErr bool
}{
{
name: "no arguments",
opts: BrowseOptions{
SelectorArg: "",
},
baseRepo: ghrepo.New("jlsestak", "cli"),
expectedURL: "https://github.com/jlsestak/cli",
},
{
name: "settings flag",
opts: BrowseOptions{
SettingsFlag: true,
},
baseRepo: ghrepo.New("bchadwic", "ObscuredByClouds"),
expectedURL: "https://github.com/bchadwic/ObscuredByClouds/settings",
},
{
name: "projects flag",
func Test_parsePathFromFileArg(t *testing.T) {
tests := []struct {
name string
currentDir string
fileArg string
expectedPath string
}{
{
name: "empty paths",
currentDir: "",
fileArg: "",
expectedPath: "",
},
{
name: "root directory",
currentDir: "",
fileArg: ".",
expectedPath: "",
},
{
name: "relative path",
currentDir: "",
fileArg: filepath.FromSlash("foo/bar.py"),
expectedPath: "foo/bar.py",
},
{
name: "go to parent folder",
currentDir: "pkg/cmd/browse/",
fileArg: filepath.FromSlash("../"),
expectedPath: "pkg/cmd",
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
command: gh browse
pname: gh
plink: gh.yaml
options:
- option: actions
shorthand: a
value_type: bool
default_value: "false"
description: Open repository actions
- option: blame
value_type: bool
default_value: "false"
func TestNewCmdBrowse(t *testing.T) {
tests := []struct {
name string
cli string
factory func(*cmdutil.Factory) *cmdutil.Factory
wants BrowseOptions
wantsErr bool
}{
{
name: "no arguments",
cli: "",
wantsErr: false,
},
{
name: "settings flag",
cli: "--settings",
wants: BrowseOptions{
SettingsFlag: true,
},
wantsErr: false,
},
{
name: "projects flag",
cli: "--projects",
wants: BrowseOptions{
ProjectsFlag: true,
},
wantsErr: false,
},
{
N/A
N/A
command: gh cache
short: Work with GitHub Actions caches.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
command: gh cache
short: Work with GitHub Actions caches.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh cache delete
short: Delete GitHub Actions caches.
long: Deletion requires authorization with the `repo` scope.
pname: gh cache
plink: gh_cache.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Delete all caches, can be used with --ref to delete all caches for a specific ref
- option: ref
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wantsErr: "must provide either cache id, cache key, or use --all",
},
{
name: "id argument",
cli: "123",
wants: DeleteOptions{Identifier: "123"},
},
{
name: "key argument",
cli: "A-Cache-Key",
wants: DeleteOptions{Identifier: "A-Cache-Key"},
},
{
name: "delete all flag",
cli: "--all",
wants: DeleteOptions{DeleteAll: true},
},
{
name: "delete all and succeed-on-no-caches flags",
cli: "--all --succeed-on-no-caches",
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
opts DeleteOptions
stubs func(*httpmock.Registry)
tty bool
wantErr bool
wantErrMsg string
wantStderr string
wantStdout string
}{
{
name: "deletes cache tty",
opts: DeleteOptions{Identifier: "123"},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/actions/caches/123"),
httpmock.StatusStringResponse(204, ""),
)
},
tty: true,
wantStdout: "✓ Deleted 1 cache from OWNER/REPO\n",
},
{
name: "deletes cache notty",
opts: DeleteOptions{Identifier: "123"},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/actions/caches/123"),
httpmock.StatusStringResponse(204, ""),
N/A
command: gh cache delete
short: Delete GitHub Actions caches.
long: Deletion requires authorization with the `repo` scope.
pname: gh cache
plink: gh_cache.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Delete all caches, can be used with --ref to delete all caches for a specific ref
- option: ref
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wantsErr: "must provide either cache id, cache key, or use --all",
},
{
name: "id argument",
cli: "123",
wants: DeleteOptions{Identifier: "123"},
},
{
name: "key argument",
cli: "A-Cache-Key",
wants: DeleteOptions{Identifier: "A-Cache-Key"},
},
{
name: "delete all flag",
cli: "--all",
wants: DeleteOptions{DeleteAll: true},
},
{
name: "delete all and succeed-on-no-caches flags",
cli: "--all --succeed-on-no-caches",
N/A
command: gh cache delete
short: Delete GitHub Actions caches.
long: Deletion requires authorization with the `repo` scope.
pname: gh cache
plink: gh_cache.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Delete all caches, can be used with --ref to delete all caches for a specific ref
- option: ref
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wantsErr: "must provide either cache id, cache key, or use --all",
},
{
name: "id argument",
cli: "123",
wants: DeleteOptions{Identifier: "123"},
},
{
name: "key argument",
cli: "A-Cache-Key",
wants: DeleteOptions{Identifier: "A-Cache-Key"},
},
{
name: "delete all flag",
cli: "--all",
wants: DeleteOptions{DeleteAll: true},
},
{
name: "delete all and succeed-on-no-caches flags",
cli: "--all --succeed-on-no-caches",
N/A
command: gh cache delete
short: Delete GitHub Actions caches.
long: Deletion requires authorization with the `repo` scope.
pname: gh cache
plink: gh_cache.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Delete all caches, can be used with --ref to delete all caches for a specific ref
- option: ref
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wantsErr: "must provide either cache id, cache key, or use --all",
},
{
name: "id argument",
cli: "123",
wants: DeleteOptions{Identifier: "123"},
},
{
name: "key argument",
cli: "A-Cache-Key",
wants: DeleteOptions{Identifier: "A-Cache-Key"},
},
{
name: "delete all flag",
cli: "--all",
wants: DeleteOptions{DeleteAll: true},
},
{
name: "delete all and succeed-on-no-caches flags",
cli: "--all --succeed-on-no-caches",
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
func TestListRun(t *testing.T) {
var now = time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
tests := []struct {
name string
opts ListOptions
stubs func(*httpmock.Registry)
tty bool
wantErr bool
wantErrMsg string
wantStderr string
wantStdout string
}{
{
name: "displays results tty",
tty: true,
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.JSONResponse(shared.CachePayload{
ActionsCaches: []shared.Cache{
{
Id: 1,
Key: "foo",
CreatedAt: time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
LastAccessedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
SizeInBytes: 100,
},
{
Id: 2,
Key: "bar",
func Test_humanFileSize(t *testing.T) {
tests := []struct {
name string
size int64
want string
}{
{
name: "min bytes",
size: 1,
want: "1 B",
},
{
name: "max bytes",
size: 1023,
want: "1023 B",
},
{
name: "min kibibytes",
size: 1024,
want: "1.00 KiB",
},
{
name: "max kibibytes",
size: 1024*1024 - 1,
want: "1023.99 KiB",
},
{
name: "min mibibytes",
size: 1024 * 1024,
want: "1.00 MiB",
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
N/A
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
input: "",
wants: ListOptions{
Limit: 30,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
name: "with limit",
input: "--limit 100",
wants: ListOptions{
Limit: 100,
Order: "desc",
Sort: "last_accessed_at",
Key: "",
Ref: "",
},
},
{
N/A
command: gh cache list
short: List GitHub Actions caches
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh cache
plink: gh_cache.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh co
short: Check out a pull request in git
pname: gh
plink: gh.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
N/A
N/A
N/A
N/A
command: gh co
short: Check out a pull request in git
pname: gh
plink: gh.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
N/A
N/A
N/A
N/A
command: gh co
short: Check out a pull request in git
pname: gh
plink: gh.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
N/A
N/A
N/A
N/A
command: gh co
short: Check out a pull request in git
pname: gh
plink: gh.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
N/A
N/A
N/A
N/A
command: gh co
short: Check out a pull request in git
pname: gh
plink: gh.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
N/A
N/A
N/A
N/A
command: gh codespace short: Connect to and manage codespaces pname: gh plink: gh.yaml
N/A
N/A
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
func TestApp_VSCode(t *testing.T) {
type args struct {
codespaceName string
useInsiders bool
useWeb bool
}
tests := []struct {
name string
args args
wantErr bool
wantURL string
}{
{
name: "open VS Code",
args: args{
codespaceName: "monalisa-cli-cli-abcdef",
useInsiders: false,
},
wantErr: false,
wantURL: "vscode://github.codespaces/connect?name=monalisa-cli-cli-abcdef&windowId=_blank",
},
{
name: "open VS Code Insiders",
args: args{
codespaceName: "monalisa-cli-cli-abcdef",
useInsiders: true,
},
wantErr: false,
wantURL: "vscode-insiders://github.codespaces/connect?name=monalisa-cli-cli-abcdef&windowId=_blank",
},
func TestPendingOperationDisallowsCode(t *testing.T) {
app := testingCodeApp()
selector := &CodespaceSelector{api: app.apiClient, codespaceName: "disabledCodespace"}
if err := app.VSCode(context.Background(), selector, false, false); err != nil {
if err.Error() != "codespace is disabled while it has a pending operation: Some pending operation" {
t.Errorf("expected pending operation error, but got: %v", err)
}
} else {
t.Error("expected pending operation error, but got nothing")
}
}
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace code
short: Open a codespace in Visual Studio Code
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: insiders
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace cp
short: The `cp` command copies files between the local and remote file systems.
long: |-
As with the UNIX `cp` command, the first argument specifies the source and the last
specifies the destination; additional sources may be specified after the first,
if the destination is a directory.
The `--recursive` flag is required if any source is a directory.
A `remote:` prefix on any file name argument indicates that it refers to
the file system of the remote (Codespace) machine. It is resolved relative
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestCreateCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "return error when using web flag with display-name, idle-timeout, or retention-period flags",
args: "--web --display-name foo --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
{
name: "return error when using web flag with one of display-name, idle-timeout or retention-period flags",
args: "--web --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
cmd := newCreateCmd(a)
args, _ := shlex.Split(tt.args)
cmd.SetArgs(args)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
func TestApp_Create(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts createOptions
wantErr error
wantStdout string
wantStderr string
wantURL string
isTTY bool
}{
{
name: "create codespace with default branch and 30m idle timeout",
fields: fields{
apiClient: apiCreateDefaults(&apiClientMock{
CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) {
if params.Branch != "main" {
return nil, fmt.Errorf("got branch %q, want %q", params.Branch, "main")
}
if params.IdleTimeoutMinutes != 30 {
return nil, fmt.Errorf("idle timeout minutes was %v", params.IdleTimeoutMinutes)
}
if *params.RetentionPeriodMinutes != 2880 {
return nil, fmt.Errorf("retention period minutes expected 2880, was %v", params.RetentionPeriodMinutes)
}
if params.DisplayName != "" {
return nil, fmt.Errorf("display name was %q, expected empty", params.DisplayName)
func TestBuildDisplayName(t *testing.T) {
tests := []struct {
name string
prebuildAvailability string
expectedDisplayName string
}{
{
name: "prebuild availability is none",
prebuildAvailability: "none",
expectedDisplayName: "4 cores, 8 GB RAM, 32 GB storage",
},
{
name: "prebuild availability is empty",
prebuildAvailability: "",
expectedDisplayName: "4 cores, 8 GB RAM, 32 GB storage",
},
{
name: "prebuild availability is ready",
prebuildAvailability: "ready",
expectedDisplayName: "4 cores, 8 GB RAM, 32 GB storage (Prebuild ready)",
},
{
name: "prebuild availability is in_progress",
prebuildAvailability: "in_progress",
expectedDisplayName: "4 cores, 8 GB RAM, 32 GB storage (Prebuild in progress)",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
displayName := buildDisplayName("4 cores, 8 GB RAM, 32 GB storage", tt.prebuildAvailability)
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestApp_Create(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts createOptions
wantErr error
wantStdout string
wantStderr string
wantURL string
isTTY bool
}{
{
name: "create codespace with default branch and 30m idle timeout",
fields: fields{
apiClient: apiCreateDefaults(&apiClientMock{
CreateCodespaceFunc: func(ctx context.Context, params *api.CreateCodespaceParams) (*api.Codespace, error) {
if params.Branch != "main" {
return nil, fmt.Errorf("got branch %q, want %q", params.Branch, "main")
}
if params.IdleTimeoutMinutes != 30 {
return nil, fmt.Errorf("idle timeout minutes was %v", params.IdleTimeoutMinutes)
}
if *params.RetentionPeriodMinutes != 2880 {
return nil, fmt.Errorf("retention period minutes expected 2880, was %v", params.RetentionPeriodMinutes)
}
if params.DisplayName != "" {
return nil, fmt.Errorf("display name was %q, expected empty", params.DisplayName)
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestCreateCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "return error when using web flag with display-name, idle-timeout, or retention-period flags",
args: "--web --display-name foo --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
{
name: "return error when using web flag with one of display-name, idle-timeout or retention-period flags",
args: "--web --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
cmd := newCreateCmd(a)
args, _ := shlex.Split(tt.args)
cmd.SetArgs(args)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestCreateCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "return error when using web flag with display-name, idle-timeout, or retention-period flags",
args: "--web --display-name foo --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
{
name: "return error when using web flag with one of display-name, idle-timeout or retention-period flags",
args: "--web --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
cmd := newCreateCmd(a)
args, _ := shlex.Split(tt.args)
cmd.SetArgs(args)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestCreateCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "return error when using web flag with display-name, idle-timeout, or retention-period flags",
args: "--web --display-name foo --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
{
name: "return error when using web flag with one of display-name, idle-timeout or retention-period flags",
args: "--web --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
cmd := newCreateCmd(a)
args, _ := shlex.Split(tt.args)
cmd.SetArgs(args)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh codespace create
short: Create a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Repository branch
- option: default-permissions
value_type: bool
default_value: "false"
func TestCreateCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "return error when using web flag with display-name, idle-timeout, or retention-period flags",
args: "--web --display-name foo --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
{
name: "return error when using web flag with one of display-name, idle-timeout or retention-period flags",
args: "--web --idle-timeout 30m",
wantsErr: fmt.Errorf("using --web with --display-name, --idle-timeout, or --retention-period is not supported"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
cmd := newCreateCmd(a)
args, _ := shlex.Split(tt.args)
cmd.SetArgs(args)
cmd.SetIn(&bytes.Buffer{})
cmd.SetOut(&bytes.Buffer{})
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
func TestDelete(t *testing.T) {
now, _ := time.Parse(time.RFC3339, "2021-09-22T00:00:00Z")
daysAgo := func(n int) string {
return now.Add(time.Hour * -time.Duration(24*n)).Format(time.RFC3339)
}
tests := []struct {
name string
opts deleteOptions
codespaces []*api.Codespace
confirms map[string]bool
deleteErr error
wantErr string
wantDeleted []string
wantStdout string
wantStderr string
}{
{
name: "by name",
opts: deleteOptions{
codespaceName: "hubot-robawt-abc",
},
codespaces: []*api.Codespace{
{
Name: "hubot-robawt-abc",
},
},
wantDeleted: []string{"hubot-robawt-abc"},
wantStderr: "1 codespace(s) deleted successfully\n",
},
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace delete
short: Delete codespaces based on selection criteria.
long: |-
All codespaces for the authenticated user can be deleted, as well as codespaces for a
specific repository. Alternatively, only codespaces older than N days can be deleted.
Organization administrators may delete any codespace billed to the organization.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: all
value_type: bool
N/A
N/A
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
func TestEdit(t *testing.T) {
tests := []struct {
name string
opts editOptions
cliArgs []string // alternative to opts; will test command dispatcher
wantEdits *api.EditCodespaceParams
mockCodespace *api.Codespace
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "edit codespace display name",
opts: editOptions{
selector: &CodespaceSelector{codespaceName: "hubot"},
displayName: "hubot-changed",
machine: "",
},
wantEdits: &api.EditCodespaceParams{
DisplayName: "hubot-changed",
},
mockCodespace: &api.Codespace{
Name: "hubot",
DisplayName: "hubot-changed",
},
wantStdout: "",
wantErr: false,
},
{
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
func TestEdit(t *testing.T) {
tests := []struct {
name string
opts editOptions
cliArgs []string // alternative to opts; will test command dispatcher
wantEdits *api.EditCodespaceParams
mockCodespace *api.Codespace
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "edit codespace display name",
opts: editOptions{
selector: &CodespaceSelector{codespaceName: "hubot"},
displayName: "hubot-changed",
machine: "",
},
wantEdits: &api.EditCodespaceParams{
DisplayName: "hubot-changed",
},
mockCodespace: &api.Codespace{
Name: "hubot",
DisplayName: "hubot-changed",
},
wantStdout: "",
wantErr: false,
},
{
N/A
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
func TestEdit(t *testing.T) {
tests := []struct {
name string
opts editOptions
cliArgs []string // alternative to opts; will test command dispatcher
wantEdits *api.EditCodespaceParams
mockCodespace *api.Codespace
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "edit codespace display name",
opts: editOptions{
selector: &CodespaceSelector{codespaceName: "hubot"},
displayName: "hubot-changed",
machine: "",
},
wantEdits: &api.EditCodespaceParams{
DisplayName: "hubot-changed",
},
mockCodespace: &api.Codespace{
Name: "hubot",
DisplayName: "hubot-changed",
},
wantStdout: "",
wantErr: false,
},
{
N/A
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
func TestEdit(t *testing.T) {
tests := []struct {
name string
opts editOptions
cliArgs []string // alternative to opts; will test command dispatcher
wantEdits *api.EditCodespaceParams
mockCodespace *api.Codespace
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "edit codespace display name",
opts: editOptions{
selector: &CodespaceSelector{codespaceName: "hubot"},
displayName: "hubot-changed",
machine: "",
},
wantEdits: &api.EditCodespaceParams{
DisplayName: "hubot-changed",
},
mockCodespace: &api.Codespace{
Name: "hubot",
DisplayName: "hubot-changed",
},
wantStdout: "",
wantErr: false,
},
{
N/A
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
N/A
N/A
N/A
command: gh codespace edit
short: Edit a codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: display-name
shorthand: d
value_type: string
N/A
N/A
N/A
command: gh codespace jupyter
short: Open a codespace in JupyterLab
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: repo
shorthand: R
value_type: string
N/A
N/A
command: gh codespace jupyter
short: Open a codespace in JupyterLab
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh codespace jupyter
short: Open a codespace in JupyterLab
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh codespace jupyter
short: Open a codespace in JupyterLab
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
func TestApp_List(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *listOptions
wantError error
wantURL string
}{
{
name: "list codespaces, no flags",
fields: fields{
apiClient: &apiClientMock{
ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
if opts.OrgName != "" {
return nil, fmt.Errorf("should not be called with an orgName")
}
return []*api.Codespace{
{
DisplayName: "CS1",
CreatedAt: "2023-01-01T00:00:00Z",
},
}, nil
},
},
},
opts: &listOptions{},
},
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
N/A
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
N/A
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
func TestApp_List(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *listOptions
wantError error
wantURL string
}{
{
name: "list codespaces, no flags",
fields: fields{
apiClient: &apiClientMock{
ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
if opts.OrgName != "" {
return nil, fmt.Errorf("should not be called with an orgName")
}
return []*api.Codespace{
{
DisplayName: "CS1",
CreatedAt: "2023-01-01T00:00:00Z",
},
}, nil
},
},
},
opts: &listOptions{},
},
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
func TestApp_List(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *listOptions
wantError error
wantURL string
}{
{
name: "list codespaces, no flags",
fields: fields{
apiClient: &apiClientMock{
ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
if opts.OrgName != "" {
return nil, fmt.Errorf("should not be called with an orgName")
}
return []*api.Codespace{
{
DisplayName: "CS1",
CreatedAt: "2023-01-01T00:00:00Z",
},
}, nil
},
},
},
opts: &listOptions{},
},
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
N/A
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
func TestApp_List(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *listOptions
wantError error
wantURL string
}{
{
name: "list codespaces, no flags",
fields: fields{
apiClient: &apiClientMock{
ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
if opts.OrgName != "" {
return nil, fmt.Errorf("should not be called with an orgName")
}
return []*api.Codespace{
{
DisplayName: "CS1",
CreatedAt: "2023-01-01T00:00:00Z",
},
}, nil
},
},
},
opts: &listOptions{},
},
N/A
N/A
command: gh codespace list
short: List codespaces of the authenticated user.
long: |-
Alternatively, organization administrators may list all codespaces billed to the organization.
For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestListCmdFlagError(t *testing.T) {
tests := []struct {
name string
args string
wantsErr error
}{
{
name: "list codespaces,--repo, --org and --user flag",
args: "--repo foo/bar --org github --user github",
wantsErr: fmt.Errorf("using `--org` or `--user` with `--repo` is not allowed"),
},
{
name: "list codespaces,--web, --org and --user flag",
args: "--web --org github --user github",
wantsErr: fmt.Errorf("using `--web` with `--org` or `--user` is not supported, please use with `--repo` instead"),
},
{
name: "list codespaces, negative --limit flag",
args: "--limit -1",
wantsErr: fmt.Errorf("invalid limit: -1"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
}
func TestApp_List(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *listOptions
wantError error
wantURL string
}{
{
name: "list codespaces, no flags",
fields: fields{
apiClient: &apiClientMock{
ListCodespacesFunc: func(ctx context.Context, opts api.ListCodespacesOptions) ([]*api.Codespace, error) {
if opts.OrgName != "" {
return nil, fmt.Errorf("should not be called with an orgName")
}
return []*api.Codespace{
{
DisplayName: "CS1",
CreatedAt: "2023-01-01T00:00:00Z",
},
}, nil
},
},
},
opts: &listOptions{},
},
N/A
N/A
command: gh codespace logs
short: Access codespace logs
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: follow
shorthand: f
value_type: bool
func TestPendingOperationDisallowsLogs(t *testing.T) {
app := testingLogsApp()
selector := &CodespaceSelector{api: app.apiClient, codespaceName: "disabledCodespace"}
if err := app.Logs(context.Background(), selector, false); err != nil {
if err.Error() != "codespace is disabled while it has a pending operation: Some pending operation" {
t.Errorf("expected pending operation error, but got: %v", err)
}
} else {
t.Error("expected pending operation error, but got nothing")
}
}
N/A
command: gh codespace logs
short: Access codespace logs
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: follow
shorthand: f
value_type: bool
N/A
N/A
N/A
command: gh codespace logs
short: Access codespace logs
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: follow
shorthand: f
value_type: bool
N/A
N/A
N/A
command: gh codespace logs
short: Access codespace logs
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: follow
shorthand: f
value_type: bool
N/A
N/A
N/A
command: gh codespace logs
short: Access codespace logs
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: follow
shorthand: f
value_type: bool
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
func TestListPorts(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
mockApi := GetMockApi(false)
ios, _, _, _ := iostreams.Test()
a := &App{
io: ios,
apiClient: mockApi,
}
selector := &CodespaceSelector{api: a.apiClient, codespaceName: "codespace-name"}
err := a.ListPorts(ctx, selector, nil)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestPortsUpdateVisibilitySuccess(t *testing.T) {
portVisibilities := []portVisibility{
{
number: 80,
visibility: "org",
},
{
number: 9999,
visibility: "public",
},
}
err := runUpdateVisibilityTest(t, portVisibilities, true)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
func TestPortsUpdateVisibilityFailure(t *testing.T) {
portVisibilities := []portVisibility{
{
number: 9999,
visibility: "public",
},
{
number: 80,
visibility: "org",
},
}
err := runUpdateVisibilityTest(t, portVisibilities, false)
if err == nil {
t.Fatalf("runUpdateVisibilityTest succeeded unexpectedly")
}
}
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports
short: List ports in a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace ports forward short: Forward ports pname: gh codespace ports plink: gh_codespace_ports.yaml
N/A
N/A
command: gh codespace ports visibility short: Change the visibility of the forwarded port pname: gh codespace ports plink: gh_codespace_ports.yaml
N/A
N/A
command: gh codespace rebuild
short: Rebuilding recreates your codespace.
long: |-
Your code and any current changes will be preserved. Your codespace will be rebuilt using
your working directory's dev container. A full rebuild also removes cached Docker images.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
func TestAlreadyRebuildingCodespace(t *testing.T) {
rebuildingCodespace := &api.Codespace{
Name: "rebuildingCodespace",
State: api.CodespaceStateRebuilding,
}
app := testingRebuildApp(*rebuildingCodespace)
selector := &CodespaceSelector{api: app.apiClient, codespaceName: "rebuildingCodespace"}
err := app.Rebuild(context.Background(), selector, false)
if err != nil {
t.Errorf("rebuilding a codespace that was already rebuilding: %v", err)
}
}
N/A
command: gh codespace rebuild
short: Rebuilding recreates your codespace.
long: |-
Your code and any current changes will be preserved. Your codespace will be rebuilt using
your working directory's dev container. A full rebuild also removes cached Docker images.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
N/A
N/A
N/A
command: gh codespace rebuild
short: Rebuilding recreates your codespace.
long: |-
Your code and any current changes will be preserved. Your codespace will be rebuilt using
your working directory's dev container. A full rebuild also removes cached Docker images.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
N/A
N/A
N/A
command: gh codespace rebuild
short: Rebuilding recreates your codespace.
long: |-
Your code and any current changes will be preserved. Your codespace will be rebuilt using
your working directory's dev container. A full rebuild also removes cached Docker images.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
N/A
N/A
N/A
command: gh codespace rebuild
short: Rebuilding recreates your codespace.
long: |-
Your code and any current changes will be preserved. Your codespace will be rebuilt using
your working directory's dev container. A full rebuild also removes cached Docker images.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
func TestPendingOperationDisallowsSSH(t *testing.T) {
app := testingSSHApp()
selector := &CodespaceSelector{api: app.apiClient, codespaceName: "disabledCodespace"}
if err := app.SSH(context.Background(), []string{}, sshOptions{selector: selector}); err != nil {
if err.Error() != "codespace is disabled while it has a pending operation: Some pending operation" {
t.Errorf("expected pending operation error, but got: %v", err)
}
} else {
t.Error("expected pending operation error, but got nothing")
}
}
func TestGenerateAutomaticSSHKeys(t *testing.T) {
tests := []struct {
// These files exist when calling generateAutomaticSSHKeys
existingFiles []string
// These files should exist after generateAutomaticSSHKeys finishes
wantFinalFiles []string
}{
// Basic case: no existing keys, they should be created
{
nil,
[]string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
},
// Basic case: keys already exist
{
[]string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
[]string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
},
// Backward compatibility: both old keys exist, they should be renamed
{
[]string{automaticPrivateKeyNameOld, automaticPrivateKeyNameOld + ".pub"},
[]string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
},
// Backward compatibility: old private key exists but not the public key, the new keys should be created
{
[]string{automaticPrivateKeyNameOld},
[]string{automaticPrivateKeyNameOld, automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
},
// Backward compatibility: old public key exists but not the private key, the new keys should be created
{
[]string{automaticPrivateKeyNameOld + ".pub"},
func TestSelectSSHKeys(t *testing.T) {
// This string will be substituted in sshArgs for test cases
// This is to work around the temp test ssh dir not being known until the test is executing
substituteSSHDir := "SUB_SSH_DIR"
tests := []struct {
sshDirFiles []string
sshConfigKeys []string
sshArgs []string
profileOpt string
wantKeyPair *ssh.KeyPair
wantShouldAddArg bool
}{
// -i tests
{
sshArgs: []string{"-i", "custom-private-key"},
wantKeyPair: &ssh.KeyPair{PrivateKeyPath: "custom-private-key", PublicKeyPath: "custom-private-key.pub"},
},
{
sshArgs: []string{"-i", path.Join(substituteSSHDir, automaticPrivateKeyName)},
wantKeyPair: &ssh.KeyPair{PrivateKeyPath: automaticPrivateKeyName, PublicKeyPath: automaticPrivateKeyName + ".pub"},
},
{
// Edge case check for missing arg value
sshArgs: []string{"-i"},
},
// Auto key exists tests
{
sshDirFiles: []string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace ssh
short: The `ssh` command is used to SSH into a codespace. In its simplest form, you can
long: |-
run `gh cs ssh`, select a codespace interactively, and connect.
The `ssh` command will automatically create a public/private ssh key pair in the
`~/.ssh` directory if you do not have an existing valid key pair. When selecting the
key pair to use, the preferred order is:
1. Key specified by `-i` in `<ssh-flags>`
2. Automatic key, if it already exists
3. First valid key pair in ssh config (according to `ssh -G`)
N/A
N/A
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
func TestApp_StopCodespace(t *testing.T) {
type fields struct {
apiClient apiClient
}
tests := []struct {
name string
fields fields
opts *stopOptions
}{
{
name: "Stop a codespace I own",
opts: &stopOptions{
selector: &CodespaceSelector{codespaceName: "test-codespace"},
},
fields: fields{
apiClient: &apiClientMock{
GetCodespaceFunc: func(ctx context.Context, name string, includeConnection bool) (*api.Codespace, error) {
if name != "test-codespace" {
return nil, fmt.Errorf("got codespace name %s, wanted %s", name, "test-codespace")
}
return &api.Codespace{
State: api.CodespaceStateAvailable,
}, nil
},
StopCodespaceFunc: func(ctx context.Context, name string, orgName string, userName string) error {
if name != "test-codespace" {
return fmt.Errorf("got codespace name %s, wanted %s", name, "test-codespace")
}
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
N/A
N/A
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
N/A
N/A
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
N/A
N/A
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
N/A
N/A
N/A
command: gh codespace stop
short: Stop a running codespace
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: org
shorthand: o
value_type: string
N/A
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
func Test_NewCmdView(t *testing.T) {
tests := []struct {
tName string
codespaceName string
opts *viewOptions
cliArgs []string
wantErr bool
wantStdout string
errMsg string
}{
{
tName: "selector throws because no terminal found",
opts: &viewOptions{},
wantErr: true,
errMsg: "choosing codespace: error getting answers: no terminal",
},
{
tName: "command fails because provided codespace doesn't exist",
codespaceName: "i-dont-exist",
opts: &viewOptions{},
wantErr: true,
errMsg: "getting full codespace details: codespace not found",
},
{
tName: "command succeeds because codespace exists (no details)",
codespaceName: "monalisa-cli-cli-abcdef",
opts: &viewOptions{},
wantErr: false,
wantStdout: "Name\tmonalisa-cli-cli-abcdef\nState\t\nRepository\t\nGit Status\t - 0 commits ahead, 0 commits behind\nDevcontainer Path\t\nMachine Display Name\t\nIdle Timeout\t0 minutes\nCreated At\t\nRetention Period\t\n",
},
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh codespace view
short: View details about a codespace
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh codespace
plink: gh_codespace.yaml
options:
- option: codespace
shorthand: c
value_type: string
description: Name of the codespace
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh completion
short: Generate shell completion scripts for GitHub CLI commands.
long: |-
When installing GitHub CLI through a package manager, it's possible that
no additional shell configuration is necessary to gain completion support. For
Homebrew, see <https://docs.brew.sh/Shell-Completion>
If you need to set up completions manually, follow the instructions below. The exact
config file locations might vary based on your system. Make sure to restart your
shell before testing whether completions are working.
### bash
func TestNewCmdCompletion(t *testing.T) {
tests := []struct {
name string
args string
wantOut string
wantErr string
}{
{
name: "no arguments",
args: "completion",
wantOut: "complete -o default -F __start_gh gh",
},
{
name: "zsh completion",
args: "completion -s zsh",
wantOut: "#compdef gh",
},
{
name: "fish completion",
args: "completion -s fish",
wantOut: "complete -c gh ",
},
{
name: "PowerShell completion",
args: "completion -s powershell",
wantOut: "Register-ArgumentCompleter",
},
{
name: "unsupported shell",
args: "completion -s csh",
func TestRequestableReviewersForCompletion(t *testing.T) {
tests := []struct {
name string
expectedReviewers []string
httpStubs func(*httpmock.Registry, *testing.T)
}{
{
name: "when users and teams are both available, both are listed",
expectedReviewers: []string{"MonaLisa\tMona Display Name", "OWNER/core", "OWNER/robots", "hubot"},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
reg.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data": {"viewer": {"login": "OWNER"} } }`))
reg.Register(
httpmock.GraphQL(`query RepositoryAssignableUsers\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "assignableUsers": {
"nodes": [
{ "login": "hubot", "id": "HUBOTID", "name": "" },
{ "login": "MonaLisa", "id": "MONAID", "name": "Mona Display Name" }
],
"pageInfo": { "hasNextPage": false }
} } } }
`))
reg.Register(
httpmock.GraphQL(`query OrganizationTeamList\b`),
httpmock.StringResponse(`
{ "data": { "organization": { "teams": {
"nodes": [
{ "slug": "core", "id": "COREID" },
N/A
command: gh completion
short: Generate shell completion scripts for GitHub CLI commands.
long: |-
When installing GitHub CLI through a package manager, it's possible that
no additional shell configuration is necessary to gain completion support. For
Homebrew, see <https://docs.brew.sh/Shell-Completion>
If you need to set up completions manually, follow the instructions below. The exact
config file locations might vary based on your system. Make sure to restart your
shell before testing whether completions are working.
### bash
N/A
N/A
N/A
command: gh config short: Display or change configuration settings for gh. pname: gh plink: gh.yaml
N/A
N/A
N/A
command: gh config clear-cache short: Clear the cli cache pname: gh config plink: gh_config.yaml
func TestClearCacheRun(t *testing.T) {
cacheDir := filepath.Join(t.TempDir(), "gh-cli-cache")
ios, _, stdout, stderr := iostreams.Test()
opts := &ClearCacheOptions{
IO: ios,
CacheDir: cacheDir,
}
if err := os.Mkdir(opts.CacheDir, 0600); err != nil {
assert.NoError(t, err)
}
if err := clearCacheRun(opts); err != nil {
assert.NoError(t, err)
}
assert.NoDirExistsf(t, opts.CacheDir, fmt.Sprintf("Cache dir: %s still exists", opts.CacheDir))
assert.Equal(t, "✓ Cleared the cache\n", stdout.String())
assert.Equal(t, "", stderr.String())
}
N/A
command: gh config get
short: Print the value of a given configuration key
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Get per-host setting
func TestNewCmdConfigGet(t *testing.T) {
tests := []struct {
name string
input string
output GetOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: GetOptions{},
wantsErr: true,
},
{
name: "get key",
input: "key",
output: GetOptions{Key: "key"},
wantsErr: false,
},
{
name: "get key with host",
input: "key --host test.com",
output: GetOptions{Hostname: "test.com", Key: "key"},
wantsErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &cmdutil.Factory{
func Test_getRun(t *testing.T) {
tests := []struct {
name string
input *GetOptions
stdout string
err error
}{
{
name: "get key",
input: &GetOptions{
Key: "editor",
Config: func() gh.Config {
cfg := config.NewBlankConfig()
cfg.Set("", "editor", "ed")
return cfg
}(),
},
stdout: "ed\n",
},
{
name: "get key scoped by host",
input: &GetOptions{
Hostname: "github.com",
Key: "editor",
Config: func() gh.Config {
cfg := config.NewBlankConfig()
cfg.Set("", "editor", "ed")
cfg.Set("github.com", "editor", "vim")
return cfg
}(),
N/A
command: gh config get
short: Print the value of a given configuration key
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Get per-host setting
func TestNewCmdConfigGet(t *testing.T) {
tests := []struct {
name string
input string
output GetOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: GetOptions{},
wantsErr: true,
},
{
name: "get key",
input: "key",
output: GetOptions{Key: "key"},
wantsErr: false,
},
{
name: "get key with host",
input: "key --host test.com",
output: GetOptions{Hostname: "test.com", Key: "key"},
wantsErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &cmdutil.Factory{
N/A
N/A
command: gh config list
short: Print a list of configuration keys and values
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Get per-host configuration
func TestNewCmdConfigList(t *testing.T) {
tests := []struct {
name string
input string
output ListOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: ListOptions{},
wantsErr: false,
},
{
name: "list with host",
input: "--host HOST.com",
output: ListOptions{Hostname: "HOST.com"},
wantsErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &cmdutil.Factory{
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
},
}
argv, err := shlex.Split(tt.input)
func Test_listRun(t *testing.T) {
tests := []struct {
name string
input *ListOptions
config gh.Config
stdout string
wantErr bool
}{
{
name: "list",
config: func() gh.Config {
cfg := config.NewBlankConfig()
cfg.Set("HOST", "git_protocol", "ssh")
cfg.Set("HOST", "editor", "/usr/bin/vim")
cfg.Set("HOST", "prompt", "disabled")
cfg.Set("HOST", "prefer_editor_prompt", "enabled")
cfg.Set("HOST", "pager", "less")
cfg.Set("HOST", "http_unix_socket", "")
cfg.Set("HOST", "browser", "brave")
return cfg
}(),
input: &ListOptions{Hostname: "HOST"},
stdout: heredoc.Doc(`
git_protocol=ssh
editor=/usr/bin/vim
prompt=disabled
prefer_editor_prompt=enabled
pager=less
http_unix_socket=
browser=brave
N/A
command: gh config list
short: Print a list of configuration keys and values
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Get per-host configuration
func TestNewCmdConfigList(t *testing.T) {
tests := []struct {
name string
input string
output ListOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: ListOptions{},
wantsErr: false,
},
{
name: "list with host",
input: "--host HOST.com",
output: ListOptions{Hostname: "HOST.com"},
wantsErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &cmdutil.Factory{
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
},
}
argv, err := shlex.Split(tt.input)
N/A
N/A
command: gh config set
short: Update configuration with a value for the given key
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Set per-host setting
func TestNewCmdConfigSet(t *testing.T) {
tests := []struct {
name string
input string
output SetOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: SetOptions{},
wantsErr: true,
},
{
name: "no value argument",
input: "key",
output: SetOptions{},
wantsErr: true,
},
{
name: "set key value",
input: "key value",
output: SetOptions{Key: "key", Value: "value"},
wantsErr: false,
},
{
name: "set key value with host",
input: "key value --host test.com",
output: SetOptions{Hostname: "test.com", Key: "key", Value: "value"},
wantsErr: false,
func Test_setRun(t *testing.T) {
tests := []struct {
name string
input *SetOptions
expectedValue string
stdout string
stderr string
wantsErr bool
errMsg string
}{
{
name: "set key value",
input: &SetOptions{
Config: config.NewBlankConfig(),
Key: "editor",
Value: "vim",
},
expectedValue: "vim",
},
{
name: "set key value scoped by host",
input: &SetOptions{
Config: config.NewBlankConfig(),
Hostname: "github.com",
Key: "editor",
Value: "vim",
},
expectedValue: "vim",
},
{
func Test_ValidateValue(t *testing.T) {
err := ValidateValue("git_protocol", "sshpps")
assert.EqualError(t, err, "invalid value")
err = ValidateValue("git_protocol", "ssh")
assert.NoError(t, err)
err = ValidateValue("editor", "vim")
assert.NoError(t, err)
err = ValidateValue("got", "123")
assert.NoError(t, err)
err = ValidateValue("http_unix_socket", "really_anything/is/allowed/and/net.Dial\\(...\\)/will/ultimately/validate")
assert.NoError(t, err)
}
N/A
command: gh config set
short: Update configuration with a value for the given key
pname: gh config
plink: gh_config.yaml
options:
- option: host
shorthand: h
value_type: string
description: Set per-host setting
func TestNewCmdConfigSet(t *testing.T) {
tests := []struct {
name string
input string
output SetOptions
wantsErr bool
}{
{
name: "no arguments",
input: "",
output: SetOptions{},
wantsErr: true,
},
{
name: "no value argument",
input: "key",
output: SetOptions{},
wantsErr: true,
},
{
name: "set key value",
input: "key value",
output: SetOptions{Key: "key", Value: "value"},
wantsErr: false,
},
{
name: "set key value with host",
input: "key value --host test.com",
output: SetOptions{Hostname: "test.com", Key: "key", Value: "value"},
wantsErr: false,
N/A
command: gh copilot
short: Runs the GitHub Copilot CLI.
long: |-
Executing the Copilot CLI through `gh` is currently in preview and subject to change.
If already installed, `gh` will execute the Copilot CLI found in your `PATH`.
If the Copilot CLI is not installed, it will be downloaded to /Users/daamanana/.local/share/gh/copilot.
Use `--remove` to remove the downloaded Copilot CLI.
This command is only supported on Windows, Linux, and Darwin, on amd64/x64
or arm64 architectures.
func TestNewCmdCopilot(t *testing.T) {
tests := []struct {
name string
args string
wantOpts CopilotOptions
wantErrString string
wantHelp bool
}{
{
name: "no argument",
args: "",
wantOpts: CopilotOptions{
CopilotArgs: []string{},
},
wantErrString: "",
},
{
name: "with arguments",
args: "some-arg some-other-arg",
wantOpts: CopilotOptions{
CopilotArgs: []string{"some-arg", "some-other-arg"},
},
},
{
name: "with --remove alone",
args: "--remove",
wantOpts: CopilotOptions{
Remove: true,
},
},
func TestRemoveCopilot(t *testing.T) {
t.Run("removes existing install directory", func(t *testing.T) {
// Create a temporary directory to simulate the install directory
tmpDir := t.TempDir()
installDir := filepath.Join(tmpDir, "copilot")
require.NoError(t, os.MkdirAll(installDir, 0755), "failed to create test directory")
// Create a dummy file in the directory
dummyFile := filepath.Join(installDir, "copilot")
require.NoError(t, os.WriteFile(dummyFile, []byte("test"), 0755), "failed to create test file")
err := removeCopilot(installDir)
require.NoError(t, err, "unexpected error")
_, err = os.Stat(installDir)
require.True(t, os.IsNotExist(err), "expected install directory to be removed")
})
t.Run("handles non-existent directory", func(t *testing.T) {
tmpDir := t.TempDir()
installDir := filepath.Join(tmpDir, "copilot")
require.ErrorContains(t, removeCopilot(installDir), "failed to remove Copilot CLI")
})
}
t.Run("removes existing install directory", func(t *testing.T) {
// Create a temporary directory to simulate the install directory
tmpDir := t.TempDir()
installDir := filepath.Join(tmpDir, "copilot")
require.NoError(t, os.MkdirAll(installDir, 0755), "failed to create test directory")
// Create a dummy file in the directory
dummyFile := filepath.Join(installDir, "copilot")
require.NoError(t, os.WriteFile(dummyFile, []byte("test"), 0755), "failed to create test file")
err := removeCopilot(installDir)
require.NoError(t, err, "unexpected error")
_, err = os.Stat(installDir)
require.True(t, os.IsNotExist(err), "expected install directory to be removed")
})
N/A
command: gh copilot
short: Runs the GitHub Copilot CLI.
long: |-
Executing the Copilot CLI through `gh` is currently in preview and subject to change.
If already installed, `gh` will execute the Copilot CLI found in your `PATH`.
If the Copilot CLI is not installed, it will be downloaded to /Users/daamanana/.local/share/gh/copilot.
Use `--remove` to remove the downloaded Copilot CLI.
This command is only supported on Windows, Linux, and Darwin, on amd64/x64
or arm64 architectures.
func TestNewCmdCopilot(t *testing.T) {
tests := []struct {
name string
args string
wantOpts CopilotOptions
wantErrString string
wantHelp bool
}{
{
name: "no argument",
args: "",
wantOpts: CopilotOptions{
CopilotArgs: []string{},
},
wantErrString: "",
},
{
name: "with arguments",
args: "some-arg some-other-arg",
wantOpts: CopilotOptions{
CopilotArgs: []string{"some-arg", "some-other-arg"},
},
},
{
name: "with --remove alone",
args: "--remove",
wantOpts: CopilotOptions{
Remove: true,
},
},
N/A
command: gh extension
short: GitHub CLI extensions are repositories that provide additional gh commands.
long: |-
The name of the extension repository must start with `gh-` and it must contain an
executable of the same name. All arguments passed to the `gh <extname>` invocation
will be forwarded to the `gh-<extname>` executable of the extension.
An extension cannot override any of the core gh commands. If an extension name conflicts
with a core gh command, you can use `gh extension exec <extname>`.
When an extension is executed, gh will check for new versions once every 24 hours and display
an upgrade notice. See `gh help environment` for information on disabling extension notices.
func TestUpdateAvailable_IsLocal(t *testing.T) {
e := &Extension{
kind: LocalKind,
}
assert.False(t, e.UpdateAvailable())
}
func TestUpdateAvailable_NoCurrentVersion(t *testing.T) {
e := &Extension{
kind: LocalKind,
}
assert.False(t, e.UpdateAvailable())
}
func TestUpdateAvailable_NoLatestVersion(t *testing.T) {
e := &Extension{
kind: BinaryKind,
currentVersion: "1.0.0",
}
assert.False(t, e.UpdateAvailable())
}
N/A
N/A
command: gh extension browse
short: This command will take over your terminal and run a fully interactive
long: |-
interface for browsing, adding, and removing gh extensions. A terminal
width greater than 100 columns is recommended.
To learn how to control this interface, press `?` after running to see
the help text.
Press `q` to quit.
Running this command with `--single-column` should make this command
func Test_getSelectedReadme(t *testing.T) {
reg := httpmock.Registry{}
defer reg.Verify(t)
content := base64.StdEncoding.EncodeToString([]byte("lol"))
reg.Register(
httpmock.REST("GET", "repos/cli/gh-cool/readme"),
httpmock.JSONResponse(view.RepoReadme{Content: content}))
client := &http.Client{Transport: ®}
rg := newReadmeGetter(client, time.Second)
opts := ExtBrowseOpts{
Rg: rg,
}
readme := tview.NewTextView()
ui := uiRegistry{
List: tview.NewList(),
}
extEntries := []extEntry{
{
Name: "gh-cool",
FullName: "cli/gh-cool",
Installed: false,
Official: true,
description: "it's just cool ok",
},
{
Name: "gh-screensaver",
func Test_getExtensionRepos(t *testing.T) {
reg := httpmock.Registry{}
defer reg.Verify(t)
client := &http.Client{Transport: ®}
values := url.Values{
"page": []string{"1"},
"per_page": []string{"100"},
"q": []string{"topic:gh-extension"},
}
cfg := config.NewBlankConfig()
cfg.AuthenticationFunc = func() gh.AuthConfig {
authCfg := &config.AuthConfig{}
authCfg.SetDefaultHost("github.com", "")
return authCfg
}
reg.Register(
httpmock.QueryMatcher("GET", "search/repositories", values),
httpmock.JSONResponse(map[string]interface{}{
"incomplete_results": false,
"total_count": 4,
"items": []interface{}{
map[string]interface{}{
"name": "gh-screensaver",
"full_name": "vilmibm/gh-screensaver",
"description": "terminal animations",
"owner": map[string]interface{}{
func Test_extEntry(t *testing.T) {
cases := []struct {
name string
ee extEntry
expectedTitle string
expectedDesc string
}{
{
name: "official",
ee: extEntry{
Name: "gh-cool",
FullName: "cli/gh-cool",
Installed: false,
Official: true,
description: "it's just cool ok",
},
expectedTitle: "cli/gh-cool [yellow](official)",
expectedDesc: "it's just cool ok",
},
{
name: "no description",
ee: extEntry{
Name: "gh-nodesc",
FullName: "barryburton/gh-nodesc",
Installed: false,
Official: false,
description: "",
},
expectedTitle: "barryburton/gh-nodesc",
expectedDesc: "no description provided",
N/A
command: gh extension browse
short: This command will take over your terminal and run a fully interactive
long: |-
interface for browsing, adding, and removing gh extensions. A terminal
width greater than 100 columns is recommended.
To learn how to control this interface, press `?` after running to see
the help text.
Press `q` to quit.
Running this command with `--single-column` should make this command
N/A
N/A
N/A
command: gh extension browse
short: This command will take over your terminal and run a fully interactive
long: |-
interface for browsing, adding, and removing gh extensions. A terminal
width greater than 100 columns is recommended.
To learn how to control this interface, press `?` after running to see
the help text.
Press `q` to quit.
Running this command with `--single-column` should make this command
N/A
N/A
N/A
command: gh extension create
short: Create a new extension
pname: gh extension
plink: gh_extension.yaml
options:
- option: precompiled
value_type: string
description: 'Create a precompiled extension. Possible values: go, other'
N/A
N/A
command: gh extension create
short: Create a new extension
pname: gh extension
plink: gh_extension.yaml
options:
- option: precompiled
value_type: string
description: 'Create a precompiled extension. Possible values: go, other'
N/A
N/A
command: gh extension install
short: Install a GitHub CLI extension from a GitHub or local repository.
long: |-
For GitHub repositories, the repository argument can be specified in
`OWNER/REPO` format or as a full repository URL.
The URL format is useful when the repository is not hosted on `github.com`.
For remote repositories, the GitHub CLI first looks for the release artifacts assuming
that it's a binary extension i.e. prebuilt binaries provided as part of the release.
In the absence of a release, the repository itself is cloned assuming that it's a
script extension i.e. prebuilt executable or script exists on its root.
N/A
N/A
command: gh extension install
short: Install a GitHub CLI extension from a GitHub or local repository.
long: |-
For GitHub repositories, the repository argument can be specified in
`OWNER/REPO` format or as a full repository URL.
The URL format is useful when the repository is not hosted on `github.com`.
For remote repositories, the GitHub CLI first looks for the release artifacts assuming
that it's a binary extension i.e. prebuilt binaries provided as part of the release.
In the absence of a release, the repository itself is cloned assuming that it's a
script extension i.e. prebuilt executable or script exists on its root.
N/A
N/A
N/A
command: gh extension install
short: Install a GitHub CLI extension from a GitHub or local repository.
long: |-
For GitHub repositories, the repository argument can be specified in
`OWNER/REPO` format or as a full repository URL.
The URL format is useful when the repository is not hosted on `github.com`.
For remote repositories, the GitHub CLI first looks for the release artifacts assuming
that it's a binary extension i.e. prebuilt binaries provided as part of the release.
In the absence of a release, the repository itself is cloned assuming that it's a
script extension i.e. prebuilt executable or script exists on its root.
N/A
N/A
N/A
command: gh extension list short: List installed extension commands pname: gh extension plink: gh_extension.yaml
N/A
N/A
command: gh extension remove short: Remove an installed extension pname: gh extension plink: gh_extension.yaml
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
N/A
command: gh extension search
short: Search for gh extensions.
long: |-
With no arguments, this command prints out the first 30 extensions
available to install sorted by number of stars. More extensions can
be fetched by specifying a higher limit with the `--limit` flag.
When connected to a terminal, this command prints out three columns.
The first has a ✓ if the extension is already installed locally. The
second is the full name of the extension repository in `OWNER/REPO`
format. The third is the extension's description.
N/A
N/A
N/A
command: gh extension upgrade
short: Upgrade installed extensions
pname: gh extension
plink: gh_extension.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Upgrade all extensions
- option: dry-run
value_type: bool
default_value: "false"
N/A
N/A
command: gh extension upgrade
short: Upgrade installed extensions
pname: gh extension
plink: gh_extension.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Upgrade all extensions
- option: dry-run
value_type: bool
default_value: "false"
N/A
N/A
command: gh extension upgrade
short: Upgrade installed extensions
pname: gh extension
plink: gh_extension.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Upgrade all extensions
- option: dry-run
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh extension upgrade
short: Upgrade installed extensions
pname: gh extension
plink: gh_extension.yaml
options:
- option: all
value_type: bool
default_value: "false"
description: Upgrade all extensions
- option: dry-run
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh gist short: Work with GitHub gists. pname: gh plink: gh.yaml
N/A
N/A
N/A
command: gh gist clone short: Clone a GitHub gist locally. pname: gh gist plink: gh_gist.yaml
func Test_GistClone(t *testing.T) {
tests := []struct {
name string
args string
want string
}{
{
name: "shorthand",
args: "GIST",
want: "git clone https://gist.github.com/GIST.git",
},
{
name: "shorthand with directory",
args: "GIST target_directory",
want: "git clone https://gist.github.com/GIST.git target_directory",
},
{
name: "clone arguments",
args: "GIST -- -o upstream --depth 1",
want: "git clone -o upstream --depth 1 https://gist.github.com/GIST.git",
},
{
name: "clone arguments with directory",
args: "GIST target_directory -- -o upstream --depth 1",
want: "git clone -o upstream --depth 1 https://gist.github.com/GIST.git target_directory",
},
{
name: "HTTPS URL",
args: "https://gist.github.com/OWNER/GIST",
want: "git clone https://gist.github.com/OWNER/GIST",
func Test_GistClone_flagError(t *testing.T) {
_, err := runCloneCommand(nil, "--depth 1 GIST")
if err == nil || err.Error() != "unknown flag: --depth\nSeparate git clone flags with '--'." {
t.Errorf("unexpected error %v", err)
}
}
func Test_formatRemoteURL(t *testing.T) {
type args struct {
hostname string
gistID string
protocol string
}
tests := []struct {
name string
args args
want string
}{
{
name: "github.com HTTPS",
args: args{
hostname: "github.com",
protocol: "https",
gistID: "ID",
},
want: "https://gist.github.com/ID.git",
},
{
name: "github.com SSH",
args: args{
hostname: "github.com",
protocol: "ssh",
gistID: "ID",
},
want: "git@gist.github.com:ID.git",
},
{
N/A
command: gh gist create
short: Create a new GitHub gist with given contents.
long: |-
Gists can be created from one or multiple files. Alternatively, pass `-` as
filename to read from standard input.
By default, gists are secret; use `--public` to make publicly listed ones.
pname: gh gist
plink: gh_gist.yaml
options:
- option: desc
shorthand: d
func Test_processFiles(t *testing.T) {
fakeStdin := strings.NewReader("hey cool how is it going")
files, err := processFiles(io.NopCloser(fakeStdin), "", []string{"-"})
if err != nil {
t.Fatalf("unexpected error processing files: %s", err)
}
assert.Equal(t, 1, len(files))
assert.Equal(t, "hey cool how is it going", files["gistfile0.txt"].Content)
}
func Test_guessGistName_stdin(t *testing.T) {
files := map[string]*shared.GistFile{
"gistfile0.txt": {Content: "sample content"},
}
gistName := guessGistName(files)
assert.Equal(t, "", gistName)
}
func Test_guessGistName_userFiles(t *testing.T) {
files := map[string]*shared.GistFile{
"fig.txt": {Content: "I am a fig"},
"apple.txt": {Content: "I am an apple"},
"gistfile0.txt": {Content: "sample content"},
}
gistName := guessGistName(files)
assert.Equal(t, "apple.txt", gistName)
}
N/A
command: gh gist create
short: Create a new GitHub gist with given contents.
long: |-
Gists can be created from one or multiple files. Alternatively, pass `-` as
filename to read from standard input.
By default, gists are secret; use `--public` to make publicly listed ones.
pname: gh gist
plink: gh_gist.yaml
options:
- option: desc
shorthand: d
N/A
N/A
N/A
command: gh gist create
short: Create a new GitHub gist with given contents.
long: |-
Gists can be created from one or multiple files. Alternatively, pass `-` as
filename to read from standard input.
By default, gists are secret; use `--public` to make publicly listed ones.
pname: gh gist
plink: gh_gist.yaml
options:
- option: desc
shorthand: d
N/A
N/A
N/A
command: gh gist create
short: Create a new GitHub gist with given contents.
long: |-
Gists can be created from one or multiple files. Alternatively, pass `-` as
filename to read from standard input.
By default, gists are secret; use `--public` to make publicly listed ones.
pname: gh gist
plink: gh_gist.yaml
options:
- option: desc
shorthand: d
N/A
N/A
command: gh gist create
short: Create a new GitHub gist with given contents.
long: |-
Gists can be created from one or multiple files. Alternatively, pass `-` as
filename to read from standard input.
By default, gists are secret; use `--public` to make publicly listed ones.
pname: gh gist
plink: gh_gist.yaml
options:
- option: desc
shorthand: d
N/A
N/A
N/A
command: gh gist delete
short: Delete a GitHub gist.
long: |-
To delete a gist interactively, use `gh gist delete` with no arguments.
To delete a gist non-interactively, supply the gist id or url.
pname: gh gist
plink: gh_gist.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
want DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "valid selector",
cli: "123",
tty: true,
want: DeleteOptions{
Selector: "123",
},
},
{
name: "valid selector, no ID supplied",
cli: "",
tty: true,
want: DeleteOptions{
Selector: "",
},
},
{
name: "no ID supplied with --yes",
cli: "--yes",
tty: true,
want: DeleteOptions{
func Test_deleteRun(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
cancel bool
httpStubs func(*httpmock.Registry)
mockPromptGists bool
noGists bool
wantErr bool
wantStdout string
wantStderr string
}{
{
name: "successfully delete",
opts: &DeleteOptions{
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Files: map[string]*shared.GistFile{"cool.txt": {Filename: "cool.txt"}}}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(200, "{}"))
},
wantStdout: "✓ Gist \"cool.txt\" deleted\n",
},
{
name: "successfully delete with prompt",
opts: &DeleteOptions{
Selector: "",
},
func Test_gistDelete(t *testing.T) {
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
hostname string
gistID string
wantErr error
wantErrString string
}{
{
name: "successful delete",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(204, "{}"),
)
},
hostname: "github.com",
gistID: "1234",
},
{
name: "when a gist is not found, it returns a NotFoundError",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(404, "{}"),
)
},
hostname: "github.com",
gistID: "1234",
N/A
command: gh gist delete
short: Delete a GitHub gist.
long: |-
To delete a gist interactively, use `gh gist delete` with no arguments.
To delete a gist non-interactively, supply the gist id or url.
pname: gh gist
plink: gh_gist.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
want DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "valid selector",
cli: "123",
tty: true,
want: DeleteOptions{
Selector: "123",
},
},
{
name: "valid selector, no ID supplied",
cli: "",
tty: true,
want: DeleteOptions{
Selector: "",
},
},
{
name: "no ID supplied with --yes",
cli: "--yes",
tty: true,
want: DeleteOptions{
func Test_deleteRun(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
cancel bool
httpStubs func(*httpmock.Registry)
mockPromptGists bool
noGists bool
wantErr bool
wantStdout string
wantStderr string
}{
{
name: "successfully delete",
opts: &DeleteOptions{
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Files: map[string]*shared.GistFile{"cool.txt": {Filename: "cool.txt"}}}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(200, "{}"))
},
wantStdout: "✓ Gist \"cool.txt\" deleted\n",
},
{
name: "successfully delete with prompt",
opts: &DeleteOptions{
Selector: "",
},
N/A
N/A
command: gh gist edit
short: Edit one of your gists
pname: gh gist
plink: gh_gist.yaml
options:
- option: add
shorthand: a
value_type: string
description: Add a new file to the gist
- option: desc
shorthand: d
value_type: string
func Test_getFilesToAdd(t *testing.T) {
filename := "gist-test.txt"
gf, err := getFilesToAdd(filename, []byte("hello"))
require.NoError(t, err)
assert.Equal(t, map[string]*gistFileToUpdate{
filename: {
NewFilename: filename,
Content: "hello",
},
}, gf)
}
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants EditOptions
wantsErr bool
}{
{
name: "no flags",
cli: "123",
wants: EditOptions{
Selector: "123",
},
},
{
name: "filename",
cli: "123 --filename cool.md",
wants: EditOptions{
Selector: "123",
EditFilename: "cool.md",
},
},
{
name: "add",
cli: "123 --add cool.md",
wants: EditOptions{
Selector: "123",
AddFilename: "cool.md",
},
},
func Test_editRun(t *testing.T) {
fileToAdd := filepath.Join(t.TempDir(), "gist-test.txt")
err := os.WriteFile(fileToAdd, []byte("hello"), 0600)
require.NoError(t, err)
tests := []struct {
name string
opts *EditOptions
mockGist *shared.Gist
mockGistList bool
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.MockPrompter)
isTTY bool
stdin string
wantErr string
wantLastRequestParameters map[string]interface{}
}{
{
name: "no such gist",
wantErr: "gist not found: 1234",
opts: &EditOptions{
Selector: "1234",
},
},
{
name: "one file",
isTTY: false,
opts: &EditOptions{
Selector: "1234",
},
N/A
command: gh gist edit
short: Edit one of your gists
pname: gh gist
plink: gh_gist.yaml
options:
- option: add
shorthand: a
value_type: string
description: Add a new file to the gist
- option: desc
shorthand: d
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants EditOptions
wantsErr bool
}{
{
name: "no flags",
cli: "123",
wants: EditOptions{
Selector: "123",
},
},
{
name: "filename",
cli: "123 --filename cool.md",
wants: EditOptions{
Selector: "123",
EditFilename: "cool.md",
},
},
{
name: "add",
cli: "123 --add cool.md",
wants: EditOptions{
Selector: "123",
AddFilename: "cool.md",
},
},
N/A
command: gh gist edit
short: Edit one of your gists
pname: gh gist
plink: gh_gist.yaml
options:
- option: add
shorthand: a
value_type: string
description: Add a new file to the gist
- option: desc
shorthand: d
value_type: string
N/A
N/A
command: gh gist edit
short: Edit one of your gists
pname: gh gist
plink: gh_gist.yaml
options:
- option: add
shorthand: a
value_type: string
description: Add a new file to the gist
- option: desc
shorthand: d
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants EditOptions
wantsErr bool
}{
{
name: "no flags",
cli: "123",
wants: EditOptions{
Selector: "123",
},
},
{
name: "filename",
cli: "123 --filename cool.md",
wants: EditOptions{
Selector: "123",
EditFilename: "cool.md",
},
},
{
name: "add",
cli: "123 --add cool.md",
wants: EditOptions{
Selector: "123",
AddFilename: "cool.md",
},
},
N/A
command: gh gist edit
short: Edit one of your gists
pname: gh gist
plink: gh_gist.yaml
options:
- option: add
shorthand: a
value_type: string
description: Add a new file to the gist
- option: desc
shorthand: d
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants EditOptions
wantsErr bool
}{
{
name: "no flags",
cli: "123",
wants: EditOptions{
Selector: "123",
},
},
{
name: "filename",
cli: "123 --filename cool.md",
wants: EditOptions{
Selector: "123",
EditFilename: "cool.md",
},
},
{
name: "add",
cli: "123 --add cool.md",
wants: EditOptions{
Selector: "123",
AddFilename: "cool.md",
},
},
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
func Test_listRun(t *testing.T) {
const query = `query GistList\b`
sixHours, _ := time.ParseDuration("6h")
sixHoursAgo := time.Now().Add(-sixHours)
absTime, _ := time.Parse(time.RFC3339, "2020-07-30T15:24:28Z")
tests := []struct {
name string
opts *ListOptions
wantErr bool
wantOut string
stubs func(*httpmock.Registry)
color bool
nontty bool
}{
{
name: "no gists",
opts: &ListOptions{},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(query),
httpmock.StringResponse(`{ "data": { "viewer": {
"gists": { "nodes": [] }
} } }`))
},
wantErr: true,
},
{
name: "default behavior",
opts: &ListOptions{},
func Test_highlightMatch(t *testing.T) {
regex := regexp.MustCompilePOSIX(`[Oo]cto`)
tests := []struct {
name string
input string
cs *iostreams.ColorScheme
want string
}{
{
name: "single match",
input: "Octo",
cs: &iostreams.ColorScheme{},
want: "Octo",
},
{
name: "single match (color)",
input: "Octo",
cs: &iostreams.ColorScheme{
Enabled: true,
},
want: "\x1b[0;30;43mOcto\x1b[0m",
},
{
name: "single match with extra",
input: "Hello, Octocat!",
cs: &iostreams.ColorScheme{},
want: "Hello, Octocat!",
},
{
name: "single match with extra (color)",
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
N/A
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
N/A
N/A
command: gh gist list
short: List gists from your user account.
long: |-
You can use a regular expression to filter the description, file names,
or even the content of files in the gist using `--filter`.
For supported regular expression syntax, see <https://pkg.go.dev/regexp/syntax>.
Use `--include-content` to include content of files, noting that
this will be slower and increase the rate limit used. Instead of printing a table,
code will be printed with highlights similar to `gh search code`:
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: 10,
Visibility: "all",
},
},
{
name: "public",
cli: "--public",
wants: ListOptions{
Limit: 10,
Visibility: "public",
},
},
{
name: "secret",
cli: "--secret",
wants: ListOptions{
Limit: 10,
Visibility: "secret",
},
},
N/A
command: gh gist rename short: Rename a file in the given gist ID / URL. pname: gh gist plink: gh_gist.yaml
func TestNewCmdRename(t *testing.T) {
tests := []struct {
name string
input string
output RenameOptions
errMsg string
tty bool
wantErr bool
}{
{
name: "no arguments",
input: "",
errMsg: "accepts 3 arg(s), received 0",
wantErr: true,
},
{
name: "missing old filename and new filename",
input: "123",
errMsg: "accepts 3 arg(s), received 1",
wantErr: true,
},
{
name: "missing new filename",
input: "123 old.txt",
errMsg: "accepts 3 arg(s), received 2",
wantErr: true,
},
{
name: "rename",
input: "123 old.txt new.txt",
func TestRenameRun(t *testing.T) {
tests := []struct {
name string
opts *RenameOptions
gist *shared.Gist
httpStubs func(*httpmock.Registry)
nontty bool
stdin string
wantOut string
wantParams map[string]interface{}
}{
{
name: "no such gist",
wantOut: "gist not found: 1234",
},
{
name: "rename from old.txt to new.txt",
opts: &RenameOptions{
Selector: "1234",
OldFileName: "old.txt",
NewFileName: "new.txt",
},
gist: &shared.Gist{
ID: "1234",
Files: map[string]*shared.GistFile{
"old.txt": {
Filename: "old.txt",
Type: "text/plain",
},
},
N/A
command: gh gist view
short: View the given gist or select from recent gists.
pname: gh gist
plink: gh_gist.yaml
options:
- option: filename
shorthand: f
value_type: string
description: Display a single file from the gist
- option: files
value_type: bool
default_value: "false"
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
wants ViewOptions
tty bool
}{
{
name: "tty no arguments",
tty: true,
cli: "123",
wants: ViewOptions{
Raw: false,
Selector: "123",
ListFiles: false,
},
},
{
name: "nontty no arguments",
cli: "123",
wants: ViewOptions{
Raw: true,
Selector: "123",
ListFiles: false,
},
},
{
name: "filename passed",
cli: "-fcool.txt 123",
tty: true,
func Test_viewRun(t *testing.T) {
tests := []struct {
name string
opts *ViewOptions
wantOut string
mockGist *shared.Gist
mockGistList bool
isTTY bool
wantErr string
}{
{
name: "no such gist",
isTTY: false,
opts: &ViewOptions{
Selector: "1234",
ListFiles: false,
},
wantErr: "not found",
},
{
name: "one file",
isTTY: true,
opts: &ViewOptions{
Selector: "1234",
ListFiles: false,
},
mockGist: &shared.Gist{
Files: map[string]*shared.GistFile{
"cicada.txt": {
Content: "bwhiizzzbwhuiiizzzz",
N/A
command: gh gist view
short: View the given gist or select from recent gists.
pname: gh gist
plink: gh_gist.yaml
options:
- option: filename
shorthand: f
value_type: string
description: Display a single file from the gist
- option: files
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh gist view
short: View the given gist or select from recent gists.
pname: gh gist
plink: gh_gist.yaml
options:
- option: filename
shorthand: f
value_type: string
description: Display a single file from the gist
- option: files
value_type: bool
default_value: "false"
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
wants ViewOptions
tty bool
}{
{
name: "tty no arguments",
tty: true,
cli: "123",
wants: ViewOptions{
Raw: false,
Selector: "123",
ListFiles: false,
},
},
{
name: "nontty no arguments",
cli: "123",
wants: ViewOptions{
Raw: true,
Selector: "123",
ListFiles: false,
},
},
{
name: "filename passed",
cli: "-fcool.txt 123",
tty: true,
N/A
N/A
command: gh gist view
short: View the given gist or select from recent gists.
pname: gh gist
plink: gh_gist.yaml
options:
- option: filename
shorthand: f
value_type: string
description: Display a single file from the gist
- option: files
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh gist view
short: View the given gist or select from recent gists.
pname: gh gist
plink: gh_gist.yaml
options:
- option: filename
shorthand: f
value_type: string
description: Display a single file from the gist
- option: files
value_type: bool
default_value: "false"
N/A
N/A
N/A
command: gh gpg-key short: Manage GPG keys registered with your GitHub account. pname: gh plink: gh.yaml
N/A
N/A
N/A
command: gh gpg-key add
short: Add a GPG key to your GitHub account
pname: gh gpg-key
plink: gh_gpg-key.yaml
options:
- option: title
shorthand: t
value_type: string
description: Title for the new key
func Test_runAdd(t *testing.T) {
tests := []struct {
name string
stdin string
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
wantErrMsg string
opts AddOptions
}{
{
name: "valid key",
stdin: "-----BEGIN PGP PUBLIC KEY BLOCK-----",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "user/gpg_keys"),
httpmock.RESTPayload(200, ``, func(payload map[string]interface{}) {
assert.Contains(t, payload, "armored_public_key")
assert.NotContains(t, payload, "title")
}))
},
wantStdout: "✓ GPG key added to your account\n",
wantStderr: "",
wantErrMsg: "",
opts: AddOptions{KeyFile: "-"},
},
{
name: "valid key with title",
stdin: "-----BEGIN PGP PUBLIC KEY BLOCK-----",
httpStubs: func(reg *httpmock.Registry) {
N/A
command: gh gpg-key add
short: Add a GPG key to your GitHub account
pname: gh gpg-key
plink: gh_gpg-key.yaml
options:
- option: title
shorthand: t
value_type: string
description: Title for the new key
N/A
N/A
N/A
command: gh gpg-key delete
short: Delete a GPG key from your GitHub account
pname: gh gpg-key
plink: gh_gpg-key.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "tty",
tty: true,
input: "ABC123",
output: DeleteOptions{KeyID: "ABC123", Confirmed: false},
},
{
name: "confirm flag tty",
tty: true,
input: "ABC123 --yes",
output: DeleteOptions{KeyID: "ABC123", Confirmed: true},
},
{
name: "shorthand confirm flag tty",
tty: true,
input: "ABC123 -y",
output: DeleteOptions{KeyID: "ABC123", Confirmed: true},
},
{
name: "no tty",
input: "ABC123",
func Test_deleteRun(t *testing.T) {
keysResp := "[{\"id\":123,\"key_id\":\"ABC123\"}]"
tests := []struct {
name string
tty bool
opts DeleteOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.PrompterMock)
wantStdout string
wantErr bool
wantErrMsg string
}{
{
name: "delete tty",
tty: true,
opts: DeleteOptions{KeyID: "ABC123", Confirmed: false},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return nil
}
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "user/gpg_keys"), httpmock.StatusStringResponse(200, keysResp))
reg.Register(httpmock.REST("DELETE", "user/gpg_keys/123"), httpmock.StatusStringResponse(204, ""))
},
wantStdout: "✓ GPG key ABC123 deleted from your account\n",
},
{
name: "delete with confirm flag tty",
tty: true,
N/A
command: gh gpg-key delete
short: Delete a GPG key from your GitHub account
pname: gh gpg-key
plink: gh_gpg-key.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "tty",
tty: true,
input: "ABC123",
output: DeleteOptions{KeyID: "ABC123", Confirmed: false},
},
{
name: "confirm flag tty",
tty: true,
input: "ABC123 --yes",
output: DeleteOptions{KeyID: "ABC123", Confirmed: true},
},
{
name: "shorthand confirm flag tty",
tty: true,
input: "ABC123 -y",
output: DeleteOptions{KeyID: "ABC123", Confirmed: true},
},
{
name: "no tty",
input: "ABC123",
N/A
N/A
command: gh gpg-key list short: Lists GPG keys in your GitHub account pname: gh gpg-key plink: gh_gpg-key.yaml
func Test_listRun(t *testing.T) {
tests := []struct {
name string
opts ListOptions
isTTY bool
wantStdout string
wantStderr string
wantErr bool
}{
{
name: "list tty",
opts: ListOptions{HTTPClient: func() (*http.Client, error) {
createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
expiresAt, _ := time.Parse(time.RFC3339, "2099-01-01T15:44:24+01:00")
noExpires := time.Time{}
reg := &httpmock.Registry{}
reg.Register(
httpmock.REST("GET", "user/gpg_keys"),
httpmock.StringResponse(fmt.Sprintf(`[
{
"id": 1234,
"key_id": "ABCDEF1234567890",
"public_key": "xJMEWfoofoofoo",
"emails": [{"email": "johnny@test.com"}],
"created_at": "%[1]s",
"expires_at": "%[2]s"
},
{
"id": 5678,
"key_id": "1234567890ABCDEF",
N/A
command: gh issue
short: Work with GitHub issues.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
command: gh issue
short: Work with GitHub issues.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh issue close
short: Close issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: duplicate-of
value_type: string
description: Mark as duplicate of another issue by number or URL
func TestNewCmdClose(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdClose)
tests := []struct {
name string
input string
output CloseOptions
expectedBaseRepo ghrepo.Interface
wantErr bool
errMsg string
}{
{
name: "comment",
input: "123 --comment 'closing comment'",
output: CloseOptions{
IssueNumber: 123,
Comment: "closing comment",
},
},
{
name: "reason",
input: "123 --reason 'not planned'",
output: CloseOptions{
IssueNumber: 123,
Reason: "not planned",
},
},
{
name: "reason duplicate",
func TestCloseRun(t *testing.T) {
tests := []struct {
name string
opts *CloseOptions
httpStubs func(*httpmock.Registry)
wantStderr string
wantErr bool
errMsg string
}{
{
name: "close issue by number",
opts: &CloseOptions{
IssueNumber: 13,
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
} } }`),
)
reg.Register(
httpmock.GraphQL(`mutation IssueClose\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, "THE-ID", inputs["issueId"])
}),
)
N/A
command: gh issue close
short: Close issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: duplicate-of
value_type: string
description: Mark as duplicate of another issue by number or URL
func TestNewCmdClose(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdClose)
tests := []struct {
name string
input string
output CloseOptions
expectedBaseRepo ghrepo.Interface
wantErr bool
errMsg string
}{
{
name: "comment",
input: "123 --comment 'closing comment'",
output: CloseOptions{
IssueNumber: 123,
Comment: "closing comment",
},
},
{
name: "reason",
input: "123 --reason 'not planned'",
output: CloseOptions{
IssueNumber: 123,
Reason: "not planned",
},
},
{
name: "reason duplicate",
N/A
command: gh issue close
short: Close issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: duplicate-of
value_type: string
description: Mark as duplicate of another issue by number or URL
func TestNewCmdClose(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdClose)
tests := []struct {
name string
input string
output CloseOptions
expectedBaseRepo ghrepo.Interface
wantErr bool
errMsg string
}{
{
name: "comment",
input: "123 --comment 'closing comment'",
output: CloseOptions{
IssueNumber: 123,
Comment: "closing comment",
},
},
{
name: "reason",
input: "123 --reason 'not planned'",
output: CloseOptions{
IssueNumber: 123,
Reason: "not planned",
},
},
{
name: "reason duplicate",
func TestCloseRun(t *testing.T) {
tests := []struct {
name string
opts *CloseOptions
httpStubs func(*httpmock.Registry)
wantStderr string
wantErr bool
errMsg string
}{
{
name: "close issue by number",
opts: &CloseOptions{
IssueNumber: 13,
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
} } }`),
)
reg.Register(
httpmock.GraphQL(`mutation IssueClose\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, "THE-ID", inputs["issueId"])
}),
)
N/A
command: gh issue close
short: Close issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: duplicate-of
value_type: string
description: Mark as duplicate of another issue by number or URL
func TestNewCmdClose(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdClose)
tests := []struct {
name string
input string
output CloseOptions
expectedBaseRepo ghrepo.Interface
wantErr bool
errMsg string
}{
{
name: "comment",
input: "123 --comment 'closing comment'",
output: CloseOptions{
IssueNumber: 123,
Comment: "closing comment",
},
},
{
name: "reason",
input: "123 --reason 'not planned'",
output: CloseOptions{
IssueNumber: 123,
Reason: "not planned",
},
},
{
name: "reason duplicate",
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
func Test_commentRun(t *testing.T) {
tests := []struct {
name string
input *shared.CommentableOptions
emptyComments bool
comments api.Comments
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
wantsErr bool
}{
{
name: "creating new comment with interactive editor succeeds",
input: &shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
InteractiveEditSurvey: func(string) (string, error) { return "comment body", nil },
ConfirmSubmitSurvey: func() (bool, error) { return true, nil },
},
emptyComments: true,
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
},
{
name: "updating last comment with interactive editor fails if there are no comments and decline prompt to create",
input: &shared.CommentableOptions{
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
func Test_commentRun(t *testing.T) {
tests := []struct {
name string
input *shared.CommentableOptions
emptyComments bool
comments api.Comments
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
wantsErr bool
}{
{
name: "creating new comment with interactive editor succeeds",
input: &shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
InteractiveEditSurvey: func(string) (string, error) { return "comment body", nil },
ConfirmSubmitSurvey: func() (bool, error) { return true, nil },
},
emptyComments: true,
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/issues/123#issuecomment-456\n",
},
{
name: "updating last comment with interactive editor fails if there are no comments and decline prompt to create",
input: &shared.CommentableOptions{
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue comment
short: Add a comment to a GitHub issue.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh issue
plink: gh_issue.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
},
{
name: "issue number",
input: "1",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "-t mytitle",
wantsErr: true,
},
{
name: "empty tty",
tty: true,
cli: "",
func Test_createRun(t *testing.T) {
tests := []struct {
name string
opts CreateOptions
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.PrompterMock)
wantsStdout string
wantsStderr string
wantsBrowse string
wantsErr string
}{
{
name: "no args",
opts: CreateOptions{
Detector: &fd.EnabledDetectorMock{},
WebMode: true,
},
wantsBrowse: "https://github.com/OWNER/REPO/issues/new",
wantsStderr: "Opening https://github.com/OWNER/REPO/issues/new in your browser.\n",
},
{
name: "title and body",
opts: CreateOptions{
Detector: &fd.EnabledDetectorMock{},
WebMode: true,
Title: "myissue",
Body: "hello cli",
},
wantsBrowse: "https://github.com/OWNER/REPO/issues/new?body=hello+cli&title=myissue",
wantsStderr: "Opening https://github.com/OWNER/REPO/issues/new in your browser.\n",
func TestIssueCreate(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"id": "REPOID",
"hasIssuesEnabled": true
} } }`),
)
http.Register(
httpmock.GraphQL(`mutation IssueCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "createIssue": { "issue": {
"URL": "https://github.com/OWNER/REPO/issues/12"
} } } }`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["repositoryId"], "REPOID")
assert.Equal(t, inputs["title"], "hello")
assert.Equal(t, inputs["body"], "cash rules everything around me")
}),
)
output, err := runCommand(http, true, `-t hello -b "cash rules everything around me"`, nil)
if err != nil {
t.Errorf("error running command `issue create`: %v", err)
}
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "-t mytitle",
wantsErr: true,
},
{
name: "empty tty",
tty: true,
cli: "",
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
func TestIssueCreate_recover(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"id": "REPOID",
"hasIssuesEnabled": true
} } }`))
// Should only be one fetch of metadata.
http.Register(
httpmock.GraphQL(`query RepositoryLabelList\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "labels": {
"nodes": [
{ "name": "TODO", "id": "TODOID" },
{ "name": "bug", "id": "BUGID" }
],
"pageInfo": { "hasNextPage": false }
} } } }
`))
http.Register(
httpmock.GraphQL(`mutation IssueCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "createIssue": { "issue": {
"URL": "https://github.com/OWNER/REPO/issues/12"
} } } }
`, func(inputs map[string]interface{}) {
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
N/A
command: gh issue create
short: Create an issue on GitHub.
long: |-
Adding an issue to projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--assignee` flag supports the following special values:
- `@me`: assign yourself
- `@copilot`: assign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "-t mytitle",
wantsErr: true,
},
{
name: "empty tty",
tty: true,
cli: "",
N/A
N/A
command: gh issue delete
short: Delete issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
func TestNewCmdDelete(t *testing.T) {
argparsetest.TestArgParsing(t, NewCmdDelete)
}
func TestIssueDelete(t *testing.T) {
httpRegistry := &httpmock.Registry{}
defer httpRegistry.Verify(t)
httpRegistry.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
} } }`),
)
httpRegistry.Register(
httpmock.GraphQL(`mutation IssueDelete\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "THE-ID")
}),
)
pm := prompter.NewMockPrompter(t)
pm.RegisterConfirmDeletion("13", func(_ string) error { return nil })
output, err := runCommand(httpRegistry, pm, true, "13")
if err != nil {
t.Fatalf("error running command `issue delete`: %v", err)
}
r := regexp.MustCompile(`Deleted issue OWNER/REPO#13 \(The title of the issue\)`)
func TestIssueDelete_confirm(t *testing.T) {
httpRegistry := &httpmock.Registry{}
defer httpRegistry.Verify(t)
httpRegistry.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 13, "title": "The title of the issue"}
} } }`),
)
httpRegistry.Register(
httpmock.GraphQL(`mutation IssueDelete\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "THE-ID")
}),
)
output, err := runCommand(httpRegistry, nil, true, "13 --confirm")
if err != nil {
t.Fatalf("error running command `issue delete`: %v", err)
}
r := regexp.MustCompile(`Deleted issue OWNER/REPO#13 \(The title of the issue\)`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
N/A
command: gh issue delete
short: Delete issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
N/A
N/A
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
func TestDevelopRun(t *testing.T) {
featureEnabledPayload := `{"data":{"LinkedBranch":{"fields":[{"name":"id"},{"name":"ref"}]}}}`
featureDisabledPayload := `{"data":{"LinkedBranch":null}}`
tests := []struct {
name string
opts *DevelopOptions
cmdStubs func(*run.CommandStubber)
runStubs func(*run.CommandStubber)
remotes map[string]string
httpStubs func(*httpmock.Registry, *testing.T)
expectedOut string
expectedErrOut string
wantErr string
tty bool
}{
{
name: "returns an error when the feature is not supported by the API",
opts: &DevelopOptions{
IssueNumber: 42,
List: true,
},
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`{"data":{"repository":{"hasIssuesEnabled":true,"issue":{"id":"SOMEID","number":42}}}}`),
)
reg.Register(
httpmock.GraphQL(`query LinkedBranchFeature\b`),
httpmock.StringResponse(featureDisabledPayload),
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
N/A
command: gh issue develop
short: Manage linked branches for an issue.
long: |-
When using the `--base` flag, the new development branch will be created from the specified
remote branch. The new branch will be configured as the base branch for pull requests created using
`gh pr create`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: base
shorthand: b
value_type: string
func TestNewCmdDevelop(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdDevelop)
tests := []struct {
name string
input string
output DevelopOptions
expectedBaseRepo ghrepo.Interface
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "branch-repo flag",
input: "1 --branch-repo owner/repo",
output: DevelopOptions{
IssueNumber: 1,
BranchRepo: "owner/repo",
},
},
{
name: "base flag",
input: "1 --base feature",
output: DevelopOptions{
IssueNumber: 1,
BaseBranch: "feature",
},
},
N/A
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
func Test_editRun(t *testing.T) {
tests := []struct {
name string
input *EditOptions
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
wantErr bool
}{
{
name: "non-interactive",
input: &EditOptions{
Detector: &fd.EnabledDetectorMock{},
IssueNumbers: []int{123},
Interactive: false,
Editable: prShared.Editable{
Title: prShared.EditableString{
Value: "new title",
Edited: true,
},
Body: prShared.EditableString{
Value: "new body",
Edited: true,
},
Assignees: prShared.EditableAssignees{
EditableSlice: prShared.EditableSlice{
Add: []string{"monalisa", "hubot"},
Remove: []string{"octocat"},
Edited: true,
},
func TestApiActorsSupported(t *testing.T) {
t.Run("when actors are assignable, query includes assignedActors", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`assignedActors`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we don't care.
_ = editRun(&EditOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
IssueNumbers: []int{123},
Editable: prShared.Editable{
Assignees: prShared.EditableAssignees{
EditableSlice: prShared.EditableSlice{
Add: []string{"monalisa", "octocat"},
Edited: true,
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue edit
short: Edit one or more issues within the same repository.
long: |-
Editing issues' projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
- `@copilot`: assign or unassign Copilot (not supported on GitHub Enterprise Server)
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{},
wantsErr: true,
},
{
name: "issue number argument",
input: "23",
output: EditOptions{
IssueNumbers: []int{23},
Interactive: true,
},
wantsErr: false,
},
{
name: "title flag",
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestIssueList_nontty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.FileResponse("./fixtures/issueList.json"))
output, err := runCommand(http, false, "")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
assert.Equal(t, "", output.Stderr())
//nolint:staticcheck // prefer exact matchers over ExpectLines
test.ExpectLines(t, output.String(),
`1[\t]+number won[\t]+label[\t]+\d+`,
`2[\t]+number too[\t]+label[\t]+\d+`,
`4[\t]+number fore[\t]+label[\t]+\d+`)
}
func TestIssueList_tty(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.FileResponse("./fixtures/issueList.json"))
output, err := runCommand(http, true, "")
if err != nil {
t.Errorf("error running command `issue list`: %v", err)
}
assert.Equal(t, heredoc.Doc(`
Showing 3 of 3 open issues in OWNER/REPO
ID TITLE LABELS UPDATED
#1 number won label about 1 day ago
#2 number too label about 1 month ago
#4 number fore label about 2 years ago
`), output.String())
assert.Equal(t, ``, output.Stderr())
}
func TestIssueList_tty_withFlags(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "probablyCher", params["assignee"].(string))
assert.Equal(t, "foo", params["author"].(string))
assert.Equal(t, "me", params["mention"].(string))
assert.Equal(t, []interface{}{"OPEN"}, params["states"].([]interface{}))
}))
output, err := runCommand(http, true, "-a probablyCher -s open -A foo --mention me")
assert.EqualError(t, err, "no issues match your search in OWNER/REPO")
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestIssueList_tty_withAppFlag(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "app/dependabot", params["author"].(string))
}))
output, err := runCommand(http, true, "--app dependabot")
assert.EqualError(t, err, "no issues match your search in OWNER/REPO")
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestIssueList_withInvalidLimitFlag(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
_, err := runCommand(http, true, "--limit=0")
if err == nil || err.Error() != "invalid limit: 0" {
t.Errorf("error running command `issue list`: %v", err)
}
}
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestIssueList_tty_withFlags(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issues": { "nodes": [] }
} } }`, func(_ string, params map[string]interface{}) {
assert.Equal(t, "probablyCher", params["assignee"].(string))
assert.Equal(t, "foo", params["author"].(string))
assert.Equal(t, "me", params["mention"].(string))
assert.Equal(t, []interface{}{"OPEN"}, params["states"].([]interface{}))
}))
output, err := runCommand(http, true, "-a probablyCher -s open -A foo --mention me")
assert.EqualError(t, err, "no issues match your search in OWNER/REPO")
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh issue list
short: List issues in a GitHub repository. By default, this only lists open issues.
pname: gh issue
plink: gh_issue.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh issue lock
short: Lock issue conversation
pname: gh issue
plink: gh_issue.yaml
options:
- option: reason
shorthand: r
value_type: string
description: Optional reason for locking conversation (off_topic, resolved, spam, too_heated).
func Test_NewCmdLock(t *testing.T) {
cases := []struct {
name string
args string
want LockOptions
wantErr string
tty bool
}{
{
name: "sets reason",
args: "--reason off_topic 451",
want: LockOptions{
Reason: "off_topic",
IssueNumber: 451,
},
},
{
name: "no args",
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "no flags",
args: "451",
want: LockOptions{
IssueNumber: 451,
},
},
{
name: "issue number argument",
args: "451 --repo owner/repo",
func Test_NewCmdUnlock(t *testing.T) {
cases := []struct {
name string
args string
want LockOptions
wantErr string
tty bool
}{
{
name: "no args",
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "no flags",
args: "451",
want: LockOptions{
IssueNumber: 451,
},
},
{
name: "issue number argument",
args: "451 --repo owner/repo",
want: LockOptions{
IssueNumber: 451,
},
},
{
name: "argument is hash prefixed number",
// Escaping is required here to avoid what I think is shellex treating it as a comment.
args: "\\#451 --repo owner/repo",
func Test_runLock(t *testing.T) {
cases := []struct {
name string
opts LockOptions
promptStubs func(*testing.T, *prompter.PrompterMock)
httpStubs func(*testing.T, *httpmock.Registry)
wantOut string
wantErrOut string
wantErr string
tty bool
state string
}{
{
name: "lock issue nontty",
state: Lock,
opts: LockOptions{
IssueNumber: 451,
ParentCmd: "issue",
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 451,
"__typename": "Issue" }}}}`))
reg.Register(
httpmock.GraphQL(`mutation LockLockable\b`),
httpmock.StringResponse(`
{ "data": {
N/A
command: gh issue lock
short: Lock issue conversation
pname: gh issue
plink: gh_issue.yaml
options:
- option: reason
shorthand: r
value_type: string
description: Optional reason for locking conversation (off_topic, resolved, spam, too_heated).
func Test_NewCmdLock(t *testing.T) {
cases := []struct {
name string
args string
want LockOptions
wantErr string
tty bool
}{
{
name: "sets reason",
args: "--reason off_topic 451",
want: LockOptions{
Reason: "off_topic",
IssueNumber: 451,
},
},
{
name: "no args",
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "no flags",
args: "451",
want: LockOptions{
IssueNumber: 451,
},
},
{
name: "issue number argument",
args: "451 --repo owner/repo",
N/A
N/A
command: gh issue pin short: Pin an issue to a repository. long: The issue can be specified by issue number or URL. pname: gh issue plink: gh_issue.yaml
func TestNewCmdPin(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdPin)
}
func TestPinRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *PinOptions
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
}{
{
name: "pin issue",
tty: true,
opts: &PinOptions{IssueNumber: 20},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"issue": { "id": "ISSUE-ID", "number": 20, "title": "Issue Title", "isPinned": false}
} } }`),
)
reg.Register(
httpmock.GraphQL(`mutation IssuePin\b`),
httpmock.GraphQLMutation(`{"id": "ISSUE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "ISSUE-ID")
},
),
)
},
N/A
command: gh issue reopen
short: Reopen issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Add a reopening comment
func TestNewCmdReopen(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdReopen)
}
func TestIssueReopen(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 2, "state": "CLOSED", "title": "The title of the issue"}
} } }`),
)
http.Register(
httpmock.GraphQL(`mutation IssueReopen\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "2")
if err != nil {
t.Fatalf("error running command `issue reopen`: %v", err)
}
r := regexp.MustCompile(`Reopened issue OWNER/REPO#2 \(The title of the issue\)`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
func TestIssueReopen_alreadyOpen(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "number": 2, "state": "OPEN", "title": "The title of the issue"}
} } }`),
)
output, err := runCommand(http, true, "2")
if err != nil {
t.Fatalf("error running command `issue reopen`: %v", err)
}
r := regexp.MustCompile(`Issue OWNER/REPO#2 \(The title of the issue\) is already open`)
if !r.MatchString(output.Stderr()) {
t.Fatalf("output did not match regexp /%s/\n> output\n%q\n", r, output.Stderr())
}
}
N/A
command: gh issue reopen
short: Reopen issue
pname: gh issue
plink: gh_issue.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Add a reopening comment
func TestIssueReopen_withComment(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 2, "state": "CLOSED", "title": "The title of the issue"}
} } }`),
)
http.Register(
httpmock.GraphQL(`mutation CommentCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "addComment": { "commentEdge": { "node": {
"url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
} } } } }`,
func(inputs map[string]interface{}) {
assert.Equal(t, "THE-ID", inputs["subjectId"])
assert.Equal(t, "reopening comment", inputs["body"])
}),
)
http.Register(
httpmock.GraphQL(`mutation IssueReopen\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "THE-ID")
}),
)
N/A
N/A
command: gh issue status
short: Show status of relevant issues
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestIssueStatus(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`))
http.Register(
httpmock.GraphQL(`query IssueStatus\b`),
httpmock.FileResponse("./fixtures/issueStatus.json"))
output, err := runCommand(http, true, "")
if err != nil {
t.Errorf("error running command `issue status`: %v", err)
}
expectedIssues := []*regexp.Regexp{
regexp.MustCompile(`(?m)8.*carrots.*about.*ago`),
regexp.MustCompile(`(?m)9.*squash.*about.*ago`),
regexp.MustCompile(`(?m)10.*broccoli.*about.*ago`),
regexp.MustCompile(`(?m)11.*swiss chard.*about.*ago`),
}
for _, r := range expectedIssues {
if !r.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/\n> output\n%s\n", r, output)
return
}
}
}
func TestIssueStatus_blankSlate(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`))
http.Register(
httpmock.GraphQL(`query IssueStatus\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"assigned": { "nodes": [] },
"mentioned": { "nodes": [] },
"authored": { "nodes": [] }
} } }`))
output, err := runCommand(http, true, "")
if err != nil {
t.Errorf("error running command `issue status`: %v", err)
}
expectedOutput := `
Relevant issues in OWNER/REPO
Issues assigned to you
There are no issues assigned to you
Issues mentioning you
There are no issues mentioning you
func TestIssueStatus_disabledIssues(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`))
http.Register(
httpmock.GraphQL(`query IssueStatus\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": false
} } }`))
_, err := runCommand(http, true, "")
if err == nil || err.Error() != "the 'OWNER/REPO' repository has disabled issues" {
t.Errorf("error running command `issue status`: %v", err)
}
}
N/A
command: gh issue status
short: Show status of relevant issues
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh issue status
short: Show status of relevant issues
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh issue status
short: Show status of relevant issues
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh issue transfer short: Transfer issue to another repository pname: gh issue plink: gh_issue.yaml
func TestNewCmdTransfer(t *testing.T) {
tests := []struct {
name string
cli string
wants TransferOptions
wantBaseRepo ghrepo.Interface
wantErr bool
}{
{
name: "no argument",
cli: "",
wantErr: true,
},
{
name: "issue number argument",
cli: "--repo cli/repo 23 OWNER/REPO",
wants: TransferOptions{
IssueNumber: 23,
DestRepoSelector: "OWNER/REPO",
},
wantBaseRepo: ghrepo.New("cli", "repo"),
},
{
name: "argument is hash prefixed number",
// Escaping is required here to avoid what I think is shellex treating it as a comment.
cli: "--repo cli/repo \\#23 OWNER/REPO",
wants: TransferOptions{
IssueNumber: 23,
DestRepoSelector: "OWNER/REPO",
},
func Test_transferRun_noflags(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
output, err := runCommand(http, "")
if err != nil {
assert.Equal(t, "issue and destination repository are required", err.Error())
}
assert.Equal(t, "", output.String())
}
func Test_transferRunSuccessfulIssueTransfer(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"hasIssuesEnabled": true,
"issue": { "id": "THE-ID", "number": 1234, "title": "The title of the issue"}
} } }`))
http.Register(
httpmock.GraphQL(`query IssueRepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"id": "dest-id",
"name": "REPO1",
"owner": { "login": "OWNER1" },
"viewerPermission": "WRITE",
"hasIssuesEnabled": true
}}}`))
http.Register(
httpmock.GraphQL(`mutation IssueTransfer\b`),
httpmock.GraphQLMutation(`{"data":{"transferIssue":{"issue":{"url":"https://github.com/OWNER1/REPO1/issues/1"}}}}`, func(input map[string]interface{}) {
assert.Equal(t, input["issueId"], "THE-ID")
assert.Equal(t, input["repositoryId"], "dest-id")
}))
N/A
command: gh issue unlock short: Unlock issue conversation pname: gh issue plink: gh_issue.yaml
N/A
N/A
command: gh issue unpin short: Unpin an issue from a repository. long: The issue can be specified by issue number or URL. pname: gh issue plink: gh_issue.yaml
func TestNewCmdUnpin(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdUnpin)
}
func TestUnpinRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *UnpinOptions
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
}{
{
name: "unpin issue",
tty: true,
opts: &UnpinOptions{IssueNumber: 20},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"issue": { "id": "ISSUE-ID", "number": 20, "title": "Issue Title", "isPinned": true}
} } }`),
)
reg.Register(
httpmock.GraphQL(`mutation IssueUnpin\b`),
httpmock.GraphQLMutation(`{"id": "ISSUE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["issueId"], "ISSUE-ID")
},
),
)
},
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
"assignees",
"author",
"body",
"closed",
"comments",
"closedByPullRequestsReferences",
"createdAt",
"closedAt",
"id",
"labels",
"milestone",
"number",
"projectCards",
"projectItems",
"reactionGroups",
"state",
"title",
"updatedAt",
"url",
"isPinned",
"stateReason",
})
}
func TestNewCmdView(t *testing.T) {
// Test shared parsing of issue number / URL.
argparsetest.TestArgParsing(t, NewCmdView)
}
func TestIssueView_web(t *testing.T) {
ios, _, stdout, stderr := iostreams.Test()
ios.SetStdoutTTY(true)
ios.SetStderrTTY(true)
browser := &browser.Stub{}
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query IssueByNumber\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "hasIssuesEnabled": true, "issue": {
"number": 123,
"url": "https://github.com/OWNER/REPO/issues/123"
} } } }
`))
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
err := viewRun(&ViewOptions{
IO: ios,
Browser: browser,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
func TestIssueView_tty_Comments(t *testing.T) {
tests := map[string]struct {
cli string
httpStubs func(*httpmock.Registry)
expectedOutputs []string
wantsErr bool
}{
"without comments flag": {
cli: "123",
httpStubs: func(r *httpmock.Registry) {
r.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse("./fixtures/issueView_previewSingleComment.json"))
mockEmptyV2ProjectItems(t, r)
},
expectedOutputs: []string{
`some title OWNER/REPO#123`,
`some body`,
`———————— Not showing 5 comments ————————`,
`marseilles \(Collaborator\) • Jan 1, 2020 • Newest comment`,
`Comment 5`,
`Use --comments to view the full conversation`,
`View this issue on GitHub: https://github.com/OWNER/REPO/issues/123`,
},
},
"with comments flag": {
cli: "123 --comments",
httpStubs: func(r *httpmock.Registry) {
r.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse("./fixtures/issueView_previewSingleComment.json"))
r.Register(httpmock.GraphQL(`query CommentsForIssue\b`), httpmock.FileResponse("./fixtures/issueView_previewFullComments.json"))
mockEmptyV2ProjectItems(t, r)
},
func TestIssueView_nontty_Comments(t *testing.T) {
tests := map[string]struct {
cli string
httpStubs func(*httpmock.Registry)
expectedOutputs []string
wantsErr bool
}{
"without comments flag": {
cli: "123",
httpStubs: func(r *httpmock.Registry) {
r.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse("./fixtures/issueView_previewSingleComment.json"))
mockEmptyV2ProjectItems(t, r)
},
expectedOutputs: []string{
`title:\tsome title`,
`state:\tOPEN`,
`author:\tmarseilles`,
`comments:\t6`,
`number:\t123`,
`some body`,
},
},
"with comments flag": {
cli: "123 --comments",
httpStubs: func(r *httpmock.Registry) {
r.Register(httpmock.GraphQL(`query IssueByNumber\b`), httpmock.FileResponse("./fixtures/issueView_previewSingleComment.json"))
r.Register(httpmock.GraphQL(`query CommentsForIssue\b`), httpmock.FileResponse("./fixtures/issueView_previewFullComments.json"))
mockEmptyV2ProjectItems(t, r)
},
expectedOutputs: []string{
N/A
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
N/A
N/A
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
N/A
N/A
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
N/A
N/A
N/A
command: gh issue view
short: Display the title, body, and other information about an issue.
long: |-
With `--web` flag, open the issue in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh issue
plink: gh_issue.yaml
options:
- option: comments
shorthand: c
value_type: bool
N/A
N/A
N/A
command: gh label
short: Work with GitHub labels.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh label
short: Work with GitHub labels.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh label clone
short: Clones labels from a source repository to a destination repository on GitHub.
long: |-
By default, the destination repository is the current repository.
All labels from the source repository will be copied to the destination
repository. Labels in the destination repository that are not in the source
repository will not be deleted or modified.
Labels from the source repository that already exist in the destination
repository will be skipped. You can overwrite existing labels in the
destination repository using the `--force` flag.
func TestNewCmdClone(t *testing.T) {
tests := []struct {
name string
input string
output cloneOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot clone labels: source-repository argument required",
},
{
name: "source-repository argument",
input: "OWNER/REPO",
output: cloneOptions{
SourceRepo: ghrepo.New("OWNER", "REPO"),
},
},
{
name: "force flag",
input: "OWNER/REPO --force",
output: cloneOptions{
SourceRepo: ghrepo.New("OWNER", "REPO"),
Force: true,
},
},
}
func TestCloneRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *cloneOptions
httpStubs func(*httpmock.Registry)
wantStdout string
wantErr bool
wantErrMsg string
}{
{
name: "clones all labels",
tty: true,
opts: &cloneOptions{SourceRepo: ghrepo.New("cli", "cli")},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query LabelList\b`),
httpmock.GraphQLQuery(`
{
"data": {
"repository": {
"labels": {
"totalCount": 2,
"nodes": [
{
"name": "bug",
"color": "d73a4a",
"description": "Something isn't working"
},
{
N/A
command: gh label clone
short: Clones labels from a source repository to a destination repository on GitHub.
long: |-
By default, the destination repository is the current repository.
All labels from the source repository will be copied to the destination
repository. Labels in the destination repository that are not in the source
repository will not be deleted or modified.
Labels from the source repository that already exist in the destination
repository will be skipped. You can overwrite existing labels in the
destination repository using the `--force` flag.
func TestNewCmdClone(t *testing.T) {
tests := []struct {
name string
input string
output cloneOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot clone labels: source-repository argument required",
},
{
name: "source-repository argument",
input: "OWNER/REPO",
output: cloneOptions{
SourceRepo: ghrepo.New("OWNER", "REPO"),
},
},
{
name: "force flag",
input: "OWNER/REPO --force",
output: cloneOptions{
SourceRepo: ghrepo.New("OWNER", "REPO"),
Force: true,
},
},
}
N/A
command: gh label create
short: Create a new label on GitHub, or update an existing one with `--force`.
long: |-
Must specify name for the label. The description and color are optional.
If a color isn't provided, a random one will be chosen.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot create label: name argument required",
},
{
name: "name argument",
input: "test",
output: createOptions{Name: "test"},
},
{
name: "description flag",
input: "test --description 'some description'",
output: createOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: createOptions{Name: "test", Color: "FFFFFF"},
},
{
func TestCreateRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *createOptions
httpStubs func(*httpmock.Registry)
wantStdout string
}{
{
name: "creates label",
tty: true,
opts: &createOptions{Name: "test", Description: "some description"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/labels"),
httpmock.StatusStringResponse(201, "{}"),
)
},
wantStdout: "✓ Label \"test\" created in OWNER/REPO\n",
},
{
name: "creates label notty",
tty: false,
opts: &createOptions{Name: "test", Description: "some description"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/labels"),
httpmock.StatusStringResponse(201, "{}"),
)
},
N/A
command: gh label create
short: Create a new label on GitHub, or update an existing one with `--force`.
long: |-
Must specify name for the label. The description and color are optional.
If a color isn't provided, a random one will be chosen.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot create label: name argument required",
},
{
name: "name argument",
input: "test",
output: createOptions{Name: "test"},
},
{
name: "description flag",
input: "test --description 'some description'",
output: createOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: createOptions{Name: "test", Color: "FFFFFF"},
},
{
N/A
command: gh label create
short: Create a new label on GitHub, or update an existing one with `--force`.
long: |-
Must specify name for the label. The description and color are optional.
If a color isn't provided, a random one will be chosen.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot create label: name argument required",
},
{
name: "name argument",
input: "test",
output: createOptions{Name: "test"},
},
{
name: "description flag",
input: "test --description 'some description'",
output: createOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: createOptions{Name: "test", Color: "FFFFFF"},
},
{
N/A
command: gh label create
short: Create a new label on GitHub, or update an existing one with `--force`.
long: |-
Must specify name for the label. The description and color are optional.
If a color isn't provided, a random one will be chosen.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
N/A
N/A
N/A
command: gh label delete
short: Delete a label from a repository
pname: gh label
plink: gh_label.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output deleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
wantErrMsg: "cannot delete label: name argument required",
},
{
name: "name argument",
tty: true,
input: "test",
output: deleteOptions{Name: "test"},
},
{
name: "confirm argument",
input: "test --yes",
output: deleteOptions{Name: "test", Confirmed: true},
},
{
name: "confirm no tty",
input: "test",
wantErr: true,
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *deleteOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.PrompterMock)
wantStdout string
wantErr bool
errMsg string
}{
{
name: "deletes label",
tty: true,
opts: &deleteOptions{Name: "test"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/labels/test"),
httpmock.StatusStringResponse(204, "{}"),
)
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return nil
}
},
wantStdout: "✓ Label \"test\" deleted from OWNER/REPO\n",
},
{
name: "deletes label notty",
N/A
command: gh label delete
short: Delete a label from a repository
pname: gh label
plink: gh_label.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output deleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
wantErrMsg: "cannot delete label: name argument required",
},
{
name: "name argument",
tty: true,
input: "test",
output: deleteOptions{Name: "test"},
},
{
name: "confirm argument",
input: "test --yes",
output: deleteOptions{Name: "test", Confirmed: true},
},
{
name: "confirm no tty",
input: "test",
wantErr: true,
N/A
N/A
command: gh label edit
short: Update a label on GitHub.
long: |-
A label can be renamed using the `--name` flag.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
input string
output editOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot update label: name argument required",
},
{
name: "name argument",
input: "test",
wantErr: true,
errMsg: "specify at least one of `--color`, `--description`, or `--name`",
},
{
name: "description flag",
input: "test --description 'some description'",
output: editOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: editOptions{Name: "test", Color: "FFFFFF"},
},
func TestEditRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *editOptions
httpStubs func(*httpmock.Registry)
wantStdout string
wantErrMsg string
}{
{
name: "updates label",
tty: true,
opts: &editOptions{Name: "test", Description: "some description"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("PATCH", "repos/OWNER/REPO/labels/test"),
httpmock.StatusStringResponse(201, "{}"),
)
},
wantStdout: "✓ Label \"test\" updated in OWNER/REPO\n",
},
{
name: "updates label notty",
tty: false,
opts: &editOptions{Name: "test", Description: "some description"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("PATCH", "repos/OWNER/REPO/labels/test"),
httpmock.StatusStringResponse(201, "{}"),
)
N/A
command: gh label edit
short: Update a label on GitHub.
long: |-
A label can be renamed using the `--name` flag.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
input string
output editOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot update label: name argument required",
},
{
name: "name argument",
input: "test",
wantErr: true,
errMsg: "specify at least one of `--color`, `--description`, or `--name`",
},
{
name: "description flag",
input: "test --description 'some description'",
output: editOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: editOptions{Name: "test", Color: "FFFFFF"},
},
N/A
command: gh label edit
short: Update a label on GitHub.
long: |-
A label can be renamed using the `--name` flag.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
input string
output editOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot update label: name argument required",
},
{
name: "name argument",
input: "test",
wantErr: true,
errMsg: "specify at least one of `--color`, `--description`, or `--name`",
},
{
name: "description flag",
input: "test --description 'some description'",
output: editOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: editOptions{Name: "test", Color: "FFFFFF"},
},
N/A
command: gh label edit
short: Update a label on GitHub.
long: |-
A label can be renamed using the `--name` flag.
The label color needs to be 6 character hex value.
pname: gh label
plink: gh_label.yaml
options:
- option: color
shorthand: c
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
input string
output editOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "cannot update label: name argument required",
},
{
name: "name argument",
input: "test",
wantErr: true,
errMsg: "specify at least one of `--color`, `--description`, or `--name`",
},
{
name: "description flag",
input: "test --description 'some description'",
output: editOptions{Name: "test", Description: "some description"},
},
{
name: "color flag",
input: "test --color FFFFFF",
output: editOptions{Name: "test", Color: "FFFFFF"},
},
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
func TestListRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *listOptions
httpStubs func(*httpmock.Registry)
wantErr bool
wantErrMsg string
wantStdout string
wantStderr string
}{
{
name: "lists labels",
tty: true,
opts: &listOptions{},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query LabelList\b`),
httpmock.StringResponse(`
{
"data": {
"repository": {
"labels": {
"totalCount": 2,
"nodes": [
{
"name": "bug",
"color": "d73a4a",
"description": "This is a bug label"
},
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh label list
short: Display labels in a GitHub repository.
long: |-
When using the `--search` flag results are sorted by best match of the query.
This behavior cannot be configured with the `--order` or `--sort` flags.
For more information about output formatting flags, see `gh help formatting`.
pname: gh label
plink: gh_label.yaml
options:
- option: jq
shorthand: q
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}},
},
{
name: "limit flag",
input: "--limit 10",
output: listOptions{Query: listQueryOptions{Limit: 10, Order: "asc", Sort: "created"}},
},
{
name: "invalid limit flag",
input: "--limit 0",
wantErr: true,
errMsg: "invalid limit: 0",
},
{
name: "web flag",
input: "--web",
output: listOptions{Query: listQueryOptions{Limit: 30, Order: "asc", Sort: "created"}, WebMode: true},
},
{
N/A
N/A
command: gh licenses short: View license information for third-party libraries used in this build of the GitHub CLI. pname: gh plink: gh.yaml
N/A
N/A
command: gh org short: Work with GitHub organizations. pname: gh plink: gh.yaml
N/A
N/A
command: gh org list
short: List organizations for the authenticated user.
pname: gh org
plink: gh_org.yaml
options:
- option: limit
shorthand: L
value_type: int
description: Maximum number of organizations to list (default 30)
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
},
},
{
name: "with limit",
cli: "-L 101",
wants: ListOptions{
Limit: 101,
},
},
{
name: "too many arguments",
cli: "123 456",
wantsErr: "accepts at most 1 arg(s), received 2",
},
{
name: "invalid limit",
cli: "-L 0",
wantsErr: "invalid limit: 0",
func TestListRun(t *testing.T) {
tests := []struct {
name string
opts ListOptions
isTTY bool
wantOut string
}{
{
name: "no organizations found",
opts: ListOptions{HttpClient: func() (*http.Client, error) {
r := &httpmock.Registry{}
r.Register(
httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data": {"viewer": {"login": "octocat"}}}`))
r.Register(
httpmock.GraphQL(`query OrganizationList\b`),
httpmock.StringResponse(`
{ "data": { "user": {
"organizations": { "nodes": [], "totalCount": 0 }
} } }`,
),
)
return &http.Client{Transport: r}, nil
}},
isTTY: true,
wantOut: heredoc.Doc(`
func TestRunListOrg(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
gock.New("https://api.github.com").
N/A
command: gh org list
short: List organizations for the authenticated user.
pname: gh org
plink: gh_org.yaml
options:
- option: limit
shorthand: L
value_type: int
description: Maximum number of organizations to list (default 30)
N/A
N/A
command: gh pr
short: Work with GitHub pull requests.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
command: gh pr
short: Work with GitHub pull requests.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh pr checkout
short: Check out a pull request in git
pname: gh pr
plink: gh_pr.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
func TestNewCmdCheckout(t *testing.T) {
tests := []struct {
name string
args string
wantsOpts CheckoutOptions
wantErr error
}{
{
name: "recurse submodules",
args: "--recurse-submodules 123",
wantsOpts: CheckoutOptions{
RecurseSubmodules: true,
},
},
{
name: "force",
args: "--force 123",
wantsOpts: CheckoutOptions{
Force: true,
},
},
{
name: "detach",
args: "--detach 123",
wantsOpts: CheckoutOptions{
Detach: true,
},
},
{
name: "branch",
func Test_checkoutRun(t *testing.T) {
tests := []struct {
name string
opts *CheckoutOptions
httpStubs func(*httpmock.Registry)
runStubs func(*run.CommandStubber)
promptStubs func(*prompter.MockPrompter)
remotes map[string]string
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "checkout with ssh remote URL",
opts: &CheckoutOptions{
PRResolver: func() PRResolver {
baseRepo, pr := stubPR("OWNER/REPO:master", "OWNER/REPO:feature")
return &stubPRResolver{
pr: pr,
baseRepo: baseRepo,
}
}(),
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
},
Branch: func() (string, error) {
return "main", nil
func TestSpecificPRResolver(t *testing.T) {
t.Run("when the PR Finder returns results, those are returned", func(t *testing.T) {
t.Parallel()
baseRepo, pr := stubPR("OWNER/REPO:master", "OWNER/REPO:feature")
mockFinder := shared.NewMockFinder("123", pr, baseRepo)
mockFinder.ExpectFields([]string{"number", "headRefName", "headRepository", "headRepositoryOwner", "isCrossRepository", "maintainerCanModify"})
resolver := &specificPRResolver{
prFinder: mockFinder,
selector: "123",
}
resolvedPR, resolvedBaseRepo, err := resolver.Resolve()
require.NoError(t, err)
require.Equal(t, pr, resolvedPR)
require.True(t, ghrepo.IsSame(baseRepo, resolvedBaseRepo), "expected repos to be the same")
})
t.Run("when the PR Finder errors, that error is returned", func(t *testing.T) {
t.Parallel()
mockFinder := shared.NewMockFinder("123", nil, nil)
resolver := &specificPRResolver{
prFinder: mockFinder,
selector: "123",
}
_, _, err := resolver.Resolve()
N/A
command: gh pr checkout
short: Check out a pull request in git
pname: gh pr
plink: gh_pr.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
func TestNewCmdCheckout(t *testing.T) {
tests := []struct {
name string
args string
wantsOpts CheckoutOptions
wantErr error
}{
{
name: "recurse submodules",
args: "--recurse-submodules 123",
wantsOpts: CheckoutOptions{
RecurseSubmodules: true,
},
},
{
name: "force",
args: "--force 123",
wantsOpts: CheckoutOptions{
Force: true,
},
},
{
name: "detach",
args: "--detach 123",
wantsOpts: CheckoutOptions{
Detach: true,
},
},
{
name: "branch",
N/A
N/A
command: gh pr checkout
short: Check out a pull request in git
pname: gh pr
plink: gh_pr.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
func TestNewCmdCheckout(t *testing.T) {
tests := []struct {
name string
args string
wantsOpts CheckoutOptions
wantErr error
}{
{
name: "recurse submodules",
args: "--recurse-submodules 123",
wantsOpts: CheckoutOptions{
RecurseSubmodules: true,
},
},
{
name: "force",
args: "--force 123",
wantsOpts: CheckoutOptions{
Force: true,
},
},
{
name: "detach",
args: "--detach 123",
wantsOpts: CheckoutOptions{
Detach: true,
},
},
{
name: "branch",
N/A
N/A
command: gh pr checkout
short: Check out a pull request in git
pname: gh pr
plink: gh_pr.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
func TestNewCmdCheckout(t *testing.T) {
tests := []struct {
name string
args string
wantsOpts CheckoutOptions
wantErr error
}{
{
name: "recurse submodules",
args: "--recurse-submodules 123",
wantsOpts: CheckoutOptions{
RecurseSubmodules: true,
},
},
{
name: "force",
args: "--force 123",
wantsOpts: CheckoutOptions{
Force: true,
},
},
{
name: "detach",
args: "--detach 123",
wantsOpts: CheckoutOptions{
Detach: true,
},
},
{
name: "branch",
N/A
N/A
command: gh pr checkout
short: Check out a pull request in git
pname: gh pr
plink: gh_pr.yaml
options:
- option: branch
shorthand: b
value_type: string
description: Local branch name to use (default [the name of the head branch])
func TestNewCmdCheckout(t *testing.T) {
tests := []struct {
name string
args string
wantsOpts CheckoutOptions
wantErr error
}{
{
name: "recurse submodules",
args: "--recurse-submodules 123",
wantsOpts: CheckoutOptions{
RecurseSubmodules: true,
},
},
{
name: "force",
args: "--force 123",
wantsOpts: CheckoutOptions{
Force: true,
},
},
{
name: "detach",
args: "--detach 123",
wantsOpts: CheckoutOptions{
Detach: true,
},
},
{
name: "branch",
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
func Test_checksRun(t *testing.T) {
tests := []struct {
name string
tty bool
watch bool
failFast bool
required bool
disableDetector bool
httpStubs func(*httpmock.Registry)
wantOut string
wantErr string
}{
{
name: "no commits tty",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
httpmock.StringResponse(`{"data":{"node":{}}}`),
)
},
wantOut: "",
wantErr: "no commit found on the pull request",
},
{
name: "no checks tty",
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query PullRequestStatusChecks\b`),
func TestChecksRun_web(t *testing.T) {
tests := []struct {
name string
isTTY bool
wantStderr string
wantStdout string
wantBrowse string
}{
{
name: "tty",
isTTY: true,
wantStderr: "Opening https://github.com/OWNER/REPO/pull/123/checks in your browser.\n",
wantStdout: "",
wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks",
},
{
name: "nontty",
isTTY: false,
wantStderr: "",
wantStdout: "",
wantBrowse: "https://github.com/OWNER/REPO/pull/123/checks",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
browser := &browser.Stub{}
ios, _, stdout, stderr := iostreams.Test()
ios.SetStdoutTTY(tc.isTTY)
ios.SetStdinTTY(tc.isTTY)
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
N/A
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
N/A
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
func TestNewCmdChecks(t *testing.T) {
tests := []struct {
name string
cli string
wants ChecksOptions
wantsError string
}{
{
name: "no arguments",
cli: "",
wants: ChecksOptions{
Interval: time.Duration(10000000000),
},
},
{
name: "pr argument",
cli: "1234",
wants: ChecksOptions{
SelectorArg: "1234",
Interval: time.Duration(10000000000),
},
},
{
name: "watch flag",
cli: "--watch",
wants: ChecksOptions{
Watch: true,
Interval: time.Duration(10000000000),
},
},
N/A
N/A
command: gh pr checks
short: Show CI status for a single pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When the `--json` flag is used, it includes a `bucket` field, which categorizes
the `state` field into `pass`, `fail`, `pending`, `skipping`, or `cancel`.
pname: gh pr
plink: gh_pr.yaml
options:
- option: fail-fast
N/A
N/A
N/A
command: gh pr close
short: Close a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: delete-branch
shorthand: d
value_type: bool
func TestNoArgs(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
_, err := runCommand(http, true, "")
assert.EqualError(t, err, "cannot close pull request: number, url, or branch required")
}
func TestPrClose(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:feature")
pr.Title = "The title of the PR"
shared.StubFinderForRunCommandStyleTests(t, "96", pr, baseRepo)
http.Register(
httpmock.GraphQL(`mutation PullRequestClose\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "96")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "✓ Closed pull request OWNER/REPO#96 (The title of the PR)\n", output.Stderr())
}
func TestPrClose_alreadyClosed(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:feature")
pr.State = "CLOSED"
pr.Title = "The title of the PR"
shared.StubFinderForRunCommandStyleTests(t, "96", pr, baseRepo)
output, err := runCommand(http, true, "96")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "! Pull request OWNER/REPO#96 (The title of the PR) is already closed\n", output.Stderr())
}
N/A
command: gh pr close
short: Close a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: delete-branch
shorthand: d
value_type: bool
func TestPrClose_withComment(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
baseRepo, pr := stubPR("OWNER/REPO", "OWNER/REPO:feature")
pr.Title = "The title of the PR"
shared.StubFinderForRunCommandStyleTests(t, "96", pr, baseRepo)
http.Register(
httpmock.GraphQL(`mutation CommentCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "addComment": { "commentEdge": { "node": {
"url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
} } } } }`,
func(inputs map[string]interface{}) {
assert.Equal(t, "THE-ID", inputs["subjectId"])
assert.Equal(t, "closing comment", inputs["body"])
}),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestClose\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "96 --comment 'closing comment'")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
N/A
N/A
command: gh pr close
short: Close a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Leave a closing comment
- option: delete-branch
shorthand: d
value_type: bool
N/A
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
func Test_commentRun(t *testing.T) {
tests := []struct {
name string
input *shared.CommentableOptions
emptyComments bool
comments api.Comments
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
wantsErr bool
}{
{
name: "creating new comment with interactive editor succeeds",
input: &shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
InteractiveEditSurvey: func(string) (string, error) { return "comment body", nil },
ConfirmSubmitSurvey: func() (bool, error) { return true, nil },
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/pull/123#issuecomment-456\n",
},
{
name: "updating last comment with interactive editor fails if there are no comments and decline prompt to create",
input: &shared.CommentableOptions{
Interactive: true,
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
func Test_commentRun(t *testing.T) {
tests := []struct {
name string
input *shared.CommentableOptions
emptyComments bool
comments api.Comments
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
wantsErr bool
}{
{
name: "creating new comment with interactive editor succeeds",
input: &shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
InteractiveEditSurvey: func(string) (string, error) { return "comment body", nil },
ConfirmSubmitSurvey: func() (bool, error) { return true, nil },
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockCommentCreate(t, reg)
},
stdout: "https://github.com/OWNER/REPO/pull/123#issuecomment-456\n",
},
{
name: "updating last comment with interactive editor fails if there are no comments and decline prompt to create",
input: &shared.CommentableOptions{
Interactive: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr comment
short: Add a comment to a GitHub pull request.
long: |-
Without the body text supplied through flags, the command will interactively
prompt for the comment text.
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: The comment body text
func TestNewCmdComment(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output shared.CommentableOptions
wantsErr bool
isTTY bool
}{
{
name: "no arguments",
input: "",
output: shared.CommentableOptions{
Interactive: true,
InputType: 0,
Body: "",
},
isTTY: true,
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: shared.CommentableOptions{},
isTTY: true,
wantsErr: true,
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
func Test_createRun(t *testing.T) {
tests := []struct {
name string
setup func(*CreateOptions, *testing.T) func()
cmdStubs func(*run.CommandStubber)
promptStubs func(*prompter.PrompterMock)
httpStubs func(*httpmock.Registry, *testing.T)
expectedOutputs []string
expectedOut string
expectedErrOut string
expectedBrowse string
wantErr string
tty bool
customBranchConfig bool
}{
{
name: "nontty web",
setup: func(opts *CreateOptions, t *testing.T) func() {
opts.WebMode = true
opts.HeadBranch = "feature"
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
},
expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1",
},
{
name: "nontty",
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
func Test_createRun_GHES(t *testing.T) {
tests := []struct {
name string
setup func(*CreateOptions, *testing.T) func()
cmdStubs func(*run.CommandStubber)
promptStubs func(*prompter.PrompterMock)
httpStubs func(*httpmock.Registry, *testing.T)
expectedOutputs []string
expectedOut string
expectedErrOut string
tty bool
customBranchConfig bool
}{
{
name: "dry-run-nontty-with-all-opts",
tty: false,
setup: func(opts *CreateOptions, t *testing.T) func() {
opts.TitleProvided = true
opts.BodyProvided = true
opts.Title = "TITLE"
opts.Body = "BODY"
opts.BaseBranch = "trunk"
opts.HeadBranch = "feature"
opts.Assignees = []string{"monalisa"}
opts.Labels = []string{"bug", "todo"}
opts.Reviewers = []string{"hubot", "monalisa", "OWNER/core", "OWNER/robots"}
opts.Milestone = "big one.oh"
opts.DryRun = true
return func() {}
},
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func Test_createRun(t *testing.T) {
tests := []struct {
name string
setup func(*CreateOptions, *testing.T) func()
cmdStubs func(*run.CommandStubber)
promptStubs func(*prompter.PrompterMock)
httpStubs func(*httpmock.Registry, *testing.T)
expectedOutputs []string
expectedOut string
expectedErrOut string
expectedBrowse string
wantErr string
tty bool
customBranchConfig bool
}{
{
name: "nontty web",
setup: func(opts *CreateOptions, t *testing.T) func() {
opts.WebMode = true
opts.HeadBranch = "feature"
return func() {}
},
cmdStubs: func(cs *run.CommandStubber) {
cs.Register(`git( .+)? log( .+)? origin/master\.\.\.feature`, 0, "")
},
expectedBrowse: "https://github.com/OWNER/REPO/compare/master...feature?body=&expand=1",
},
{
name: "nontty",
httpStubs: func(reg *httpmock.Registry, t *testing.T) {
func TestNoRepoCanBeDetermined(t *testing.T) {
// Given no head repo can be determined from git config
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git status --porcelain`, 0, "")
cs.Register(`git config --get-regexp \^branch\\\..+\\\.\(remote\|merge\|pushremote\|gh-merge-base\)\$`, 0, "")
cs.Register(`git rev-parse --symbolic-full-name feature@{push}`, 1, "")
cs.Register("git config remote.pushDefault", 1, "")
cs.Register("git config push.default", 1, "")
// And Given there is no remote on the correct SHA
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, heredoc.Doc(`
deadbeef HEAD
deadb00f refs/remotes/origin/feature`))
// When the command is run with no TTY
reg := &httpmock.Registry{}
reg.StubRepoInfoResponse("OWNER", "REPO", "master")
defer reg.Verify(t)
ios, _, _, stderr := iostreams.Test()
opts := CreateOptions{
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
},
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
N/A
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
command: gh pr create
short: Create a pull request on GitHub.
long: |-
Upon success, the URL of the created pull request will be printed.
When the current branch isn't fully pushed to a git remote, a prompt will ask where
to push the branch and offer an option to fork the base repository. Any fork created this
way will only have the default branch of the upstream repository. Use `--head` to
explicitly skip any forking or pushing behavior.
`--head` supports `<user>:<branch>` syntax to select a head repo owned by `<user>`.
Using an organization as the `<user>` is currently not supported.
func TestNewCmdCreate(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
tty bool
stdin string
cli string
config string
wantsErr bool
wantsOpts CreateOptions
}{
{
name: "empty non-tty",
tty: false,
cli: "",
wantsErr: true,
},
{
name: "only title non-tty",
tty: false,
cli: "--title mytitle",
wantsErr: true,
},
{
name: "minimum non-tty",
tty: false,
cli: "--title mytitle --body ''",
N/A
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
func Test_NewCmdDiff(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DiffOptions
wantErr string
}{
{
name: "name only",
args: "--name-only",
want: DiffOptions{
NameOnly: true,
},
},
{
name: "number argument",
args: "123",
isTTY: true,
want: DiffOptions{
SelectorArg: "123",
UseColor: true,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: DiffOptions{
SelectorArg: "",
func Test_diffRun(t *testing.T) {
pr := &api.PullRequest{Number: 123, URL: "https://github.com/OWNER/REPO/pull/123"}
tests := []struct {
name string
opts DiffOptions
wantFields []string
wantStdout string
wantStderr string
wantBrowsedURL string
httpStubs func(*httpmock.Registry)
}{
{
name: "no color",
opts: DiffOptions{
SelectorArg: "123",
UseColor: false,
Patch: false,
},
wantFields: []string{"number"},
wantStdout: fmt.Sprintf(testDiff, "", "", "", ""),
httpStubs: func(reg *httpmock.Registry) {
stubDiffRequest(reg, "application/vnd.github.v3.diff", fmt.Sprintf(testDiff, "", "", "", ""))
},
},
{
name: "with color",
opts: DiffOptions{
SelectorArg: "123",
UseColor: true,
func Test_colorDiffLines(t *testing.T) {
inputs := []struct {
input, output string
}{
{
input: "",
output: "",
},
{
input: "\n",
output: "\n",
},
{
input: "foo\nbar\nbaz\n",
output: "foo\nbar\nbaz\n",
},
{
input: "foo\nbar\nbaz",
output: "foo\nbar\nbaz\n",
},
{
input: fmt.Sprintf("+foo\n-b%sr\n+++ baz\n", strings.Repeat("a", 2*lineBufferSize)),
output: fmt.Sprintf(
"%[4]s+foo%[2]s\n%[5]s-b%[1]sr%[2]s\n%[3]s+++ baz%[2]s\n",
strings.Repeat("a", 2*lineBufferSize),
"\x1b[m",
"\x1b[1;37m",
"\x1b[32m",
"\x1b[31m",
),
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
func Test_NewCmdDiff(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DiffOptions
wantErr string
}{
{
name: "name only",
args: "--name-only",
want: DiffOptions{
NameOnly: true,
},
},
{
name: "number argument",
args: "123",
isTTY: true,
want: DiffOptions{
SelectorArg: "123",
UseColor: true,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: DiffOptions{
SelectorArg: "",
N/A
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
func Test_NewCmdDiff(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DiffOptions
wantErr string
}{
{
name: "name only",
args: "--name-only",
want: DiffOptions{
NameOnly: true,
},
},
{
name: "number argument",
args: "123",
isTTY: true,
want: DiffOptions{
SelectorArg: "123",
UseColor: true,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: DiffOptions{
SelectorArg: "",
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
func Test_NewCmdDiff(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DiffOptions
wantErr string
}{
{
name: "name only",
args: "--name-only",
want: DiffOptions{
NameOnly: true,
},
},
{
name: "number argument",
args: "123",
isTTY: true,
want: DiffOptions{
SelectorArg: "123",
UseColor: true,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: DiffOptions{
SelectorArg: "",
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
N/A
N/A
N/A
command: gh pr diff
short: View changes in a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
With `--web` flag, open the pull request diff in a web browser instead.
Use `--exclude` to filter out files matching a glob pattern. The pattern
uses forward slashes as path separators on all platforms. You can repeat
the flag to exclude multiple patterns.
pname: gh pr
func Test_NewCmdDiff(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DiffOptions
wantErr string
}{
{
name: "name only",
args: "--name-only",
want: DiffOptions{
NameOnly: true,
},
},
{
name: "number argument",
args: "123",
isTTY: true,
want: DiffOptions{
SelectorArg: "123",
UseColor: true,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: DiffOptions{
SelectorArg: "",
N/A
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
func Test_editRun(t *testing.T) {
tests := []struct {
name string
input *EditOptions
httpStubs func(*testing.T, *httpmock.Registry)
stdout string
stderr string
}{
{
name: "non-interactive",
input: &EditOptions{
Detector: &fd.EnabledDetectorMock{},
SelectorArg: "123",
Finder: shared.NewMockFinder("123", &api.PullRequest{
URL: "https://github.com/OWNER/REPO/pull/123",
}, ghrepo.New("OWNER", "REPO")),
Interactive: false,
Editable: shared.Editable{
Title: shared.EditableString{
Value: "new title",
Edited: true,
},
Body: shared.EditableString{
Value: "new body",
Edited: true,
},
Base: shared.EditableString{
Value: "base-branch-name",
Edited: true,
},
func TestProjectsV1Deprecation(t *testing.T) {
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`projectCards`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
f := &cmdutil.Factory{
IOStreams: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
}
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = editRun(&EditOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
Detector: &fd.EnabledDetectorMock{},
Finder: shared.NewFinder(f),
SelectorArg: "https://github.com/cli/cli/pull/123",
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr edit
short: Edit a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
Editing a pull request's projects requires authorization with the `project` scope.
To authorize, run `gh auth refresh -s project`.
The `--add-assignee` and `--remove-assignee` flags both support
the following special values:
- `@me`: assign or unassign yourself
func TestNewCmdEdit(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
input string
stdin string
output EditOptions
expectedBaseRepo ghrepo.Interface
wantsErr bool
}{
{
name: "no argument",
input: "",
output: EditOptions{
SelectorArg: "",
Interactive: true,
},
wantsErr: false,
},
{
name: "two arguments",
input: "1 2",
output: EditOptions{},
wantsErr: true,
},
{
name: "URL argument",
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestPRList(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
http.Register(httpmock.GraphQL(`query PullRequestList\b`), httpmock.FileResponse("./fixtures/prList.json"))
output, err := runCommand(http, nil, true, "")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, heredoc.Doc(`
Showing 3 of 3 open pull requests in OWNER/REPO
ID TITLE BRANCH CREATED AT
#32 New feature feature about 3 hours ago
#29 Fixed bad bug hubot:bug-fix about 1 month ago
#28 Improve documentation docs about 2 years ago
`), output.String())
assert.Equal(t, ``, output.Stderr())
}
func TestPRList_nontty(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
http.Register(httpmock.GraphQL(`query PullRequestList\b`), httpmock.FileResponse("./fixtures/prList.json"))
output, err := runCommand(http, nil, false, "")
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "", output.Stderr())
assert.Equal(t, `32 New feature feature DRAFT 2022-08-24T20:01:12Z
29 Fixed bad bug hubot:bug-fix OPEN 2022-07-20T19:01:12Z
28 Improve documentation docs MERGED 2020-01-26T19:01:12Z
`, output.String())
}
func TestPRList_filtering(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
assert.Equal(t, []interface{}{"OPEN", "CLOSED", "MERGED"}, params["state"].([]interface{}))
}))
output, err := runCommand(http, nil, true, `-s all`)
assert.Error(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestPRList_filteringDraft(t *testing.T) {
tests := []struct {
name string
cli string
expectedQuery string
}{
{
name: "draft",
cli: "--draft",
expectedQuery: `draft:true repo:OWNER/REPO state:open type:pr`,
},
{
name: "non-draft",
cli: "--draft=false",
expectedQuery: `draft:false repo:OWNER/REPO state:open type:pr`,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
http.Register(
httpmock.GraphQL(`query PullRequestSearch\b`),
httpmock.GraphQLQuery(`{}`, func(_ string, params map[string]interface{}) {
assert.Equal(t, test.expectedQuery, params["q"].(string))
}))
// TODO advancedIssueSearchCleanup
func TestPRList_web(t *testing.T) {
tests := []struct {
name string
cli string
expectedBrowserURL string
}{
{
name: "filters",
cli: "-a peter -l bug -l docs -L 10 -s merged -B trunk",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=assignee%3Apeter+base%3Atrunk+is%3Amerged+label%3Abug+label%3Adocs+type%3Apr",
},
{
name: "draft",
cli: "--draft=true",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=draft%3Atrue+state%3Aopen+type%3Apr",
},
{
name: "non-draft",
cli: "--draft=0",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=draft%3Afalse+state%3Aopen+type%3Apr",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestPRList_withInvalidLimitFlag(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
_, err := runCommand(http, nil, true, `--limit=0`)
assert.EqualError(t, err, "invalid value for --limit: 0")
}
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
N/A
N/A
N/A
command: gh pr list
short: List pull requests in a GitHub repository. By default, this only lists open PRs.
pname: gh pr
plink: gh_pr.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: assignee
shorthand: a
value_type: string
description: Filter by assignee
func TestPRList_web(t *testing.T) {
tests := []struct {
name string
cli string
expectedBrowserURL string
}{
{
name: "filters",
cli: "-a peter -l bug -l docs -L 10 -s merged -B trunk",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=assignee%3Apeter+base%3Atrunk+is%3Amerged+label%3Abug+label%3Adocs+type%3Apr",
},
{
name: "draft",
cli: "--draft=true",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=draft%3Atrue+state%3Aopen+type%3Apr",
},
{
name: "non-draft",
cli: "--draft=0",
expectedBrowserURL: "https://github.com/OWNER/REPO/pulls?q=draft%3Afalse+state%3Aopen+type%3Apr",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
N/A
N/A
command: gh pr lock
short: Lock pull request conversation
pname: gh pr
plink: gh_pr.yaml
options:
- option: reason
shorthand: r
value_type: string
description: Optional reason for locking conversation (off_topic, resolved, spam, too_heated).
N/A
N/A
command: gh pr lock
short: Lock pull request conversation
pname: gh pr
plink: gh_pr.yaml
options:
- option: reason
shorthand: r
value_type: string
description: Optional reason for locking conversation (off_topic, resolved, spam, too_heated).
N/A
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
assert.NotContains(t, input, "commitHeadline")
}),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 1 --merge")
func TestPrMerge_blocked(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "BLOCKED",
},
baseRepo("OWNER", "REPO", "main"),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 1 --merge")
assert.EqualError(t, err, "SilentError")
assert.Equal(t, "", output.String())
assert.Equal(t, heredoc.Docf(`
X Pull request OWNER/REPO#1 is not mergeable: the base branch policy prohibits the merge.
To have the pull request merged after all the requirements have been met, add the %[1]s--auto%[1]s flag.
To use administrator privileges to immediately merge the pull request, add the %[1]s--admin%[1]s flag.
`, "`"), output.Stderr())
}
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func TestPrAddToMergeQueueAdmin(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
IsInMergeQueue: false,
IsMergeQueueEnabled: true,
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"mergeCommitAllowed": true,
"rebaseMergeAllowed": true,
"squashMergeAllowed": true
} } }`))
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
func TestPrAddToMergeQueueAdminWithMergeStrategy(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
IsInMergeQueue: false,
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
assert.NotContains(t, input, "commitHeadline")
}),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge_withAuthorFlag(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
assert.Equal(t, "octocat@github.com", input["authorEmail"].(string))
assert.NotContains(t, input, "commitHeadline")
}),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
N/A
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge_deleteBranch_mergeQueue(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"",
&api.PullRequest{
ID: "PR_10",
Number: 10,
State: "OPEN",
Title: "Blueberries are a good fruit",
HeadRefName: "blueberries",
BaseRefName: "main",
MergeStateStatus: "CLEAN",
IsMergeQueueEnabled: true,
},
baseRepo("OWNER", "REPO", "main"),
)
_, err := runCommand(http, nil, "blueberries", true, `pr merge --merge --delete-branch`)
assert.Contains(t, err.Error(), "X Cannot use `-d` or `--delete-branch` when merge queue enabled")
}
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
N/A
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge_withMatchCommitHeadFlag(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, 3, len(input))
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
assert.Equal(t, "285ed5ab740f53ff6b0b4b629c59a9df23b9c6db", input["expectedHeadOid"].(string))
}))
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 1 --merge --match-head-commit 285ed5ab740f53ff6b0b4b629c59a9df23b9c6db")
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "MERGE", input["mergeMethod"].(string))
assert.NotContains(t, input, "commitHeadline")
}),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 1 --merge")
func TestPrMerge_blocked(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "BLOCKED",
},
baseRepo("OWNER", "REPO", "main"),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 1 --merge")
assert.EqualError(t, err, "SilentError")
assert.Equal(t, "", output.String())
assert.Equal(t, heredoc.Docf(`
X Pull request OWNER/REPO#1 is not mergeable: the base branch policy prohibits the merge.
To have the pull request merged after all the requirements have been met, add the %[1]s--auto%[1]s flag.
To use administrator privileges to immediately merge the pull request, add the %[1]s--admin%[1]s flag.
`, "`"), output.Stderr())
}
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge_rebase(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"2",
&api.PullRequest{
ID: "THE-ID",
Number: 2,
Title: "The title of the PR",
State: "OPEN",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "REBASE", input["mergeMethod"].(string))
assert.NotContains(t, input, "commitHeadline")
}))
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 2 --rebase")
func TestPRMergeEmptyStrategyNonTTY(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
BaseRefName: "main",
},
baseRepo("OWNER", "REPO", "main"),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "blueberries", false, "pr merge 1")
assert.EqualError(t, err, "--merge, --rebase, or --squash required when not running interactively")
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
func Test_NewCmdMerge(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want MergeOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: MergeOptions{
SelectorArg: "123",
DeleteBranch: false,
IsDeleteBranchIndicated: false,
CanDeleteLocalBranch: true,
MergeMethod: PullRequestMergeMethodMerge,
MergeStrategyEmpty: true,
Body: "",
BodySet: false,
AuthorEmail: "",
},
},
{
func TestPrMerge_squash(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"3",
&api.PullRequest{
ID: "THE-ID",
Number: 3,
Title: "The title of the PR",
State: "OPEN",
MergeStateStatus: "CLEAN",
},
baseRepo("OWNER", "REPO", "main"),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestMerge\b`),
httpmock.GraphQLMutation(`{}`, func(input map[string]interface{}) {
assert.Equal(t, "THE-ID", input["pullRequestId"].(string))
assert.Equal(t, "SQUASH", input["mergeMethod"].(string))
assert.NotContains(t, input, "commitHeadline")
}))
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "main", true, "pr merge 3 --squash")
func TestPRMergeEmptyStrategyNonTTY(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t,
"1",
&api.PullRequest{
ID: "THE-ID",
Number: 1,
State: "OPEN",
Title: "The title of the PR",
MergeStateStatus: "CLEAN",
BaseRefName: "main",
},
baseRepo("OWNER", "REPO", "main"),
)
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git rev-parse --verify refs/heads/`, 0, "")
output, err := runCommand(http, nil, "blueberries", false, "pr merge 1")
assert.EqualError(t, err, "--merge, --rebase, or --squash required when not running interactively")
assert.Equal(t, "", output.String())
assert.Equal(t, "", output.Stderr())
}
N/A
N/A
command: gh pr merge
short: Merge a pull request on GitHub.
long: |-
Without an argument, the pull request that belongs to the current branch
is selected.
When targeting a branch that requires a merge queue, no merge strategy is required.
If required checks have not yet passed, auto-merge will be enabled.
If required checks have passed, the pull request will be added to the merge queue.
To bypass a merge queue and merge directly, pass the `--admin` flag.
pname: gh pr
plink: gh_pr.yaml
N/A
N/A
N/A
command: gh pr ready
short: Mark a pull request as ready for review.
long: |-
Without an argument, the pull request that belongs to the current branch
is marked as ready.
If supported by your plan, convert to draft with `--undo`
pname: gh pr
plink: gh_pr.yaml
options:
- option: undo
value_type: bool
func Test_NewCmdReady(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ReadyOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReadyOptions{
SelectorArg: "123",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReadyOptions{
SelectorArg: "",
},
},
{
name: "no argument with --repo override",
args: "-R owner/repo",
isTTY: true,
wantErr: "argument required when using the --repo flag",
},
func TestPRReady(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "OPEN",
IsDraft: true,
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestReadyForReview\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "123")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "✓ Pull request OWNER/REPO#123 is marked as \"ready for review\"\n", output.Stderr())
}
func TestPRReady_alreadyReady(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "OPEN",
IsDraft: false,
}, ghrepo.New("OWNER", "REPO"))
output, err := runCommand(http, true, "123")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "! Pull request OWNER/REPO#123 is already \"ready for review\"\n", output.Stderr())
}
N/A
command: gh pr ready
short: Mark a pull request as ready for review.
long: |-
Without an argument, the pull request that belongs to the current branch
is marked as ready.
If supported by your plan, convert to draft with `--undo`
pname: gh pr
plink: gh_pr.yaml
options:
- option: undo
value_type: bool
func TestPRReadyUndo(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "OPEN",
IsDraft: false,
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation ConvertPullRequestToDraft\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "123 --undo")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "✓ Pull request OWNER/REPO#123 is converted to \"draft\"\n", output.Stderr())
}
func TestPRReadyUndo_alreadyDraft(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "OPEN",
IsDraft: true,
}, ghrepo.New("OWNER", "REPO"))
output, err := runCommand(http, true, "123 --undo")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "! Pull request OWNER/REPO#123 is already \"in draft\"\n", output.Stderr())
}
N/A
N/A
command: gh pr reopen
short: Reopen a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Add a reopening comment
func TestPRReopen(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "CLOSED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestReopen\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
output, err := runCommand(http, true, "123")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "✓ Reopened pull request OWNER/REPO#123 (The title of the PR)\n", output.Stderr())
}
func TestPRReopen_alreadyOpen(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "OPEN",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
output, err := runCommand(http, true, "123")
assert.NoError(t, err)
assert.Equal(t, "", output.String())
assert.Equal(t, "! Pull request OWNER/REPO#123 (The title of the PR) is already open\n", output.Stderr())
}
func TestPRReopen_alreadyMerged(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
output, err := runCommand(http, true, "123")
assert.EqualError(t, err, "SilentError")
assert.Equal(t, "", output.String())
assert.Equal(t, "X Pull request OWNER/REPO#123 (The title of the PR) can't be reopened because it was already merged\n", output.Stderr())
}
N/A
command: gh pr reopen
short: Reopen a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: comment
shorthand: c
value_type: string
description: Add a reopening comment
func TestPRReopen_withComment(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "THE-ID",
Number: 123,
State: "CLOSED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation CommentCreate\b`),
httpmock.GraphQLMutation(`
{ "data": { "addComment": { "commentEdge": { "node": {
"url": "https://github.com/OWNER/REPO/issues/123#issuecomment-456"
} } } } }`,
func(inputs map[string]interface{}) {
assert.Equal(t, "THE-ID", inputs["subjectId"])
assert.Equal(t, "reopening comment", inputs["body"])
}),
)
http.Register(
httpmock.GraphQL(`mutation PullRequestReopen\b`),
httpmock.GraphQLMutation(`{"id": "THE-ID"}`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "THE-ID")
}),
)
N/A
N/A
command: gh pr revert
short: Revert a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: Body for the revert pull request
- option: body-file
shorthand: F
value_type: string
func TestPRRevert_missingArgument(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
// No arguments provided.
_, err := runCommand(http, true, "")
// Exits non-zero and prints an argument error.
assert.EqualError(t, err, "cannot revert pull request: number, url, or branch required")
}
func TestPRRevert_acceptedIdentifierFormats(t *testing.T) {
tests := []struct {
name string
args string
}{
{
name: "Revert by pull request number",
args: "123",
},
{
name: "Revert by pull request identifier",
args: "owner/repo#123",
},
{
name: "Revert by pull request URL",
args: "https://github.com/owner/repo/pull/123",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, tt.args, &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
func TestPRRevert_notRevertable(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "OPEN",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
// Target PR is not merged.
output, err := runCommand(http, true, "123")
// API error, non-zero exit.
assert.EqualError(t, err, "SilentError")
assert.Equal(t, "X Pull request OWNER/REPO#123 (The title of the PR) can't be reverted because it has not been merged\n", output.Stderr())
// No URL printed.
assert.Equal(t, "", output.String())
}
N/A
command: gh pr revert
short: Revert a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: Body for the revert pull request
- option: body-file
shorthand: F
value_type: string
func TestPRRevert_withTitleAndBody(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestRevert\b`),
httpmock.GraphQLMutation(`
{ "data": { "revertPullRequest": { "pullRequest": {
"ID": "SOME-ID"
}, "revertPullRequest": {
"ID": "NEW-ID",
"Number": 456,
"URL": "https://github.com/OWNER/REPO/pull/456"
} } } }
`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "SOME-ID")
assert.Equal(t, inputs["title"], "Revert PR title")
assert.Equal(t, inputs["body"], "Revert PR body")
}),
)
output, err := runCommand(http, true, "123 --title 'Revert PR title' --body 'Revert PR body'")
N/A
N/A
command: gh pr revert
short: Revert a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: Body for the revert pull request
- option: body-file
shorthand: F
value_type: string
N/A
N/A
N/A
command: gh pr revert
short: Revert a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: Body for the revert pull request
- option: body-file
shorthand: F
value_type: string
func TestPRRevert_withDraft(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestRevert\b`),
httpmock.GraphQLMutation(`
{ "data": { "revertPullRequest": { "pullRequest": {
"ID": "SOME-ID"
}, "revertPullRequest": {
"ID": "NEW-ID",
"Number": 456,
"URL": "https://github.com/OWNER/REPO/pull/456"
} } } }
`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "SOME-ID")
assert.Equal(t, inputs["draft"], true)
}),
)
output, err := runCommand(http, true, "123 --draft")
// Revert PR created as a draft.
N/A
N/A
command: gh pr revert
short: Revert a pull request
pname: gh pr
plink: gh_pr.yaml
options:
- option: body
shorthand: b
value_type: string
description: Body for the revert pull request
- option: body-file
shorthand: F
value_type: string
func TestPRRevert_withTitleAndBody(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "123", &api.PullRequest{
ID: "SOME-ID",
Number: 123,
State: "MERGED",
Title: "The title of the PR",
}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestRevert\b`),
httpmock.GraphQLMutation(`
{ "data": { "revertPullRequest": { "pullRequest": {
"ID": "SOME-ID"
}, "revertPullRequest": {
"ID": "NEW-ID",
"Number": 456,
"URL": "https://github.com/OWNER/REPO/pull/456"
} } } }
`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["pullRequestId"], "SOME-ID")
assert.Equal(t, inputs["title"], "Revert PR title")
assert.Equal(t, inputs["body"], "Revert PR body")
}),
)
output, err := runCommand(http, true, "123 --title 'Revert PR title' --body 'Revert PR body'")
N/A
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
func TestPRReview(t *testing.T) {
tests := []struct {
args string
wantEvent string
wantBody string
}{
{
args: `--request-changes -b"bad"`,
wantEvent: "REQUEST_CHANGES",
wantBody: "bad",
},
{
args: `--approve`,
wantEvent: "APPROVE",
wantBody: "",
},
{
args: `--approve -b"hot damn"`,
wantEvent: "APPROVE",
wantBody: "hot damn",
},
{
args: `--comment --body "i dunno"`,
wantEvent: "COMMENT",
wantBody: "i dunno",
},
}
for _, tt := range tests {
t.Run(tt.args, func(t *testing.T) {
func TestPRReview_interactive(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
shared.StubFinderForRunCommandStyleTests(t, "", &api.PullRequest{ID: "THE-ID", Number: 123}, ghrepo.New("OWNER", "REPO"))
http.Register(
httpmock.GraphQL(`mutation PullRequestReviewAdd\b`),
httpmock.GraphQLMutation(`{"data": {} }`,
func(inputs map[string]interface{}) {
assert.Equal(t, inputs["event"], "APPROVE")
assert.Equal(t, inputs["body"], "cool story")
}),
)
pm := &prompter.PrompterMock{
SelectFunc: func(_, _ string, _ []string) (int, error) { return 1, nil },
MarkdownEditorFunc: func(_, _ string, _ bool) (string, error) { return "cool story", nil },
ConfirmFunc: func(_ string, _ bool) (bool, error) { return true, nil },
}
output, err := runCommand(http, pm, true, "")
assert.NoError(t, err)
assert.Equal(t, heredoc.Doc(`
Got:
cool story
`), output.String())
assert.Equal(t, "✓ Approved pull request OWNER/REPO#123\n", output.Stderr())
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
N/A
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
N/A
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
N/A
command: gh pr review
short: Add a review to a pull request.
long: Without an argument, the pull request that belongs to the current branch is reviewed.
pname: gh pr
plink: gh_pr.yaml
options:
- option: approve
shorthand: a
value_type: bool
default_value: "false"
description: Approve pull request
- option: body
func Test_NewCmdReview(t *testing.T) {
tmpFile := filepath.Join(t.TempDir(), "my-body.md")
err := os.WriteFile(tmpFile, []byte("a body from file"), 0600)
require.NoError(t, err)
tests := []struct {
name string
args string
stdin string
isTTY bool
want ReviewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ReviewOptions{
SelectorArg: "123",
ReviewType: 0,
Body: "",
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ReviewOptions{
SelectorArg: "",
ReviewType: 0,
N/A
N/A
command: gh pr status
short: Show status of relevant pull requests.
long: |-
The status shows a summary of pull requests that includes information such as
pull request number, title, CI checks, reviews, etc.
To see more details of CI checks, run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
func TestPRStatus(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
http.Register(httpmock.GraphQL(`query PullRequestStatus\b`), httpmock.FileResponse("./fixtures/prStatus.json"))
// stub successful git commands
rs, cleanup := run.Stub()
defer cleanup(t)
rs.Register(`git rev-parse --symbolic-full-name blueberries@{push}`, 128, "")
rs.Register(`git config --get-regexp \^branch\\.`, 0, "")
rs.Register(`git config remote.pushDefault`, 0, "")
rs.Register(`git config push.default`, 1, "")
output, err := runCommand(http, "blueberries", true, "")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expectedPrs := []*regexp.Regexp{
regexp.MustCompile(`#8.*\[strawberries\]`),
regexp.MustCompile(`#9.*\[apples\].*✓ Auto-merge enabled`),
regexp.MustCompile(`#10.*\[blueberries\]`),
regexp.MustCompile(`#11.*\[figs\]`),
}
for _, r := range expectedPrs {
if !r.MatchString(output.String()) {
t.Errorf("output did not match regexp /%s/", r)
}
}
func TestPRStatus_reviewsAndChecks(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
// status,conclusion matches the old StatusContextRollup query
http.Register(httpmock.GraphQL(`status,conclusion`), httpmock.FileResponse("./fixtures/prStatusChecks.json"))
// stub successful git command
rs, cleanup := run.Stub()
defer cleanup(t)
rs.Register(`git config --get-regexp \^branch\\.`, 0, "")
rs.Register(`git config remote.pushDefault`, 0, "")
rs.Register(`git rev-parse --symbolic-full-name blueberries@{push}`, 128, "")
rs.Register(`git config push.default`, 1, "")
output, err := runCommand(http, "blueberries", true, "")
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expected := []string{
"✓ Checks passing + Changes requested ! Merge conflict status unknown",
"- Checks pending ✓ 2 Approved",
"× 1/3 checks failing - Review required ✓ No merge conflicts",
"✓ Checks passing × Merge conflicts",
}
for _, line := range expected {
if !strings.Contains(output.String(), line) {
t.Errorf("output did not contain %q: %q", line, output.String())
}
func TestPRStatus_reviewsAndChecksWithStatesByCount(t *testing.T) {
http := initFakeHTTP()
defer http.Verify(t)
// checkRunCount,checkRunCountsByState matches the new StatusContextRollup query
http.Register(httpmock.GraphQL(`checkRunCount,checkRunCountsByState`), httpmock.FileResponse("./fixtures/prStatusChecksWithStatesByCount.json"))
// stub successful git command
rs, cleanup := run.Stub()
defer cleanup(t)
rs.Register(`git config --get-regexp \^branch\\.`, 0, "")
rs.Register(`git config remote.pushDefault`, 0, "")
rs.Register(`git rev-parse --symbolic-full-name blueberries@{push}`, 128, "")
rs.Register(`git config push.default`, 1, "")
output, err := runCommandWithDetector(http, "blueberries", true, "", &fd.EnabledDetectorMock{})
if err != nil {
t.Errorf("error running command `pr status`: %v", err)
}
expected := []string{
"✓ Checks passing + Changes requested ! Merge conflict status unknown",
"- Checks pending ✓ 2 Approved",
"× 1/3 checks failing - Review required ✓ No merge conflicts",
"✓ Checks passing × Merge conflicts",
}
for _, line := range expected {
if !strings.Contains(output.String(), line) {
t.Errorf("output did not contain %q: %q", line, output.String())
}
N/A
command: gh pr status
short: Show status of relevant pull requests.
long: |-
The status shows a summary of pull requests that includes information such as
pull request number, title, CI checks, reviews, etc.
To see more details of CI checks, run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr status
short: Show status of relevant pull requests.
long: |-
The status shows a summary of pull requests that includes information such as
pull request number, title, CI checks, reviews, etc.
To see more details of CI checks, run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr status
short: Show status of relevant pull requests.
long: |-
The status shows a summary of pull requests that includes information such as
pull request number, title, CI checks, reviews, etc.
To see more details of CI checks, run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr status
short: Show status of relevant pull requests.
long: |-
The status shows a summary of pull requests that includes information such as
pull request number, title, CI checks, reviews, etc.
To see more details of CI checks, run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr unlock short: Unlock pull request conversation pname: gh pr plink: gh_pr.yaml
N/A
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
"additions",
"assignees",
"author",
"autoMergeRequest",
"baseRefName",
"baseRefOid",
"body",
"changedFiles",
"closed",
"closedAt",
"closingIssuesReferences",
"comments",
"commits",
"createdAt",
"deletions",
"files",
"fullDatabaseId",
"headRefName",
"headRefOid",
"headRepository",
"headRepositoryOwner",
"id",
"isCrossRepository",
"isDraft",
"labels",
"latestReviews",
"maintainerCanModify",
"mergeCommit",
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "number argument",
args: "123",
isTTY: true,
want: ViewOptions{
SelectorArg: "123",
BrowserMode: false,
},
},
{
name: "no argument",
args: "",
isTTY: true,
want: ViewOptions{
SelectorArg: "",
BrowserMode: false,
},
},
{
name: "web mode",
args: "123 -w",
isTTY: true,
func TestPRView_Preview_nontty(t *testing.T) {
tests := map[string]struct {
branch string
args string
fixtures map[string]string
expectedOutputs []string
}{
"Open PR without metadata": {
branch: "master",
args: "12",
fixtures: map[string]string{
"PullRequestByNumber": "./fixtures/prViewPreview.json",
},
expectedOutputs: []string{
`title:\tBlueberries are from a fork\n`,
`state:\tOPEN\n`,
`author:\tnobody\n`,
`labels:\t\n`,
`assignees:\t\n`,
`reviewers:\t\n`,
`projects:\t\n`,
`milestone:\t\n`,
`url:\thttps://github.com/OWNER/REPO/pull/12\n`,
`additions:\t100\n`,
`deletions:\t10\n`,
`number:\t12\n`,
`blueberries taste good`,
},
},
"Open PR with metadata by number": {
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
func TestPRView_tty_Comments(t *testing.T) {
tests := map[string]struct {
branch string
cli string
fixtures map[string]string
expectedOutputs []string
wantsErr bool
}{
"without comments flag": {
branch: "master",
cli: "123",
fixtures: map[string]string{
"PullRequestByNumber": "./fixtures/prViewPreviewSingleComment.json",
"ReviewsForPullRequest": "./fixtures/prViewPreviewReviews.json",
},
expectedOutputs: []string{
`some title OWNER/REPO#12`,
`1 \x{1f615} • 2 \x{1f440} • 3 \x{2764}\x{fe0f}`,
`some body`,
`———————— Not showing 9 comments ————————`,
`marseilles \(Collaborator\) • Jan 9, 2020 • Newest comment`,
`4 \x{1f389} • 5 \x{1f604} • 6 \x{1f680}`,
`Comment 5`,
`Use --comments to view the full conversation`,
`View this pull request on GitHub: https://github.com/OWNER/REPO/pull/12`,
},
},
"with comments flag": {
branch: "master",
cli: "123 --comments",
func TestPRView_nontty_Comments(t *testing.T) {
tests := map[string]struct {
branch string
cli string
fixtures map[string]string
expectedOutputs []string
wantsErr bool
}{
"without comments flag": {
branch: "master",
cli: "123",
fixtures: map[string]string{
"PullRequestByNumber": "./fixtures/prViewPreviewSingleComment.json",
"ReviewsForPullRequest": "./fixtures/prViewPreviewReviews.json",
},
expectedOutputs: []string{
`title:\tsome title`,
`state:\tOPEN`,
`author:\tnobody`,
`url:\thttps://github.com/OWNER/REPO/pull/12`,
`some body`,
},
},
"with comments flag": {
branch: "master",
cli: "123 --comments",
fixtures: map[string]string{
"PullRequestByNumber": "./fixtures/prViewPreviewSingleComment.json",
"ReviewsForPullRequest": "./fixtures/prViewPreviewReviews.json",
"CommentsForPullRequest": "./fixtures/prViewPreviewFullComments.json",
N/A
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh pr view
short: Display the title, body, and other information about a pull request.
long: |-
Without an argument, the pull request that belongs to the current branch
is displayed.
With `--web` flag, open the pull request in a web browser instead.
For more information about output formatting flags, see `gh help formatting`.
pname: gh pr
plink: gh_pr.yaml
options:
N/A
N/A
N/A
command: gh preview short: Preview commands are for testing, demonstrative, and development purposes only. long: They should be considered unstable and can change at any time. pname: gh plink: gh.yaml
func TestNewCmdPreview(t *testing.T) {
tests := []struct {
name string
input string
wantRepo string
wantSkillName string
wantVersion string
wantAllowHiddenDirs bool
wantErr bool
}{
{
name: "repo and skill",
input: "github/awesome-copilot my-skill",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
},
{
name: "repo and skill with version",
input: "github/awesome-copilot my-skill@v1.2.0",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
wantVersion: "v1.2.0",
},
{
name: "repo and skill with SHA",
input: "github/awesome-copilot my-skill@abc123def456",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
wantVersion: "abc123def456",
},
func TestPreviewRun(t *testing.T) {
skillContent := heredoc.Doc(`
---
name: my-skill
description: A test skill
---
# My Skill
This is the skill content.
`)
encodedContent := base64.StdEncoding.EncodeToString([]byte(skillContent))
tests := []struct {
name string
opts *PreviewOptions
tty bool
httpStubs func(*httpmock.Registry)
wantStdout string
wantErr string
}{
{
name: "preview specific skill",
tty: true,
opts: &PreviewOptions{
repo: ghrepo.New("github", "awesome-copilot"),
SkillName: "my-skill",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/github/awesome-copilot/releases/latest"),
func TestPreviewRun_UnsupportedHost(t *testing.T) {
ios, _, _, _ := iostreams.Test()
err := previewRun(&PreviewOptions{
IO: ios,
HttpClient: func() (*http.Client, error) { return &http.Client{}, nil },
repo: ghrepo.NewWithHost("github", "awesome-copilot", "acme.ghes.com"),
Telemetry: &telemetry.NoOpService{},
})
require.ErrorContains(t, err, "does not currently support GitHub Enterprise Server")
}
N/A
N/A
command: gh preview prompter short: Execute a test program to preview the prompter. long: Without an argument, all prompts will be run. pname: gh preview plink: gh_preview.yaml
N/A
N/A
command: gh project
short: Work with GitHub Projects.
long: |-
The minimum required scope for the token is: `project`.
You can verify your token scope by running `gh auth status` and
add the `project` scope by running `gh auth refresh -s project`.
pname: gh
plink: gh.yaml
N/A
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdClose(t *testing.T) {
tests := []struct {
name string
cli string
wants closeOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: closeOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: closeOpts{
owner: "monalisa",
},
},
{
func TestRunClose_User(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get user project ID
func TestRunClose_Org(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get org project ID
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdClose(t *testing.T) {
tests := []struct {
name string
cli string
wants closeOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: closeOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: closeOpts{
owner: "monalisa",
},
},
{
N/A
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdClose(t *testing.T) {
tests := []struct {
name string
cli string
wants closeOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: closeOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: closeOpts{
owner: "monalisa",
},
},
{
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project close
short: Close a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdClose(t *testing.T) {
tests := []struct {
name string
cli string
wants closeOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: closeOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: closeOpts{
owner: "monalisa",
},
},
{
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
func TestRunCopy_User(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get user project ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserProject.*",
"variables": map[string]interface{}{
"login": "monalisa",
"number": 1,
"firstItems": 0,
"afterItems": nil,
"firstFields": 0,
"afterFields": nil,
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"projectV2": map[string]string{
"id": "an ID",
},
},
},
})
func TestRunCopy_Org(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get org project ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query OrgProject.*",
"variables": map[string]interface{}{
"login": "github",
"number": 1,
"firstItems": 0,
"afterItems": nil,
"firstFields": 0,
"afterFields": nil,
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"projectV2": map[string]string{
"id": "an ID",
},
},
},
})
// get source org ID
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
N/A
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
N/A
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
N/A
N/A
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
N/A
N/A
N/A
command: gh project copy
short: Copy a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: drafts
value_type: bool
default_value: "false"
description: Include draft issues when copying
- option: format
value_type: string
func TestNewCmdCopy(t *testing.T) {
tests := []struct {
name string
cli string
wants copyOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: copyOpts{
title: "t",
},
},
{
name: "number",
cli: "123 --title t",
wants: copyOpts{
number: 123,
title: "t",
},
},
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestIssueCreate_projectsV2(t *testing.T) {
http := &httpmock.Registry{}
defer http.Verify(t)
http.StubIssueRepoInfoResponse("OWNER", "REPO")
http.Register(
httpmock.GraphQL(`query RepositoryProjectList\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "projects": {
"nodes": [],
"pageInfo": { "hasNextPage": false }
} } } }
`))
http.Register(
httpmock.GraphQL(`query OrganizationProjectList\b`),
httpmock.StringResponse(`
{ "data": { "organization": { "projects": {
"nodes": [],
"pageInfo": { "hasNextPage": false }
} } } }
`))
http.Register(
httpmock.GraphQL(`query RepositoryProjectV2List\b`),
httpmock.StringResponse(`
{ "data": { "repository": { "projectsV2": {
"nodes": [
{ "title": "CleanupV2", "id": "CLEANUPV2ID" },
{ "title": "RoadmapV2", "id": "ROADMAPV2ID" }
],
"pageInfo": { "hasNextPage": false }
func TestProjectsV1Deprecation(t *testing.T) {
t.Run("non-interactive submission", func(t *testing.T) {
t.Run("when projects v1 is supported, queries for it", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.StubIssueRepoInfoResponse("OWNER", "REPO")
reg.Register(
// ( is required to avoid matching projectsV2
httpmock.GraphQL(`projects\(`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = createRun(&CreateOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
t.Run("when projects v1 is supported, queries for it", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
// ( is required to avoid matching projectsV2
httpmock.GraphQL(`projects\(`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = createRun(&CreateOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
WebMode: true,
// Required to force a lookup of projects
Projects: []string{"Project"},
})
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
cli string
wants createOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "title",
cli: "--title t",
wants: createOpts{
title: "t",
},
},
{
name: "owner",
cli: "--title t --owner monalisa",
wants: createOpts{
owner: "monalisa",
title: "t",
},
},
{
name: "json",
cli: "--title t --format json",
wants: createOpts{
title: "t",
},
N/A
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
cli string
wants createOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "title",
cli: "--title t",
wants: createOpts{
title: "t",
},
},
{
name: "owner",
cli: "--title t --owner monalisa",
wants: createOpts{
owner: "monalisa",
title: "t",
},
},
{
name: "json",
cli: "--title t --format json",
wants: createOpts{
title: "t",
},
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project create
short: Create a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
cli string
wants createOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "title",
cli: "--title t",
wants: createOpts{
title: "t",
},
},
{
name: "owner",
cli: "--title t --owner monalisa",
wants: createOpts{
owner: "monalisa",
title: "t",
},
},
{
name: "json",
cli: "--title t --format json",
wants: createOpts{
title: "t",
},
N/A
command: gh project delete
short: Delete a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants deleteOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: deleteOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: deleteOpts{
owner: "monalisa",
},
},
{
func TestRunDelete_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
func TestRunDelete_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
N/A
command: gh project delete
short: Delete a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants deleteOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: deleteOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: deleteOpts{
owner: "monalisa",
},
},
{
N/A
N/A
command: gh project delete
short: Delete a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project delete
short: Delete a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants deleteOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: deleteOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: deleteOpts{
owner: "monalisa",
},
},
{
N/A
command: gh project delete
short: Delete a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestProjectsV1Deprecation(t *testing.T) {
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`projectCards`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = editRun(&EditOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
IssueNumbers: []int{123},
Editable: prShared.Editable{
Projects: prShared.EditableProjects{
EditableSlice: prShared.EditableSlice{
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`projectCards`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = editRun(&EditOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
IssueNumbers: []int{123},
Editable: prShared.Editable{
Projects: prShared.EditableProjects{
EditableSlice: prShared.EditableSlice{
Add: []string{"Test Project"},
t.Run("when projects v1 is not supported, is not included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Exclude(t, httpmock.GraphQL(`projectCards`))
reg.Register(
httpmock.GraphQL(`.*`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we're not really interested in it.
_ = editRun(&EditOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.DisabledDetectorMock{},
IssueNumbers: []int{123},
Editable: prShared.Editable{
Projects: prShared.EditableProjects{
EditableSlice: prShared.EditableSlice{
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
N/A
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
N/A
N/A
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
command: gh project edit
short: Edit a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: description
shorthand: d
value_type: string
description: New description of the project
- option: format
value_type: string
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
cli string
wants editOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "visibility-error",
cli: "--visibility v",
wantsErr: true,
wantsErrMsg: "invalid argument \"v\" for \"--visibility\" flag: valid values are {PUBLIC|PRIVATE}",
},
{
name: "no-args",
cli: "",
wantsErr: true,
wantsErrMsg: "no fields to edit",
},
{
name: "title",
cli: "--title t",
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
func TestNewCmdCreateField(t *testing.T) {
tests := []struct {
name string
cli string
wants createFieldOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-name-and-data-type",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"data-type\", \"name\" not set",
},
{
name: "not-a-number",
cli: "x --name n --data-type TEXT",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "single-select-no-options",
cli: "123 --name n --data-type SINGLE_SELECT",
wantsErr: true,
wantsErrMsg: "passing `--single-select-options` is required for SINGLE_SELECT data type",
},
{
name: "number",
cli: "123 --name n --data-type TEXT",
func TestRunCreateField_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
func TestRunCreateField_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
command: gh project field-create
short: Create a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: data-type
value_type: string
description: 'DataType of the new field.: {TEXT|SINGLE_SELECT|DATE|NUMBER}'
N/A
N/A
N/A
command: gh project field-delete
short: Delete a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdDeleteField(t *testing.T) {
tests := []struct {
name string
cli string
wants deleteFieldOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "no id",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"id\" not set",
},
{
name: "id",
cli: "--id 123",
wants: deleteFieldOpts{
fieldID: "123",
},
},
{
name: "json",
cli: "--id 123 --format json",
wants: deleteFieldOpts{
fieldID: "123",
},
wantsExporter: true,
},
func TestRunDeleteField(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// delete Field
gock.New("https://api.github.com").
Post("/graphql").
BodyString(`{"query":"mutation DeleteField.*","variables":{"input":{"fieldId":"an ID"}}}`).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"deleteProjectV2Field": map[string]interface{}{
"projectV2Field": map[string]interface{}{
"id": "Field ID",
},
},
},
})
client := queries.NewTestClient()
ios, _, stdout, _ := iostreams.Test()
ios.SetStdoutTTY(true)
config := deleteFieldConfig{
opts: deleteFieldOpts{
fieldID: "an ID",
},
client: client,
io: ios,
}
func TestRunDeleteField_JSON(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// delete Field
gock.New("https://api.github.com").
Post("/graphql").
BodyString(`{"query":"mutation DeleteField.*","variables":{"input":{"fieldId":"an ID"}}}`).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"deleteProjectV2Field": map[string]interface{}{
"projectV2Field": map[string]interface{}{
"__typename": "ProjectV2Field",
"id": "Field ID",
"name": "a name",
},
},
},
})
client := queries.NewTestClient()
ios, _, stdout, _ := iostreams.Test()
config := deleteFieldConfig{
opts: deleteFieldOpts{
fieldID: "an ID",
exporter: cmdutil.NewJSONExporter(),
},
client: client,
N/A
command: gh project field-delete
short: Delete a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-delete
short: Delete a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-delete
short: Delete a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-delete
short: Delete a field in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants listOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: listOpts{
number: 123,
limit: 30,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: listOpts{
owner: "monalisa",
limit: 30,
},
func TestRunList_User_tty(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// list project fields
func TestRunList_User(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// list project fields
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project field-list
short: List the fields in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdaddItem(t *testing.T) {
tests := []struct {
name string
cli string
wants addItemOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-url",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"url\" not set",
},
{
name: "not-a-number",
cli: "x --url github.com/cli/cli",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "url",
cli: "--url github.com/cli/cli",
wants: addItemOpts{
itemURL: "github.com/cli/cli",
},
},
{
name: "number",
func TestRunAddItem_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
func TestRunAddItem_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
gock.New("https://api.github.com").
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-add
short: Add a pull request or an issue to a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdarchiveItem(t *testing.T) {
tests := []struct {
name string
cli string
wants archiveItemOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-id",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"id\" not set",
},
{
name: "not-a-number",
cli: "x --id 123",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "id",
cli: "--id 123",
wants: archiveItemOpts{
itemID: "123",
},
},
{
name: "number",
func TestRunArchive_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
func TestRunArchive_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
gock.New("https://api.github.com").
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-archive
short: Archive an item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdCreateItem(t *testing.T) {
tests := []struct {
name string
cli string
wants createItemOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-title",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"title\" not set",
},
{
name: "not-a-number",
cli: "x --title t",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "title",
cli: "--title t",
wants: createItemOpts{
title: "t",
},
},
{
name: "number",
func TestRunCreateItem_Draft_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
gock.New("https://api.github.com").
func TestRunCreateItem_Draft_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
gock.New("https://api.github.com").
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-create
short: Create a draft issue item in a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
value_type: string
description: Body for the draft issue
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdDeleteItem(t *testing.T) {
tests := []struct {
name string
cli string
wants deleteItemOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-id",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"id\" not set",
},
{
name: "not-a-number",
cli: "x --id 123",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "item-id",
cli: "--id 123",
wants: deleteItemOpts{
itemID: "123",
},
},
{
name: "number",
func TestRunDelete_User(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
// get project ID
gock.New("https://api.github.com").
func TestRunDelete_Org(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get org ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "github",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"organization": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"user"},
},
},
})
// get project ID
gock.New("https://api.github.com").
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
command: gh project item-delete
short: Delete an item from a project by ID
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
func TestNewCmdeditItem(t *testing.T) {
tests := []struct {
name string
cli string
wants editItemOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "missing-id",
cli: "",
wantsErr: true,
wantsErrMsg: "required flag(s) \"id\" not set",
},
{
name: "invalid-flags",
cli: "--id 123 --text t --date 2023-01-01",
wantsErr: true,
wantsErrMsg: "only one of `--text`, `--number`, `--date`, `--single-select-option-id` or `--iteration-id` may be used",
},
{
name: "item-id",
cli: "--id 123",
wants: editItemOpts{
itemID: "123",
},
},
{
name: "number",
func TestRunItemEdit_Draft(t *testing.T) {
defer gock.Off()
// gock.Observe(gock.DumpRequest)
// edit item
gock.New("https://api.github.com").
Post("/graphql").
BodyString(`{"query":"mutation EditDraftIssueItem.*","variables":{"input":{"draftIssueId":"DI_item_id","title":"a title","body":"a new body"}}}`).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"updateProjectV2DraftIssue": map[string]interface{}{
"draftIssue": map[string]interface{}{
"title": "a title",
"body": "a new body",
},
},
},
})
client := queries.NewTestClient()
ios, _, stdout, _ := iostreams.Test()
ios.SetStdoutTTY(true)
config := editItemConfig{
io: ios,
opts: editItemOpts{
title: "a title",
titleChanged: true,
func TestRunItemEdit_DraftTitleOnly(t *testing.T) {
defer gock.Off()
gock.New("https://api.github.com").
Post("/graphql").
BodyString(`{"query":"query DraftIssueByID.*","variables":{"id":"DI_item_id"}}`).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"node": map[string]interface{}{
"id": "DI_item_id",
"title": "existing title",
"body": "existing body",
},
},
})
gock.New("https://api.github.com").
Post("/graphql").
BodyString(`{"query":"mutation EditDraftIssueItem.*","variables":{"input":{"draftIssueId":"DI_item_id","title":"new title","body":"existing body"}}}`).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"updateProjectV2DraftIssue": map[string]interface{}{
"draftIssue": map[string]interface{}{
"title": "new title",
"body": "existing body",
},
},
},
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
command: gh project item-edit
short: Edit either a draft issue or a project item. Both usages require the ID of the item to edit.
long: |-
For non-draft issues, the ID of the project is also required, and only a single field value can be updated per invocation.
Remove project item field value using `--clear` flag.
For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: body
N/A
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
func TestIssueList_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"hasIssuesEnabled": true,
"issues": {
"totalCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rnQ",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
}
func TestIssueList_Search_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"hasIssuesEnabled": true
},
"search": {
"issueCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rl0",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
func TestPRList_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"pullRequests": {
"totalCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rnQ",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
}
}
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
command: gh project item-list
short: List the items in a project.
long: |-
If supported by the API host (github.com and GHES 3.20+), the --query option can
N/A
N/A
N/A
command: gh project link
short: Link a project to a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be linked to this project
func TestNewCmdLink(t *testing.T) {
tests := []struct {
name string
cli string
wants linkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: linkOpts{
owner: "OWNER",
repo: "REPO",
},
},
{
func TestRunLink_Repo(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]string{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
"login": "monalisa",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
func TestRunLink_Team(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]string{
"login": "monalisa-org",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
"login": "monalisa-org",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
N/A
command: gh project link
short: Link a project to a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be linked to this project
func TestNewCmdLink(t *testing.T) {
tests := []struct {
name string
cli string
wants linkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: linkOpts{
owner: "OWNER",
repo: "REPO",
},
},
{
N/A
command: gh project link
short: Link a project to a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be linked to this project
func TestNewCmdLink(t *testing.T) {
tests := []struct {
name string
cli string
wants linkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: linkOpts{
owner: "OWNER",
repo: "REPO",
},
},
{
N/A
command: gh project link
short: Link a project to a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be linked to this project
func TestNewCmdLink(t *testing.T) {
tests := []struct {
name string
cli string
wants linkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: linkOpts{
owner: "OWNER",
repo: "REPO",
},
},
{
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
func TestIssueList_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query IssueList\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"hasIssuesEnabled": true,
"issues": {
"totalCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rnQ",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
}
func TestIssueList_Search_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query IssueSearch\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"hasIssuesEnabled": true
},
"search": {
"issueCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rl0",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
func TestPRList_withProjectItems(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query PullRequestList\b`),
httpmock.GraphQLQuery(`{
"data": {
"repository": {
"pullRequests": {
"totalCount": 1,
"nodes": [
{
"projectItems": {
"nodes": [
{
"id": "PVTI_lAHOAA3WC84AW6WNzgJ8rnQ",
"project": {
"id": "PVT_kwHOAA3WC84AW6WN",
"title": "Test Public Project"
},
"status": {
"optionId": "47fc9ee4",
"name": "In Progress"
}
}
],
"totalCount": 1
}
}
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
func TestNewCmdlist(t *testing.T) {
tests := []struct {
name string
cli string
wants listOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "owner",
cli: "--owner monalisa",
wants: listOpts{
owner: "monalisa",
limit: 30,
},
},
{
name: "closed",
cli: "--closed",
wants: listOpts{
closed: true,
limit: 30,
},
},
{
name: "web",
cli: "--web",
wants: listOpts{
web: true,
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
func TestNewCmdlist(t *testing.T) {
tests := []struct {
name string
cli string
wants listOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "owner",
cli: "--owner monalisa",
wants: listOpts{
owner: "monalisa",
limit: 30,
},
},
{
name: "closed",
cli: "--closed",
wants: listOpts{
closed: true,
limit: 30,
},
},
{
name: "web",
cli: "--web",
wants: listOpts{
web: true,
N/A
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
N/A
N/A
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
N/A
N/A
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
func TestNewCmdlist(t *testing.T) {
tests := []struct {
name string
cli string
wants listOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "owner",
cli: "--owner monalisa",
wants: listOpts{
owner: "monalisa",
limit: 30,
},
},
{
name: "closed",
cli: "--closed",
wants: listOpts{
closed: true,
limit: 30,
},
},
{
name: "web",
cli: "--web",
wants: listOpts{
web: true,
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
N/A
N/A
N/A
command: gh project list
short: List the projects for an owner
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: closed
value_type: bool
default_value: "false"
description: Include closed projects
- option: format
value_type: string
func TestNewCmdlist(t *testing.T) {
tests := []struct {
name string
cli string
wants listOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "owner",
cli: "--owner monalisa",
wants: listOpts{
owner: "monalisa",
limit: 30,
},
},
{
name: "closed",
cli: "--closed",
wants: listOpts{
closed: true,
limit: 30,
},
},
{
name: "web",
cli: "--web",
wants: listOpts{
web: true,
N/A
N/A
command: gh project unlink
short: Unlink a project from a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be unlinked from this project
func TestNewCmdUnlink(t *testing.T) {
tests := []struct {
name string
cli string
wants unlinkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: unlinkOpts{
repo: "REPO",
owner: "OWNER",
},
},
{
func TestRunUnlink_Repo(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]string{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
"login": "monalisa",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
func TestRunUnlink_Team(t *testing.T) {
defer gock.Off()
gock.Observe(gock.DumpRequest)
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]string{
"login": "monalisa-org",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
"login": "monalisa-org",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
N/A
command: gh project unlink
short: Unlink a project from a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be unlinked from this project
func TestNewCmdUnlink(t *testing.T) {
tests := []struct {
name string
cli string
wants unlinkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: unlinkOpts{
repo: "REPO",
owner: "OWNER",
},
},
{
N/A
command: gh project unlink
short: Unlink a project from a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be unlinked from this project
func TestNewCmdUnlink(t *testing.T) {
tests := []struct {
name string
cli string
wants unlinkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: unlinkOpts{
repo: "REPO",
owner: "OWNER",
},
},
{
N/A
command: gh project unlink
short: Unlink a project from a repository or a team
pname: gh project
plink: gh_project.yaml
options:
- option: owner
value_type: string
description: Login of the owner. Use "@me" for the current user.
- option: repo
shorthand: R
value_type: string
description: The repository to be unlinked from this project
func TestNewCmdUnlink(t *testing.T) {
tests := []struct {
name string
cli string
wants unlinkOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "specify-repo-and-team",
cli: "--repo my-repo --team my-team",
wantsErr: true,
wantsErrMsg: "specify only one of `--repo` or `--team`",
},
{
name: "specify-nothing",
cli: "",
wants: unlinkOpts{
repo: "REPO",
owner: "OWNER",
},
},
{
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestProjectsV1Deprecation(t *testing.T) {
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`projectCards`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = viewRun(&ViewOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
IssueNumber: 123,
})
// Verify that our request contained projectCards
t.Run("when projects v1 is supported, is included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Register(
httpmock.GraphQL(`projectCards`),
// Simulate a GraphQL error to early exit the test.
httpmock.StatusStringResponse(500, ""),
)
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we have no way to really stub it without
// fully stubbing a GQL error structure in the request body.
_ = viewRun(&ViewOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.EnabledDetectorMock{},
IssueNumber: 123,
})
// Verify that our request contained projectCards
reg.Verify(t)
t.Run("when projects v1 is not supported, is not included in query", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
reg := &httpmock.Registry{}
reg.Exclude(t, httpmock.GraphQL(`projectCards`))
_, cmdTeardown := run.Stub()
defer cmdTeardown(t)
// Ignore the error because we're not really interested in it.
_ = viewRun(&ViewOptions{
IO: ios,
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
Detector: &fd.DisabledDetectorMock{},
IssueNumber: 123,
})
// Verify that our request contained projectCards
reg.Verify(t)
})
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdview(t *testing.T) {
tests := []struct {
name string
cli string
wants viewOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: viewOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: viewOpts{
owner: "monalisa",
},
},
{
N/A
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdview(t *testing.T) {
tests := []struct {
name string
cli string
wants viewOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: viewOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: viewOpts{
owner: "monalisa",
},
},
{
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
N/A
N/A
N/A
command: gh project view
short: View a project
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh project
plink: gh_project.yaml
options:
- option: format
value_type: string
description: 'Output format: {json}'
func TestNewCmdview(t *testing.T) {
tests := []struct {
name string
cli string
wants viewOpts
wantsErr bool
wantsErrMsg string
wantsExporter bool
}{
{
name: "not-a-number",
cli: "x",
wantsErr: true,
wantsErrMsg: "invalid number: x",
},
{
name: "number",
cli: "123",
wants: viewOpts{
number: 123,
},
},
{
name: "owner",
cli: "--owner monalisa",
wants: viewOpts{
owner: "monalisa",
},
},
{
func TestRunViewWeb_TTY(t *testing.T) {
tests := []struct {
name string
setup func(*viewOpts, *testing.T) func()
promptStubs func(*prompter.PrompterMock)
gqlStubs func(*testing.T)
expectedBrowse string
tty bool
}{
{
name: "Org project --web with tty",
tty: true,
setup: func(vo *viewOpts, t *testing.T) func() {
return func() {
vo.web = true
}
},
gqlStubs: func(t *testing.T) {
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query ViewerLoginAndOrgs.*",
"variables": map[string]interface{}{
"after": nil,
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
N/A
command: gh release
short: Manage releases
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh release
short: Manage releases
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
func Test_createRun(t *testing.T) {
const contentCmd = `git tag --list .* --format=%\(contents\)`
const signatureCmd = `git tag --list .* --format=%\(contents:signature\)`
defaultRunStubs := func(rs *run.CommandStubber) {
rs.Register(contentCmd, 0, "")
rs.Register(signatureCmd, 0, "")
}
tests := []struct {
name string
isTTY bool
opts CreateOptions
httpStubs func(t *testing.T, reg *httpmock.Registry)
runStubs func(rs *run.CommandStubber)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "create a release",
isTTY: true,
opts: CreateOptions{
TagName: "v1.2.3",
Name: "The Big 1.2",
Body: "* Fixed bugs",
BodyProvided: true,
Target: "",
},
runStubs: defaultRunStubs,
func Test_createRun_interactive(t *testing.T) {
const contentCmd = `git tag --list .* --format=%\(contents\)`
const signatureCmd = `git tag --list .* --format=%\(contents:signature\)`
defaultRunStubs := func(rs *run.CommandStubber) {
rs.Register(contentCmd, 1, "")
}
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
prompterStubs func(*testing.T, *prompter.MockPrompter)
runStubs func(*run.CommandStubber)
opts *CreateOptions
wantParams map[string]interface{}
wantOut string
wantErr string
}{
{
name: "create a release from existing tag",
opts: &CreateOptions{},
prompterStubs: func(t *testing.T, pm *prompter.MockPrompter) {
pm.RegisterSelect("Choose a tag",
[]string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"},
func(_, _ string, opts []string) (int, error) {
return prompter.IndexFor(opts, "v1.2.3")
})
pm.RegisterSelect("Release notes",
[]string{"Write my own", "Write using generated notes as template", "Leave blank"},
func(_, _ string, opts []string) (int, error) {
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
N/A
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
N/A
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
N/A
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_createRun_interactive(t *testing.T) {
const contentCmd = `git tag --list .* --format=%\(contents\)`
const signatureCmd = `git tag --list .* --format=%\(contents:signature\)`
defaultRunStubs := func(rs *run.CommandStubber) {
rs.Register(contentCmd, 1, "")
}
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
prompterStubs func(*testing.T, *prompter.MockPrompter)
runStubs func(*run.CommandStubber)
opts *CreateOptions
wantParams map[string]interface{}
wantOut string
wantErr string
}{
{
name: "create a release from existing tag",
opts: &CreateOptions{},
prompterStubs: func(t *testing.T, pm *prompter.MockPrompter) {
pm.RegisterSelect("Choose a tag",
[]string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"},
func(_, _ string, opts []string) (int, error) {
return prompter.IndexFor(opts, "v1.2.3")
})
pm.RegisterSelect("Release notes",
[]string{"Write my own", "Write using generated notes as template", "Leave blank"},
func(_, _ string, opts []string) (int, error) {
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
N/A
N/A
N/A
command: gh release create
short: Create a new GitHub Release for a repository.
long: |-
A list of asset files may be given to upload to the new release. To define a
display label for an asset, append text starting with `#` after the file name.
If a matching git tag does not yet exist, one will automatically get created
from the latest state of the default branch.
Use `--target` to point to a different branch or commit for the automatic tag creation.
Use `--verify-tag` to abort the release if the tag doesn't already exist.
To fetch the new tag locally after the release, do `git fetch --tags origin`.
func Test_NewCmdCreate(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
af1, err := os.Create(filepath.Join(tempDir, "windows.zip"))
require.NoError(t, err)
af1.Close()
af2, err := os.Create(filepath.Join(tempDir, "linux.tgz"))
require.NoError(t, err)
af2.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want CreateOptions
wantErr string
}{
{
name: "no arguments tty",
args: "",
isTTY: true,
want: CreateOptions{
TagName: "",
Target: "",
Name: "",
Body: "",
func Test_createRun_interactive(t *testing.T) {
const contentCmd = `git tag --list .* --format=%\(contents\)`
const signatureCmd = `git tag --list .* --format=%\(contents:signature\)`
defaultRunStubs := func(rs *run.CommandStubber) {
rs.Register(contentCmd, 1, "")
}
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
prompterStubs func(*testing.T, *prompter.MockPrompter)
runStubs func(*run.CommandStubber)
opts *CreateOptions
wantParams map[string]interface{}
wantOut string
wantErr string
}{
{
name: "create a release from existing tag",
opts: &CreateOptions{},
prompterStubs: func(t *testing.T, pm *prompter.MockPrompter) {
pm.RegisterSelect("Choose a tag",
[]string{"v1.2.3", "v1.2.2", "v1.0.0", "v0.1.2", "Create a new tag"},
func(_, _ string, opts []string) (int, error) {
return prompter.IndexFor(opts, "v1.2.3")
})
pm.RegisterSelect("Release notes",
[]string{"Write my own", "Write using generated notes as template", "Leave blank"},
func(_, _ string, opts []string) (int, error) {
N/A
N/A
command: gh release delete
short: Delete a release
pname: gh release
plink: gh_release.yaml
options:
- option: cleanup-tag
value_type: bool
default_value: "false"
description: Delete the specified tag in addition to its release
- option: "yes"
shorthand: "y"
value_type: bool
func Test_NewCmdDelete(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DeleteOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: false,
},
},
{
name: "skip confirm",
args: "v1.2.3 -y",
isTTY: true,
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: true,
CleanupTag: false,
},
},
{
name: "cleanup tag",
func Test_deleteRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts DeleteOptions
prompterStubs func(*prompter.PrompterMock)
runStubs func(*run.CommandStubber)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "interactive confirm",
isTTY: true,
opts: DeleteOptions{
TagName: "v1.2.3",
},
wantStdout: "",
wantStderr: "✓ Deleted release v1.2.3\n! Note that the v1.2.3 git tag still remains in the repository\n",
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
if p == "Delete release v1.2.3 in OWNER/REPO?" {
return true, nil
}
return false, prompter.NoSuchPromptErr(p)
}
},
},
{
name: "skipping confirmation",
N/A
command: gh release delete
short: Delete a release
pname: gh release
plink: gh_release.yaml
options:
- option: cleanup-tag
value_type: bool
default_value: "false"
description: Delete the specified tag in addition to its release
- option: "yes"
shorthand: "y"
value_type: bool
func Test_NewCmdDelete(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DeleteOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: false,
},
},
{
name: "skip confirm",
args: "v1.2.3 -y",
isTTY: true,
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: true,
CleanupTag: false,
},
},
{
name: "cleanup tag",
N/A
N/A
command: gh release delete
short: Delete a release
pname: gh release
plink: gh_release.yaml
options:
- option: cleanup-tag
value_type: bool
default_value: "false"
description: Delete the specified tag in addition to its release
- option: "yes"
shorthand: "y"
value_type: bool
N/A
N/A
N/A
command: gh release delete-asset
short: Delete an asset from a release
pname: gh release
plink: gh_release.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func Test_NewCmdDeleteAsset(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DeleteAssetOptions
wantErr string
}{
{
name: "tag and asset arguments",
args: "v1.2.3 test-asset",
isTTY: true,
want: DeleteAssetOptions{
TagName: "v1.2.3",
SkipConfirm: false,
AssetName: "test-asset",
},
},
{
name: "skip confirm",
args: "v1.2.3 test-asset -y",
isTTY: true,
want: DeleteAssetOptions{
TagName: "v1.2.3",
SkipConfirm: true,
AssetName: "test-asset",
},
},
{
name: "no arguments",
func Test_deleteAssetRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts DeleteAssetOptions
prompterStubs func(*prompter.PrompterMock)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "interactive confirm",
isTTY: true,
opts: DeleteAssetOptions{
TagName: "v1.2.3",
AssetName: "test-asset",
},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
if p == "Delete asset test-asset in release v1.2.3 in OWNER/REPO?" {
return true, nil
}
return false, prompter.NoSuchPromptErr(p)
}
},
wantStdout: ``,
wantStderr: "✓ Deleted asset test-asset from release v1.2.3\n",
},
{
name: "skipping confirmation",
N/A
command: gh release delete-asset
short: Delete an asset from a release
pname: gh release
plink: gh_release.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
N/A
N/A
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string(nil),
Destination: ".",
Concurrency: 5,
},
},
{
name: "version and file pattern",
args: "v1.2.3 -p *.tgz",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"*.tgz"},
Destination: ".",
Concurrency: 5,
},
},
func Test_downloadRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
wantFiles []string
}{
{
name: "download all assets",
isTTY: true,
opts: DownloadOptions{
TagName: "v1.2.3",
Destination: ".",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
func Test_downloadRun_cloberAndSkip(t *testing.T) {
oldAssetContents := "older copy to be clobbered"
oldZipballContents := "older zipball to be clobbered"
// this should be shorter than oldAssetContents and oldZipballContents
newContents := "somedata"
tests := []struct {
name string
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantFileSize int64
wantArchiveSize int64
}{
{
name: "no clobber or skip",
opts: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"windows-64bit.zip"},
Destination: "tmp/packages",
Concurrency: 2,
},
wantErr: "already exists (use `--clobber` to overwrite file or `--skip-existing` to skip file)",
wantFileSize: int64(len(oldAssetContents)),
wantArchiveSize: int64(len(oldZipballContents)),
},
{
name: "clobber",
opts: DownloadOptions{
TagName: "v1.2.3",
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string(nil),
Destination: ".",
Concurrency: 5,
},
},
{
name: "version and file pattern",
args: "v1.2.3 -p *.tgz",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"*.tgz"},
Destination: ".",
Concurrency: 5,
},
},
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_downloadRun_cloberAndSkip(t *testing.T) {
oldAssetContents := "older copy to be clobbered"
oldZipballContents := "older zipball to be clobbered"
// this should be shorter than oldAssetContents and oldZipballContents
newContents := "somedata"
tests := []struct {
name string
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantFileSize int64
wantArchiveSize int64
}{
{
name: "no clobber or skip",
opts: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"windows-64bit.zip"},
Destination: "tmp/packages",
Concurrency: 2,
},
wantErr: "already exists (use `--clobber` to overwrite file or `--skip-existing` to skip file)",
wantFileSize: int64(len(oldAssetContents)),
wantArchiveSize: int64(len(oldZipballContents)),
},
{
name: "clobber",
opts: DownloadOptions{
TagName: "v1.2.3",
N/A
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string(nil),
Destination: ".",
Concurrency: 5,
},
},
{
name: "version and file pattern",
args: "v1.2.3 -p *.tgz",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"*.tgz"},
Destination: ".",
Concurrency: 5,
},
},
N/A
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string(nil),
Destination: ".",
Concurrency: 5,
},
},
{
name: "version and file pattern",
args: "v1.2.3 -p *.tgz",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"*.tgz"},
Destination: ".",
Concurrency: 5,
},
},
N/A
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_NewCmdDownload(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want DownloadOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string(nil),
Destination: ".",
Concurrency: 5,
},
},
{
name: "version and file pattern",
args: "v1.2.3 -p *.tgz",
isTTY: true,
want: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"*.tgz"},
Destination: ".",
Concurrency: 5,
},
},
N/A
command: gh release download
short: Download assets from a GitHub release.
long: |-
Without an explicit tag name argument, assets are downloaded from the
latest release in the project. In this case, `--pattern` or `--archive`
is required.
pname: gh release
plink: gh_release.yaml
options:
- option: archive
shorthand: A
value_type: string
func Test_downloadRun_cloberAndSkip(t *testing.T) {
oldAssetContents := "older copy to be clobbered"
oldZipballContents := "older zipball to be clobbered"
// this should be shorter than oldAssetContents and oldZipballContents
newContents := "somedata"
tests := []struct {
name string
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantFileSize int64
wantArchiveSize int64
}{
{
name: "no clobber or skip",
opts: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"windows-64bit.zip"},
Destination: "tmp/packages",
Concurrency: 2,
},
wantErr: "already exists (use `--clobber` to overwrite file or `--skip-existing` to skip file)",
wantFileSize: int64(len(oldAssetContents)),
wantArchiveSize: int64(len(oldZipballContents)),
},
{
name: "clobber",
opts: DownloadOptions{
TagName: "v1.2.3",
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
func Test_editRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts EditOptions
httpStubs func(t *testing.T, reg *httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "edit the tag name",
isTTY: true,
opts: EditOptions{
TagName: "v1.2.4",
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockSuccessfulEditResponse(reg, func(params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"tag_name": "v1.2.4",
}, params)
})
},
wantStdout: "https://github.com/OWNER/REPO/releases/tag/v1.2.3\n",
wantStderr: "",
},
{
name: "edit the target",
isTTY: true,
opts: EditOptions{
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
N/A
N/A
command: gh release edit
short: Edit a release
pname: gh release
plink: gh_release.yaml
options:
- option: discussion-category
value_type: string
description: Start a discussion in the specified category when publishing a draft
- option: draft
value_type: bool
default_value: "false"
description: Save the release as a draft instead of publishing it
func Test_NewCmdEdit(t *testing.T) {
tempDir := t.TempDir()
tf, err := os.CreateTemp(tempDir, "release-create")
require.NoError(t, err)
fmt.Fprint(tf, "MY NOTES")
tf.Close()
tests := []struct {
name string
args string
isTTY bool
stdin string
want EditOptions
wantErr string
}{
{
name: "no arguments notty",
args: "",
isTTY: false,
wantErr: "accepts 1 arg(s), received 0",
},
{
name: "provide title and notes",
args: "v1.2.3 --title 'Some Title' --notes 'Some Notes'",
isTTY: false,
want: EditOptions{
TagName: "",
Name: stringPtr("Some Title"),
Body: stringPtr("Some Notes"),
},
func Test_editRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts EditOptions
httpStubs func(t *testing.T, reg *httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "edit the tag name",
isTTY: true,
opts: EditOptions{
TagName: "v1.2.4",
},
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
mockSuccessfulEditResponse(reg, func(params map[string]interface{}) {
assert.Equal(t, map[string]interface{}{
"tag_name": "v1.2.4",
}, params)
})
},
wantStdout: "https://github.com/OWNER/REPO/releases/tag/v1.2.3\n",
wantStderr: "",
},
{
name: "edit the target",
isTTY: true,
opts: EditOptions{
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: false,
ExcludePreReleases: false,
Order: "desc",
},
},
{
name: "exclude drafts",
args: "--exclude-drafts",
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: true,
ExcludePreReleases: false,
Order: "desc",
},
},
{
func Test_listRun(t *testing.T) {
oneDayAgo := time.Now().Add(time.Duration(-24) * time.Hour)
frozenTime, err := time.Parse(time.RFC3339, "2020-08-31T15:44:24+02:00")
require.NoError(t, err)
httpStubs := func(createdAt time.Time) func(t *testing.T, reg *httpmock.Registry) {
return func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`\bRepositoryReleaseList\(`),
httpmock.GraphQLQuery(
fmt.Sprintf(`
{ "data": { "repository": { "releases": {
"nodes": [
{
"name": "",
"tagName": "v1.1.0",
"isLatest": false,
"isDraft": true,
"isPrerelease": false,
"immutable": false,
"createdAt": "%[1]s",
"publishedAt": "%[1]s"
},
{
"name": "The big 1.0",
"tagName": "v1.0.0",
"isLatest": true,
"isDraft": false,
"isPrerelease": false,
func TestExportReleases(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
createdAt, _ := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
publishedAt, _ := time.Parse(time.RFC3339, "2024-02-01T00:00:00Z")
rs := []Release{{
Name: "v1",
TagName: "tag",
IsDraft: true,
IsLatest: false,
IsPrerelease: true,
IsImmutable: true,
CreatedAt: createdAt,
PublishedAt: publishedAt,
}}
exporter := cmdutil.NewJSONExporter()
exporter.SetFields(releaseFields)
require.NoError(t, exporter.Write(ios, rs))
require.JSONEq(t,
`[{"createdAt":"2024-01-01T00:00:00Z","isDraft":true,"isLatest":false,"isPrerelease":true,"isImmutable":true,"name":"v1","publishedAt":"2024-02-01T00:00:00Z","tagName":"tag"}]`,
stdout.String())
}
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: false,
ExcludePreReleases: false,
Order: "desc",
},
},
{
name: "exclude drafts",
args: "--exclude-drafts",
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: true,
ExcludePreReleases: false,
Order: "desc",
},
},
{
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: false,
ExcludePreReleases: false,
Order: "desc",
},
},
{
name: "exclude drafts",
args: "--exclude-drafts",
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: true,
ExcludePreReleases: false,
Order: "desc",
},
},
{
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
N/A
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
N/A
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: false,
ExcludePreReleases: false,
Order: "desc",
},
},
{
name: "exclude drafts",
args: "--exclude-drafts",
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: true,
ExcludePreReleases: false,
Order: "desc",
},
},
{
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: false,
ExcludePreReleases: false,
Order: "desc",
},
},
{
name: "exclude drafts",
args: "--exclude-drafts",
want: ListOptions{
LimitResults: 30,
ExcludeDrafts: true,
ExcludePreReleases: false,
Order: "desc",
},
},
{
N/A
N/A
command: gh release list
short: List releases in a repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: exclude-drafts
value_type: bool
default_value: "false"
description: Exclude draft releases
- option: exclude-pre-releases
value_type: bool
N/A
N/A
N/A
command: gh release upload
short: Upload asset files to a GitHub Release.
long: |-
To define a display label for an asset, append text starting with `#` after the
file name.
When using `--clobber`, existing assets are deleted before new assets are uploaded.
If the upload fails, the original assets will be lost.
pname: gh release
plink: gh_release.yaml
options:
- option: clobber
func Test_typeForFilename(t *testing.T) {
tests := []struct {
name string
file string
want string
}{
{
name: "tar",
file: "ball.tar",
want: "application/x-tar",
},
{
name: "tgz",
file: "ball.tgz",
want: "application/x-gtar",
},
{
name: "tar.gz",
file: "ball.tar.gz",
want: "application/x-gtar",
},
{
name: "bz2",
file: "ball.tar.bz2",
want: "application/x-bzip2",
},
{
name: "zip",
file: "archive.zip",
want: "application/zip",
func Test_uploadWithDelete_retry(t *testing.T) {
retryInterval = 0
ctx := context.Background()
tries := 0
client := funcClient(func(req *http.Request) (*http.Response, error) {
tries++
if tries == 1 {
return nil, errors.New("made up exception")
} else if tries == 2 {
return &http.Response{
Request: req,
StatusCode: 500,
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
}, nil
}
return &http.Response{
Request: req,
StatusCode: 200,
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
}, nil
})
err := uploadWithDelete(ctx, client, "http://example.com/upload", AssetForUpload{
Name: "asset",
Label: "",
Size: 8,
Open: func() (io.ReadCloser, error) {
return io.NopCloser(bytes.NewBufferString(`somebody`)), nil
},
MIMEType: "application/octet-stream",
func Test_SanitizeFileName(t *testing.T) {
tests := []struct {
name string
expected string
}{
{
name: "foo",
expected: "foo",
},
{
name: "foo bar",
expected: "foo.bar",
},
{
name: ".foo",
expected: "default.foo",
},
{
name: "Foo bar",
expected: "Foo.bar",
},
{
name: "Hello, दुनिया",
expected: "default.Hello",
},
{
name: "this+has+plusses.jpg",
expected: "this+has+plusses.jpg",
},
{
N/A
command: gh release upload
short: Upload asset files to a GitHub Release.
long: |-
To define a display label for an asset, append text starting with `#` after the
file name.
When using `--clobber`, existing assets are deleted before new assets are uploaded.
If the upload fails, the original assets will be lost.
pname: gh release
plink: gh_release.yaml
options:
- option: clobber
N/A
N/A
N/A
command: gh release verify short: Verify that a GitHub Release is accompanied by a valid cryptographically signed attestation. long: "An attestation is a claim made by GitHub regarding a release and its assets.\n\nThis command checks that the specified release (or the latest release, if no tag is given) has a valid attestation. \nIt fetches the attestation for the release and prints metadata about all assets referenced in the attestation, including their digests.\n\nFor more information about output formatting flags, see `gh help formatting`."
func TestNewCmdVerify_Args(t *testing.T) {
tests := []struct {
name string
args []string
wantTag string
wantErr string
}{
{
name: "valid tag arg",
args: []string{"v1.2.3"},
wantTag: "v1.2.3",
},
{
name: "no tag arg",
args: []string{},
wantTag: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: testIO,
HttpClient: func() (*http.Client, error) {
return nil, nil
},
ExternalHttpClient: func() (*http.Client, error) {
return nil, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
func Test_verifyRun_Success(t *testing.T) {
ios, _, _, _ := iostreams.Test()
tagName := "v6"
fakeHTTP := &httpmock.Registry{}
defer fakeHTTP.Verify(t)
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
baseRepo, err := ghrepo.FromFullName("owner/repo")
require.NoError(t, err)
result := &verification.AttestationProcessingResult{
Attestation: &api.Attestation{
Bundle: data.GitHubReleaseBundle(t),
BundleURL: "https://example.com",
},
VerificationResult: nil,
}
cfg := &VerifyConfig{
Opts: &VerifyOptions{
TagName: tagName,
BaseRepo: baseRepo,
Exporter: nil,
},
IO: ios,
HttpClient: &http.Client{Transport: fakeHTTP},
AttClient: api.NewTestClient(),
func Test_verifyRun_FailedNoAttestations_SHA1(t *testing.T) {
ios, _, _, _ := iostreams.Test()
tagName := "v1"
fakeHTTP := &httpmock.Registry{}
defer fakeHTTP.Verify(t)
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
baseRepo, err := ghrepo.FromFullName("owner/repo")
require.NoError(t, err)
var capturedParams api.FetchParams
attClient := &api.MockClient{
OnGetByDigest: func(params api.FetchParams) ([]*api.Attestation, error) {
capturedParams = params
return api.OnGetByDigestFailure(params)
},
}
cfg := &VerifyConfig{
Opts: &VerifyOptions{
TagName: tagName,
BaseRepo: baseRepo,
Exporter: nil,
},
IO: ios,
HttpClient: &http.Client{Transport: fakeHTTP},
AttClient: attClient,
AttVerifier: nil,
N/A
command: gh release verify short: Verify that a GitHub Release is accompanied by a valid cryptographically signed attestation. long: "An attestation is a claim made by GitHub regarding a release and its assets.\n\nThis command checks that the specified release (or the latest release, if no tag is given) has a valid attestation. \nIt fetches the attestation for the release and prints metadata about all assets referenced in the attestation, including their digests.\n\nFor more information about output formatting flags, see `gh help formatting`."
N/A
N/A
command: gh release verify short: Verify that a GitHub Release is accompanied by a valid cryptographically signed attestation. long: "An attestation is a claim made by GitHub regarding a release and its assets.\n\nThis command checks that the specified release (or the latest release, if no tag is given) has a valid attestation. \nIt fetches the attestation for the release and prints metadata about all assets referenced in the attestation, including their digests.\n\nFor more information about output formatting flags, see `gh help formatting`."
N/A
N/A
N/A
command: gh release verify short: Verify that a GitHub Release is accompanied by a valid cryptographically signed attestation. long: "An attestation is a claim made by GitHub regarding a release and its assets.\n\nThis command checks that the specified release (or the latest release, if no tag is given) has a valid attestation. \nIt fetches the attestation for the release and prints metadata about all assets referenced in the attestation, including their digests.\n\nFor more information about output formatting flags, see `gh help formatting`."
N/A
N/A
N/A
command: gh release verify-asset
short: Verify that a given asset file originated from a specific GitHub Release using cryptographically signed attestations.
long: |-
An attestation is a claim made by GitHub regarding a release and its assets.
This command checks that the asset you provide matches a valid attestation for the specified release (or the latest release, if no tag is given).
func TestNewCmdVerifyAsset_Args(t *testing.T) {
tests := []struct {
name string
args []string
wantTag string
wantFile string
wantErr string
}{
{
name: "valid args",
args: []string{"v1.2.3", "../../attestation/test/data/github_release_artifact.zip"},
wantTag: "v1.2.3",
wantFile: test.NormalizeRelativePath("../../attestation/test/data/github_release_artifact.zip"),
},
{
name: "valid flag with no tag",
args: []string{"../../attestation/test/data/github_release_artifact.zip"},
wantFile: test.NormalizeRelativePath("../../attestation/test/data/github_release_artifact.zip"),
},
{
name: "no args",
args: []string{},
wantErr: "you must specify an asset filepath",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
testIO, _, _, _ := iostreams.Test()
func Test_verifyAssetRun_Success(t *testing.T) {
ios, _, _, _ := iostreams.Test()
tagName := "v6"
fakeHTTP := &httpmock.Registry{}
defer fakeHTTP.Verify(t)
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
baseRepo, err := ghrepo.FromFullName("owner/repo")
require.NoError(t, err)
result := &verification.AttestationProcessingResult{
Attestation: &api.Attestation{
Bundle: data.GitHubReleaseBundle(t),
BundleURL: "https://example.com",
},
VerificationResult: nil,
}
releaseAssetPath := test.NormalizeRelativePath("../../attestation/test/data/github_release_artifact.zip")
cfg := &VerifyAssetConfig{
Opts: &VerifyAssetOptions{
AssetFilePath: releaseAssetPath,
TagName: tagName,
BaseRepo: baseRepo,
Exporter: nil,
},
func Test_verifyAssetRun_SuccessNoTagArg(t *testing.T) {
ios, _, _, _ := iostreams.Test()
tagName := "v6"
fakeHTTP := &httpmock.Registry{}
defer fakeHTTP.Verify(t)
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
shared.StubFetchRefSHA(t, fakeHTTP, "OWNER", "REPO", tagName, fakeSHA)
shared.StubFetchRelease(t, fakeHTTP, "OWNER", "REPO", "", `{
"tag_name": "v6",
"draft": false,
"url": "https://api.github.com/repos/OWNER/REPO/releases/23456"
}`)
baseRepo, err := ghrepo.FromFullName("OWNER/REPO")
require.NoError(t, err)
result := &verification.AttestationProcessingResult{
Attestation: &api.Attestation{
Bundle: data.GitHubReleaseBundle(t),
BundleURL: "https://example.com",
},
VerificationResult: nil,
}
releaseAssetPath := test.NormalizeRelativePath("../../attestation/test/data/github_release_artifact.zip")
cfg := &VerifyAssetConfig{
Opts: &VerifyAssetOptions{
AssetFilePath: releaseAssetPath,
TagName: "", // No tag argument provided
N/A
command: gh release verify-asset
short: Verify that a given asset file originated from a specific GitHub Release using cryptographically signed attestations.
long: |-
An attestation is a claim made by GitHub regarding a release and its assets.
This command checks that the asset you provide matches a valid attestation for the specified release (or the latest release, if no tag is given).
N/A
N/A
command: gh release verify-asset
short: Verify that a given asset file originated from a specific GitHub Release using cryptographically signed attestations.
long: |-
An attestation is a claim made by GitHub regarding a release and its assets.
This command checks that the asset you provide matches a valid attestation for the specified release (or the latest release, if no tag is given).
N/A
N/A
N/A
command: gh release verify-asset
short: Verify that a given asset file originated from a specific GitHub Release using cryptographically signed attestations.
long: |-
An attestation is a claim made by GitHub regarding a release and its assets.
This command checks that the asset you provide matches a valid attestation for the specified release (or the latest release, if no tag is given).
N/A
N/A
N/A
command: gh release view
short: View information about a GitHub Release.
long: |-
Without an explicit tag name argument, the latest release in the project
is shown.
For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: jq
shorthand: q
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
"apiUrl",
"author",
"assets",
"body",
"createdAt",
"databaseId",
"id",
"isDraft",
"isPrerelease",
"isImmutable",
"name",
"publishedAt",
"tagName",
"tarballUrl",
"targetCommitish",
"uploadUrl",
"url",
"zipballUrl",
})
}
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "version argument",
args: "v1.2.3",
isTTY: true,
want: ViewOptions{
TagName: "v1.2.3",
WebMode: false,
},
},
{
name: "no arguments",
args: "",
isTTY: true,
want: ViewOptions{
TagName: "",
WebMode: false,
},
},
{
name: "web mode",
args: "-w",
isTTY: true,
func Test_viewRun(t *testing.T) {
oneHourAgo := time.Now().Add(time.Duration(-24) * time.Hour)
frozenTime, err := time.Parse(time.RFC3339, "2020-08-31T15:44:24+02:00")
require.NoError(t, err)
tests := []struct {
name string
isTTY bool
releaseBody string
releasedAt time.Time
opts ViewOptions
wantErr string
wantStdout string
wantStderr string
}{
{
name: "view specific release",
isTTY: true,
releaseBody: `* Fixed bugs\n`,
releasedAt: oneHourAgo,
opts: ViewOptions{
TagName: "v1.2.3",
},
wantStdout: heredoc.Doc(`
v1.2.3
MonaLisa released this about 1 day ago
• Fixed bugs
N/A
command: gh release view
short: View information about a GitHub Release.
long: |-
Without an explicit tag name argument, the latest release in the project
is shown.
For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh release view
short: View information about a GitHub Release.
long: |-
Without an explicit tag name argument, the latest release in the project
is shown.
For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh release view
short: View information about a GitHub Release.
long: |-
Without an explicit tag name argument, the latest release in the project
is shown.
For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh release view
short: View information about a GitHub Release.
long: |-
Without an explicit tag name argument, the latest release in the project
is shown.
For more information about output formatting flags, see `gh help formatting`.
pname: gh release
plink: gh_release.yaml
options:
- option: jq
shorthand: q
N/A
N/A
N/A
command: gh repo short: Work with GitHub repositories. pname: gh plink: gh.yaml
func TestNormalizeRepoName(t *testing.T) {
// confirmed using GitHub.com/new
tests := []struct {
LocalName string
NormalizedName string
}{
{
LocalName: "cli",
NormalizedName: "cli",
},
{
LocalName: "cli.git",
NormalizedName: "cli",
},
{
LocalName: "@-#$^",
NormalizedName: "---",
},
{
LocalName: "[cli]",
NormalizedName: "-cli-",
},
{
LocalName: "Hello World, I'm a new repo!",
NormalizedName: "Hello-World-I-m-a-new-repo-",
},
{
LocalName: " @E3H*(#$#_$-ZVp,n.7lGq*_eMa-(-zAZSJYg!",
NormalizedName: "-E3H-_--ZVp-n.7lGq-_eMa---zAZSJYg-",
},
N/A
command: gh repo archive
short: Archive a GitHub repository.
long: With no argument, archives the current repository.
pname: gh repo
plink: gh_repo.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdArchive(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
output ArchiveOptions
errMsg string
}{
{
name: "no arguments no tty",
input: "",
errMsg: "--yes required when not running interactively",
wantErr: true,
},
{
name: "repo argument tty",
input: "OWNER/REPO --confirm",
output: ArchiveOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: ios,
}
argv, err := shlex.Split(tt.input)
assert.NoError(t, err)
var gotOpts *ArchiveOptions
cmd := NewCmdArchive(f, func(opts *ArchiveOptions) error {
func Test_ArchiveRun(t *testing.T) {
queryResponse := `{ "data": { "repository": { "id": "THE-ID","isArchived": %s} } }`
tests := []struct {
name string
opts ArchiveOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(pm *prompter.MockPrompter)
isTTY bool
wantStdout string
wantStderr string
}{
{
name: "unarchived repo tty",
wantStdout: "✓ Archived repository OWNER/REPO\n",
prompterStubs: func(pm *prompter.MockPrompter) {
pm.RegisterConfirm("Archive OWNER/REPO?", func(_ string, _ bool) (bool, error) {
return true, nil
})
},
isTTY: true,
opts: ArchiveOptions{RepoArg: "OWNER/REPO"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(fmt.Sprintf(queryResponse, "false")))
reg.Register(
httpmock.GraphQL(`mutation ArchiveRepository\b`),
httpmock.StringResponse(`{}`))
},
},
N/A
command: gh repo archive
short: Archive a GitHub repository.
long: With no argument, archives the current repository.
pname: gh repo
plink: gh_repo.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdArchive(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
output ArchiveOptions
errMsg string
}{
{
name: "no arguments no tty",
input: "",
errMsg: "--yes required when not running interactively",
wantErr: true,
},
{
name: "repo argument tty",
input: "OWNER/REPO --confirm",
output: ArchiveOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: ios,
}
argv, err := shlex.Split(tt.input)
assert.NoError(t, err)
var gotOpts *ArchiveOptions
cmd := NewCmdArchive(f, func(opts *ArchiveOptions) error {
N/A
N/A
command: gh repo autolink
short: Autolinks link issues, pull requests, commit messages, and release descriptions to external third-party services.
long: |-
Autolinks require `admin` role to view or manage.
For more information, see <https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/configuring-autolinks-to-reference-external-resources>
pname: gh repo
plink: gh_repo.yaml
options:
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh repo autolink
short: Autolinks link issues, pull requests, commit messages, and release descriptions to external third-party services.
long: |-
Autolinks require `admin` role to view or manage.
For more information, see <https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/managing-repository-settings/configuring-autolinks-to-reference-external-resources>
pname: gh repo
plink: gh_repo.yaml
options:
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh repo autolink create
short: Create a new autolink reference for a repository.
long: |-
The `keyPrefix` argument specifies the prefix that will generate a link when it is appended by certain characters.
The `urlTemplate` argument specifies the target URL that will be generated when the keyPrefix is found, which
must contain `<num>` variable for the reference number.
By default, autolinks are alphanumeric with `--numeric` flag used to create a numeric autolink.
The `<num>` variable behavior differs depending on whether the autolink is alphanumeric or numeric:
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "one argument",
input: "TEST-",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "two argument",
input: "TICKET- https://example.com/TICKET?query=<num>",
output: createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
},
},
{
name: "numeric flag",
func TestCreateRun(t *testing.T) {
tests := []struct {
name string
opts *createOptions
stubCreator stubAutolinkCreator
expectedErr error
errMsg string
wantStdout string
wantStderr string
}{
{
name: "success, alphanumeric",
opts: &createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
},
stubCreator: stubAutolinkCreator{},
wantStdout: "✓ Created repository autolink 1 on OWNER/REPO\n",
},
{
name: "success, numeric",
opts: &createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
Numeric: true,
},
stubCreator: stubAutolinkCreator{},
wantStdout: "✓ Created repository autolink 1 on OWNER/REPO\n",
},
{
N/A
command: gh repo autolink create
short: Create a new autolink reference for a repository.
long: |-
The `keyPrefix` argument specifies the prefix that will generate a link when it is appended by certain characters.
The `urlTemplate` argument specifies the target URL that will be generated when the keyPrefix is found, which
must contain `<num>` variable for the reference number.
By default, autolinks are alphanumeric with `--numeric` flag used to create a numeric autolink.
The `<num>` variable behavior differs depending on whether the autolink is alphanumeric or numeric:
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "one argument",
input: "TEST-",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "two argument",
input: "TICKET- https://example.com/TICKET?query=<num>",
output: createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
},
},
{
name: "numeric flag",
N/A
command: gh repo autolink delete
short: Delete an autolink reference for a repository.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output deleteOptions
isTTY bool
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
isTTY: true,
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
isTTY: true,
output: deleteOptions{ID: "123"},
},
{
name: "yes flag",
input: "123 --yes",
isTTY: true,
output: deleteOptions{ID: "123", Confirmed: true},
},
{
name: "non-TTY",
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
opts *deleteOptions
isTTY bool
stubDeleter stubAutolinkDeleter
stubViewer stubAutolinkViewer
prompterStubs func(*prompter.PrompterMock)
wantStdout string
expectedErr error
expectedErrMsg string
}{
{
name: "delete",
opts: &deleteOptions{
ID: "123",
},
isTTY: true,
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
N/A
command: gh repo autolink delete
short: Delete an autolink reference for a repository.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: "yes"
value_type: bool
default_value: "false"
description: Confirm deletion without prompting
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output deleteOptions
isTTY bool
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
isTTY: true,
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
isTTY: true,
output: deleteOptions{ID: "123"},
},
{
name: "yes flag",
input: "123 --yes",
isTTY: true,
output: deleteOptions{ID: "123", Confirmed: true},
},
{
name: "non-TTY",
N/A
N/A
command: gh repo autolink list
short: Gets all autolink references that are configured for a repository.
long: |-
Information about autolinks is only available to repository administrators.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdList, []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
})
}
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{},
},
{
name: "web flag",
input: "--web",
output: listOptions{WebMode: true},
},
{
name: "json flag",
input: "--json id",
output: listOptions{},
wantExporter: true,
},
{
name: "invalid json flag",
input: "--json invalid",
output: listOptions{},
wantErr: true,
func TestListRun(t *testing.T) {
tests := []struct {
name string
opts *listOptions
isTTY bool
stubLister stubAutolinkLister
expectedErr error
wantStdout string
wantStderr string
}{
{
name: "list tty",
opts: &listOptions{},
isTTY: true,
stubLister: stubAutolinkLister{
autolinks: []shared.Autolink{
{
ID: 1,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
{
ID: 2,
KeyPrefix: "STORY-",
URLTemplate: "https://example.com/STORY?id=<num>",
IsAlphanumeric: false,
},
},
},
N/A
command: gh repo autolink list
short: Gets all autolink references that are configured for a repository.
long: |-
Information about autolinks is only available to repository administrators.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
N/A
N/A
N/A
command: gh repo autolink list
short: Gets all autolink references that are configured for a repository.
long: |-
Information about autolinks is only available to repository administrators.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{},
},
{
name: "web flag",
input: "--web",
output: listOptions{WebMode: true},
},
{
name: "json flag",
input: "--json id",
output: listOptions{},
wantExporter: true,
},
{
name: "invalid json flag",
input: "--json invalid",
output: listOptions{},
wantErr: true,
N/A
N/A
command: gh repo autolink list
short: Gets all autolink references that are configured for a repository.
long: |-
Information about autolinks is only available to repository administrators.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
N/A
N/A
N/A
command: gh repo autolink list
short: Gets all autolink references that are configured for a repository.
long: |-
Information about autolinks is only available to repository administrators.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{},
},
{
name: "web flag",
input: "--web",
output: listOptions{WebMode: true},
},
{
name: "json flag",
input: "--json id",
output: listOptions{},
wantExporter: true,
},
{
name: "invalid json flag",
input: "--json invalid",
output: listOptions{},
wantErr: true,
N/A
N/A
command: gh repo autolink view
short: View an autolink reference for a repository.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
})
}
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
input string
output viewOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
output: viewOptions{ID: "123"},
},
{
name: "json flag",
input: "123 --json id",
output: viewOptions{ID: "123"},
wantExporter: true,
},
{
name: "invalid json flag",
input: "123 --json invalid",
wantErr: true,
func TestViewRun(t *testing.T) {
tests := []struct {
name string
opts *viewOptions
stubViewer stubAutolinkViewer
expectedErr error
wantStdout string
}{
{
name: "view",
opts: &viewOptions{
ID: "1",
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 1,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
wantStdout: heredoc.Doc(`
Autolink in OWNER/REPO
ID: 1
Key Prefix: TICKET-
URL Template: https://example.com/TICKET?query=<num>
Alphanumeric: true
`),
},
N/A
command: gh repo autolink view
short: View an autolink reference for a repository.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh repo autolink view
short: View an autolink reference for a repository.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
input string
output viewOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
output: viewOptions{ID: "123"},
},
{
name: "json flag",
input: "123 --json id",
output: viewOptions{ID: "123"},
wantExporter: true,
},
{
name: "invalid json flag",
input: "123 --json invalid",
wantErr: true,
N/A
N/A
command: gh repo autolink view
short: View an autolink reference for a repository.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo autolink
plink: gh_repo_autolink.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh repo clone
short: Clone a GitHub repository locally. Pass additional `git clone` flags by listing
long: |-
them after `--`.
If the `OWNER/` portion of the `OWNER/REPO` repository argument is omitted, it
defaults to the name of the authenticating user.
When a protocol scheme is not provided in the repository argument, the `git_protocol` will be
chosen from your configuration, which can be checked via `gh config get git_protocol`. If the protocol
scheme is provided, the repository will be cloned using the specified protocol.
func TestNewCmdClone(t *testing.T) {
testCases := []struct {
name string
args string
wantOpts CloneOptions
wantErr string
}{
{
name: "no arguments",
args: "",
wantErr: "cannot clone: repository argument required",
},
{
name: "repo argument",
args: "OWNER/REPO",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{},
},
},
{
name: "directory argument",
args: "OWNER/REPO mydir",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{"mydir"},
},
},
{
name: "git clone arguments",
func Test_RepoClone(t *testing.T) {
tests := []struct {
name string
args string
want string
}{
{
name: "shorthand",
args: "OWNER/REPO",
want: "git clone https://github.com/OWNER/REPO.git",
},
{
name: "shorthand with directory",
args: "OWNER/REPO target_directory",
want: "git clone https://github.com/OWNER/REPO.git target_directory",
},
{
name: "clone arguments",
args: "OWNER/REPO -- -o upstream --depth 1",
want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git",
},
{
name: "clone arguments with directory",
args: "OWNER/REPO target_directory -- -o upstream --depth 1",
want: "git clone -o upstream --depth 1 https://github.com/OWNER/REPO.git target_directory",
},
{
name: "HTTPS URL",
args: "https://github.com/OWNER/REPO",
want: "git clone https://github.com/OWNER/REPO.git",
func Test_RepoClone_hasParent(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"name": "REPO",
"owner": {
"login": "OWNER"
},
"parent": {
"name": "ORIG",
"owner": {
"login": "hubot"
},
"defaultBranchRef": {
"name": "trunk"
}
}
} } }
`))
httpClient := &http.Client{Transport: reg}
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "")
cs.Register(`git -C REPO remote add -t trunk upstream https://github.com/hubot/ORIG.git`, 0, "")
N/A
command: gh repo clone
short: Clone a GitHub repository locally. Pass additional `git clone` flags by listing
long: |-
them after `--`.
If the `OWNER/` portion of the `OWNER/REPO` repository argument is omitted, it
defaults to the name of the authenticating user.
When a protocol scheme is not provided in the repository argument, the `git_protocol` will be
chosen from your configuration, which can be checked via `gh config get git_protocol`. If the protocol
scheme is provided, the repository will be cloned using the specified protocol.
func TestNewCmdClone(t *testing.T) {
testCases := []struct {
name string
args string
wantOpts CloneOptions
wantErr string
}{
{
name: "no arguments",
args: "",
wantErr: "cannot clone: repository argument required",
},
{
name: "repo argument",
args: "OWNER/REPO",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{},
},
},
{
name: "directory argument",
args: "OWNER/REPO mydir",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{"mydir"},
},
},
{
name: "git clone arguments",
func Test_RepoClone_hasParent_noUpstream(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"name": "REPO",
"owner": {
"login": "OWNER"
},
"parent": {
"name": "ORIG",
"owner": {
"login": "hubot"
},
"defaultBranchRef": {
"name": "trunk"
}
}
} } }
`))
httpClient := &http.Client{Transport: reg}
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "")
cs.Register(`git -C REPO config --add remote.origin.gh-resolved base`, 0, "")
func Test_RepoClone_noParent_noUpstream(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"name": "REPO",
"owner": {
"login": "OWNER"
}
} } }
`))
httpClient := &http.Client{Transport: reg}
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "")
_, err := runCloneCommand(httpClient, "OWNER/REPO --no-upstream")
if err != nil {
t.Fatalf("error running command `repo clone`: %v", err)
}
}
N/A
command: gh repo clone
short: Clone a GitHub repository locally. Pass additional `git clone` flags by listing
long: |-
them after `--`.
If the `OWNER/` portion of the `OWNER/REPO` repository argument is omitted, it
defaults to the name of the authenticating user.
When a protocol scheme is not provided in the repository argument, the `git_protocol` will be
chosen from your configuration, which can be checked via `gh config get git_protocol`. If the protocol
scheme is provided, the repository will be cloned using the specified protocol.
func TestNewCmdClone(t *testing.T) {
testCases := []struct {
name string
args string
wantOpts CloneOptions
wantErr string
}{
{
name: "no arguments",
args: "",
wantErr: "cannot clone: repository argument required",
},
{
name: "repo argument",
args: "OWNER/REPO",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{},
},
},
{
name: "directory argument",
args: "OWNER/REPO mydir",
wantOpts: CloneOptions{
Repository: "OWNER/REPO",
GitArgs: []string{"mydir"},
},
},
{
name: "git clone arguments",
func Test_RepoClone_hasParent_upstreamRemoteName(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`
{ "data": { "repository": {
"name": "REPO",
"owner": {
"login": "OWNER"
},
"parent": {
"name": "ORIG",
"owner": {
"login": "hubot"
},
"defaultBranchRef": {
"name": "trunk"
}
}
} } }
`))
httpClient := &http.Client{Transport: reg}
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git clone https://github.com/OWNER/REPO.git`, 0, "")
cs.Register(`git -C REPO remote add -t trunk test https://github.com/hubot/ORIG.git`, 0, "")
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNoRepoCanBeDetermined(t *testing.T) {
// Given no head repo can be determined from git config
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
cs.Register(`git status --porcelain`, 0, "")
cs.Register(`git config --get-regexp \^branch\\\..+\\\.\(remote\|merge\|pushremote\|gh-merge-base\)\$`, 0, "")
cs.Register(`git rev-parse --symbolic-full-name feature@{push}`, 1, "")
cs.Register("git config remote.pushDefault", 1, "")
cs.Register("git config push.default", 1, "")
// And Given there is no remote on the correct SHA
cs.Register(`git show-ref --verify -- HEAD refs/remotes/origin/feature`, 0, heredoc.Doc(`
deadbeef HEAD
deadb00f refs/remotes/origin/feature`))
// When the command is run with no TTY
reg := &httpmock.Registry{}
reg.StubRepoInfoResponse("OWNER", "REPO", "master")
defer reg.Verify(t)
ios, _, _, stderr := iostreams.Test()
opts := CreateOptions{
HttpClient: func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil
},
Config: func() (gh.Config, error) {
return config.NewBlankConfig(), nil
},
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
input string
output createOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "one argument",
input: "TEST-",
wantErr: true,
errMsg: "Cannot create autolink: keyPrefix and urlTemplate arguments are both required",
},
{
name: "two argument",
input: "TICKET- https://example.com/TICKET?query=<num>",
output: createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
},
},
{
name: "numeric flag",
func TestCreateRun(t *testing.T) {
tests := []struct {
name string
opts *createOptions
stubCreator stubAutolinkCreator
expectedErr error
errMsg string
wantStdout string
wantStderr string
}{
{
name: "success, alphanumeric",
opts: &createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
},
stubCreator: stubAutolinkCreator{},
wantStdout: "✓ Created repository autolink 1 on OWNER/REPO\n",
},
{
name: "success, numeric",
opts: &createOptions{
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
Numeric: true,
},
stubCreator: stubAutolinkCreator{},
wantStdout: "✓ Created repository autolink 1 on OWNER/REPO\n",
},
{
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
N/A
N/A
N/A
command: gh repo create
short: Create a new GitHub repository.
long: |-
To create a repository interactively, use `gh repo create` with no arguments.
To create a remote repository non-interactively, supply the repository name and one of `--public`, `--private`, or `--internal`.
Pass `--clone` to clone the new repository locally.
If the `OWNER/` portion of the `OWNER/REPO` name argument is omitted, it
defaults to the name of the authenticating user.
To create a remote repository from an existing local repository, specify the source directory with `--source`.
func TestNewCmdCreate(t *testing.T) {
tests := []struct {
name string
tty bool
cli string
wantsErr bool
errMsg string
wantsOpts CreateOptions
}{
{
name: "no args tty",
tty: true,
cli: "",
wantsOpts: CreateOptions{Interactive: true},
},
{
name: "no args no-tty",
tty: false,
cli: "",
wantsErr: true,
errMsg: "at least one argument required in non-interactive mode",
},
{
name: "new repo from remote",
cli: "NEWREPO --public --clone",
wantsOpts: CreateOptions{
Name: "NEWREPO",
Public: true,
Clone: true},
},
N/A
N/A
command: gh repo delete short: Delete a GitHub repository. long: "With no argument, deletes the current repository. Otherwise, deletes the specified repository.\n\nFor safety, when no repository argument is provided, the `--yes` flag is ignored \nand you will be prompted for confirmation. To delete the current repository non-interactively, \nspecify it explicitly (e.g., `gh repo delete owner/repo --yes`).\n\nDeletion requires authorization with the `delete_repo` scope.\nTo authorize, run `gh auth refresh -s delete_repo`"
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
output deleteOptions
isTTY bool
wantErr bool
errMsg string
}{
{
name: "no argument",
input: "",
isTTY: true,
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
isTTY: true,
output: deleteOptions{ID: "123"},
},
{
name: "yes flag",
input: "123 --yes",
isTTY: true,
output: deleteOptions{ID: "123", Confirmed: true},
},
{
name: "non-TTY",
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
opts *deleteOptions
isTTY bool
stubDeleter stubAutolinkDeleter
stubViewer stubAutolinkViewer
prompterStubs func(*prompter.PrompterMock)
wantStdout string
expectedErr error
expectedErrMsg string
}{
{
name: "delete",
opts: &deleteOptions{
ID: "123",
},
isTTY: true,
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 123,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
stubDeleter: stubAutolinkDeleter{
err: nil,
},
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
tty bool
output DeleteOptions
wantErr bool
wantErrMsg string
wantStderr string
}{
{
name: "confirm flag",
tty: true,
input: "OWNER/REPO --confirm",
output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
{
name: "yes flag",
tty: true,
input: "OWNER/REPO --yes",
output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
{
name: "no confirmation notty",
input: "OWNER/REPO",
output: DeleteOptions{RepoArg: "OWNER/REPO"},
wantErr: true,
wantErrMsg: "--yes required when not running interactively",
},
{
N/A
command: gh repo delete short: Delete a GitHub repository. long: "With no argument, deletes the current repository. Otherwise, deletes the specified repository.\n\nFor safety, when no repository argument is provided, the `--yes` flag is ignored \nand you will be prompted for confirmation. To delete the current repository non-interactively, \nspecify it explicitly (e.g., `gh repo delete owner/repo --yes`).\n\nDeletion requires authorization with the `delete_repo` scope.\nTo authorize, run `gh auth refresh -s delete_repo`"
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
input string
tty bool
output DeleteOptions
wantErr bool
wantErrMsg string
wantStderr string
}{
{
name: "confirm flag",
tty: true,
input: "OWNER/REPO --confirm",
output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
{
name: "yes flag",
tty: true,
input: "OWNER/REPO --yes",
output: DeleteOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
{
name: "no confirmation notty",
input: "OWNER/REPO",
output: DeleteOptions{RepoArg: "OWNER/REPO"},
wantErr: true,
wantErrMsg: "--yes required when not running interactively",
},
{
N/A
N/A
command: gh repo deploy-key
short: Manage deploy keys in a repository
pname: gh repo
plink: gh_repo.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh repo deploy-key
short: Manage deploy keys in a repository
pname: gh repo
plink: gh_repo.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh repo deploy-key add
short: Add a deploy key to a GitHub repository.
long: |-
Note that any key added by gh will be associated with the current authentication token.
If you de-authorize the GitHub CLI app or authentication token from your account, any
deploy keys added by GitHub CLI will be removed as well.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: allow-write
shorthand: w
value_type: bool
func Test_addRun(t *testing.T) {
tests := []struct {
name string
opts AddOptions
isTTY bool
stdin string
httpStubs func(t *testing.T, reg *httpmock.Registry)
wantStdout string
wantStderr string
wantErr bool
}{
{
name: "add from stdin",
isTTY: true,
opts: AddOptions{
KeyFile: "-",
Title: "my sacred key",
AllowWrite: false,
},
stdin: "PUBKEY\n",
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/keys"),
httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) {
if title := payload["title"].(string); title != "my sacred key" {
t.Errorf("POST title %q, want %q", title, "my sacred key")
}
if key := payload["key"].(string); key != "PUBKEY\n" {
t.Errorf("POST key %q, want %q", key, "PUBKEY\n")
}
N/A
command: gh repo deploy-key add
short: Add a deploy key to a GitHub repository.
long: |-
Note that any key added by gh will be associated with the current authentication token.
If you de-authorize the GitHub CLI app or authentication token from your account, any
deploy keys added by GitHub CLI will be removed as well.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: allow-write
shorthand: w
value_type: bool
N/A
N/A
N/A
command: gh repo deploy-key add
short: Add a deploy key to a GitHub repository.
long: |-
Note that any key added by gh will be associated with the current authentication token.
If you de-authorize the GitHub CLI app or authentication token from your account, any
deploy keys added by GitHub CLI will be removed as well.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: allow-write
shorthand: w
value_type: bool
N/A
N/A
N/A
command: gh repo deploy-key delete short: Delete a deploy key from a GitHub repository pname: gh repo deploy-key plink: gh_repo_deploy-key.yaml
func Test_deleteRun(t *testing.T) {
ios, _, stdout, stderr := iostreams.Test()
ios.SetStdinTTY(false)
ios.SetStdoutTTY(true)
ios.SetStderrTTY(true)
tr := httpmock.Registry{}
defer tr.Verify(t)
tr.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/keys/1234"),
httpmock.StringResponse(`{}`))
err := deleteRun(&DeleteOptions{
IO: ios,
HTTPClient: func() (*http.Client, error) {
return &http.Client{Transport: &tr}, nil
},
BaseRepo: func() (ghrepo.Interface, error) {
return ghrepo.New("OWNER", "REPO"), nil
},
KeyID: "1234",
})
assert.NoError(t, err)
assert.Equal(t, "", stderr.String())
assert.Equal(t, "✓ Deploy key deleted from OWNER/REPO\n", stdout.String())
}
N/A
command: gh repo deploy-key list
short: List deploy keys in a GitHub repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
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
}{
{
name: "list tty",
isTTY: true,
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/keys"),
httpmock.StringResponse(fmt.Sprintf(`[
{
"id": 1234,
"key": "ssh-rsa AAAABbBB123",
"title": "Mac",
"created_at": "%[1]s",
"read_only": true
},
{
"id": 5678,
"key": "ssh-rsa EEEEEEEK247",
"title": "hubot@Windows",
"created_at": "%[1]s",
N/A
command: gh repo deploy-key list
short: List deploy keys in a GitHub repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh repo deploy-key list
short: List deploy keys in a GitHub repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh repo deploy-key list
short: List deploy keys in a GitHub repository
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh repo deploy-key
plink: gh_repo_deploy-key.yaml
options:
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
- option: json
value_type: string
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
func Test_editRun(t *testing.T) {
tests := []struct {
name string
opts EditOptions
httpStubs func(*testing.T, *httpmock.Registry)
wantsStderr string
wantsErr string
}{
{
name: "change name and description",
opts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Homepage: sp("newURL"),
Description: sp("hello world!"),
},
},
httpStubs: func(t *testing.T, r *httpmock.Registry) {
r.Register(
httpmock.REST("PATCH", "repos/OWNER/REPO"),
httpmock.RESTPayload(200, `{}`, func(payload map[string]interface{}) {
assert.Equal(t, 2, len(payload))
assert.Equal(t, "newURL", payload["homepage"])
assert.Equal(t, "hello world!", payload["description"])
}))
},
},
{
name: "add and remove topics",
opts: EditOptions{
func Test_editRun_interactive(t *testing.T) {
editList := []string{
"Default Branch Name",
"Description",
"Home Page URL",
"Issues",
"Merge Options",
"Projects",
"Template Repository",
"Topics",
"Visibility",
"Wikis"}
tests := []struct {
name string
opts EditOptions
promptStubs func(*prompter.MockPrompter)
httpStubs func(*testing.T, *httpmock.Registry)
wantsStderr string
wantsErr string
}{
{
name: "forking of org repo",
opts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
InteractiveMode: true,
},
promptStubs: func(pm *prompter.MockPrompter) {
el := append(editList, optionAllowForking)
pm.RegisterMultiSelect("What do you want to edit?", nil, el,
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
N/A
N/A
N/A
command: gh repo edit
short: Edit repository settings.
long: To toggle a setting off, use the `--<flag>=false` syntax.
pname: gh repo
plink: gh_repo.yaml
options:
- option: accept-visibility-change-consequences
value_type: bool
default_value: "false"
description: Accept the consequences of changing the repository visibility
- option: add-topic
value_type: stringSlice
func TestNewCmdEdit(t *testing.T) {
tests := []struct {
name string
args string
wantOpts EditOptions
wantErr string
}{
{
name: "change repo description",
args: "--description hello",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{
Description: sp("hello"),
},
},
},
{
name: "deny public visibility change without accepting consequences",
args: "--visibility public",
wantOpts: EditOptions{
Repository: ghrepo.NewWithHost("OWNER", "REPO", "github.com"),
Edits: EditRepositoryInput{},
},
wantErr: "use of --visibility flag requires --accept-visibility-change-consequences flag",
},
{
name: "allow public visibility change with accepting consequences",
args: "--visibility public --accept-visibility-change-consequences",
wantOpts: EditOptions{
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
func TestRepoFork(t *testing.T) {
forkResult := `{
"node_id": "123",
"name": "REPO",
"clone_url": "https://github.com/someone/repo.git",
"created_at": "2011-01-26T19:01:12Z",
"owner": {
"login": "someone"
}
}`
forkPost := func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/forks"),
httpmock.StringResponse(forkResult))
}
tests := []struct {
name string
opts *ForkOptions
tty bool
httpStubs func(*httpmock.Registry)
execStubs func(*run.CommandStubber)
promptStubs func(*prompter.MockPrompter)
cfgStubs func(*testing.T, gh.Config)
remotes []*context.Remote
wantOut string
wantErrOut string
wantErr bool
errMsg string
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
N/A
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
func TestRepoFork(t *testing.T) {
forkResult := `{
"node_id": "123",
"name": "REPO",
"clone_url": "https://github.com/someone/repo.git",
"created_at": "2011-01-26T19:01:12Z",
"owner": {
"login": "someone"
}
}`
forkPost := func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/forks"),
httpmock.StringResponse(forkResult))
}
tests := []struct {
name string
opts *ForkOptions
tty bool
httpStubs func(*httpmock.Registry)
execStubs func(*run.CommandStubber)
promptStubs func(*prompter.MockPrompter)
cfgStubs func(*testing.T, gh.Config)
remotes []*context.Remote
wantOut string
wantErrOut string
wantErr bool
errMsg string
N/A
N/A
command: gh repo fork
short: Create a fork of a repository.
long: |-
With no argument, creates a fork of the current repository. Otherwise, forks
the specified repository.
By default, the new fork is set to be your `origin` remote and any existing
origin remote is renamed to `upstream`. To alter this behavior, you can set
a name for the new fork's remote with `--remote-name`.
The `upstream` remote will be set as the default remote repository.
func TestNewCmdFork(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ForkOptions
wantErr bool
errMsg string
}{
{
name: "repo with git args",
cli: "foo/bar -- --foo=bar",
wants: ForkOptions{
Repository: "foo/bar",
GitArgs: []string{"--foo=bar"},
RemoteName: "origin",
Rename: true,
},
},
{
name: "git args without repo",
cli: "-- --foo bar",
wantErr: true,
errMsg: "repository argument required when passing git clone flags",
},
{
name: "repo",
cli: "foo/bar",
wants: ForkOptions{
Repository: "foo/bar",
N/A
N/A
command: gh repo gitignore short: List and view available repository gitignore templates pname: gh repo plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo gitignore list short: List available repository gitignore templates pname: gh repo gitignore plink: gh_repo_gitignore.yaml
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
tty bool
}{
{
name: "happy path no arguments",
args: []string{},
wantErr: false,
tty: false,
},
{
name: "too many arguments",
args: []string{"foo"},
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 {
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: "gitignore list tty",
isTTY: true,
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "gitignore/templates"),
httpmock.StringResponse(`[
"AL",
"Actionscript",
"Ada",
"Agda",
"Android",
"AppEngine",
"AppceleratorTitanium",
"ArchLinuxPackages",
"Autotools",
"Ballerina",
"C",
"C++",
N/A
command: gh repo gitignore view
short: View an available repository `.gitignore` template.
long: |-
`<template>` is a case-sensitive `.gitignore` template name.
For a list of available templates, run `gh repo gitignore list`.
pname: gh repo gitignore
plink: gh_repo_gitignore.yaml
func TestViewRun(t *testing.T) {
tests := []struct {
name string
opts *ViewOptions
isTTY bool
httpStubs func(t *testing.T, reg *httpmock.Registry)
wantStdout string
wantStderr string
wantErr bool
errMsg string
}{
{
name: "No template",
opts: &ViewOptions{},
wantErr: true,
errMsg: "no template provided",
},
{
name: "happy path with template",
opts: &ViewOptions{Template: "Go"},
wantErr: false,
httpStubs: func(t *testing.T, reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "gitignore/templates/Go"),
httpmock.StringResponse(`{
"name": "Go",
"source": "# If you prefer the allow list template instead of the deny list, see community template:\n# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore\n#\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, built with go test -c\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Dependency directories (remove the comment below to include it)\n# vendor/\n\n# Go workspace file\ngo.work\ngo.work.sum\n\n# env file\n.env\n"
}`,
))
},
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
wantOpts *ViewOptions
tty bool
}{
{
name: "No template",
args: []string{},
wantErr: true,
wantOpts: &ViewOptions{
Template: "",
},
},
{
name: "Happy path single template",
args: []string{"Go"},
wantErr: false,
wantOpts: &ViewOptions{
Template: "Go",
},
},
{
name: "Happy path too many templates",
args: []string{"Go", "Ruby"},
wantErr: true,
wantOpts: &ViewOptions{
Template: "",
N/A
command: gh repo license short: Explore repository licenses pname: gh repo plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo license list short: List common repository licenses. long: For even more licenses, visit <https://choosealicense.com/appendix> pname: gh repo license plink: gh_repo_license.yaml
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
tty bool
}{
{
name: "happy path no arguments",
args: []string{},
wantErr: false,
tty: false,
},
{
name: "too many arguments",
args: []string{"foo"},
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 {
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",
N/A
command: gh repo license view
short: View a specific repository license by license key or SPDX ID.
long: Run `gh repo license list` to see available commonly used licenses. For even more licenses, visit <https://choosealicense.com/appendix>.
pname: gh repo license
plink: gh_repo_license.yaml
options:
- option: web
shorthand: w
value_type: bool
default_value: "false"
description: Open https://choosealicense.com/ in the browser
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
args []string
wantErr bool
wantOpts *ViewOptions
tty bool
}{
{
name: "No license key or SPDX ID provided",
args: []string{},
wantErr: true,
wantOpts: &ViewOptions{
License: "",
},
},
{
name: "Happy path single license key",
args: []string{"mit"},
wantErr: false,
wantOpts: &ViewOptions{
License: "mit",
},
},
{
name: "Happy path too many license keys",
args: []string{"mit", "apache-2.0"},
wantErr: true,
wantOpts: &ViewOptions{
License: "",
func TestViewRun(t *testing.T) {
tests := []struct {
name string
opts *ViewOptions
isTTY bool
httpStubs func(reg *httpmock.Registry)
wantStdout string
wantStderr string
wantErr bool
errMsg string
wantBrowsedURL string
}{
{
name: "happy path with license no tty",
opts: &ViewOptions{License: "mit"},
wantErr: false,
isTTY: false,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "licenses/mit"),
httpmock.StringResponse(`{
"key": "mit",
"name": "MIT License",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit",
"node_id": "MDc6TGljZW5zZTEz",
"html_url": "http://choosealicense.com/licenses/mit/",
"description": "A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.",
"implementation": "Create a text file (typically named LICENSE or LICENSE.txt) in the root of your source code and copy the text of the license into the file. Replace [year] with the current year and [fullname] with the name (or names) of the copyright holders.",
"permissions": [
N/A
command: gh repo license view
short: View a specific repository license by license key or SPDX ID.
long: Run `gh repo license list` to see available commonly used licenses. For even more licenses, visit <https://choosealicense.com/appendix>.
pname: gh repo license
plink: gh_repo_license.yaml
options:
- option: web
shorthand: w
value_type: bool
default_value: "false"
description: Open https://choosealicense.com/ in the browser
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdList, []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
})
}
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
input string
output listOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
output: listOptions{},
},
{
name: "web flag",
input: "--web",
output: listOptions{WebMode: true},
},
{
name: "json flag",
input: "--json id",
output: listOptions{},
wantExporter: true,
},
{
name: "invalid json flag",
input: "--json invalid",
output: listOptions{},
wantErr: true,
func TestListRun(t *testing.T) {
tests := []struct {
name string
opts *listOptions
isTTY bool
stubLister stubAutolinkLister
expectedErr error
wantStdout string
wantStderr string
}{
{
name: "list tty",
opts: &listOptions{},
isTTY: true,
stubLister: stubAutolinkLister{
autolinks: []shared.Autolink{
{
ID: 1,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
{
ID: 2,
KeyPrefix: "STORY-",
URLTemplate: "https://example.com/STORY?id=<num>",
IsAlphanumeric: false,
},
},
},
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
N/A
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo list
short: List repositories owned by a user or organization.
long: |-
Note that the list will only include repositories owned by the provided argument,
and the `--fork` or `--source` flags will not traverse ownership boundaries. For example,
when listing the forks in an organization, the output would not include those owned by individual users.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
options:
- option: archived
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr string
}{
{
name: "no arguments",
cli: "",
wants: ListOptions{
Limit: 30,
Owner: "",
Visibility: "",
Fork: false,
Source: false,
Language: "",
Topic: []string(nil),
Archived: false,
NonArchived: false,
},
},
{
name: "with owner",
cli: "monalisa",
wants: ListOptions{
Limit: 30,
Owner: "monalisa",
Visibility: "",
Fork: false,
N/A
N/A
command: gh repo rename
short: Rename a GitHub repository.
long: |-
`<new-name>` is the desired repository name without the owner.
By default, the current repository is renamed. Otherwise, the repository specified
with `--repo` is renamed.
To transfer repository ownership to another user account or organization,
you must follow additional steps on `github.com`.
pname: gh repo
plink: gh_repo.yaml
func TestNewCmdRename(t *testing.T) {
testCases := []struct {
name string
input string
output RenameOptions
errMsg string
tty bool
wantErr bool
}{
{
name: "no arguments no tty",
input: "",
errMsg: "new name argument required when not running interactively",
wantErr: true,
},
{
name: "one argument no tty confirmed",
input: "REPO --yes",
output: RenameOptions{
newRepoSelector: "REPO",
},
},
{
name: "one argument no tty",
input: "REPO",
errMsg: "--yes required when passing a single argument",
wantErr: true,
},
{
name: "one argument tty confirmed",
func TestRenameRun(t *testing.T) {
testCases := []struct {
name string
opts RenameOptions
httpStubs func(*httpmock.Registry)
execStubs func(*run.CommandStubber)
promptStubs func(*prompter.MockPrompter)
wantOut string
tty bool
wantErr bool
errMsg string
}{
{
name: "none argument",
wantOut: "✓ Renamed repository OWNER/NEW_REPO\n✓ Updated the \"origin\" remote\n",
promptStubs: func(pm *prompter.MockPrompter) {
pm.RegisterInput("Rename OWNER/REPO to:", func(_, _ string) (string, error) {
return "NEW_REPO", nil
})
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("PATCH", "repos/OWNER/REPO"),
httpmock.StatusStringResponse(200, `{"name":"NEW_REPO","owner":{"login":"OWNER"}}`))
},
execStubs: func(cs *run.CommandStubber) {
cs.Register(`git remote set-url origin https://github.com/OWNER/NEW_REPO.git`, 0, "")
},
tty: true,
},
N/A
command: gh repo rename
short: Rename a GitHub repository.
long: |-
`<new-name>` is the desired repository name without the owner.
By default, the current repository is renamed. Otherwise, the repository specified
with `--repo` is renamed.
To transfer repository ownership to another user account or organization,
you must follow additional steps on `github.com`.
pname: gh repo
plink: gh_repo.yaml
func TestNewCmdRename(t *testing.T) {
testCases := []struct {
name string
input string
output RenameOptions
errMsg string
tty bool
wantErr bool
}{
{
name: "no arguments no tty",
input: "",
errMsg: "new name argument required when not running interactively",
wantErr: true,
},
{
name: "one argument no tty confirmed",
input: "REPO --yes",
output: RenameOptions{
newRepoSelector: "REPO",
},
},
{
name: "one argument no tty",
input: "REPO",
errMsg: "--yes required when passing a single argument",
wantErr: true,
},
{
name: "one argument tty confirmed",
N/A
N/A
command: gh repo rename
short: Rename a GitHub repository.
long: |-
`<new-name>` is the desired repository name without the owner.
By default, the current repository is renamed. Otherwise, the repository specified
with `--repo` is renamed.
To transfer repository ownership to another user account or organization,
you must follow additional steps on `github.com`.
pname: gh repo
plink: gh_repo.yaml
func TestNewCmdRename(t *testing.T) {
testCases := []struct {
name string
input string
output RenameOptions
errMsg string
tty bool
wantErr bool
}{
{
name: "no arguments no tty",
input: "",
errMsg: "new name argument required when not running interactively",
wantErr: true,
},
{
name: "one argument no tty confirmed",
input: "REPO --yes",
output: RenameOptions{
newRepoSelector: "REPO",
},
},
{
name: "one argument no tty",
input: "REPO",
errMsg: "--yes required when passing a single argument",
wantErr: true,
},
{
name: "one argument tty confirmed",
N/A
N/A
command: gh repo set-default
short: This command sets the default remote repository to use when querying the
long: |-
GitHub API for the locally cloned repository.
gh uses the default repository for things like:
- viewing and creating pull requests
- viewing and creating issues
- viewing and creating releases
- working with GitHub Actions
func TestNewCmdSetDefault(t *testing.T) {
tests := []struct {
name string
gitStubs func(*run.CommandStubber)
remotes func() (context.Remotes, error)
input string
output SetDefaultOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
gitStubs: func(cs *run.CommandStubber) {
cs.Register(`git rev-parse --git-dir`, 0, ".git")
},
input: "",
output: SetDefaultOptions{},
},
{
name: "repo argument",
gitStubs: func(cs *run.CommandStubber) {
cs.Register(`git rev-parse --git-dir`, 0, ".git")
},
input: "cli/cli",
output: SetDefaultOptions{Repo: ghrepo.New("cli", "cli")},
},
{
name: "invalid repo argument",
gitStubs: func(cs *run.CommandStubber) {
cs.Register(`git rev-parse --git-dir`, 0, ".git")
N/A
command: gh repo set-default
short: This command sets the default remote repository to use when querying the
long: |-
GitHub API for the locally cloned repository.
gh uses the default repository for things like:
- viewing and creating pull requests
- viewing and creating issues
- viewing and creating releases
- working with GitHub Actions
N/A
N/A
N/A
command: gh repo set-default
short: This command sets the default remote repository to use when querying the
long: |-
GitHub API for the locally cloned repository.
gh uses the default repository for things like:
- viewing and creating pull requests
- viewing and creating issues
- viewing and creating releases
- working with GitHub Actions
N/A
N/A
command: gh repo sync
short: Sync destination repository from source repository. Syncing uses the default branch
long: |-
of the source repository to update the matching branch on the destination
repository so they are equal. A fast forward update will be used except when the
`--force` flag is specified, then the two branches will
be synced using a hard reset.
Without an argument, the local repository is selected as the destination repository.
The source repository is the parent of the destination repository by default.
This can be overridden with the `--source` flag.
func TestNewCmdSync(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output SyncOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
tty: true,
input: "",
output: SyncOptions{},
},
{
name: "destination repo",
tty: true,
input: "cli/cli",
output: SyncOptions{
DestArg: "cli/cli",
},
},
{
name: "source repo",
tty: true,
input: "--source cli/cli",
output: SyncOptions{
SrcArg: "cli/cli",
},
func Test_SyncRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *SyncOptions
remotes []*context.Remote
httpStubs func(*httpmock.Registry)
gitStubs func(*mockGitClient)
wantStdout string
wantErr bool
errMsg string
}{
{
name: "sync local repo with parent - tty",
tty: true,
opts: &SyncOptions{},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
},
gitStubs: func(mgc *mockGitClient) {
mgc.On("Fetch", "origin", "refs/heads/trunk").Return(nil).Once()
mgc.On("HasLocalBranch", "trunk").Return(true).Once()
mgc.On("IsAncestor", "trunk", "FETCH_HEAD").Return(true, nil).Once()
mgc.On("CurrentBranch").Return("trunk", nil).Once()
mgc.On("IsDirty").Return(false, nil).Once()
mgc.On("MergeFastForward", "FETCH_HEAD").Return(nil).Once()
},
wantStdout: "✓ Synced the \"trunk\" branch from \"OWNER/REPO\" to local repository\n",
N/A
command: gh repo sync
short: Sync destination repository from source repository. Syncing uses the default branch
long: |-
of the source repository to update the matching branch on the destination
repository so they are equal. A fast forward update will be used except when the
`--force` flag is specified, then the two branches will
be synced using a hard reset.
Without an argument, the local repository is selected as the destination repository.
The source repository is the parent of the destination repository by default.
This can be overridden with the `--source` flag.
func TestNewCmdSync(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output SyncOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
tty: true,
input: "",
output: SyncOptions{},
},
{
name: "destination repo",
tty: true,
input: "cli/cli",
output: SyncOptions{
DestArg: "cli/cli",
},
},
{
name: "source repo",
tty: true,
input: "--source cli/cli",
output: SyncOptions{
SrcArg: "cli/cli",
},
N/A
command: gh repo sync
short: Sync destination repository from source repository. Syncing uses the default branch
long: |-
of the source repository to update the matching branch on the destination
repository so they are equal. A fast forward update will be used except when the
`--force` flag is specified, then the two branches will
be synced using a hard reset.
Without an argument, the local repository is selected as the destination repository.
The source repository is the parent of the destination repository by default.
This can be overridden with the `--source` flag.
func TestNewCmdSync(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output SyncOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
tty: true,
input: "",
output: SyncOptions{},
},
{
name: "destination repo",
tty: true,
input: "cli/cli",
output: SyncOptions{
DestArg: "cli/cli",
},
},
{
name: "source repo",
tty: true,
input: "--source cli/cli",
output: SyncOptions{
SrcArg: "cli/cli",
},
func Test_SyncRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *SyncOptions
remotes []*context.Remote
httpStubs func(*httpmock.Registry)
gitStubs func(*mockGitClient)
wantStdout string
wantErr bool
errMsg string
}{
{
name: "sync local repo with parent - tty",
tty: true,
opts: &SyncOptions{},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(`{"data":{"repository":{"defaultBranchRef":{"name": "trunk"}}}}`))
},
gitStubs: func(mgc *mockGitClient) {
mgc.On("Fetch", "origin", "refs/heads/trunk").Return(nil).Once()
mgc.On("HasLocalBranch", "trunk").Return(true).Once()
mgc.On("IsAncestor", "trunk", "FETCH_HEAD").Return(true, nil).Once()
mgc.On("CurrentBranch").Return("trunk", nil).Once()
mgc.On("IsDirty").Return(false, nil).Once()
mgc.On("MergeFastForward", "FETCH_HEAD").Return(nil).Once()
},
wantStdout: "✓ Synced the \"trunk\" branch from \"OWNER/REPO\" to local repository\n",
N/A
N/A
command: gh repo sync
short: Sync destination repository from source repository. Syncing uses the default branch
long: |-
of the source repository to update the matching branch on the destination
repository so they are equal. A fast forward update will be used except when the
`--force` flag is specified, then the two branches will
be synced using a hard reset.
Without an argument, the local repository is selected as the destination repository.
The source repository is the parent of the destination repository by default.
This can be overridden with the `--source` flag.
func TestNewCmdSync(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output SyncOptions
wantErr bool
errMsg string
}{
{
name: "no argument",
tty: true,
input: "",
output: SyncOptions{},
},
{
name: "destination repo",
tty: true,
input: "cli/cli",
output: SyncOptions{
DestArg: "cli/cli",
},
},
{
name: "source repo",
tty: true,
input: "--source cli/cli",
output: SyncOptions{
SrcArg: "cli/cli",
},
N/A
command: gh repo unarchive
short: Unarchive a GitHub repository.
long: With no argument, unarchives the current repository.
pname: gh repo
plink: gh_repo.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdUnarchive(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
output UnarchiveOptions
errMsg string
}{
{
name: "no arguments no tty",
input: "",
errMsg: "--yes required when not running interactively",
wantErr: true,
},
{
name: "repo argument tty",
input: "OWNER/REPO --confirm",
output: UnarchiveOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: ios,
}
argv, err := shlex.Split(tt.input)
assert.NoError(t, err)
var gotOpts *UnarchiveOptions
cmd := NewCmdUnarchive(f, func(opts *UnarchiveOptions) error {
func Test_UnarchiveRun(t *testing.T) {
queryResponse := `{ "data": { "repository": { "id": "THE-ID","isArchived": %s} } }`
tests := []struct {
name string
opts UnarchiveOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.PrompterMock)
isTTY bool
wantStdout string
wantStderr string
}{
{
name: "archived repo tty",
wantStdout: "✓ Unarchived repository OWNER/REPO\n",
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmFunc = func(p string, d bool) (bool, error) {
if p == "Unarchive OWNER/REPO?" {
return true, nil
}
return false, prompter.NoSuchPromptErr(p)
}
},
isTTY: true,
opts: UnarchiveOptions{RepoArg: "OWNER/REPO"},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepositoryInfo\b`),
httpmock.StringResponse(fmt.Sprintf(queryResponse, "true")))
reg.Register(
httpmock.GraphQL(`mutation UnarchiveRepository\b`),
N/A
command: gh repo unarchive
short: Unarchive a GitHub repository.
long: With no argument, unarchives the current repository.
pname: gh repo
plink: gh_repo.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdUnarchive(t *testing.T) {
tests := []struct {
name string
input string
wantErr bool
output UnarchiveOptions
errMsg string
}{
{
name: "no arguments no tty",
input: "",
errMsg: "--yes required when not running interactively",
wantErr: true,
},
{
name: "repo argument tty",
input: "OWNER/REPO --confirm",
output: UnarchiveOptions{RepoArg: "OWNER/REPO", Confirmed: true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: ios,
}
argv, err := shlex.Split(tt.input)
assert.NoError(t, err)
var gotOpts *UnarchiveOptions
cmd := NewCmdUnarchive(f, func(opts *UnarchiveOptions) error {
N/A
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdView, []string{
"id",
"isAlphanumeric",
"keyPrefix",
"urlTemplate",
})
}
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
input string
output viewOptions
wantErr bool
wantExporter bool
errMsg string
}{
{
name: "no argument",
input: "",
wantErr: true,
errMsg: "accepts 1 arg(s), received 0",
},
{
name: "id provided",
input: "123",
output: viewOptions{ID: "123"},
},
{
name: "json flag",
input: "123 --json id",
output: viewOptions{ID: "123"},
wantExporter: true,
},
{
name: "invalid json flag",
input: "123 --json invalid",
wantErr: true,
func TestViewRun(t *testing.T) {
tests := []struct {
name string
opts *viewOptions
stubViewer stubAutolinkViewer
expectedErr error
wantStdout string
}{
{
name: "view",
opts: &viewOptions{
ID: "1",
},
stubViewer: stubAutolinkViewer{
autolink: &shared.Autolink{
ID: 1,
KeyPrefix: "TICKET-",
URLTemplate: "https://example.com/TICKET?query=<num>",
IsAlphanumeric: true,
},
},
wantStdout: heredoc.Doc(`
Autolink in OWNER/REPO
ID: 1
Key Prefix: TICKET-
URL Template: https://example.com/TICKET?query=<num>
Alphanumeric: true
`),
},
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
N/A
N/A
N/A
command: gh repo view
short: Display the description and the README of a GitHub repository.
long: |-
With no argument, the repository for the current directory is displayed.
With `--web`, open the repository in a web browser instead.
With `--branch`, view a specific branch of the repository.
For more information about output formatting flags, see `gh help formatting`.
pname: gh repo
plink: gh_repo.yaml
N/A
N/A
N/A
command: gh ruleset
short: Repository rulesets are a way to define a set of rules that apply to a repository.
long: These commands allow you to view information about them.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
command: gh ruleset
short: Repository rulesets are a way to define a set of rules that apply to a repository.
long: These commands allow you to view information about them.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
command: gh ruleset check
short: View information about GitHub rules that apply to a given branch.
long: |-
The provided branch name does not need to exist; rules will be displayed that would apply
to a branch with that name. All rules are returned regardless of where they are configured.
If no branch name is provided, then the current branch will be used.
The `--default` flag can be used to view rules that apply to the default branch of the
repository.
pname: gh ruleset
plink: gh_ruleset.yaml
func Test_NewCmdCheck(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want CheckOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: CheckOptions{
Branch: "",
Default: false,
WebMode: false,
},
},
{
name: "branch name",
args: "my-branch",
isTTY: true,
want: CheckOptions{
Branch: "my-branch",
Default: false,
WebMode: false,
},
},
{
name: "default",
func Test_checkRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts CheckOptions
wantErr string
wantStdout string
wantStderr string
wantBrowse string
}{
{
name: "view rules for branch",
isTTY: true,
opts: CheckOptions{
Branch: "my-branch",
},
wantStdout: heredoc.Doc(`
6 rules apply to branch my-branch in repo my-org/repo-name
- commit_author_email_pattern: [name: ] [negate: false] [operator: ends_with] [pattern: @example.com]
(configured in ruleset 1234 from organization my-org)
- commit_author_email_pattern: [name: ] [negate: false] [operator: ends_with] [pattern: @example.com]
(configured in ruleset 5678 from repository my-org/repo-name)
- commit_message_pattern: [name: ] [negate: false] [operator: starts_with] [pattern: fff]
(configured in ruleset 1234 from organization my-org)
- commit_message_pattern: [name: ] [negate: false] [operator: contains] [pattern: asdf]
(configured in ruleset 5678 from repository my-org/repo-name)
N/A
command: gh ruleset check
short: View information about GitHub rules that apply to a given branch.
long: |-
The provided branch name does not need to exist; rules will be displayed that would apply
to a branch with that name. All rules are returned regardless of where they are configured.
If no branch name is provided, then the current branch will be used.
The `--default` flag can be used to view rules that apply to the default branch of the
repository.
pname: gh ruleset
plink: gh_ruleset.yaml
func Test_NewCmdCheck(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want CheckOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: CheckOptions{
Branch: "",
Default: false,
WebMode: false,
},
},
{
name: "branch name",
args: "my-branch",
isTTY: true,
want: CheckOptions{
Branch: "my-branch",
Default: false,
WebMode: false,
},
},
{
name: "default",
N/A
command: gh ruleset check
short: View information about GitHub rules that apply to a given branch.
long: |-
The provided branch name does not need to exist; rules will be displayed that would apply
to a branch with that name. All rules are returned regardless of where they are configured.
If no branch name is provided, then the current branch will be used.
The `--default` flag can be used to view rules that apply to the default branch of the
repository.
pname: gh ruleset
plink: gh_ruleset.yaml
func Test_NewCmdCheck(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want CheckOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: CheckOptions{
Branch: "",
Default: false,
WebMode: false,
},
},
{
name: "branch name",
args: "my-branch",
isTTY: true,
want: CheckOptions{
Branch: "my-branch",
Default: false,
WebMode: false,
},
},
{
name: "default",
N/A
N/A
command: gh ruleset list
short: List GitHub rulesets for a repository or organization.
long: |-
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the `--repo` flag. You can also use the `--org` flag to list rulesets
configured for the provided organization.
Use the `--parents` flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is `true`.
Your access token must have the `admin:org` scope to use the `--org` flag, which can be granted by running `gh auth refresh -s admin:org`.
pname: gh ruleset
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
Limit: 30,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
{
name: "limit",
args: "--limit 1",
isTTY: true,
want: ListOptions{
Limit: 1,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
func Test_listRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts ListOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
wantBrowse string
}{
{
name: "list repo rulesets",
isTTY: true,
wantStdout: heredoc.Doc(`
Showing 3 of 3 rulesets in OWNER/REPO
ID NAME SOURCE STATUS RULES
4 test OWNER/REPO (repo) evaluate 1
42 asdf OWNER/REPO (repo) active 2
77 foobar Org-Name (org) disabled 4
`),
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.GraphQL(`query RepoRulesetList\b`),
httpmock.FileResponse("./fixtures/rulesetList.json"),
)
},
wantStderr: "",
N/A
command: gh ruleset list
short: List GitHub rulesets for a repository or organization.
long: |-
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the `--repo` flag. You can also use the `--org` flag to list rulesets
configured for the provided organization.
Use the `--parents` flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is `true`.
Your access token must have the `admin:org` scope to use the `--org` flag, which can be granted by running `gh auth refresh -s admin:org`.
pname: gh ruleset
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
Limit: 30,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
{
name: "limit",
args: "--limit 1",
isTTY: true,
want: ListOptions{
Limit: 1,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
N/A
N/A
command: gh ruleset list
short: List GitHub rulesets for a repository or organization.
long: |-
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the `--repo` flag. You can also use the `--org` flag to list rulesets
configured for the provided organization.
Use the `--parents` flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is `true`.
Your access token must have the `admin:org` scope to use the `--org` flag, which can be granted by running `gh auth refresh -s admin:org`.
pname: gh ruleset
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
Limit: 30,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
{
name: "limit",
args: "--limit 1",
isTTY: true,
want: ListOptions{
Limit: 1,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
N/A
command: gh ruleset list
short: List GitHub rulesets for a repository or organization.
long: |-
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the `--repo` flag. You can also use the `--org` flag to list rulesets
configured for the provided organization.
Use the `--parents` flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is `true`.
Your access token must have the `admin:org` scope to use the `--org` flag, which can be granted by running `gh auth refresh -s admin:org`.
pname: gh ruleset
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
Limit: 30,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
{
name: "limit",
args: "--limit 1",
isTTY: true,
want: ListOptions{
Limit: 1,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
N/A
command: gh ruleset list
short: List GitHub rulesets for a repository or organization.
long: |-
If no options are provided, the current repository's rulesets are listed. You can query a different
repository's rulesets by using the `--repo` flag. You can also use the `--org` flag to list rulesets
configured for the provided organization.
Use the `--parents` flag to control whether rulesets configured at higher levels that also apply to the provided
repository or organization should be returned. The default is `true`.
Your access token must have the `admin:org` scope to use the `--org` flag, which can be granted by running `gh auth refresh -s admin:org`.
pname: gh ruleset
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ListOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ListOptions{
Limit: 30,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
{
name: "limit",
args: "--limit 1",
isTTY: true,
want: ListOptions{
Limit: 1,
IncludeParents: true,
WebMode: false,
Organization: "",
},
},
N/A
N/A
command: gh ruleset view
short: View information about a GitHub ruleset.
long: |-
If no ID is provided, an interactive prompt will be used to choose
the ruleset to view.
Use the `--parents` flag to control whether rulesets configured at higher
levels that also apply to the provided repository or organization should
be returned. The default is `true`.
pname: gh ruleset
plink: gh_ruleset.yaml
options:
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ViewOptions{
ID: "",
WebMode: false,
IncludeParents: true,
InteractiveMode: true,
Organization: "",
},
},
{
name: "only ID",
args: "3",
isTTY: true,
want: ViewOptions{
ID: "3",
WebMode: false,
IncludeParents: true,
InteractiveMode: false,
Organization: "",
func Test_viewRun(t *testing.T) {
repoRulesetStdout := heredoc.Doc(`
Test Ruleset
ID: 42
Source: my-owner/repo-name (Repository)
Enforcement: Active
You can bypass: pull requests only
Bypass List
- OrganizationAdmin (ID: 1), mode: always
- RepositoryRole (ID: 5), mode: always
Conditions
- ref_name: [exclude: []] [include: [~ALL]]
Rules
- commit_author_email_pattern: [name: ] [negate: false] [operator: ends_with] [pattern: @example.com]
- commit_message_pattern: [name: ] [negate: false] [operator: contains] [pattern: asdf]
- creation
`)
tests := []struct {
name string
isTTY bool
opts ViewOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.MockPrompter)
wantErr string
wantStdout string
N/A
command: gh ruleset view
short: View information about a GitHub ruleset.
long: |-
If no ID is provided, an interactive prompt will be used to choose
the ruleset to view.
Use the `--parents` flag to control whether rulesets configured at higher
levels that also apply to the provided repository or organization should
be returned. The default is `true`.
pname: gh ruleset
plink: gh_ruleset.yaml
options:
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ViewOptions{
ID: "",
WebMode: false,
IncludeParents: true,
InteractiveMode: true,
Organization: "",
},
},
{
name: "only ID",
args: "3",
isTTY: true,
want: ViewOptions{
ID: "3",
WebMode: false,
IncludeParents: true,
InteractiveMode: false,
Organization: "",
N/A
command: gh ruleset view
short: View information about a GitHub ruleset.
long: |-
If no ID is provided, an interactive prompt will be used to choose
the ruleset to view.
Use the `--parents` flag to control whether rulesets configured at higher
levels that also apply to the provided repository or organization should
be returned. The default is `true`.
pname: gh ruleset
plink: gh_ruleset.yaml
options:
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ViewOptions{
ID: "",
WebMode: false,
IncludeParents: true,
InteractiveMode: true,
Organization: "",
},
},
{
name: "only ID",
args: "3",
isTTY: true,
want: ViewOptions{
ID: "3",
WebMode: false,
IncludeParents: true,
InteractiveMode: false,
Organization: "",
N/A
N/A
command: gh ruleset view
short: View information about a GitHub ruleset.
long: |-
If no ID is provided, an interactive prompt will be used to choose
the ruleset to view.
Use the `--parents` flag to control whether rulesets configured at higher
levels that also apply to the provided repository or organization should
be returned. The default is `true`.
pname: gh ruleset
plink: gh_ruleset.yaml
options:
func Test_NewCmdView(t *testing.T) {
tests := []struct {
name string
args string
isTTY bool
want ViewOptions
wantErr string
}{
{
name: "no arguments",
args: "",
isTTY: true,
want: ViewOptions{
ID: "",
WebMode: false,
IncludeParents: true,
InteractiveMode: true,
Organization: "",
},
},
{
name: "only ID",
args: "3",
isTTY: true,
want: ViewOptions{
ID: "3",
WebMode: false,
IncludeParents: true,
InteractiveMode: false,
Organization: "",
N/A
N/A
command: gh run
short: List, view, and watch recent workflow runs from GitHub Actions.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
func TestNewCmdRun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RunOptions
wantsErr bool
errMsg string
stdin string
}{
{
name: "blank nontty",
wantsErr: true,
errMsg: "workflow ID, name, or filename required when not running interactively",
},
{
name: "blank tty",
tty: true,
wants: RunOptions{
Prompt: true,
},
},
{
name: "ref flag",
tty: true,
cli: "--ref 12345abc",
wants: RunOptions{
Prompt: true,
Ref: "12345abc",
},
func Test_magicFieldValue(t *testing.T) {
f, err := os.CreateTemp(t.TempDir(), "gh-test")
if err != nil {
t.Fatal(err)
}
defer f.Close()
fmt.Fprint(f, "file contents")
ios, _, _, _ := iostreams.Test()
type args struct {
v string
opts RunOptions
}
tests := []struct {
name string
args args
want interface{}
wantErr bool
}{
{
name: "string",
args: args{v: "hello"},
want: "hello",
wantErr: false,
},
{
name: "file",
args: args{
func Test_findInputs(t *testing.T) {
tests := []struct {
name string
YAML []byte
wantErr bool
errMsg string
wantOut []WorkflowInput
}{
{
name: "blank",
YAML: []byte{},
wantErr: true,
errMsg: "invalid YAML file",
},
{
name: "no event specified",
YAML: []byte("name: workflow"),
wantErr: true,
errMsg: "invalid workflow: no 'on' key",
},
{
name: "not workflow_dispatch",
YAML: []byte("name: workflow\non: pull_request"),
wantErr: true,
errMsg: "unable to manually run a workflow without a workflow_dispatch event",
},
{
name: "bad inputs",
YAML: []byte("name: workflow\non:\n workflow_dispatch:\n inputs: lol "),
wantErr: true,
N/A
command: gh run
short: List, view, and watch recent workflow runs from GitHub Actions.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh run cancel
short: Cancel a workflow run
pname: gh run
plink: gh_run.yaml
options:
- option: force
value_type: bool
default_value: "false"
description: Force cancel a workflow run
func TestNewCmdCancel(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants CancelOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: CancelOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "with arg",
cli: "1234",
wants: CancelOptions{
RunID: "1234",
Force: false,
},
},
{
name: "with arg and force flag",
cli: "1234 --force",
func TestRunCancel(t *testing.T) {
inProgressRun := shared.TestRun(1234, shared.InProgress, "")
completedRun := shared.TestRun(4567, shared.Completed, shared.Failure)
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.MockPrompter)
opts *CancelOptions
wantErr bool
wantOut string
errMsg string
}{
{
name: "cancel run",
opts: &CancelOptions{
RunID: "1234",
},
wantErr: false,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/1234"),
httpmock.JSONResponse(inProgressRun))
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
httpmock.JSONResponse(shared.TestWorkflow))
reg.Register(
httpmock.REST("POST", "repos/OWNER/REPO/actions/runs/1234/cancel"),
httpmock.StatusStringResponse(202, "{}"))
},
wantOut: "✓ Request to cancel workflow 1234 submitted.\n",
N/A
command: gh run cancel
short: Cancel a workflow run
pname: gh run
plink: gh_run.yaml
options:
- option: force
value_type: bool
default_value: "false"
description: Force cancel a workflow run
func TestNewCmdCancel(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants CancelOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: CancelOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "with arg",
cli: "1234",
wants: CancelOptions{
RunID: "1234",
Force: false,
},
},
{
name: "with arg and force flag",
cli: "1234 --force",
N/A
N/A
command: gh run delete short: Delete a workflow run pname: gh run plink: gh_run.yaml
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
config string
isTTY bool
opts *DeleteOptions
wantAliases map[string]string
wantStdout string
wantStderr string
wantErrMsg string
}{
{
name: "delete alias",
config: heredoc.Doc(`
aliases:
il: issue list
co: pr checkout
`),
isTTY: true,
opts: &DeleteOptions{
Name: "co",
All: false,
},
wantAliases: map[string]string{
"il": "issue list",
},
wantStderr: "✓ Deleted alias co; was pr checkout\n",
},
{
name: "delete all aliases",
func TestDeleteRun(t *testing.T) {
tests := []struct {
name string
opts DeleteOptions
stubs func(*httpmock.Registry)
tty bool
wantErr bool
wantErrMsg string
wantStderr string
wantStdout string
}{
{
name: "deletes cache tty",
opts: DeleteOptions{Identifier: "123"},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/actions/caches/123"),
httpmock.StatusStringResponse(204, ""),
)
},
tty: true,
wantStdout: "✓ Deleted 1 cache from OWNER/REPO\n",
},
{
name: "deletes cache notty",
opts: DeleteOptions{Identifier: "123"},
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("DELETE", "repos/OWNER/REPO/actions/caches/123"),
httpmock.StatusStringResponse(204, ""),
func Test_deleteRun(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
cancel bool
httpStubs func(*httpmock.Registry)
mockPromptGists bool
noGists bool
wantErr bool
wantStdout string
wantStderr string
}{
{
name: "successfully delete",
opts: &DeleteOptions{
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(shared.Gist{ID: "1234", Files: map[string]*shared.GistFile{"cool.txt": {Filename: "cool.txt"}}}))
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(200, "{}"))
},
wantStdout: "✓ Gist \"cool.txt\" deleted\n",
},
{
name: "successfully delete with prompt",
opts: &DeleteOptions{
Selector: "",
},
N/A
command: gh run download
short: Download artifacts generated by a GitHub Actions workflow run.
long: |-
The contents of each artifact will be extracted under separate directories based on
the artifact name. If only a single artifact is specified, it will be extracted into
the current directory.
By default, this command downloads the latest artifact created and uploaded through
GitHub Actions. Because workflows can delete or overwrite artifacts, `<run-id>`
must be used to select an artifact from a specific workflow run.
pname: gh run
plink: gh_run.yaml
func TestRunDownload(t *testing.T) {
tempDir := t.TempDir()
store := &LiveStore{
outputPath: tempDir,
}
baseOpts := Options{
ArtifactPath: artifactPath,
APIClient: api.NewTestClient(),
OCIClient: oci.MockClient{},
DigestAlgorithm: "sha512",
Owner: "sigstore",
Store: store,
Limit: 30,
Logger: io.NewTestHandler(),
}
t.Run("fetch and store attestations successfully with owner", func(t *testing.T) {
err := runDownload(&baseOpts)
require.NoError(t, err)
artifact, err := artifact.NewDigestedArtifact(baseOpts.OCIClient, baseOpts.ArtifactPath, baseOpts.DigestAlgorithm)
require.NoError(t, err)
expectedFilePath := expectedFilePath(tempDir, artifact.DigestWithAlg())
require.FileExists(t, expectedFilePath)
actualLineCount, err := countLines(expectedFilePath)
require.NoError(t, err)
func Test_downloadRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantStdout string
wantStderr string
wantFiles []string
}{
{
name: "download all assets",
isTTY: true,
opts: DownloadOptions{
TagName: "v1.2.3",
Destination: ".",
Concurrency: 2,
},
httpStubs: func(reg *httpmock.Registry) {
shared.StubFetchRelease(t, reg, "OWNER", "REPO", "v1.2.3", `{
"assets": [
{ "name": "windows-32bit.zip", "size": 12,
"url": "https://api.github.com/assets/1234" },
{ "name": "windows-64bit.zip", "size": 34,
"url": "https://api.github.com/assets/3456" },
{ "name": "linux.tgz", "size": 56,
"url": "https://api.github.com/assets/5678" }
],
"tarball_url": "https://api.github.com/repos/OWNER/REPO/tarball/v1.2.3",
func Test_downloadRun_cloberAndSkip(t *testing.T) {
oldAssetContents := "older copy to be clobbered"
oldZipballContents := "older zipball to be clobbered"
// this should be shorter than oldAssetContents and oldZipballContents
newContents := "somedata"
tests := []struct {
name string
opts DownloadOptions
httpStubs func(*httpmock.Registry)
wantErr string
wantFileSize int64
wantArchiveSize int64
}{
{
name: "no clobber or skip",
opts: DownloadOptions{
TagName: "v1.2.3",
FilePatterns: []string{"windows-64bit.zip"},
Destination: "tmp/packages",
Concurrency: 2,
},
wantErr: "already exists (use `--clobber` to overwrite file or `--skip-existing` to skip file)",
wantFileSize: int64(len(oldAssetContents)),
wantArchiveSize: int64(len(oldZipballContents)),
},
{
name: "clobber",
opts: DownloadOptions{
TagName: "v1.2.3",
N/A
command: gh run download
short: Download artifacts generated by a GitHub Actions workflow run.
long: |-
The contents of each artifact will be extracted under separate directories based on
the artifact name. If only a single artifact is specified, it will be extracted into
the current directory.
By default, this command downloads the latest artifact created and uploaded through
GitHub Actions. Because workflows can delete or overwrite artifacts, `<run-id>`
must be used to select an artifact from a specific workflow run.
pname: gh run
plink: gh_run.yaml
N/A
N/A
N/A
command: gh run download
short: Download artifacts generated by a GitHub Actions workflow run.
long: |-
The contents of each artifact will be extracted under separate directories based on
the artifact name. If only a single artifact is specified, it will be extracted into
the current directory.
By default, this command downloads the latest artifact created and uploaded through
GitHub Actions. Because workflows can delete or overwrite artifacts, `<run-id>`
must be used to select an artifact from a specific workflow run.
pname: gh run
plink: gh_run.yaml
N/A
N/A
N/A
command: gh run download
short: Download artifacts generated by a GitHub Actions workflow run.
long: |-
The contents of each artifact will be extracted under separate directories based on
the artifact name. If only a single artifact is specified, it will be extracted into
the current directory.
By default, this command downloads the latest artifact created and uploaded through
GitHub Actions. Because workflows can delete or overwrite artifacts, `<run-id>`
must be used to select an artifact from a specific workflow run.
pname: gh run
plink: gh_run.yaml
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func Test_listRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleDateString := sampleDate.Format(time.RFC3339)
tests := []struct {
name string
tty bool
capiStubs func(*testing.T, *capi.CapiClientMock)
limit int
web bool
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "viewer-scoped no sessions",
tty: true,
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.ListLatestSessionsForViewerFunc = func(ctx context.Context, limit int) ([]*capi.Session, error) {
return nil, nil
}
},
wantErr: cmdutil.NewNoResultsError("no agent tasks found"),
},
{
name: "viewer-scoped respects --limit",
tty: true,
limit: 999,
func TestListRun(t *testing.T) {
var now = time.Date(2023, 1, 1, 1, 1, 1, 1, time.UTC)
tests := []struct {
name string
opts ListOptions
stubs func(*httpmock.Registry)
tty bool
wantErr bool
wantErrMsg string
wantStderr string
wantStdout string
}{
{
name: "displays results tty",
tty: true,
stubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/caches"),
httpmock.JSONResponse(shared.CachePayload{
ActionsCaches: []shared.Cache{
{
Id: 1,
Key: "foo",
CreatedAt: time.Date(2021, 1, 1, 1, 1, 1, 1, time.UTC),
LastAccessedAt: time.Date(2022, 1, 1, 1, 1, 1, 1, time.UTC),
SizeInBytes: 100,
},
{
Id: 2,
Key: "bar",
func Test_listRun(t *testing.T) {
tests := []struct {
name string
input *ListOptions
config gh.Config
stdout string
wantErr bool
}{
{
name: "list",
config: func() gh.Config {
cfg := config.NewBlankConfig()
cfg.Set("HOST", "git_protocol", "ssh")
cfg.Set("HOST", "editor", "/usr/bin/vim")
cfg.Set("HOST", "prompt", "disabled")
cfg.Set("HOST", "prefer_editor_prompt", "enabled")
cfg.Set("HOST", "pager", "less")
cfg.Set("HOST", "http_unix_socket", "")
cfg.Set("HOST", "browser", "brave")
return cfg
}(),
input: &ListOptions{Hostname: "HOST"},
stdout: heredoc.Doc(`
git_protocol=ssh
editor=/usr/bin/vim
prompt=disabled
prefer_editor_prompt=enabled
pager=less
http_unix_socket=
browser=brave
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
N/A
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
N/A
command: gh run list
short: List recent workflow runs.
long: |-
Note that providing the `workflow_name` to the `-w` flag will not fetch disabled workflows.
Also pass the `-a` flag to fetch disabled workflow runs using the `workflow_name` and the `-w` flag.
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
To see runs associated with a pull request, users should run `gh pr checks`.
For more information about output formatting flags, see `gh help formatting`.
pname: gh run
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ListOptions
wantsErr bool
}{
{
name: "blank",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "limit",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "bad limit",
cli: "--limit hi",
wantsErr: true,
},
{
name: "workflow",
cli: "--workflow foo.yml",
wants: ListOptions{
N/A
command: gh run rerun
short: Rerun an entire run, only failed jobs, or a specific job from a run.
long: Note that due to historical reasons, the `--job` flag may not take what you expect.
pname: gh run
plink: gh_run.yaml
options:
- option: debug
shorthand: d
value_type: bool
default_value: "false"
description: Rerun with debug logging
- option: failed
func TestNewCmdRerun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RerunOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: RerunOptions{
Prompt: true,
},
},
{
name: "with arg nontty",
cli: "1234",
wants: RerunOptions{
RunID: "1234",
},
},
{
name: "with arg tty",
tty: true,
cli: "1234",
func TestRerun(t *testing.T) {
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.MockPrompter)
opts *RerunOptions
tty bool
wantErr bool
errOut string
wantOut string
wantDebug bool
}{
{
name: "arg",
tty: true,
opts: &RerunOptions{
RunID: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/1234"),
httpmock.JSONResponse(shared.FailedRun))
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/123"),
httpmock.JSONResponse(workflowShared.WorkflowsPayload{
Workflows: []workflowShared.Workflow{
shared.TestWorkflow,
},
}))
reg.Register(
N/A
command: gh run rerun
short: Rerun an entire run, only failed jobs, or a specific job from a run.
long: Note that due to historical reasons, the `--job` flag may not take what you expect.
pname: gh run
plink: gh_run.yaml
options:
- option: debug
shorthand: d
value_type: bool
default_value: "false"
description: Rerun with debug logging
- option: failed
func TestNewCmdRerun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RerunOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: RerunOptions{
Prompt: true,
},
},
{
name: "with arg nontty",
cli: "1234",
wants: RerunOptions{
RunID: "1234",
},
},
{
name: "with arg tty",
tty: true,
cli: "1234",
N/A
N/A
command: gh run rerun
short: Rerun an entire run, only failed jobs, or a specific job from a run.
long: Note that due to historical reasons, the `--job` flag may not take what you expect.
pname: gh run
plink: gh_run.yaml
options:
- option: debug
shorthand: d
value_type: bool
default_value: "false"
description: Rerun with debug logging
- option: failed
func TestNewCmdRerun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RerunOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: RerunOptions{
Prompt: true,
},
},
{
name: "with arg nontty",
cli: "1234",
wants: RerunOptions{
RunID: "1234",
},
},
{
name: "with arg tty",
tty: true,
cli: "1234",
N/A
N/A
command: gh run rerun
short: Rerun an entire run, only failed jobs, or a specific job from a run.
long: Note that due to historical reasons, the `--job` flag may not take what you expect.
pname: gh run
plink: gh_run.yaml
options:
- option: debug
shorthand: d
value_type: bool
default_value: "false"
description: Rerun with debug logging
- option: failed
func TestNewCmdRerun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RerunOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: RerunOptions{
Prompt: true,
},
},
{
name: "with arg nontty",
cli: "1234",
wants: RerunOptions{
RunID: "1234",
},
},
{
name: "with arg tty",
tty: true,
cli: "1234",
N/A
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func Test_viewRun(t *testing.T) {
sampleDate := time.Now().Add(-6 * time.Hour) // 6h ago
sampleCompletedAt := sampleDate.Add(5 * time.Minute)
tests := []struct {
name string
tty bool
opts ViewOptions
promptStubs func(*testing.T, *prompter.MockPrompter)
capiStubs func(*testing.T, *capi.CapiClientMock)
logRendererStubs func(*testing.T, *shared.LogRendererMock)
jsonFields []string
wantOut string
wantErr error
wantStderr string
wantBrowserURL string
}{
{
name: "with session id, not found (tty)",
tty: true,
opts: ViewOptions{
SelectorArg: "some-session-id",
SessionID: "some-session-id",
},
capiStubs: func(t *testing.T, m *capi.CapiClientMock) {
m.GetSessionFunc = func(_ context.Context, _ string) (*capi.Session, error) {
return nil, capi.ErrSessionNotFound
}
},
wantStderr: "session not found\n",
func Test_viewRun(t *testing.T) {
tests := []struct {
name string
opts *ViewOptions
wantOut string
mockGist *shared.Gist
mockGistList bool
isTTY bool
wantErr string
}{
{
name: "no such gist",
isTTY: false,
opts: &ViewOptions{
Selector: "1234",
ListFiles: false,
},
wantErr: "not found",
},
{
name: "one file",
isTTY: true,
opts: &ViewOptions{
Selector: "1234",
ListFiles: false,
},
mockGist: &shared.Gist{
Files: map[string]*shared.GistFile{
"cicada.txt": {
Content: "bwhiizzzbwhuiiizzzz",
func TestRunView_User(t *testing.T) {
defer gock.Off()
// get user ID
gock.New("https://api.github.com").
Post("/graphql").
MatchType("json").
JSON(map[string]interface{}{
"query": "query UserOrgOwner.*",
"variables": map[string]interface{}{
"login": "monalisa",
},
}).
Reply(200).
JSON(map[string]interface{}{
"data": map[string]interface{}{
"user": map[string]interface{}{
"id": "an ID",
},
},
"errors": []interface{}{
map[string]interface{}{
"type": "NOT_FOUND",
"path": []string{"organization"},
},
},
})
gock.New("https://api.github.com").
Post("/graphql").
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
func TestViewRun(t *testing.T) {
emptyZipArchive := createZipArchive(t, map[string]string{})
zipArchive := createZipArchive(t, map[string]string{
"0_cool job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3`),
"cool job/1_fob the barz.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"cool job/2_barz the fob.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"1_sad job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3
`),
"sad job/1_barf the quux.txt": heredoc.Doc(`
log line 1
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
N/A
N/A
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestViewRun(t *testing.T) {
emptyZipArchive := createZipArchive(t, map[string]string{})
zipArchive := createZipArchive(t, map[string]string{
"0_cool job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3`),
"cool job/1_fob the barz.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"cool job/2_barz the fob.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"1_sad job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3
`),
"sad job/1_barf the quux.txt": heredoc.Doc(`
log line 1
N/A
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
func TestViewRun(t *testing.T) {
emptyZipArchive := createZipArchive(t, map[string]string{})
zipArchive := createZipArchive(t, map[string]string{
"0_cool job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3`),
"cool job/1_fob the barz.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"cool job/2_barz the fob.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"1_sad job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3
`),
"sad job/1_barf the quux.txt": heredoc.Doc(`
log line 1
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
func TestViewRun(t *testing.T) {
emptyZipArchive := createZipArchive(t, map[string]string{})
zipArchive := createZipArchive(t, map[string]string{
"0_cool job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3`),
"cool job/1_fob the barz.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"cool job/2_barz the fob.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
`),
"1_sad job.txt": heredoc.Doc(`
log line 1
log line 2
log line 3
log line 1
log line 2
log line 3
`),
"sad job/1_barf the quux.txt": heredoc.Doc(`
log line 1
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
N/A
N/A
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
N/A
N/A
command: gh run view
short: View a summary of a workflow run.
long: |-
Due to platform limitations, `gh` may not always be able to associate jobs with their
corresponding logs when using the primary method of fetching logs in zip format.
In such cases, `gh` will attempt to fetch logs for each job individually via the API.
This fallback is slower and more resource-intensive. If more than 25 job logs are missing,
the operation will fail with an error.
Additionally, due to similar platform constraints, some log lines may not be
associated with a specific step within a job. In these cases, the step name will
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "web tty",
tty: true,
cli: "--web",
wants: ViewOptions{
Prompt: true,
Web: true,
},
},
{
name: "web nontty",
N/A
N/A
command: gh run watch
short: Watch a run until it completes, showing its progress.
long: |-
By default, all steps are displayed. The `--compact` option can be used to only
show the relevant/failed steps.
This command does not support authenticating via fine grained PATs
as it is not currently possible to create a PAT with the `checks:read` permission.
pname: gh run
plink: gh_run.yaml
options:
- option: compact
func TestNewCmdWatch(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants WatchOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: WatchOptions{
Prompt: true,
Interval: defaultInterval,
},
},
{
name: "interval",
tty: true,
cli: "-i10",
wants: WatchOptions{
Interval: 10,
Prompt: true,
},
},
{
func TestWatchRun(t *testing.T) {
failedRunStubs := func(reg *httpmock.Registry) {
inProgressRun := shared.TestRunWithCommit(2, shared.InProgress, "", "commit2")
completedRun := shared.TestRun(2, shared.Completed, shared.Failure)
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs"),
httpmock.JSONResponse(shared.RunsPayload{
WorkflowRuns: []shared.Run{
shared.TestRunWithCommit(1, shared.InProgress, "", "commit1"),
inProgressRun,
},
}))
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/2"),
httpmock.JSONResponse(inProgressRun))
reg.Register(
httpmock.REST("GET", "runs/2/jobs"),
httpmock.JSONResponse(shared.JobsPayload{
Jobs: []shared.Job{}}))
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs/2"),
httpmock.JSONResponse(completedRun))
reg.Register(
httpmock.REST("GET", "runs/2/jobs"),
httpmock.JSONResponse(shared.JobsPayload{
Jobs: []shared.Job{
shared.FailedJob,
},
}))
reg.Register(
N/A
command: gh run watch
short: Watch a run until it completes, showing its progress.
long: |-
By default, all steps are displayed. The `--compact` option can be used to only
show the relevant/failed steps.
This command does not support authenticating via fine grained PATs
as it is not currently possible to create a PAT with the `checks:read` permission.
pname: gh run
plink: gh_run.yaml
options:
- option: compact
func TestNewCmdWatch(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants WatchOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: WatchOptions{
Prompt: true,
Interval: defaultInterval,
},
},
{
name: "interval",
tty: true,
cli: "-i10",
wants: WatchOptions{
Interval: 10,
Prompt: true,
},
},
{
N/A
command: gh run watch
short: Watch a run until it completes, showing its progress.
long: |-
By default, all steps are displayed. The `--compact` option can be used to only
show the relevant/failed steps.
This command does not support authenticating via fine grained PATs
as it is not currently possible to create a PAT with the `checks:read` permission.
pname: gh run
plink: gh_run.yaml
options:
- option: compact
func TestNewCmdWatch(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants WatchOptions
wantsErr bool
}{
{
name: "blank nontty",
wantsErr: true,
},
{
name: "blank tty",
tty: true,
wants: WatchOptions{
Prompt: true,
Interval: defaultInterval,
},
},
{
name: "interval",
tty: true,
cli: "-i10",
wants: WatchOptions{
Interval: 10,
Prompt: true,
},
},
{
N/A
N/A
command: gh run watch
short: Watch a run until it completes, showing its progress.
long: |-
By default, all steps are displayed. The `--compact` option can be used to only
show the relevant/failed steps.
This command does not support authenticating via fine grained PATs
as it is not currently possible to create a PAT with the `checks:read` permission.
pname: gh run
plink: gh_run.yaml
options:
- option: compact
N/A
N/A
N/A
command: gh search
short: Search across all of GitHub.
long: |-
Excluding search results that match a qualifier
In a browser, the GitHub search syntax supports excluding results that match a search qualifier
by prefixing the qualifier with a hyphen. For example, to search for issues that
do not have the label "bug", you would use `-label:bug` as a search qualifier.
`gh` supports this syntax in `gh search` as well, but it requires extra
command line arguments to avoid the hyphen being interpreted as a command line flag because it begins with a hyphen.
func TestSearchRun_UnsupportedHost(t *testing.T) {
ios, _, _, _ := iostreams.Test()
cfg := config.NewBlankConfig()
authCfg := cfg.Authentication()
authCfg.SetDefaultHost("acme.ghes.com", "user")
cfg.AuthenticationFunc = func() gh.AuthConfig {
return authCfg
}
err := searchRun(&SearchOptions{
IO: ios,
Query: "terraform",
Page: 1,
Limit: defaultLimit,
HttpClient: func() (*http.Client, error) { return &http.Client{}, nil },
Config: func() (gh.Config, error) { return cfg, nil },
})
require.ErrorContains(t, err, "does not currently support GitHub Enterprise Server")
}
func TestNewCmdSearch(t *testing.T) {
tests := []struct {
name string
args string
wantOpts SearchOptions
wantErr string
}{
{
name: "query argument",
args: "terraform",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: defaultLimit},
},
{
name: "with page flag",
args: "terraform --page 3",
wantOpts: SearchOptions{Query: "terraform", Page: 3, Limit: defaultLimit},
},
{
name: "with limit flag",
args: "terraform --limit 5",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: 5},
},
{
name: "with limit short flag",
args: "terraform -L 10",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: 10},
},
{
name: "with owner flag",
args: "terraform --owner hashicorp",
func TestSearchRun(t *testing.T) {
const emptyCodeResponse = `{"total_count": 0, "incomplete_results": false, "items": []}`
// stubKeywordSearch registers the HTTP stubs needed for a keyword search.
// searchByKeyword fires up to 3 concurrent search/code requests (path,
// owner, primary). Stubs are one-shot in httpmock, so we register one
// per request.
stubKeywordSearch := func(reg *httpmock.Registry, codeResponse string) {
for range 3 {
reg.Register(
httpmock.REST("GET", "search/code"),
httpmock.StringResponse(codeResponse),
)
}
}
tests := []struct {
name string
opts *SearchOptions
tty bool
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
wantErr string
}{
{
name: "displays results in non-TTY",
tty: false,
opts: &SearchOptions{Query: "terraform", Page: 1, Limit: defaultLimit},
httpStubs: func(reg *httpmock.Registry) {
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
func TestNewCmdCode(t *testing.T) {
tests := []struct {
name string
input string
output CodeOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "react lifecycle",
output: CodeOptions{
Query: search.Query{
Keywords: []string{"react", "lifecycle"},
Kind: "code",
Limit: 30,
},
},
},
{
name: "web flag",
input: "--web",
output: CodeOptions{
Query: search.Query{
func TestCodeRun(t *testing.T) {
var query = search.Query{
Keywords: []string{"map"},
Kind: "code",
Limit: 30,
Qualifiers: search.Qualifiers{
Language: "go",
Repo: []string{"cli/cli"},
},
}
tests := []struct {
errMsg string
name string
opts *CodeOptions
tty bool
wantErr bool
wantStderr string
wantStdout string
wantBrowse string
}{
{
name: "displays results tty",
opts: &CodeOptions{
Query: query,
Searcher: &search.SearcherMock{
CodeFunc: func(query search.Query) (search.CodeResult, error) {
return search.CodeResult{
IncompleteResults: false,
Items: []search.Code{
{
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
func TestNewCmdCode(t *testing.T) {
tests := []struct {
name string
input string
output CodeOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "react lifecycle",
output: CodeOptions{
Query: search.Query{
Keywords: []string{"react", "lifecycle"},
Kind: "code",
Limit: 30,
},
},
},
{
name: "web flag",
input: "--web",
output: CodeOptions{
Query: search.Query{
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
N/A
N/A
N/A
command: gh search code
short: Search within code in GitHub repositories.
pname: gh search
plink: gh_search.yaml
options:
- option: extension
value_type: string
description: Filter on file extension
- option: filename
value_type: string
description: Filter on filename
- option: jq
func TestNewCmdCode(t *testing.T) {
tests := []struct {
name string
input string
output CodeOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "react lifecycle",
output: CodeOptions{
Query: search.Query{
Keywords: []string{"react", "lifecycle"},
Kind: "code",
Limit: 30,
},
},
},
{
name: "web flag",
input: "--web",
output: CodeOptions{
Query: search.Query{
func TestCodeRun(t *testing.T) {
var query = search.Query{
Keywords: []string{"map"},
Kind: "code",
Limit: 30,
Qualifiers: search.Qualifiers{
Language: "go",
Repo: []string{"cli/cli"},
},
}
tests := []struct {
errMsg string
name string
opts *CodeOptions
tty bool
wantErr bool
wantStderr string
wantStdout string
wantBrowse string
}{
{
name: "displays results tty",
opts: &CodeOptions{
Query: query,
Searcher: &search.SearcherMock{
CodeFunc: func(query search.Query) (search.CodeResult, error) {
return search.CodeResult{
IncompleteResults: false,
Items: []search.Code{
{
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
func TestNewCmdCommits(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output CommitsOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: CommitsOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "commits", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: CommitsOptions{
Query: search.Query{Keywords: []string{}, Kind: "commits", Limit: 30},
WebMode: true,
},
},
func TestCommitsRun(t *testing.T) {
var now = time.Date(2023, 1, 17, 12, 30, 0, 0, time.UTC)
var author = search.CommitUser{Date: time.Date(2022, 12, 27, 11, 30, 0, 0, time.UTC)}
var committer = search.CommitUser{Date: time.Date(2022, 12, 28, 12, 30, 0, 0, time.UTC)}
var query = search.Query{
Keywords: []string{"cli"},
Kind: "commits",
Limit: 30,
Qualifiers: search.Qualifiers{},
}
tests := []struct {
errMsg string
name string
opts *CommitsOptions
tty bool
wantErr bool
wantStderr string
wantStdout string
}{
{
name: "displays results tty",
opts: &CommitsOptions{
Query: query,
Searcher: &search.SearcherMock{
CommitsFunc: func(query search.Query) (search.CommitsResult, error) {
return search.CommitsResult{
IncompleteResults: false,
Items: []search.Commit{
{
Author: search.User{Login: "monalisa"},
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
func TestNewCmdCommits(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output CommitsOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: CommitsOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "commits", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: CommitsOptions{
Query: search.Query{Keywords: []string{}, Kind: "commits", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
func TestNewCmdCommits(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output CommitsOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: CommitsOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "commits", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: CommitsOptions{
Query: search.Query{Keywords: []string{}, Kind: "commits", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
N/A
N/A
N/A
command: gh search commits
short: Search for commits on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: author
value_type: string
description: Filter by author
- option: author-date
func TestNewCmdCommits(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output CommitsOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: CommitsOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "commits", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: CommitsOptions{
Query: search.Query{Keywords: []string{}, Kind: "commits", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search issues
short: Search for issues on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdIssues(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "issue"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
N/A
N/A
N/A
command: gh search prs
short: Search for pull requests on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: app
value_type: string
description: Filter by GitHub App author
- option: archived
func TestNewCmdPrs(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output shared.IssuesOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: shared.IssuesOptions{
Query: search.Query{
Keywords: []string{"some", "search", "terms"},
Kind: "issues",
Limit: 30,
Qualifiers: search.Qualifiers{Type: "pr"},
},
},
},
{
name: "web flag",
input: "--web",
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
func TestNewCmdRepos(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output ReposOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: ReposOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "repositories", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: ReposOptions{
Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30},
WebMode: true,
},
},
func TestReposRun(t *testing.T) {
var now = time.Date(2022, 2, 28, 12, 30, 0, 0, time.UTC)
var updatedAt = time.Date(2021, 2, 28, 12, 30, 0, 0, time.UTC)
var query = search.Query{
Keywords: []string{"cli"},
Kind: "repositories",
Limit: 30,
Qualifiers: search.Qualifiers{
Stars: ">50",
Topic: []string{"golang"},
},
}
tests := []struct {
errMsg string
name string
opts *ReposOptions
tty bool
wantErr bool
wantStderr string
wantStdout string
}{
{
name: "displays results tty",
opts: &ReposOptions{
Query: query,
Searcher: &search.SearcherMock{
RepositoriesFunc: func(query search.Query) (search.RepositoriesResult, error) {
return search.RepositoriesResult{
IncompleteResults: false,
Items: []search.Repository{
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
func TestNewCmdRepos(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output ReposOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: ReposOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "repositories", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: ReposOptions{
Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
func TestNewCmdRepos(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output ReposOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: ReposOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "repositories", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: ReposOptions{
Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
N/A
N/A
command: gh search repos
short: Search for repositories on GitHub.
long: |-
The command supports constructing queries using the GitHub search syntax,
using the parameter and qualifier flags, or a combination of the two.
pname: gh search
plink: gh_search.yaml
options:
- option: archived
value_type: bool
default_value: "false"
description: Filter based on the repository archived state {true|false}
func TestNewCmdRepos(t *testing.T) {
var trueBool = true
tests := []struct {
name string
input string
output ReposOptions
wantErr bool
errMsg string
}{
{
name: "no arguments",
input: "",
wantErr: true,
errMsg: "specify search keywords or flags",
},
{
name: "keyword arguments",
input: "some search terms",
output: ReposOptions{
Query: search.Query{Keywords: []string{"some", "search", "terms"}, Kind: "repositories", Limit: 30},
},
},
{
name: "web flag",
input: "--web",
output: ReposOptions{
Query: search.Query{Keywords: []string{}, Kind: "repositories", Limit: 30},
WebMode: true,
},
},
N/A
N/A
command: gh secret
short: Secrets can be set at the repository, or organization level for use in
long: |-
GitHub Actions, Agents, or Dependabot. User, organization, and repository secrets can be set for
use in GitHub Codespaces. Environment secrets can be set for use in
GitHub Actions. Run `gh help secret set` to learn how to get started.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
N/A
N/A
command: gh secret
short: Secrets can be set at the repository, or organization level for use in
long: |-
GitHub Actions, Agents, or Dependabot. User, organization, and repository secrets can be set for
use in GitHub Codespaces. Environment secrets can be set for use in
GitHub Actions. Run `gh help secret set` to learn how to get started.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
N/A
N/A
N/A
command: gh secret delete
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Delete a secret for a specific application: {actions|agents|codespaces|dependabot}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
SecretName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
SecretName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
multipleRemotes := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "fork"),
},
&ghContext.Remote{
Remote: &git.Remote{
Name: "upstream",
},
Repo: ghrepo.New("owner", "repo"),
},
}
singleRemote := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "repo"),
},
}
tests := []struct {
name string
args string
env map[string]string
remotes ghContext.Remotes
func Test_removeRun_repo(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
host string
httpStubs func(*httpmock.Registry)
}{
{
name: "Actions",
opts: &DeleteOptions{
Application: "actions",
SecretName: "cool_secret",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/actions/secrets/cool_secret"), "api.github.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "Actions GHES",
opts: &DeleteOptions{
Application: "actions",
SecretName: "cool_secret",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/actions/secrets/cool_secret"), "example.com"), httpmock.StatusStringResponse(204, "No Content"))
},
},
{
N/A
command: gh secret delete
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Delete a secret for a specific application: {actions|agents|codespaces|dependabot}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
SecretName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
SecretName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
N/A
N/A
command: gh secret delete
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Delete a secret for a specific application: {actions|agents|codespaces|dependabot}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
SecretName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
SecretName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
N/A
N/A
command: gh secret delete
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Delete a secret for a specific application: {actions|agents|codespaces|dependabot}'
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
SecretName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
SecretName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
N/A
N/A
command: gh secret delete
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Delete a secret for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
}{
{
name: "repo",
cli: "",
wants: ListOptions{
OrgName: "",
},
},
{
name: "org",
cli: "-oUmbrellaCorporation",
wants: ListOptions{
OrgName: "UmbrellaCorporation",
},
},
{
name: "env",
cli: "-eDevelopment",
wants: ListOptions{
EnvName: "Development",
},
},
{
name: "user",
cli: "-u",
func TestNewCmdListBaseRepoFuncs(t *testing.T) {
multipleRemotes := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "fork"),
},
&ghContext.Remote{
Remote: &git.Remote{
Name: "upstream",
},
Repo: ghrepo.New("owner", "repo"),
},
}
singleRemote := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "repo"),
},
}
tests := []struct {
name string
args string
env map[string]string
remotes ghContext.Remotes
func Test_listRun(t *testing.T) {
tests := []struct {
name string
tty bool
json bool
opts *ListOptions
wantOut []string
}{
{
name: "repo tty",
tty: true,
opts: &ListOptions{},
wantOut: []string{
"NAME UPDATED",
"SECRET_ONE about 34 years ago",
"SECRET_TWO about 2 years ago",
"SECRET_THREE about 47 years ago",
},
},
{
name: "repo not tty",
tty: false,
opts: &ListOptions{},
wantOut: []string{
"SECRET_ONE\t1988-10-11T00:00:00Z",
"SECRET_TWO\t2020-12-04T00:00:00Z",
"SECRET_THREE\t1975-11-30T00:00:00Z",
},
},
{
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
}{
{
name: "repo",
cli: "",
wants: ListOptions{
OrgName: "",
},
},
{
name: "org",
cli: "-oUmbrellaCorporation",
wants: ListOptions{
OrgName: "UmbrellaCorporation",
},
},
{
name: "env",
cli: "-eDevelopment",
wants: ListOptions{
EnvName: "Development",
},
},
{
name: "user",
cli: "-u",
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
func Test_NewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
}{
{
name: "repo",
cli: "",
wants: ListOptions{
OrgName: "",
},
},
{
name: "org",
cli: "-oUmbrellaCorporation",
wants: ListOptions{
OrgName: "UmbrellaCorporation",
},
},
{
name: "env",
cli: "-eDevelopment",
wants: ListOptions{
EnvName: "Development",
},
},
{
name: "user",
cli: "-u",
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret list
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'List secrets for a specific application: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
multipleRemotes := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "fork"),
},
&ghContext.Remote{
Remote: &git.Remote{
Name: "upstream",
},
Repo: ghrepo.New("owner", "repo"),
},
}
singleRemote := ghContext.Remotes{
&ghContext.Remote{
Remote: &git.Remote{
Name: "origin",
},
Repo: ghrepo.New("owner", "repo"),
},
}
tests := []struct {
name string
args string
env map[string]string
remotes ghContext.Remotes
func Test_setRun_repo(t *testing.T) {
tests := []struct {
name string
opts *SetOptions
wantApp string
}{
{
name: "Actions",
opts: &SetOptions{
Application: "actions",
},
wantApp: "actions",
},
{
name: "Agents",
opts: &SetOptions{
Application: "agents",
},
wantApp: "agents",
},
{
name: "Dependabot",
opts: &SetOptions{
Application: "dependabot",
},
wantApp: "dependabot",
},
{
name: "defaults to Actions",
opts: &SetOptions{
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
N/A
N/A
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
N/A
command: gh secret set
pname: gh secret
plink: gh_secret.yaml
options:
- option: app
shorthand: a
value_type: string
description: 'Set the application for a secret: {actions|agents|codespaces|dependabot}'
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
args string
wants SetOptions
stdinTTY bool
wantsErr bool
wantsErrMessage string
}{
{
name: "invalid visibility",
args: "cool_secret --org coolOrg -v mistyVeil",
wantsErr: true,
},
{
name: "when visibility is selected, requires indication of repos",
args: "cool_secret --org coolOrg -v selected",
wantsErr: true,
wantsErrMessage: "`--repos` or `--no-repos-selected` required with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --repos",
args: "cool_secret --org coolOrg -v private -r coolRepo",
wantsErr: true,
wantsErrMessage: "`--repos` is only supported with `--visibility=selected`",
},
{
name: "visibilities other than selected do not accept --no-repos-selected",
args: "cool_secret --org coolOrg -v private --no-repos-selected",
wantsErr: true,
command: gh skill
short: Install and manage agent skills from GitHub repositories.
long: |-
Working with agent skills in the GitHub CLI is in preview and
subject to change without notice.
pname: gh
plink: gh.yaml
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
func TestNewCmdInstall(t *testing.T) {
tests := []struct {
name string
cli string
wantOpts InstallOptions
wantLocalPath bool
wantErr bool
}{
{
name: "repo argument only",
cli: "monalisa/skills-repo",
wantOpts: InstallOptions{SkillSource: "monalisa/skills-repo", Scope: "project"},
},
{
name: "repo and skill",
cli: "monalisa/skills-repo git-commit",
wantOpts: InstallOptions{SkillSource: "monalisa/skills-repo", SkillName: "git-commit", Scope: "project"},
},
{
name: "all flags",
cli: "monalisa/skills-repo git-commit --agent github-copilot --scope user --pin v1.0.0 --force",
wantOpts: InstallOptions{
SkillSource: "monalisa/skills-repo",
SkillName: "git-commit",
Agent: "github-copilot",
Scope: "user",
Pin: "v1.0.0",
Force: true,
},
},
t.Run("command metadata", func(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{IOStreams: ios, Prompter: &prompter.PrompterMock{}, GitClient: &git.Client{}}
cmd := NewCmdInstall(f, &telemetry.NoOpService{}, nil)
assert.Equal(t, "install <repository> [<skill[@version]>] [flags]", cmd.Use)
assert.NotEmpty(t, cmd.Short)
assert.NotEmpty(t, cmd.Long)
assert.NotEmpty(t, cmd.Example)
assert.Contains(t, cmd.Aliases, "add")
for _, flag := range []string{"agent", "scope", "pin", "dir", "force"} {
assert.NotNil(t, cmd.Flags().Lookup(flag), "missing flag: --%s", flag)
}
})
func TestInstallRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
setup func(t *testing.T)
stubs func(*httpmock.Registry)
opts func(ios *iostreams.IOStreams, reg *httpmock.Registry) *InstallOptions
verify func(t *testing.T)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "non-interactive without repo errors",
isTTY: false,
opts: func(ios *iostreams.IOStreams, _ *httpmock.Registry) *InstallOptions {
t.Helper()
return &InstallOptions{
IO: ios,
GitClient: &git.Client{RepoDir: t.TempDir()},
}
},
wantErr: "must specify a repository to install from",
},
{
name: "non-interactive without skill name errors",
isTTY: false,
stubs: func(reg *httpmock.Registry) {
stubResolveVersion(reg, "monalisa", "skills-repo", "v1.0.0", "abc123")
stubDiscoverTree(reg, "monalisa", "skills-repo", "abc123",
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
command: gh skill install
short: Install agent skills from a GitHub repository or local directory into
long: |-
your local environment. Skills are placed in a host-specific directory
at either project scope (inside the current git repository) or user
N/A
N/A
N/A
command: gh skill preview
short: Render a skill's `SKILL.md` content in the terminal. This fetches the
long: |-
skill file from the repository and displays it using the configured
pager, without installing anything.
A file tree is shown first, followed by the rendered `SKILL.md` content.
When running interactively and the skill contains additional files
(scripts, references, etc.), a file picker lets you browse them
func TestNewCmdPreview(t *testing.T) {
tests := []struct {
name string
input string
wantRepo string
wantSkillName string
wantVersion string
wantAllowHiddenDirs bool
wantErr bool
}{
{
name: "repo and skill",
input: "github/awesome-copilot my-skill",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
},
{
name: "repo and skill with version",
input: "github/awesome-copilot my-skill@v1.2.0",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
wantVersion: "v1.2.0",
},
{
name: "repo and skill with SHA",
input: "github/awesome-copilot my-skill@abc123def456",
wantRepo: "github/awesome-copilot",
wantSkillName: "my-skill",
wantVersion: "abc123def456",
},
func TestPreviewRun(t *testing.T) {
skillContent := heredoc.Doc(`
---
name: my-skill
description: A test skill
---
# My Skill
This is the skill content.
`)
encodedContent := base64.StdEncoding.EncodeToString([]byte(skillContent))
tests := []struct {
name string
opts *PreviewOptions
tty bool
httpStubs func(*httpmock.Registry)
wantStdout string
wantErr string
}{
{
name: "preview specific skill",
tty: true,
opts: &PreviewOptions{
repo: ghrepo.New("github", "awesome-copilot"),
SkillName: "my-skill",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/github/awesome-copilot/releases/latest"),
func TestPreviewRun_UnsupportedHost(t *testing.T) {
ios, _, _, _ := iostreams.Test()
err := previewRun(&PreviewOptions{
IO: ios,
HttpClient: func() (*http.Client, error) { return &http.Client{}, nil },
repo: ghrepo.NewWithHost("github", "awesome-copilot", "acme.ghes.com"),
Telemetry: &telemetry.NoOpService{},
})
require.ErrorContains(t, err, "does not currently support GitHub Enterprise Server")
}
N/A
command: gh skill preview
short: Render a skill's `SKILL.md` content in the terminal. This fetches the
long: |-
skill file from the repository and displays it using the configured
pager, without installing anything.
A file tree is shown first, followed by the rendered `SKILL.md` content.
When running interactively and the skill contains additional files
(scripts, references, etc.), a file picker lets you browse them
N/A
N/A
N/A
command: gh skill publish
short: Validate a local repository's skills against the Agent Skills specification
long: and publish them by creating a GitHub release.
pname: gh skill
plink: gh_skill.yaml
options:
- option: dry-run
value_type: bool
default_value: "false"
description: Validate without publishing
- option: fix
value_type: bool
func TestNewCmdPublish(t *testing.T) {
tests := []struct {
name string
cli string
wantsErr bool
wantsOpts PublishOptions
}{
{
name: "fix and dry-run are mutually exclusive",
cli: "./monalisa-skills --dry-run --fix --tag v1.0.0",
wantsErr: true,
},
{
name: "fix flag only",
cli: "--fix",
wantsOpts: PublishOptions{
Fix: true,
},
},
{
name: "directory only",
cli: "./octocat-repo",
wantsOpts: PublishOptions{
Dir: "./octocat-repo",
},
},
{
name: "no args leaves dir empty",
cli: "",
wantsOpts: PublishOptions{},
func TestPublishRun_UnsupportedHost(t *testing.T) {
dir := t.TempDir()
writeSkill(t, dir, "test-skill", heredoc.Doc(`
---
name: test-skill
description: A test skill
---
Body.
`))
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
stubGitRemote(cs, map[string]string{"origin": "https://github.com/monalisa/skills-repo.git"})
ios, _, _, _ := iostreams.Test()
err := publishRun(&PublishOptions{
IO: ios,
Dir: dir,
GitClient: newTestGitClient(),
HttpClient: func() (*http.Client, error) { return nil, nil },
host: "acme.ghes.com",
})
require.ErrorContains(t, err, "does not currently support GitHub Enterprise Server")
}
func TestPublishRun(t *testing.T) {
tests := []struct {
name string
isTTY bool
setup func(t *testing.T, dir string)
stubs func(*httpmock.Registry)
cmdStubs func(*run.CommandStubber)
opts func(ios *iostreams.IOStreams, dir string, reg *httpmock.Registry) *PublishOptions
verify func(t *testing.T, dir string)
wantErr string
wantStdout string
wantStderr string
}{
{
name: "no skills found",
setup: func(_ *testing.T, _ string) {},
opts: func(ios *iostreams.IOStreams, dir string, _ *httpmock.Registry) *PublishOptions {
t.Helper()
return &PublishOptions{IO: ios, Dir: dir}
},
wantErr: "no skills found",
},
{
name: "empty skills directory has no discoverable skills",
setup: func(t *testing.T, dir string) {
t.Helper()
require.NoError(t, os.MkdirAll(filepath.Join(dir, "skills", "empty-skill"), 0o755))
},
opts: func(ios *iostreams.IOStreams, dir string, _ *httpmock.Registry) *PublishOptions {
t.Helper()
N/A
command: gh skill publish
short: Validate a local repository's skills against the Agent Skills specification
long: and publish them by creating a GitHub release.
pname: gh skill
plink: gh_skill.yaml
options:
- option: dry-run
value_type: bool
default_value: "false"
description: Validate without publishing
- option: fix
value_type: bool
N/A
N/A
command: gh skill publish
short: Validate a local repository's skills against the Agent Skills specification
long: and publish them by creating a GitHub release.
pname: gh skill
plink: gh_skill.yaml
options:
- option: dry-run
value_type: bool
default_value: "false"
description: Validate without publishing
- option: fix
value_type: bool
N/A
N/A
command: gh skill publish
short: Validate a local repository's skills against the Agent Skills specification
long: and publish them by creating a GitHub release.
pname: gh skill
plink: gh_skill.yaml
options:
- option: dry-run
value_type: bool
default_value: "false"
description: Validate without publishing
- option: fix
value_type: bool
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
func TestSearchRun_UnsupportedHost(t *testing.T) {
ios, _, _, _ := iostreams.Test()
cfg := config.NewBlankConfig()
authCfg := cfg.Authentication()
authCfg.SetDefaultHost("acme.ghes.com", "user")
cfg.AuthenticationFunc = func() gh.AuthConfig {
return authCfg
}
err := searchRun(&SearchOptions{
IO: ios,
Query: "terraform",
Page: 1,
Limit: defaultLimit,
HttpClient: func() (*http.Client, error) { return &http.Client{}, nil },
Config: func() (gh.Config, error) { return cfg, nil },
})
require.ErrorContains(t, err, "does not currently support GitHub Enterprise Server")
}
func TestNewCmdSearch(t *testing.T) {
tests := []struct {
name string
args string
wantOpts SearchOptions
wantErr string
}{
{
name: "query argument",
args: "terraform",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: defaultLimit},
},
{
name: "with page flag",
args: "terraform --page 3",
wantOpts: SearchOptions{Query: "terraform", Page: 3, Limit: defaultLimit},
},
{
name: "with limit flag",
args: "terraform --limit 5",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: 5},
},
{
name: "with limit short flag",
args: "terraform -L 10",
wantOpts: SearchOptions{Query: "terraform", Page: 1, Limit: 10},
},
{
name: "with owner flag",
args: "terraform --owner hashicorp",
func TestSearchRun(t *testing.T) {
const emptyCodeResponse = `{"total_count": 0, "incomplete_results": false, "items": []}`
// stubKeywordSearch registers the HTTP stubs needed for a keyword search.
// searchByKeyword fires up to 3 concurrent search/code requests (path,
// owner, primary). Stubs are one-shot in httpmock, so we register one
// per request.
stubKeywordSearch := func(reg *httpmock.Registry, codeResponse string) {
for range 3 {
reg.Register(
httpmock.REST("GET", "search/code"),
httpmock.StringResponse(codeResponse),
)
}
}
tests := []struct {
name string
opts *SearchOptions
tty bool
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
wantErr string
}{
{
name: "displays results in non-TTY",
tty: false,
opts: &SearchOptions{Query: "terraform", Page: 1, Limit: defaultLimit},
httpStubs: func(reg *httpmock.Registry) {
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
command: gh skill search
short: Search across all public GitHub repositories for skills matching a keyword.
long: |-
Uses the GitHub Code Search API to find `SKILL.md` files whose name or
description matches the query term.
Results are ranked by relevance: skills whose name contains the query
term appear first.
Use `--owner` to scope results to a specific GitHub user or organization.
In interactive mode, you can select skills from the results to install directly.
N/A
N/A
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
func TestNewCmdUpdate_Help(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{
IOStreams: ios,
Prompter: &prompter.PrompterMock{},
GitClient: &git.Client{},
}
cmd := NewCmdUpdate(f, func(opts *UpdateOptions) error {
return nil
})
assert.Equal(t, "update [<skill>...] [flags]", cmd.Use)
assert.NotEmpty(t, cmd.Short)
assert.NotEmpty(t, cmd.Long)
assert.NotEmpty(t, cmd.Example)
}
func TestNewCmdUpdate_Flags(t *testing.T) {
ios, _, _, _ := iostreams.Test()
f := &cmdutil.Factory{IOStreams: ios, Prompter: &prompter.PrompterMock{}, GitClient: &git.Client{}}
cmd := NewCmdUpdate(f, func(_ *UpdateOptions) error { return nil })
flags := []string{"all", "force", "dry-run", "dir", "unpin"}
for _, name := range flags {
assert.NotNil(t, cmd.Flags().Lookup(name), "missing flag: --%s", name)
}
}
func TestNewCmdUpdate_ArgsPassedToOptions(t *testing.T) {
ios, _, stdout, stderr := iostreams.Test()
f := &cmdutil.Factory{IOStreams: ios, Prompter: &prompter.PrompterMock{}, GitClient: &git.Client{}}
var gotOpts *UpdateOptions
cmd := NewCmdUpdate(f, func(opts *UpdateOptions) error {
gotOpts = opts
return nil
})
args, _ := shlex.Split("mcp-cli git-commit --all --force")
cmd.SetArgs(args)
cmd.SetOut(stdout)
cmd.SetErr(stderr)
err := cmd.Execute()
require.NoError(t, err)
assert.Equal(t, []string{"mcp-cli", "git-commit"}, gotOpts.Skills)
assert.True(t, gotOpts.All)
assert.True(t, gotOpts.Force)
}
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
N/A
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
N/A
N/A
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
N/A
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
N/A
N/A
command: gh skill update
short: Checks installed skills for available updates by comparing the local
long: |-
tree SHA (from `SKILL.md` frontmatter) against the remote repository.
N/A
N/A
command: gh ssh-key short: Manage SSH keys registered with your GitHub account. pname: gh plink: gh.yaml
N/A
N/A
N/A
command: gh ssh-key add
short: Add an SSH key to your GitHub account
pname: gh ssh-key
plink: gh_ssh-key.yaml
options:
- option: title
shorthand: t
value_type: string
description: Title for the new key
- option: type
value_type: string
description: 'Type of the ssh key: {authentication|signing} (default "authentication")'
func Test_runAdd(t *testing.T) {
tests := []struct {
name string
stdin string
opts AddOptions
httpStubs func(*httpmock.Registry)
wantStdout string
wantStderr string
wantErrMsg string
}{
{
name: "valid key format, not already in use",
stdin: "ssh-ed25519 asdf",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "user/keys"),
httpmock.StringResponse("[]"))
reg.Register(
httpmock.REST("POST", "user/keys"),
httpmock.RESTPayload(200, ``, func(payload map[string]interface{}) {
assert.Contains(t, payload, "key")
assert.Empty(t, payload["title"])
}))
},
wantStdout: "",
wantStderr: "✓ Public key added to your account\n",
wantErrMsg: "",
opts: AddOptions{KeyFile: "-"},
},
{
N/A
command: gh ssh-key add
short: Add an SSH key to your GitHub account
pname: gh ssh-key
plink: gh_ssh-key.yaml
options:
- option: title
shorthand: t
value_type: string
description: Title for the new key
- option: type
value_type: string
description: 'Type of the ssh key: {authentication|signing} (default "authentication")'
N/A
N/A
N/A
command: gh ssh-key add
short: Add an SSH key to your GitHub account
pname: gh ssh-key
plink: gh_ssh-key.yaml
options:
- option: title
shorthand: t
value_type: string
description: Title for the new key
- option: type
value_type: string
description: 'Type of the ssh key: {authentication|signing} (default "authentication")'
N/A
N/A
N/A
command: gh ssh-key delete
short: Delete an SSH key from your GitHub account
pname: gh ssh-key
plink: gh_ssh-key.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "tty",
tty: true,
input: "123",
output: DeleteOptions{KeyID: "123", Confirmed: false},
},
{
name: "confirm flag tty",
tty: true,
input: "123 --yes",
output: DeleteOptions{KeyID: "123", Confirmed: true},
},
{
name: "shorthand confirm flag tty",
tty: true,
input: "123 -y",
output: DeleteOptions{KeyID: "123", Confirmed: true},
},
{
name: "no tty",
input: "123",
func Test_deleteRun(t *testing.T) {
keyResp := "{\"title\":\"My Key\"}"
tests := []struct {
name string
tty bool
opts DeleteOptions
httpStubs func(*httpmock.Registry)
prompterStubs func(*prompter.PrompterMock)
wantStdout string
wantErr bool
wantErrMsg string
}{
{
name: "delete tty",
tty: true,
opts: DeleteOptions{KeyID: "123"},
prompterStubs: func(pm *prompter.PrompterMock) {
pm.ConfirmDeletionFunc = func(_ string) error {
return nil
}
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", "user/keys/123"), httpmock.StatusStringResponse(200, keyResp))
reg.Register(httpmock.REST("DELETE", "user/keys/123"), httpmock.StatusStringResponse(204, ""))
},
wantStdout: "✓ SSH key \"My Key\" (123) deleted from your account\n",
},
{
name: "delete with confirm flag tty",
tty: true,
N/A
command: gh ssh-key delete
short: Delete an SSH key from your GitHub account
pname: gh ssh-key
plink: gh_ssh-key.yaml
options:
- option: "yes"
shorthand: "y"
value_type: bool
default_value: "false"
description: Skip the confirmation prompt
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
tty bool
input string
output DeleteOptions
wantErr bool
wantErrMsg string
}{
{
name: "tty",
tty: true,
input: "123",
output: DeleteOptions{KeyID: "123", Confirmed: false},
},
{
name: "confirm flag tty",
tty: true,
input: "123 --yes",
output: DeleteOptions{KeyID: "123", Confirmed: true},
},
{
name: "shorthand confirm flag tty",
tty: true,
input: "123 -y",
output: DeleteOptions{KeyID: "123", Confirmed: true},
},
{
name: "no tty",
input: "123",
N/A
N/A
command: gh ssh-key list short: Lists SSH keys in your GitHub account pname: gh ssh-key plink: gh_ssh-key.yaml
func TestListRun(t *testing.T) {
tests := []struct {
name string
opts ListOptions
isTTY bool
wantStdout string
wantStderr string
wantErr bool
}{
{
name: "list authentication and signing keys; in tty",
opts: ListOptions{
HTTPClient: func() (*http.Client, error) {
createdAt := time.Now().Add(time.Duration(-24) * time.Hour)
reg := &httpmock.Registry{}
reg.Register(
httpmock.REST("GET", "user/keys"),
httpmock.StringResponse(fmt.Sprintf(`[
{
"id": 1234,
"key": "ssh-rsa AAAABbBB123",
"title": "Mac",
"created_at": "%[1]s"
},
{
"id": 5678,
"key": "ssh-rsa EEEEEEEK247",
"title": "hubot@Windows",
"created_at": "%[1]s"
}
N/A
command: gh status
pname: gh
plink: gh.yaml
options:
- option: exclude
shorthand: e
value_type: stringSlice
description: Comma separated list of repos to exclude in owner/name format
- option: org
shorthand: o
value_type: string
description: Report status within an organization
func Test_NewCmdStatus(t *testing.T) {
tests := []struct {
name string
cli string
wants StatusOptions
}{
{
name: "no arguments",
cli: "",
wants: StatusOptions{},
},
{
name: "hostname set",
cli: "--hostname ellie.williams",
wants: StatusOptions{
Hostname: "ellie.williams",
},
},
{
name: "show token",
cli: "--show-token",
wants: StatusOptions{
ShowToken: true,
},
},
{
name: "active",
cli: "--active",
wants: StatusOptions{
Active: true,
func TestJSONFields(t *testing.T) {
jsonfieldstest.ExpectCommandToSupportJSONFields(t, NewCmdStatus, []string{
"hosts",
})
}
func Test_statusRun(t *testing.T) {
tests := []struct {
name string
opts StatusOptions
jsonFields []string
env map[string]string
httpStubs func(*httpmock.Registry)
cfgStubs func(*testing.T, gh.Config)
wantErr error
wantOut string
wantErrOut string
}{
{
name: "timeout error",
opts: StatusOptions{
Hostname: "github.com",
},
cfgStubs: func(t *testing.T, c gh.Config) {
login(t, c, "github.com", "monalisa", "abc123", "https")
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("GET", ""), func(req *http.Request) (*http.Response, error) {
// timeout error
return nil, context.DeadlineExceeded
})
},
wantErr: cmdutil.SilentError,
wantErrOut: heredoc.Doc(`
github.com
X Timeout trying to log in to github.com account monalisa (GH_CONFIG_DIR/hosts.yml)
N/A
command: gh status
pname: gh
plink: gh.yaml
options:
- option: exclude
shorthand: e
value_type: stringSlice
description: Comma separated list of repos to exclude in owner/name format
- option: org
shorthand: o
value_type: string
description: Report status within an organization
N/A
N/A
N/A
command: gh status
pname: gh
plink: gh.yaml
options:
- option: exclude
shorthand: e
value_type: stringSlice
description: Comma separated list of repos to exclude in owner/name format
- option: org
shorthand: o
value_type: string
description: Report status within an organization
N/A
N/A
N/A
command: gh variable
short: Variables can be set at the repository, environment or organization level for use in
long: GitHub Actions or Dependabot. Run `gh help variable set` to learn how to get started.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh variable
short: Variables can be set at the repository, environment or organization level for use in
long: GitHub Actions or Dependabot. Run `gh help variable set` to learn how to get started.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh variable delete
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Delete a variable for an environment
- option: org
shorthand: o
value_type: string
description: Delete a variable for an organization
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
VariableName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
VariableName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
func TestRemoveRun(t *testing.T) {
tests := []struct {
name string
opts *DeleteOptions
host string
httpStubs func(*httpmock.Registry)
}{
{
name: "repo",
opts: &DeleteOptions{
VariableName: "cool_variable",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "repos/owner/repo/actions/variables/cool_variable"), "api.github.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
name: "repo GHES",
opts: &DeleteOptions{
VariableName: "cool_variable",
},
host: "example.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("DELETE", "api/v3/repos/owner/repo/actions/variables/cool_variable"), "example.com"),
httpmock.StatusStringResponse(204, "No Content"))
},
},
{
N/A
command: gh variable delete
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Delete a variable for an environment
- option: org
shorthand: o
value_type: string
description: Delete a variable for an organization
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
VariableName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
VariableName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
N/A
N/A
command: gh variable delete
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Delete a variable for an environment
- option: org
shorthand: o
value_type: string
description: Delete a variable for an organization
func TestNewCmdDelete(t *testing.T) {
tests := []struct {
name string
cli string
wants DeleteOptions
wantsErr bool
}{
{
name: "no args",
wantsErr: true,
},
{
name: "repo",
cli: "cool",
wants: DeleteOptions{
VariableName: "cool",
},
},
{
name: "org",
cli: "cool --org anOrg",
wants: DeleteOptions{
VariableName: "cool",
OrgName: "anOrg",
},
},
{
name: "env",
cli: "cool --env anEnv",
wants: DeleteOptions{
N/A
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
func TestNewCmdGet(t *testing.T) {
tests := []struct {
name string
cli string
wants GetOptions
wantErr error
}{
{
name: "repo",
cli: "FOO",
wants: GetOptions{
OrgName: "",
VariableName: "FOO",
},
},
{
name: "org",
cli: "-o TestOrg BAR",
wants: GetOptions{
OrgName: "TestOrg",
VariableName: "BAR",
},
},
{
name: "env",
cli: "-e Development BAZ",
wants: GetOptions{
EnvName: "Development",
VariableName: "BAZ",
},
func Test_getRun(t *testing.T) {
tf, _ := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
tests := []struct {
name string
opts *GetOptions
host string
httpStubs func(*httpmock.Registry)
jsonFields []string
wantOut string
wantErr error
}{
{
name: "getting repo variable",
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
},
host: "github.com",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.WithHost(httpmock.REST("GET", "repos/owner/repo/actions/variables/VARIABLE_ONE"), "api.github.com"),
httpmock.JSONResponse(shared.Variable{
Value: "repo_var",
}))
},
wantOut: "repo_var\n",
},
{
name: "getting GHES repo variable",
opts: &GetOptions{
VariableName: "VARIABLE_ONE",
func TestExportVariables(t *testing.T) {
ios, _, stdout, _ := iostreams.Test()
tf, _ := time.Parse(time.RFC3339, "2024-01-01T00:00:00Z")
vs := &shared.Variable{
Name: "v1",
Value: "test1",
UpdatedAt: tf,
CreatedAt: tf,
Visibility: shared.All,
SelectedReposURL: "https://someurl.com",
NumSelectedRepos: 1,
}
exporter := cmdutil.NewJSONExporter()
exporter.SetFields(shared.VariableJSONFields)
require.NoError(t, exporter.Write(ios, vs))
require.JSONEq(t,
`{"name":"v1","numSelectedRepos":1,"selectedReposURL":"https://someurl.com","updatedAt":"2024-01-01T00:00:00Z","createdAt":"2024-01-01T00:00:00Z","value":"test1","visibility":"all"}`,
stdout.String())
}
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
func TestNewCmdGet(t *testing.T) {
tests := []struct {
name string
cli string
wants GetOptions
wantErr error
}{
{
name: "repo",
cli: "FOO",
wants: GetOptions{
OrgName: "",
VariableName: "FOO",
},
},
{
name: "org",
cli: "-o TestOrg BAR",
wants: GetOptions{
OrgName: "TestOrg",
VariableName: "BAR",
},
},
{
name: "env",
cli: "-e Development BAZ",
wants: GetOptions{
EnvName: "Development",
VariableName: "BAZ",
},
N/A
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
func TestNewCmdGet(t *testing.T) {
tests := []struct {
name string
cli string
wants GetOptions
wantErr error
}{
{
name: "repo",
cli: "FOO",
wants: GetOptions{
OrgName: "",
VariableName: "FOO",
},
},
{
name: "org",
cli: "-o TestOrg BAR",
wants: GetOptions{
OrgName: "TestOrg",
VariableName: "BAR",
},
},
{
name: "env",
cli: "-e Development BAZ",
wants: GetOptions{
EnvName: "Development",
VariableName: "BAZ",
},
N/A
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
func TestNewCmdGet(t *testing.T) {
tests := []struct {
name string
cli string
wants GetOptions
wantErr error
}{
{
name: "repo",
cli: "FOO",
wants: GetOptions{
OrgName: "",
VariableName: "FOO",
},
},
{
name: "org",
cli: "-o TestOrg BAR",
wants: GetOptions{
OrgName: "TestOrg",
VariableName: "BAR",
},
},
{
name: "env",
cli: "-e Development BAZ",
wants: GetOptions{
EnvName: "Development",
VariableName: "BAZ",
},
N/A
N/A
command: gh variable get
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: Get a variable for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
}{
{
name: "repo",
cli: "",
wants: ListOptions{
OrgName: "",
},
},
{
name: "org",
cli: "-oUmbrellaCorporation",
wants: ListOptions{
OrgName: "UmbrellaCorporation",
},
},
{
name: "env",
cli: "-eDevelopment",
wants: ListOptions{
EnvName: "Development",
},
},
}
for _, tt := range tests {
func Test_listRun(t *testing.T) {
tests := []struct {
name string
tty bool
opts *ListOptions
jsonFields []string
wantOut []string
}{
{
name: "repo tty",
tty: true,
opts: &ListOptions{},
wantOut: []string{
"NAME VALUE UPDATED",
"VARIABLE_ONE one about 34 years ago",
"VARIABLE_TWO two about 2 years ago",
"VARIABLE_THREE three about 47 years ago",
},
},
{
name: "repo not tty",
tty: false,
opts: &ListOptions{},
wantOut: []string{
"VARIABLE_ONE\tone\t1988-10-11T00:00:00Z",
"VARIABLE_TWO\ttwo\t2020-12-04T00:00:00Z",
"VARIABLE_THREE\tthree\t1975-11-30T00:00:00Z",
},
},
{
func Test_listRun_populatesNumSelectedReposIfRequired(t *testing.T) {
tests := []struct {
name string
tty bool
jsonFields []string
wantPopulated bool
}{
{
name: "org tty",
tty: true,
wantPopulated: true,
},
{
name: "org tty, json with numSelectedRepos",
tty: true,
jsonFields: []string{"numSelectedRepos"},
wantPopulated: true,
},
{
name: "org tty, json without numSelectedRepos",
tty: true,
jsonFields: []string{"name"},
wantPopulated: false,
},
{
name: "org not tty",
tty: false,
wantPopulated: false,
},
{
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable list
pname: gh variable
plink: gh_variable.yaml
options:
- option: env
shorthand: e
value_type: string
description: List variables for an environment
- option: jq
shorthand: q
value_type: string
description: Filter JSON output using a jq expression
N/A
N/A
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
cli string
wants SetOptions
stdinTTY bool
wantsErr bool
}{
{
name: "invalid visibility type",
cli: "cool_variable --org coolOrg -v'mistyVeil'",
wantsErr: true,
},
{
name: "selected visibility with no repos selected",
cli: "cool_variable --org coolOrg -v'selected'",
wantsErr: true,
},
{
name: "private visibility with repos selected",
cli: "cool_variable --org coolOrg -v'private' -rcoolRepo",
wantsErr: true,
},
{
name: "no name specified",
cli: "",
wantsErr: true,
},
{
name: "multiple names specified",
func Test_setRun_repo(t *testing.T) {
tests := []struct {
name string
httpStubs func(*httpmock.Registry)
wantErr bool
}{
{
name: "create actions variable",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("POST", "repos/owner/repo/actions/variables"),
httpmock.StatusStringResponse(201, `{}`))
},
},
{
name: "update actions variable",
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("POST", "repos/owner/repo/actions/variables"),
httpmock.StatusStringResponse(409, `{}`))
reg.Register(httpmock.REST("PATCH", "repos/owner/repo/actions/variables/cool_variable"),
httpmock.StatusStringResponse(204, `{}`))
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reg := &httpmock.Registry{}
defer reg.Verify(t)
if tt.httpStubs != nil {
tt.httpStubs(reg)
func Test_setRun_env(t *testing.T) {
tests := []struct {
name string
opts *SetOptions
httpStubs func(*httpmock.Registry)
wantErr bool
}{
{
name: "create env variable",
opts: &SetOptions{},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.GraphQL(`query MapRepositoryNames\b`),
httpmock.StringResponse(`{"data":{"repo_0001":{"databaseId":1}}}`))
reg.Register(httpmock.REST("POST", "repositories/1/environments/release/variables"),
httpmock.StatusStringResponse(201, `{}`))
},
},
{
name: "update env variable",
opts: &SetOptions{},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.GraphQL(`query MapRepositoryNames\b`),
httpmock.StringResponse(`{"data":{"repo_0001":{"databaseId":1}}}`))
reg.Register(httpmock.REST("POST", "repositories/1/environments/release/variables"),
httpmock.StatusStringResponse(409, `{}`))
reg.Register(httpmock.REST("PATCH", "repositories/1/environments/release/variables/cool_variable"),
httpmock.StatusStringResponse(204, `{}`))
},
},
}
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
N/A
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
N/A
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
N/A
N/A
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
func TestNewCmdSet(t *testing.T) {
tests := []struct {
name string
cli string
wants SetOptions
stdinTTY bool
wantsErr bool
}{
{
name: "invalid visibility type",
cli: "cool_variable --org coolOrg -v'mistyVeil'",
wantsErr: true,
},
{
name: "selected visibility with no repos selected",
cli: "cool_variable --org coolOrg -v'selected'",
wantsErr: true,
},
{
name: "private visibility with repos selected",
cli: "cool_variable --org coolOrg -v'private' -rcoolRepo",
wantsErr: true,
},
{
name: "no name specified",
cli: "",
wantsErr: true,
},
{
name: "multiple names specified",
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
N/A
N/A
command: gh variable set
pname: gh variable
plink: gh_variable.yaml
options:
- option: body
shorthand: b
value_type: string
description: The value for the variable (reads from standard input if not specified)
N/A
N/A
command: gh workflow
short: List, view, and run workflows in GitHub Actions.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh workflow
short: List, view, and run workflows in GitHub Actions.
pname: gh
plink: gh.yaml
options:
- option: repo
shorthand: R
value_type: string
description: Select another repository using the [HOST/]OWNER/REPO format
N/A
N/A
N/A
command: gh workflow disable short: Disable a workflow, preventing it from running or showing up when listing workflows. pname: gh workflow plink: gh_workflow.yaml
func TestNewCmdDisable(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants DisableOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: DisableOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: DisableOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
func TestDisableRun(t *testing.T) {
tests := []struct {
name string
opts *DisableOptions
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.MockPrompter)
tty bool
wantOut string
wantErrOut string
wantErr bool
}{
{
name: "tty no arg",
opts: &DisableOptions{
Prompt: true,
},
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows"),
httpmock.JSONResponse(shared.WorkflowsPayload{
Workflows: []shared.Workflow{
shared.AWorkflow,
shared.DisabledWorkflow,
shared.AnotherWorkflow,
},
}))
reg.Register(
httpmock.REST("PUT", "repos/OWNER/REPO/actions/workflows/789/disable"),
httpmock.StatusStringResponse(204, "{}"))
N/A
command: gh workflow enable short: Enable a workflow, allowing it to be run and show up when listing workflows. pname: gh workflow plink: gh_workflow.yaml
func TestNewCmdEnable(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants EnableOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: EnableOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: EnableOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
func TestEnableRun(t *testing.T) {
tests := []struct {
name string
opts *EnableOptions
httpStubs func(*httpmock.Registry)
promptStubs func(*prompter.MockPrompter)
tty bool
wantOut string
wantErrOut string
wantErr bool
}{
{
name: "tty no arg",
opts: &EnableOptions{
Prompt: true,
},
tty: true,
httpStubs: func(reg *httpmock.Registry) {
reg.Register(
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows"),
httpmock.JSONResponse(shared.WorkflowsPayload{
Workflows: []shared.Workflow{
shared.AWorkflow,
shared.DisabledWorkflow,
shared.AnotherWorkflow,
},
}))
reg.Register(
httpmock.REST("PUT", "repos/OWNER/REPO/actions/workflows/456/enable"),
httpmock.StatusStringResponse(204, "{}"))
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "all flag",
cli: "--all",
wants: ListOptions{
Limit: defaultLimit,
All: true,
},
},
{
name: "limit flag",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "invalid limit flag",
func TestListRun(t *testing.T) {
workflows := []shared.Workflow{
{
Name: "Go",
State: shared.Active,
ID: 707,
},
{
Name: "Linter",
State: shared.Active,
ID: 666,
},
{
Name: "Release",
State: shared.DisabledManually,
ID: 451,
},
}
payload := shared.WorkflowsPayload{Workflows: workflows}
tests := []struct {
name string
opts *ListOptions
wantErr bool
wantOut string
wantErrOut string
stubs func(*httpmock.Registry)
tty bool
}{
{
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "all flag",
cli: "--all",
wants: ListOptions{
Limit: defaultLimit,
All: true,
},
},
{
name: "limit flag",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "invalid limit flag",
N/A
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
N/A
N/A
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
N/A
N/A
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
func TestNewCmdList(t *testing.T) {
tests := []struct {
name string
cli string
wants ListOptions
wantsErr bool
}{
{
name: "no arguments",
wants: ListOptions{
Limit: defaultLimit,
},
},
{
name: "all flag",
cli: "--all",
wants: ListOptions{
Limit: defaultLimit,
All: true,
},
},
{
name: "limit flag",
cli: "--limit 100",
wants: ListOptions{
Limit: 100,
},
},
{
name: "invalid limit flag",
N/A
N/A
command: gh workflow list
short: List workflow files, hiding disabled workflows by default.
long: For more information about output formatting flags, see `gh help formatting`.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: all
shorthand: a
value_type: bool
default_value: "false"
description: Include disabled workflows
- option: jq
N/A
N/A
N/A
command: gh workflow run
short: Create a `workflow_dispatch` event for a given workflow.
long: |-
This command will trigger GitHub Actions to run a given workflow file. The given workflow file must
support an `on.workflow_dispatch` trigger in order to be run in this way.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: field
shorthand: F
value_type: string
description: Add a string parameter in key=value format, respecting @ syntax (see "gh help api").
func TestNewCmdRun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RunOptions
wantsErr bool
errMsg string
stdin string
}{
{
name: "blank nontty",
wantsErr: true,
errMsg: "workflow ID, name, or filename required when not running interactively",
},
{
name: "blank tty",
tty: true,
wants: RunOptions{
Prompt: true,
},
},
{
name: "ref flag",
tty: true,
cli: "--ref 12345abc",
wants: RunOptions{
Prompt: true,
Ref: "12345abc",
},
func Test_magicFieldValue(t *testing.T) {
f, err := os.CreateTemp(t.TempDir(), "gh-test")
if err != nil {
t.Fatal(err)
}
defer f.Close()
fmt.Fprint(f, "file contents")
ios, _, _, _ := iostreams.Test()
type args struct {
v string
opts RunOptions
}
tests := []struct {
name string
args args
want interface{}
wantErr bool
}{
{
name: "string",
args: args{v: "hello"},
want: "hello",
wantErr: false,
},
{
name: "file",
args: args{
func Test_findInputs(t *testing.T) {
tests := []struct {
name string
YAML []byte
wantErr bool
errMsg string
wantOut []WorkflowInput
}{
{
name: "blank",
YAML: []byte{},
wantErr: true,
errMsg: "invalid YAML file",
},
{
name: "no event specified",
YAML: []byte("name: workflow"),
wantErr: true,
errMsg: "invalid workflow: no 'on' key",
},
{
name: "not workflow_dispatch",
YAML: []byte("name: workflow\non: pull_request"),
wantErr: true,
errMsg: "unable to manually run a workflow without a workflow_dispatch event",
},
{
name: "bad inputs",
YAML: []byte("name: workflow\non:\n workflow_dispatch:\n inputs: lol "),
wantErr: true,
N/A
command: gh workflow run
short: Create a `workflow_dispatch` event for a given workflow.
long: |-
This command will trigger GitHub Actions to run a given workflow file. The given workflow file must
support an `on.workflow_dispatch` trigger in order to be run in this way.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: field
shorthand: F
value_type: string
description: Add a string parameter in key=value format, respecting @ syntax (see "gh help api").
N/A
N/A
N/A
command: gh workflow run
short: Create a `workflow_dispatch` event for a given workflow.
long: |-
This command will trigger GitHub Actions to run a given workflow file. The given workflow file must
support an `on.workflow_dispatch` trigger in order to be run in this way.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: field
shorthand: F
value_type: string
description: Add a string parameter in key=value format, respecting @ syntax (see "gh help api").
func TestNewCmdRun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RunOptions
wantsErr bool
errMsg string
stdin string
}{
{
name: "blank nontty",
wantsErr: true,
errMsg: "workflow ID, name, or filename required when not running interactively",
},
{
name: "blank tty",
tty: true,
wants: RunOptions{
Prompt: true,
},
},
{
name: "ref flag",
tty: true,
cli: "--ref 12345abc",
wants: RunOptions{
Prompt: true,
Ref: "12345abc",
},
N/A
command: gh workflow run
short: Create a `workflow_dispatch` event for a given workflow.
long: |-
This command will trigger GitHub Actions to run a given workflow file. The given workflow file must
support an `on.workflow_dispatch` trigger in order to be run in this way.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: field
shorthand: F
value_type: string
description: Add a string parameter in key=value format, respecting @ syntax (see "gh help api").
N/A
N/A
N/A
command: gh workflow run
short: Create a `workflow_dispatch` event for a given workflow.
long: |-
This command will trigger GitHub Actions to run a given workflow file. The given workflow file must
support an `on.workflow_dispatch` trigger in order to be run in this way.
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: field
shorthand: F
value_type: string
description: Add a string parameter in key=value format, respecting @ syntax (see "gh help api").
func TestNewCmdRun(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants RunOptions
wantsErr bool
errMsg string
stdin string
}{
{
name: "blank nontty",
wantsErr: true,
errMsg: "workflow ID, name, or filename required when not running interactively",
},
{
name: "blank tty",
tty: true,
wants: RunOptions{
Prompt: true,
},
},
{
name: "ref flag",
tty: true,
cli: "--ref 12345abc",
wants: RunOptions{
Prompt: true,
Ref: "12345abc",
},
N/A
command: gh workflow view
short: View the summary of a workflow
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: ref
shorthand: r
value_type: string
description: The branch or tag name which contains the version of the workflow file you'd like to view
- option: web
shorthand: w
value_type: bool
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: ViewOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
func TestViewRun(t *testing.T) {
aWorkflow := shared.Workflow{
Name: "a workflow",
ID: 123,
Path: ".github/workflows/flow.yml",
State: shared.Active,
}
aWorkflowContent := `{"content":"bmFtZTogYSB3b3JrZmxvdwo="}`
aWorkflowInfo := heredoc.Doc(`
a workflow - flow.yml
ID: 123
Total runs 10
Recent runs
TITLE WORKFLOW BRANCH EVENT ID
X cool commit a workflow trunk push 1
* cool commit a workflow trunk push 2
✓ cool commit a workflow trunk push 3
X cool commit a workflow trunk push 4
To see more runs for this workflow, try: gh run list --workflow flow.yml
To see the YAML for this workflow, try: gh workflow view flow.yml --yaml
`)
tests := []struct {
name string
opts *ViewOptions
httpStubs func(*httpmock.Registry)
tty bool
wantOut string
N/A
command: gh workflow view
short: View the summary of a workflow
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: ref
shorthand: r
value_type: string
description: The branch or tag name which contains the version of the workflow file you'd like to view
- option: web
shorthand: w
value_type: bool
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: ViewOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
func TestViewRun(t *testing.T) {
aWorkflow := shared.Workflow{
Name: "a workflow",
ID: 123,
Path: ".github/workflows/flow.yml",
State: shared.Active,
}
aWorkflowContent := `{"content":"bmFtZTogYSB3b3JrZmxvdwo="}`
aWorkflowInfo := heredoc.Doc(`
a workflow - flow.yml
ID: 123
Total runs 10
Recent runs
TITLE WORKFLOW BRANCH EVENT ID
X cool commit a workflow trunk push 1
* cool commit a workflow trunk push 2
✓ cool commit a workflow trunk push 3
X cool commit a workflow trunk push 4
To see more runs for this workflow, try: gh run list --workflow flow.yml
To see the YAML for this workflow, try: gh workflow view flow.yml --yaml
`)
tests := []struct {
name string
opts *ViewOptions
httpStubs func(*httpmock.Registry)
tty bool
wantOut string
N/A
N/A
command: gh workflow view
short: View the summary of a workflow
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: ref
shorthand: r
value_type: string
description: The branch or tag name which contains the version of the workflow file you'd like to view
- option: web
shorthand: w
value_type: bool
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: ViewOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
N/A
N/A
command: gh workflow view
short: View the summary of a workflow
pname: gh workflow
plink: gh_workflow.yaml
options:
- option: ref
shorthand: r
value_type: string
description: The branch or tag name which contains the version of the workflow file you'd like to view
- option: web
shorthand: w
value_type: bool
func TestNewCmdView(t *testing.T) {
tests := []struct {
name string
cli string
tty bool
wants ViewOptions
wantsErr bool
}{
{
name: "blank tty",
tty: true,
wants: ViewOptions{
Prompt: true,
},
},
{
name: "blank nontty",
wantsErr: true,
},
{
name: "arg tty",
cli: "123",
tty: true,
wants: ViewOptions{
Selector: "123",
},
},
{
name: "arg nontty",
cli: "123",
N/A
N/A
Eight percent of flags appear in any documented sequence — recipes name the command, not the flag. An agent that knows a flag exists in isolation hasn't been shown when to use it.