diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 9f5d56376..35b211236 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,9 +6,10 @@ We accept pull requests for bug fixes and features where we've discussed the app Please do: -* Check existing issues to verify that the [bug][bug issues] or [feature request][feature request issues] has not already been submitted. +* Check existing issues to verify that an existing [bug][bug issues] or [feature request][feature request issues] issue does not already exist for the same problem or feature. * Open an issue if things aren't working as expected. * Open an issue to propose a significant change. +* Open an issue to propose a design for an issue labelled [`needs-design` and `help wanted`][needs design and help wanted], following the [proposing a design guidelines](#proposing-a-design) instructions below. * Open a pull request to fix a bug. * Open a pull request to fix documentation about a command. * Open a pull request for any issue labelled [`help wanted`][hw] or [`good first issue`][gfi]. @@ -52,7 +53,21 @@ We generate manual pages from source on every release. You do not need to submit ## Design guidelines -You may reference the [CLI Design System][] when suggesting features, and are welcome to use our [Google Docs Template][] to suggest designs. +### Proposing a design + +You may propose a design to solve an open bug or feature request issue that has both [the `needs-design` and `help-wanted` labels][needs design and help wanted]. + +To propose a design: + +- Open a new issue using the [design proposal issue template](./ISSUE_TEMPLATE/submit-a-design-proposal.md). +- Include a link to the issue that the design is for. +- Describe the design you are proposing to resolve the issue, leveraging the [CLI Design System][]. +- Mock up the design you are proposing using our [Google Docs Template][] or code blocks. + - Mock ups should cleary illustrate the command(s) being run and the expected output(s). + +### (core team only) Revewing a design + +A member of the core team will [triage](../docs/triage.md) the design proposal. Once a member of the core team has reviewed the design, they may add the [`help wanted`][hw] label to the issue, so a PR can be opened to provide the implementation. ## Resources @@ -62,6 +77,7 @@ You may reference the [CLI Design System][] when suggesting features, and are we [bug issues]: https://github.com/cli/cli/issues?q=is%3Aopen+is%3Aissue+label%3Abug +[needs design and help wanted]: https://github.com/cli/cli/issues?q=state%3Aclosed%20is%3Aissue%20label%3Aneeds-design%20label%3A%22help%20wanted%22 [feature request issues]: https://github.com/cli/cli/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement [hw]: https://github.com/cli/cli/labels/help%20wanted [gfi]: https://github.com/cli/cli/labels/good%20first%20issue diff --git a/.github/ISSUE_TEMPLATE/submit-a-design-proposal.md b/.github/ISSUE_TEMPLATE/submit-a-design-proposal.md new file mode 100644 index 000000000..fab4b7a88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/submit-a-design-proposal.md @@ -0,0 +1,58 @@ +--- +name: "🎨 Submit a design proposal" +about: Submit a design to resolve an open issue that has both `needs-design` and `help-wanted` labels +title: '' +labels: enhancement +assignees: '' + +--- + + + +### Link to issue for design submission + + + +### Proposed Design + + + +### Mockup + + \ No newline at end of file diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 15f909b71..50e489c54 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -2,7 +2,10 @@ GitHub takes the security of our software products and services seriously, inclu If you believe you have found a security vulnerability in GitHub CLI, you can report it to us in one of two ways: -* Report it to this repository directly using [private vulnerability reporting][]. Such reports are not eligible for a bounty reward. +* Report it to this repository directly using [private vulnerability reporting][]. + * Include a description of your investigation of the GitHub CLI's codebase and why you believe an exploit is possible. + * POCs and links to code are greatly encouraged. + * Such reports are not eligible for a bounty reward. * Submit the report through [HackerOne][] to be eligible for a bounty reward. diff --git a/docs/install_linux.md b/docs/install_linux.md index fabaa19aa..9be6aed9b 100644 --- a/docs/install_linux.md +++ b/docs/install_linux.md @@ -43,12 +43,29 @@ sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.re sudo dnf install gh --repo gh-cli ``` +
+Show dnf5 commands + +If you're using `dnf5`, commands will vary slightly: + +```bash +sudo dnf5 install dnf5-plugins +sudo dnf5 config-manager addrepo --from-repofile=https://cli.github.com/packages/rpm/gh-cli.repo +sudo dnf5 install gh --repo gh-cli +``` + +For more details, check out the [`dnf5 config-manager` documentation](https://dnf5.readthedocs.io/en/latest/dnf5_plugins/config-manager.8.html). +
+ Alternatively, install from the [community repository](https://packages.fedoraproject.org/pkgs/gh/gh/): ```bash sudo dnf install gh ``` +> [!NOTE] +> If errors regarding GPG signatures occur, see [cli/cli#9569](https://github.com/cli/cli/issues/9569) for steps to fix this. + Upgrade: ```bash @@ -91,6 +108,9 @@ sudo zypper ref sudo zypper update gh ``` +> [!NOTE] +> If errors regarding GPG signatures occur, see [cli/cli#9569](https://github.com/cli/cli/issues/9569) for steps to fix this. + ## Manual installation * [Download release binaries][releases page] that match your platform; or diff --git a/docs/triage.md b/docs/triage.md index d78a81aed..9d54fcfe7 100644 --- a/docs/triage.md +++ b/docs/triage.md @@ -33,27 +33,32 @@ the changeset is feasible and to allow the associated CI run for new contributor - can this be closed outright? - e.g. spam/junk + - add the `invalid` label - close without comment - do we not want to do it? - - e.g. have already discussed not wanting to do or duplicate issue + - e.g. we have already discussed not wanting to do or it's a duplicate issue + - add the appropriate label (e.g. `duplicate`) - comment and close -- are we ok with outside contribution for this? - - e.g. the task is relatively straightforward, but no people on our team have the bandwidth to take it on at the moment +- do we want external contribution for this? + - e.g. the task is relatively straightforward, but the core team does not have the bandwidth to take it on - ensure that the thread contains all the context necessary for someone new to pick this up - - add `help wanted` label + - add the `help wanted` label - consider adding `good first issue` label +- do we want external design contribution for this? + - e.g. the task is worthwhile, but needs design work to flesh out the details before implementation and the core team does not have the bandwidth to take it on + - ensure that the thread contains all the context necessary for someone new to pick this up + - add both the `help wanted` and `needs-design` labels - do we want to do it? + - add the `core` label - comment acknowledging that - - add `core` label - - add to the project “TODO” column if this is something that should ship soon - is it intriguing, but requires discussion? - - label `discuss` - - label `needs-investigation` if engineering research is required before action can be taken + - Add the `discuss` label + - Add the `needs-investigation` label if engineering research is required before action can be taken - does it need more info from the issue author? - ask the user for details - - add `needs-user-input` label + - add the `needs-user-input` label - is it a usage/support question? - - consider converting the Issue to a Discussion + - Convert the Issue to a Discussion ## Weekly PR audit diff --git a/go.mod b/go.mod index 9bf6be8e4..f4ef7989f 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/cli/go-gh/v2 v2.10.0 github.com/cli/oauth v1.0.1 github.com/cli/safeexec v1.0.1 - github.com/cpuguy83/go-md2man/v2 v2.0.4 + github.com/cpuguy83/go-md2man/v2 v2.0.5 github.com/creack/pty v1.1.23 github.com/distribution/reference v0.5.0 github.com/gabriel-vasile/mimetype v1.4.5 @@ -143,7 +143,7 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect - github.com/theupdateframework/go-tuf/v2 v2.0.0 // indirect + github.com/theupdateframework/go-tuf/v2 v2.0.1 // indirect github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect diff --git a/go.sum b/go.sum index cdde6c210..4f5689856 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,9 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AX github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= @@ -440,8 +441,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= -github.com/theupdateframework/go-tuf/v2 v2.0.0 h1:rD8d9RotYBprZVgC+9oyTZ5MmawepnTSTqoDuxjWgbs= -github.com/theupdateframework/go-tuf/v2 v2.0.0/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= +github.com/theupdateframework/go-tuf/v2 v2.0.1 h1:11p9tXpq10KQEujxjcIjDSivMKCMLguls7erXHZnxJQ= +github.com/theupdateframework/go-tuf/v2 v2.0.1/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= diff --git a/internal/authflow/flow.go b/internal/authflow/flow.go index 370e08784..3fc753b44 100644 --- a/internal/authflow/flow.go +++ b/internal/authflow/flow.go @@ -72,7 +72,7 @@ func AuthFlow(oauthHost string, IO *iostreams.IOStreams, notice string, addition return nil } - fmt.Fprintf(w, "%s to open %s in your browser... ", cs.Bold("Press Enter"), oauthHost) + fmt.Fprintf(w, "%s to open %s in your browser... ", cs.Bold("Press Enter"), authURL) _ = waitForEnter(IO.In) if err := b.Browse(authURL); err != nil { diff --git a/internal/prompter/prompter.go b/internal/prompter/prompter.go index 44579b189..1d4b11cbc 100644 --- a/internal/prompter/prompter.go +++ b/internal/prompter/prompter.go @@ -92,7 +92,7 @@ func (p *surveyPrompter) InputHostname() (string, error) { var result string err := p.ask( &survey.Input{ - Message: "GHE hostname:", + Message: "Hostname:", }, &result, survey.WithValidator(func(v interface{}) error { return ghinstance.HostnameValidator(v.(string)) })) diff --git a/pkg/cmd/attestation/trustedroot/trustedroot.go b/pkg/cmd/attestation/trustedroot/trustedroot.go index 6f741dcd4..5473adc0c 100644 --- a/pkg/cmd/attestation/trustedroot/trustedroot.go +++ b/pkg/cmd/attestation/trustedroot/trustedroot.go @@ -126,6 +126,12 @@ func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options) error { // Disable local caching, so we get up-to-date response from TUF repository tufOpt.CacheValidity = 0 + // Target will be either the default trusted root, or the trust domain-qualified one + ghTR := defaultTR + if opts.TrustDomain != "" { + ghTR = fmt.Sprintf("%s.%s", opts.TrustDomain, defaultTR) + } + if opts.TufUrl != "" && opts.TufRootPath != "" { tufRoot, err := os.ReadFile(opts.TufRootPath) if err != nil { @@ -136,7 +142,7 @@ func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options) error { tufOpt.RepositoryBaseURL = opts.TufUrl tufOptions = append(tufOptions, tufConfig{ tufOptions: tufOpt, - targets: []string{defaultTR}, + targets: []string{ghTR}, }) } else { // Get from both Sigstore public good and GitHub private instance @@ -147,14 +153,9 @@ func getTrustedRoot(makeTUF tufClientInstantiator, opts *Options) error { tufOpt = verification.GitHubTUFOptions() tufOpt.CacheValidity = 0 - targets := []string{defaultTR} - if opts.TrustDomain != "" { - targets = append(targets, fmt.Sprintf("%s.%s", - opts.TrustDomain, defaultTR)) - } tufOptions = append(tufOptions, tufConfig{ tufOptions: tufOpt, - targets: targets, + targets: []string{ghTR}, }) } diff --git a/pkg/cmd/auth/login/login.go b/pkg/cmd/auth/login/login.go index 9e27f28ac..1480a29a0 100644 --- a/pkg/cmd/auth/login/login.go +++ b/pkg/cmd/auth/login/login.go @@ -59,6 +59,9 @@ func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Comm Long: heredoc.Docf(` Authenticate with a GitHub host. + The default hostname is %[1]sgithub.com%[1]s. This can be overridden using the %[1]s--hostname%[1]s + 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 @@ -222,21 +225,19 @@ func loginRun(opts *LoginOptions) error { } func promptForHostname(opts *LoginOptions) (string, error) { - options := []string{"GitHub.com", "GitHub Enterprise Server"} + options := []string{"GitHub.com", "Other"} hostType, err := opts.Prompter.Select( - "What account do you want to log into?", + "Where do you use GitHub?", options[0], options) if err != nil { return "", err } - isEnterprise := hostType == 1 - - hostname := ghinstance.Default() - if isEnterprise { - hostname, err = opts.Prompter.InputHostname() + isGitHubDotCom := hostType == 0 + if isGitHubDotCom { + return ghinstance.Default(), nil } - return hostname, err + return opts.Prompter.InputHostname() } diff --git a/pkg/cmd/auth/login/login_test.go b/pkg/cmd/auth/login/login_test.go index d53dd3f0b..3264ed91c 100644 --- a/pkg/cmd/auth/login/login_test.go +++ b/pkg/cmd/auth/login/login_test.go @@ -2,6 +2,7 @@ package login import ( "bytes" + "fmt" "net/http" "regexp" "runtime" @@ -546,7 +547,7 @@ func Test_loginRun_Survey(t *testing.T) { wantErrOut: regexp.MustCompile("Tip: you can generate a Personal Access Token here https://rebecca.chambers/settings/tokens"), }, { - name: "choose enterprise", + name: "choose Other", wantHosts: heredoc.Doc(` brad.vickers: users: @@ -563,8 +564,8 @@ func Test_loginRun_Survey(t *testing.T) { prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(prompt, _ string, opts []string) (int, error) { switch prompt { - case "What account do you want to log into?": - return prompter.IndexFor(opts, "GitHub Enterprise Server") + case "Where do you use GitHub?": + return prompter.IndexFor(opts, "Other") case "What is your preferred protocol for Git operations on this host?": return prompter.IndexFor(opts, "HTTPS") case "How would you like to authenticate GitHub CLI?": @@ -606,7 +607,7 @@ func Test_loginRun_Survey(t *testing.T) { prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(prompt, _ string, opts []string) (int, error) { switch prompt { - case "What account do you want to log into?": + case "Where do you use GitHub?": return prompter.IndexFor(opts, "GitHub.com") case "What is your preferred protocol for Git operations on this host?": return prompter.IndexFor(opts, "HTTPS") @@ -640,7 +641,7 @@ func Test_loginRun_Survey(t *testing.T) { prompterStubs: func(pm *prompter.PrompterMock) { pm.SelectFunc = func(prompt, _ string, opts []string) (int, error) { switch prompt { - case "What account do you want to log into?": + case "Where do you use GitHub?": return prompter.IndexFor(opts, "GitHub.com") case "What is your preferred protocol for Git operations on this host?": return prompter.IndexFor(opts, "SSH") @@ -803,3 +804,50 @@ func Test_loginRun_Survey(t *testing.T) { }) } } + +func Test_promptForHostname(t *testing.T) { + tests := []struct { + name string + options []string + selectedIndex int + // This is so we can test that the options in the function don't change + expectedSelection string + inputHostname string + expect string + }{ + { + name: "select 'GitHub.com'", + selectedIndex: 0, + expectedSelection: "GitHub.com", + expect: "github.com", + }, + { + name: "select 'Other'", + selectedIndex: 1, + expectedSelection: "Other", + inputHostname: "github.enterprise.com", + expect: "github.enterprise.com", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + promptMock := &prompter.PrompterMock{ + SelectFunc: func(_ string, _ string, options []string) (int, error) { + if options[tt.selectedIndex] != tt.expectedSelection { + return 0, fmt.Errorf("expected %s at index %d, but got %s", tt.expectedSelection, tt.selectedIndex, options[tt.selectedIndex]) + } + return tt.selectedIndex, nil + }, + InputHostnameFunc: func() (string, error) { + return tt.inputHostname, nil + }, + } + opts := &LoginOptions{ + Prompter: promptMock, + } + hostname, err := promptForHostname(opts) + require.NoError(t, err) + require.Equal(t, tt.expect, hostname) + }) + } +} diff --git a/pkg/cmd/extension/manager.go b/pkg/cmd/extension/manager.go index 410d4c224..2431c6b83 100644 --- a/pkg/cmd/extension/manager.go +++ b/pkg/cmd/extension/manager.go @@ -29,6 +29,8 @@ import ( // ErrInitialCommitFailed indicates the initial commit when making a new extension failed. var ErrInitialCommitFailed = errors.New("initial commit failed") +const darwinAmd64 = "darwin-amd64" + type Manager struct { dataDir func() string lookPath func(string) (string, error) @@ -266,13 +268,16 @@ func (m *Manager) installBin(repo ghrepo.Interface, target string) error { // if using an ARM-based Mac and an arm64 binary is unavailable, fall back to amd64 if a relevant binary is available and Rosetta 2 is installed if asset == nil && isMacARM { for _, a := range r.Assets { - if strings.HasSuffix(a.Name, "darwin-amd64") { + if strings.HasSuffix(a.Name, darwinAmd64) { if !hasRosetta() { return fmt.Errorf( - "%[1]s unsupported for %[2]s. Install Rosetta with `softwareupdate --install-rosetta` to use the available darwin-amd64 binary, or open an issue: `gh issue create -R %[3]s/%[1]s -t'Support %[2]s'`", - repo.RepoName(), platform, repo.RepoOwner()) + "%[1]s unsupported for %[2]s. Install Rosetta with `softwareupdate --install-rosetta` to use the available %[3]s binary, or open an issue: `gh issue create -R %[4]s/%[1]s -t'Support %[2]s'`", + repo.RepoName(), platform, darwinAmd64, repo.RepoOwner()) } + fallbackMessage := fmt.Sprintf("%[1]s not available for %[2]s. Falling back to compatible %[3]s binary", repo.RepoName(), platform, darwinAmd64) + fmt.Fprintln(m.io.Out, fallbackMessage) + asset = &a break } diff --git a/pkg/cmd/extension/manager_test.go b/pkg/cmd/extension/manager_test.go index 0ad8e991e..e25bc1496 100644 --- a/pkg/cmd/extension/manager_test.go +++ b/pkg/cmd/extension/manager_test.go @@ -1098,7 +1098,7 @@ func TestManager_Install_amd64_when_supported(t *testing.T) { assert.NoError(t, err) assert.Equal(t, "FAKE BINARY", string(fakeBin)) - assert.Equal(t, "", stdout.String()) + assert.Equal(t, "gh-bin-ext not available for darwin-arm64. Falling back to compatible darwin-amd64 binary\n", stdout.String()) assert.Equal(t, "", stderr.String()) }