Repository: cli/cli

Issue:
v2.68.0 failed to run secret commands
### Describe the bug

I am trying to run the `gh secret list` after installing the `gh@2.68.0` version,
but it throws out error:

```
$ gh secret list
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x10588be10]

goroutine 1 [running]:
github.com/cli/cli/v2/pkg/cmd/secret/list.NewCmdList.func1.RequireNoAmbiguityBaseRepoFunc.1()
 	github.com/cli/cli/v2/pkg/cmd/secret/shared/base_repo.go:69 +0x90
github.com/cli/cli/v2/pkg/cmd/secret/list.NewCmdList.func1.PromptWhenAmbiguousBaseRepoFunc.2()
 	github.com/cli/cli/v2/pkg/cmd/secret/shared/base_repo.go:26 +0x44
github.com/cli/cli/v2/pkg/cmd/secret/list.listRun(0x140006ca880)
 	github.com/cli/cli/v2/pkg/cmd/secret/list/list.go:117 +0xd0
github.com/cli/cli/v2/pkg/cmd/secret/list.NewCmdList.func1(0x140004de008?, {0x107389100?, 0x4?, 0x105900851?})
 	github.com/cli/cli/v2/pkg/cmd/secret/list/list.go:94 +0x254
github.com/spf13/cobra.(*Command).execute(0x140004de008, {0x107389100, 0x0, 0x0})
 	github.com/spf13/cobra@v1.8.1/command.go:985 +0x830
github.com/spf13/cobra.(*Command).ExecuteC(0x14000554908)
 	github.com/spf13/cobra@v1.8.1/command.go:1117 +0x344
github.com/spf13/cobra.(*Command).ExecuteContextC(...)
 	github.com/spf13/cobra@v1.8.1/command.go:1050
github.com/cli/cli/v2/internal/ghcmd.Main()
 	github.com/cli/cli/v2/internal/ghcmd/cmd.go:119 +0x4b0
main.main()
 	github.com/cli/cli/v2/cmd/gh/main.go:10 +0x1c
```

### Affected version

Please run `gh version` and paste the output below.

```
gh --version
gh version 2.68.0 (2025-03-05)
https://github.com/cli/cli/releases/tag/v2.68.0
```

### Expected vs actual behavior

Expected secrets can be viewed, but actually throwing out error.

### Workaround

I have downloaded `gh@2.63.0` and replaced `/opt/homebrew/Cellar/gh/2.68.0`,
which makes it working again. 

Hints from issue comments:
@williammartin: Will look into this immediately, thanks@iamazeem: @williammartin:
Verified on Linux (Ubuntu).
All subcommands i.e. `list`, `set`, and `delete` need to be updated.

Typo? 🤔 

https://github.com/cli/cli/blob/a92528a158cc8d90681a025d992a3b7b3fd7e04a/pkg/cmd/secret/list/list.go#L78

Fix:

```diff
- opts.BaseRepo = shared.RequireNoAmbiguityBaseRepoFunc(opts.BaseRepo, f.Remotes)
+ opts.BaseRepo = shared.RequireNoAmbiguityBaseRepoFunc(f.BaseRepo, f.Remotes)
```

Tested locally and all three subcommands are working fine.@williammartin: Opened PR https://github.com/cli/cli/pull/10549@williammartin: @swingy, I just released https://github.com/cli/cli/releases/tag/v2.68.1 which I believe fixes this. Should be available on [Homebrew soon](https://github.com/Homebrew/homebrew-core/pull/209979), and should be in our apt repositories already.@eyayaw: I encountered this issue as well, on macOS. Thank you. @swingywc @williammartin 

Solution patch:
diff --git a/pkg/cmd/secret/delete/delete.go b/pkg/cmd/secret/delete/delete.go
index 1cbfbca9d..b73b59848 100644
--- a/pkg/cmd/secret/delete/delete.go
+++ b/pkg/cmd/secret/delete/delete.go
@@ -49,9 +49,9 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
 		RunE: func(cmd *cobra.Command, args []string) error {
 			// If the user specified a repo directly, then we're using the OverrideBaseRepoFunc set by EnableRepoOverride
 			// So there's no reason to use the specialised BaseRepoFunc that requires remote disambiguation.
-			if cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != "" {
-				opts.BaseRepo = f.BaseRepo
-			} else {
+			opts.BaseRepo = f.BaseRepo
+			isRepoUserProvided := cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != ""
+			if !isRepoUserProvided {
 				// If they haven't specified a repo directly, then we will wrap the BaseRepoFunc in one that errors if
 				// there might be multiple valid remotes.
 				opts.BaseRepo = shared.RequireNoAmbiguityBaseRepoFunc(opts.BaseRepo, f.Remotes)
diff --git a/pkg/cmd/secret/list/list.go b/pkg/cmd/secret/list/list.go
index c976bd60e..06476a86d 100644
--- a/pkg/cmd/secret/list/list.go
+++ b/pkg/cmd/secret/list/list.go
@@ -70,9 +70,9 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
 		RunE: func(cmd *cobra.Command, args []string) error {
 			// If the user specified a repo directly, then we're using the OverrideBaseRepoFunc set by EnableRepoOverride
 			// So there's no reason to use the specialised BaseRepoFunc that requires remote disambiguation.
-			if cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != "" {
-				opts.BaseRepo = f.BaseRepo
-			} else {
+			opts.BaseRepo = f.BaseRepo
+			isRepoUserProvided := cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != ""
+			if !isRepoUserProvided {
 				// If they haven't specified a repo directly, then we will wrap the BaseRepoFunc in one that errors if
 				// there might be multiple valid remotes.
 				opts.BaseRepo = shared.RequireNoAmbiguityBaseRepoFunc(opts.BaseRepo, f.Remotes)
diff --git a/pkg/cmd/secret/set/set.go b/pkg/cmd/secret/set/set.go
index 89f11f1cf..0a6581559 100644
--- a/pkg/cmd/secret/set/set.go
+++ b/pkg/cmd/secret/set/set.go
@@ -106,9 +106,9 @@ func NewCmdSet(f *cmdutil.Factory, runF func(*SetOptions) error) *cobra.Command
 		RunE: func(cmd *cobra.Command, args []string) error {
 			// If the user specified a repo directly, then we're using the OverrideBaseRepoFunc set by EnableRepoOverride
 			// So there's no reason to use the specialised BaseRepoFunc that requires remote disambiguation.
-			if cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != "" {
-				opts.BaseRepo = f.BaseRepo
-			} else {
+			opts.BaseRepo = f.BaseRepo
+			isRepoUserProvided := cmd.Flags().Changed("repo") || os.Getenv("GH_REPO") != ""
+			if !isRepoUserProvided {
 				// If they haven't specified a repo directly, then we will wrap the BaseRepoFunc in one that errors if
 				// there might be multiple valid remotes.
 				opts.BaseRepo = shared.RequireNoAmbiguityBaseRepoFunc(opts.BaseRepo, f.Remotes)



Test patch:
diff --git a/pkg/cmd/secret/delete/delete_test.go b/pkg/cmd/secret/delete/delete_test.go
index 2f6cad431..48200b881 100644
--- a/pkg/cmd/secret/delete/delete_test.go
+++ b/pkg/cmd/secret/delete/delete_test.go
@@ -126,7 +126,7 @@ func TestNewCmdDelete(t *testing.T) {
 }
 
 func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
-	remotes := ghContext.Remotes{
+	multipleRemotes := ghContext.Remotes{
 		&ghContext.Remote{
 			Remote: &git.Remote{
 				Name: "origin",
@@ -141,10 +141,20 @@ func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
 		},
 	}
 
+	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
 		prompterStubs func(*prompter.MockPrompter)
 		wantRepo      ghrepo.Interface
 		wantErr       error
@@ -152,6 +162,7 @@ func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
 		{
 			name:     "when there is a repo flag provided, the factory base repo func is used",
 			args:     "SECRET_NAME --repo owner/repo",
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
@@ -160,18 +171,27 @@ func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
 			env: map[string]string{
 				"GH_REPO": "owner/repo",
 			},
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
-			args: "SECRET_NAME",
+			name:    "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
+			args:    "SECRET_NAME",
+			remotes: multipleRemotes,
 			wantErr: shared.AmbiguousBaseRepoError{
-				Remotes: remotes,
+				Remotes: multipleRemotes,
 			},
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
-			args: "SECRET_NAME",
+			name:     "when there is no repo flag or GH_REPO env provided, and there is a single remote, the factory base repo func is used",
+			args:     "SECRET_NAME",
+			remotes:  singleRemote,
+			wantRepo: ghrepo.New("owner", "repo"),
+		},
+		{
+			name:    "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
+			args:    "SECRET_NAME",
+			remotes: multipleRemotes,
 			prompterStubs: func(pm *prompter.MockPrompter) {
 				pm.RegisterSelect(
 					"Select a repo",
@@ -204,7 +224,7 @@ func TestNewCmdDeleteBaseRepoFuncs(t *testing.T) {
 				},
 				Prompter: pm,
 				Remotes: func() (ghContext.Remotes, error) {
-					return remotes, nil
+					return tt.remotes, nil
 				},
 			}
 
diff --git a/pkg/cmd/secret/list/list_test.go b/pkg/cmd/secret/list/list_test.go
index f4ecde73e..5c4dd4874 100644
--- a/pkg/cmd/secret/list/list_test.go
+++ b/pkg/cmd/secret/list/list_test.go
@@ -106,7 +106,7 @@ func Test_NewCmdList(t *testing.T) {
 }
 
 func TestNewCmdListBaseRepoFuncs(t *testing.T) {
-	remotes := ghContext.Remotes{
+	multipleRemotes := ghContext.Remotes{
 		&ghContext.Remote{
 			Remote: &git.Remote{
 				Name: "origin",
@@ -121,10 +121,20 @@ func TestNewCmdListBaseRepoFuncs(t *testing.T) {
 		},
 	}
 
+	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
 		prompterStubs func(*prompter.MockPrompter)
 		wantRepo      ghrepo.Interface
 		wantErr       error
@@ -132,6 +142,7 @@ func TestNewCmdListBaseRepoFuncs(t *testing.T) {
 		{
 			name:     "when there is a repo flag provided, the factory base repo func is used",
 			args:     "--repo owner/repo",
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
@@ -139,18 +150,27 @@ func TestNewCmdListBaseRepoFuncs(t *testing.T) {
 			env: map[string]string{
 				"GH_REPO": "owner/repo",
 			},
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
-			args: "",
+			name:    "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
+			args:    "",
+			remotes: multipleRemotes,
 			wantErr: shared.AmbiguousBaseRepoError{
-				Remotes: remotes,
+				Remotes: multipleRemotes,
 			},
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
-			args: "",
+			name:     "when there is no repo flag or GH_REPO env provided, and there is a single remote, the factory base repo func is used",
+			args:     "",
+			remotes:  singleRemote,
+			wantRepo: ghrepo.New("owner", "repo"),
+		},
+		{
+			name:    "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
+			args:    "",
+			remotes: multipleRemotes,
 			prompterStubs: func(pm *prompter.MockPrompter) {
 				pm.RegisterSelect(
 					"Select a repo",
@@ -183,7 +203,7 @@ func TestNewCmdListBaseRepoFuncs(t *testing.T) {
 				},
 				Prompter: pm,
 				Remotes: func() (ghContext.Remotes, error) {
-					return remotes, nil
+					return tt.remotes, nil
 				},
 			}
 
diff --git a/pkg/cmd/secret/set/set_test.go b/pkg/cmd/secret/set/set_test.go
index 22c7aaab8..0b305eda6 100644
--- a/pkg/cmd/secret/set/set_test.go
+++ b/pkg/cmd/secret/set/set_test.go
@@ -225,7 +225,7 @@ func TestNewCmdSet(t *testing.T) {
 }
 
 func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
-	remotes := ghContext.Remotes{
+	multipleRemotes := ghContext.Remotes{
 		&ghContext.Remote{
 			Remote: &git.Remote{
 				Name: "origin",
@@ -240,10 +240,20 @@ func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
 		},
 	}
 
+	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
 		prompterStubs func(*prompter.MockPrompter)
 		wantRepo      ghrepo.Interface
 		wantErr       error
@@ -251,6 +261,7 @@ func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
 		{
 			name:     "when there is a repo flag provided, the factory base repo func is used",
 			args:     "SECRET_NAME --repo owner/repo",
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
@@ -259,18 +270,27 @@ func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
 			env: map[string]string{
 				"GH_REPO": "owner/repo",
 			},
+			remotes:  multipleRemotes,
 			wantRepo: ghrepo.New("owner", "repo"),
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
-			args: "SECRET_NAME",
+			name:    "when there is no repo flag or GH_REPO env var provided, and no prompting, the base func requiring no ambiguity is used",
+			args:    "SECRET_NAME",
+			remotes: multipleRemotes,
 			wantErr: shared.AmbiguousBaseRepoError{
-				Remotes: remotes,
+				Remotes: multipleRemotes,
 			},
 		},
 		{
-			name: "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
-			args: "SECRET_NAME",
+			name:     "when there is no repo flag or GH_REPO env provided, and there is a single remote, the factory base repo func is used",
+			args:     "SECRET_NAME",
+			remotes:  singleRemote,
+			wantRepo: ghrepo.New("owner", "repo"),
+		},
+		{
+			name:    "when there is no repo flag or GH_REPO env var provided, and can prompt, the base func resolving ambiguity is used",
+			args:    "SECRET_NAME",
+			remotes: multipleRemotes,
 			prompterStubs: func(pm *prompter.MockPrompter) {
 				pm.RegisterSelect(
 					"Select a repo",
@@ -303,7 +323,7 @@ func TestNewCmdSetBaseRepoFuncs(t *testing.T) {
 				},
 				Prompter: pm,
 				Remotes: func() (ghContext.Remotes, error) {
-					return remotes, nil
+					return tt.remotes, nil
 				},
 			}