Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package gh for openSUSE:Factory checked in at 2022-03-16 20:20:38 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/gh (Old) and /work/SRC/openSUSE:Factory/.gh.new.25692 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "gh" Wed Mar 16 20:20:38 2022 rev:18 rq:962014 version:2.5.2 Changes: -------- --- /work/SRC/openSUSE:Factory/gh/gh.changes 2022-02-18 23:03:17.077411241 +0100 +++ /work/SRC/openSUSE:Factory/.gh.new.25692/gh.changes 2022-03-16 20:20:44.288981942 +0100 @@ -1,0 +2,18 @@ +Thu Mar 10 16:24:09 UTC 2022 - ch...@computersalat.de + +- Update to 2.5.2 + https://github.com/cli/cli/releases + * Other Bugfixes + - Fix HexToRGB panic by @markphelps in #5247 + * Other Changes + - Support filtering PRs authored by GitHub Apps by @joshmgross in #5180 + - gh auth login: added flags to partially automate flow by @jonathan-enf in #5158 + - Codespaces Create: Allow Accepting Permissions by @markphelps in #5234 + * Development & Docs + - Bump github.com/itchyny/gojq from 0.12.6 to 0.12.7 by @dependabot in #5263 + - Misc. docs improvements by @mislav in #5223 + - Use StringEnumFlag helper in more places by @mislav in #5221 + - Change how gh-cli is installed on fedora by @Hanjingxue-Boling in #5204 +- updated vendor.tar.gz + +------------------------------------------------------------------- Old: ---- cli-2.5.1.tar.gz New: ---- cli-2.5.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ gh.spec ++++++ --- /var/tmp/diff_new_pack.Ms4yua/_old 2022-03-16 20:20:44.988982445 +0100 +++ /var/tmp/diff_new_pack.Ms4yua/_new 2022-03-16 20:20:44.996982451 +0100 @@ -19,7 +19,7 @@ %define goflags "-buildmode=pie -trimpath -mod=vendor -modcacherw" %define sname cli Name: gh -Version: 2.5.1 +Version: 2.5.2 Release: 0 Summary: The official CLI for GitHub License: MIT ++++++ cli-2.5.1.tar.gz -> cli-2.5.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/api/client.go new/cli-2.5.2/api/client.go --- old/cli-2.5.1/api/client.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/api/client.go 2022-03-01 15:37:58.000000000 +0100 @@ -236,7 +236,23 @@ for _, s := range strings.Split(tokenHasScopes, ",") { s = strings.TrimSpace(s) gotScopes[s] = struct{}{} - if strings.HasPrefix(s, "admin:") { + + // Certain scopes may be grouped under a single "top-level" scope. The following branch + // statements include these grouped/implied scopes when the top-level scope is encountered. + // See https://docs.github.com/en/developers/apps/building-oauth-apps/scopes-for-oauth-apps. + if s == "repo" { + gotScopes["repo:status"] = struct{}{} + gotScopes["repo_deployment"] = struct{}{} + gotScopes["public_repo"] = struct{}{} + gotScopes["repo:invite"] = struct{}{} + gotScopes["security_events"] = struct{}{} + } else if s == "user" { + gotScopes["read:user"] = struct{}{} + gotScopes["user:email"] = struct{}{} + gotScopes["user:follow"] = struct{}{} + } else if s == "codespace" { + gotScopes["codespace:secrets"] = struct{}{} + } else if strings.HasPrefix(s, "admin:") { gotScopes["read:"+strings.TrimPrefix(s, "admin:")] = struct{}{} gotScopes["write:"+strings.TrimPrefix(s, "admin:")] = struct{}{} } else if strings.HasPrefix(s, "write:") { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/docs/install_linux.md new/cli-2.5.2/docs/install_linux.md --- old/cli-2.5.1/docs/install_linux.md 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/docs/install_linux.md 2022-03-01 15:37:58.000000000 +0100 @@ -29,13 +29,19 @@ ### Fedora, CentOS, Red Hat Enterprise Linux (dnf) -Install: +Install from our package repository for immediate access to latest releases: ```bash sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo sudo dnf install gh ``` +Alternatively, install from the [community repository](https://packages.fedoraproject.org/pkgs/gh/gh/): + +```bash +sudo dnf install gh +``` + Upgrade: ```bash diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/docs/triage.md new/cli-2.5.2/docs/triage.md --- old/cli-2.5.1/docs/triage.md 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/docs/triage.md 2022-03-01 15:37:58.000000000 +0100 @@ -6,31 +6,23 @@ ## Expectations for incoming issues -All incoming issues need either an **enhancement**, **bug**, or **docs** label. +All incoming issues need either an `enhancement`, `bug`, or `docs` label. -To be considered triaged, **enhancement** issues require at least one of the following additional labels: +To be considered triaged, `enhancement` issues require at least one of the following additional labels: -- **core**: work reserved for the core CLI team -- **help wanted**: work that we would accept contributions for -- **needs-design**: work that requires input from a UX designer before it can move forward -- **needs-investigation**: work that requires a mystery be solved by the core team before it can move forward -- **needs-user-input**: work that requires more information from the reporter before it can move forward +- `core`: reserved for the core CLI team +- `help wanted`: signal that we are accepting contributions for this +- `discuss`: add to our team's queue to discuss during a sync +- `needs-investigation`: work that requires a mystery be solved by the core team before it can move forward +- `needs-user-input`: we need more information from our users before the task can move forward -To be considered triaged, **bug** issues require a severity label: one of **p1**, **p2**, or **p3** - -For a more detailed breakdown of **how** to triage an issue, see the _Issue triage flowchart_ below. +To be considered triaged, `bug` issues require a severity label: one of `p1`, `p2`, or `p3` ## Expectations for community pull requests -To be considered triaged, incoming pull requests should: - -- be checked for a corresponding **help wanted** issue -- be checked for basic quality: are the builds passing? have tests been added? -- be checked for redundancy: is there already a PR dealing with this? - -Once a pull request has been triaged, it should be moved to the **Needs Review** column of the project board. - -For a more detailed breakdown of **how** to triage an issue, see the _PR triage flowchart_ below. +All incoming pull requests are assigned to one of the engineers for review on a round-robin basis. +The person in a triage role for a week could take a glance at these pull requests, mostly to see whether +the changeset is feasible and to allow the associated CI run for new contributors. ## Issue triage flowchart @@ -46,30 +38,17 @@ - add `help wanted` label - consider adding `good first issue` label - do we want to do it? - - comment acknowledging it + - comment acknowledging that - add `core` label - - add to project TODO column if this is something that should ship soon + - add to the project ???TODO??? column if this is something that should ship soon - is it intriguing, but requires discussion? - - label `needs-design` if design input is needed, ping + - label `discuss` - label `needs-investigation` 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 - is it a usage/support question? - - offer some instructions/workaround and close - -## Pull request triage flowchart - -- can it be closed outright? - - e.g. spam/junk - - close -- do we not want to do it? - - comment and close -- is it intriguing, but requires discussion and there is no referenced issue? - - request an issue - - close -- is it something we want to include? - - add to `needs review` column + - consider converting the Issue to a Discussion ## Weekly PR audit diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/go.mod new/cli-2.5.2/go.mod --- old/cli-2.5.1/go.mod 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/go.mod 2022-03-01 15:37:58.000000000 +0100 @@ -20,7 +20,7 @@ github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.3.0 github.com/henvic/httpretty v0.0.6 - github.com/itchyny/gojq v0.12.6 + github.com/itchyny/gojq v0.12.7 github.com/joho/godotenv v1.4.0 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/mattn/go-colorable v0.1.12 @@ -38,7 +38,7 @@ github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20211205182925-97ca703d548d + golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 golang.org/x/term v0.0.0-20210503060354-a79de5458b56 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/go.sum new/cli-2.5.2/go.sum --- old/cli-2.5.1/go.sum 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/go.sum 2022-03-01 15:37:58.000000000 +0100 @@ -277,8 +277,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/itchyny/gojq v0.12.6 h1:VjaFn59Em2wTxDNGcrRkDK9ZHMNa8IksOgL13sLL4d0= -github.com/itchyny/gojq v0.12.6/go.mod h1:ZHrkfu7A+RbZLy5J1/JKpS4poEqrzItSTGDItqsfP0A= +github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ= +github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw= github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= @@ -619,8 +619,9 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211205182925-97ca703d548d h1:FjkYO/PPp4Wi0EAUOVLxePm7qVW4r4ctbWpURyuOD0E= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/internal/codespaces/api/api.go new/cli-2.5.2/internal/codespaces/api/api.go --- old/cli-2.5.1/internal/codespaces/api/api.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/internal/codespaces/api/api.go 2022-03-01 15:37:58.000000000 +0100 @@ -565,6 +565,7 @@ Branch string Machine string Location string + PermissionsOptOut bool } // CreateCodespace creates a codespace with the given parameters and returns a non-nil error if it @@ -610,10 +611,20 @@ Ref string `json:"ref"` Location string `json:"location"` Machine string `json:"machine"` + PermissionsOptOut bool `json:"devcontainer_permissions_opt_out"` } var errProvisioningInProgress = errors.New("provisioning in progress") +type AcceptPermissionsRequiredError struct { + Message string `json:"message"` + AllowPermissionsURL string `json:"allow_permissions_url"` +} + +func (e AcceptPermissionsRequiredError) Error() string { + return e.Message +} + // startCreate starts the creation of a codespace. // It may return success or an error, or errProvisioningInProgress indicating that the operation // did not complete before the GitHub API's time limit for RPCs (10s), in which case the caller @@ -629,6 +640,7 @@ Ref: params.Branch, Location: params.Location, Machine: params.Machine, + PermissionsOptOut: params.PermissionsOptOut, }) if err != nil { return nil, fmt.Errorf("error marshaling request: %w", err) @@ -648,6 +660,29 @@ if resp.StatusCode == http.StatusAccepted { return nil, errProvisioningInProgress // RPC finished before result of creation known + } else if resp.StatusCode == http.StatusUnauthorized { + var ( + ue AcceptPermissionsRequiredError + bodyCopy = &bytes.Buffer{} + r = io.TeeReader(resp.Body, bodyCopy) + ) + + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + if err := json.Unmarshal(b, &ue); err != nil { + return nil, fmt.Errorf("error unmarshaling response: %w", err) + } + + if ue.AllowPermissionsURL != "" { + return nil, ue + } + + resp.Body = ioutil.NopCloser(bodyCopy) + + return nil, api.HandleHTTPError(resp) + } else if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { return nil, api.HandleHTTPError(resp) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/actions/actions.go new/cli-2.5.2/pkg/cmd/actions/actions.go --- old/cli-2.5.1/pkg/cmd/actions/actions.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/actions/actions.go 2022-03-01 15:37:58.000000000 +0100 @@ -11,12 +11,10 @@ cs := f.IOStreams.ColorScheme() cmd := &cobra.Command{ - Use: "actions", - Short: "Learn about working with GitHub Actions", - Long: actionsExplainer(cs), - Annotations: map[string]string{ - "IsActions": "true", - }, + Use: "actions", + Short: "Learn about working with GitHub Actions", + Long: actionsExplainer(cs), + Hidden: true, } cmdutil.DisableAuthCheck(cmd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/alias/list/list.go new/cli-2.5.2/pkg/cmd/alias/list/list.go --- old/cli-2.5.1/pkg/cmd/alias/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/alias/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -24,8 +24,9 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "List your aliases", + Use: "list", + Short: "List your aliases", + Aliases: []string{"ls"}, Long: heredoc.Doc(` This command prints out all of the aliases gh is configured to use. `), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/api/api.go new/cli-2.5.2/pkg/cmd/api/api.go --- old/cli-2.5.1/pkg/cmd/api/api.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/api/api.go 2022-03-01 15:37:58.000000000 +0100 @@ -214,7 +214,7 @@ cmd.Flags().StringArrayVarP(&opts.MagicFields, "field", "F", nil, "Add a typed parameter in `key=value` format") cmd.Flags().StringArrayVarP(&opts.RawFields, "raw-field", "f", nil, "Add a string parameter in `key=value` format") cmd.Flags().StringArrayVarP(&opts.RequestHeaders, "header", "H", nil, "Add a HTTP request header in `key:value` format") - cmd.Flags().StringSliceVarP(&opts.Previews, "preview", "p", nil, "Opt into GitHub API previews") + cmd.Flags().StringSliceVarP(&opts.Previews, "preview", "p", nil, "GitHub API preview `names` to request (without the \"-preview\" suffix)") cmd.Flags().BoolVarP(&opts.ShowResponseHeaders, "include", "i", false, "Include HTTP response headers in the output") cmd.Flags().BoolVar(&opts.Paginate, "paginate", false, "Make additional HTTP requests to fetch all pages of results") cmd.Flags().StringVar(&opts.RequestInputFile, "input", "", "The `file` to use as body for the HTTP request (use \"-\" to read from standard input)") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/auth/auth.go new/cli-2.5.2/pkg/cmd/auth/auth.go --- old/cli-2.5.1/pkg/cmd/auth/auth.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/auth/auth.go 2022-03-01 15:37:58.000000000 +0100 @@ -14,8 +14,10 @@ func NewCmdAuth(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "auth <command>", - Short: "Login, logout, and refresh your authentication", - Long: `Manage gh's authentication state.`, + Short: "Authenticate gh and git with GitHub", + Annotations: map[string]string{ + "IsCore": "true", + }, } cmdutil.DisableAuthCheck(cmd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/auth/login/login.go new/cli-2.5.2/pkg/cmd/auth/login/login.go --- old/cli-2.5.1/pkg/cmd/auth/login/login.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/auth/login/login.go 2022-03-01 15:37:58.000000000 +0100 @@ -27,10 +27,11 @@ Interactive bool - Hostname string - Scopes []string - Token string - Web bool + Hostname string + Scopes []string + Token string + Web bool + GitProtocol string } func NewCmdLogin(f *cmdutil.Factory, runF func(*LoginOptions) error) *cobra.Command { @@ -49,13 +50,17 @@ Long: heredoc.Docf(` Authenticate with a GitHub host. - The default authentication mode is a web-based browser flow. + The default authentication mode is a web-based browser flow. After completion, an + authentication token will be stored internally. - Alternatively, pass in a token on standard input by using %[1]s--with-token%[1]s. + Alternatively, use %[1]s--with-token%[1]s to pass in a token on standard input. The minimum required scopes for the token are: "repo", "read:org". - The --scopes flag accepts a comma separated list of scopes you want your gh credentials to have. If - absent, this command ensures that gh has access to a minimum set of scopes. + Alternatively, gh will use the authentication token found in environment variables. + This method is most suitable for "headless" use of gh such as in automation. See + %[1]sgh help environment%[1]s for more info. + + To use gh in GitHub Actions, add %[1]sGH_TOKEN: ${{secrets.GITHUB_TOKEN}}%[1]s to "env". `, "`"), Example: heredoc.Doc(` # start interactive setup @@ -64,7 +69,7 @@ # authenticate against github.com by reading the token from a file $ gh auth login --with-token < mytoken.txt - # authenticate with a specific GitHub Enterprise Server instance + # authenticate with a specific GitHub instance $ gh auth login --hostname enterprise.internal `), RunE: func(cmd *cobra.Command, args []string) error { @@ -108,9 +113,10 @@ } cmd.Flags().StringVarP(&opts.Hostname, "hostname", "h", "", "The hostname of the GitHub instance to authenticate with") - cmd.Flags().StringSliceVarP(&opts.Scopes, "scopes", "s", nil, "Additional authentication scopes for gh to have") + cmd.Flags().StringSliceVarP(&opts.Scopes, "scopes", "s", nil, "Additional authentication scopes to request") cmd.Flags().BoolVar(&tokenStdin, "with-token", false, "Read token from standard input") cmd.Flags().BoolVarP(&opts.Web, "web", "w", false, "Open a browser to authenticate") + cmdutil.StringEnumFlag(cmd, &opts.GitProtocol, "git-protocol", "p", "", []string{"ssh", "https"}, "The protocol to use for git operations") return cmd } @@ -186,6 +192,7 @@ Web: opts.Web, Scopes: opts.Scopes, Executable: opts.MainExecutable, + GitProtocol: opts.GitProtocol, }) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/auth/shared/login_flow.go new/cli-2.5.2/pkg/cmd/auth/shared/login_flow.go --- old/cli-2.5.1/pkg/cmd/auth/shared/login_flow.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/auth/shared/login_flow.go 2022-03-01 15:37:58.000000000 +0100 @@ -29,6 +29,7 @@ Web bool Scopes []string Executable string + GitProtocol string sshContext sshContext } @@ -39,8 +40,8 @@ httpClient := opts.HTTPClient cs := opts.IO.ColorScheme() - var gitProtocol string - if opts.Interactive { + gitProtocol := strings.ToLower(opts.GitProtocol) + if opts.Interactive && gitProtocol == "" { var proto string err := prompt.SurveyAskOne(&survey.Select{ Message: "What is your preferred protocol for Git operations?", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/code.go new/cli-2.5.2/pkg/cmd/codespace/code.go --- old/cli-2.5.1/pkg/cmd/codespace/code.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/code.go 2022-03-01 15:37:58.000000000 +0100 @@ -3,17 +3,11 @@ import ( "context" "fmt" - "io/ioutil" "net/url" - "github.com/cli/cli/v2/pkg/cmdutil" "github.com/spf13/cobra" ) -type browser interface { - Browse(string) error -} - func newCodeCmd(app *App) *cobra.Command { var ( codespace string @@ -25,8 +19,7 @@ Short: "Open a codespace in Visual Studio Code", Args: noArgsConstraint, RunE: func(cmd *cobra.Command, args []string) error { - b := cmdutil.NewBrowser("", ioutil.Discard, app.io.ErrOut) - return app.VSCode(cmd.Context(), b, codespace, useInsiders) + return app.VSCode(cmd.Context(), codespace, useInsiders) }, } @@ -37,7 +30,7 @@ } // VSCode opens a codespace in the local VS VSCode application. -func (a *App) VSCode(ctx context.Context, browser browser, codespaceName string, useInsiders bool) error { +func (a *App) VSCode(ctx context.Context, codespaceName string, useInsiders bool) error { if codespaceName == "" { codespace, err := chooseCodespace(ctx, a.apiClient) if err != nil { @@ -50,7 +43,7 @@ } url := vscodeProtocolURL(codespaceName, useInsiders) - if err := browser.Browse(url); err != nil { + if err := a.browser.Browse(url); err != nil { return fmt.Errorf("error opening Visual Studio Code: %w", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/code_test.go new/cli-2.5.2/pkg/cmd/codespace/code_test.go --- old/cli-2.5.1/pkg/cmd/codespace/code_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/code_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -40,8 +40,10 @@ for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { b := &cmdutil.TestBrowser{} - a := &App{} - if err := a.VSCode(context.Background(), b, tt.args.codespaceName, tt.args.useInsiders); (err != nil) != tt.wantErr { + a := &App{ + browser: b, + } + if err := a.VSCode(context.Background(), tt.args.codespaceName, tt.args.useInsiders); (err != nil) != tt.wantErr { t.Errorf("App.VSCode() error = %v, wantErr %v", err, tt.wantErr) } b.Verify(t, tt.wantURL) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/common.go new/cli-2.5.2/pkg/cmd/codespace/common.go --- old/cli-2.5.1/pkg/cmd/codespace/common.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/common.go 2022-03-01 15:37:58.000000000 +0100 @@ -21,6 +21,10 @@ "golang.org/x/term" ) +type browser interface { + Browse(string) error +} + type executable interface { Executable() string } @@ -30,9 +34,10 @@ apiClient apiClient errLogger *log.Logger executable executable + browser browser } -func NewApp(io *iostreams.IOStreams, exe executable, apiClient apiClient) *App { +func NewApp(io *iostreams.IOStreams, exe executable, apiClient apiClient, browser browser) *App { errLogger := log.New(io.ErrOut, "", 0) return &App{ @@ -40,6 +45,7 @@ apiClient: apiClient, errLogger: errLogger, executable: exe, + browser: browser, } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/create.go new/cli-2.5.2/pkg/cmd/codespace/create.go --- old/cli-2.5.1/pkg/cmd/codespace/create.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/create.go 2022-03-01 15:37:58.000000000 +0100 @@ -9,15 +9,18 @@ "github.com/AlecAivazis/survey/v2" "github.com/cli/cli/v2/internal/codespaces" "github.com/cli/cli/v2/internal/codespaces/api" + "github.com/cli/cli/v2/pkg/cmdutil" + "github.com/cli/cli/v2/utils" "github.com/spf13/cobra" ) type createOptions struct { - repo string - branch string - machine string - showStatus bool - idleTimeout time.Duration + repo string + branch string + machine string + showStatus bool + permissionsOptOut bool + idleTimeout time.Duration } func newCreateCmd(app *App) *cobra.Command { @@ -35,6 +38,7 @@ createCmd.Flags().StringVarP(&opts.repo, "repo", "r", "", "repository name with owner: user/repo") createCmd.Flags().StringVarP(&opts.branch, "branch", "b", "", "repository branch") createCmd.Flags().StringVarP(&opts.machine, "machine", "m", "", "hardware specifications for the VM") + createCmd.Flags().BoolVarP(&opts.permissionsOptOut, "default-permissions", "", false, "do not prompt to accept additional permissions requested by the codespace") createCmd.Flags().BoolVarP(&opts.showStatus, "status", "s", false, "show status of post-create command and dotfiles") createCmd.Flags().DurationVar(&opts.idleTimeout, "idle-timeout", 0, "allowed inactivity before codespace is stopped, e.g. \"10m\", \"1h\"") @@ -108,17 +112,30 @@ return errors.New("there are no available machine types for this repository") } - a.StartProgressIndicatorWithLabel("Creating codespace") - codespace, err := a.apiClient.CreateCodespace(ctx, &api.CreateCodespaceParams{ + createParams := &api.CreateCodespaceParams{ RepositoryID: repository.ID, Branch: branch, Machine: machine, Location: locationResult.Location, IdleTimeoutMinutes: int(opts.idleTimeout.Minutes()), - }) + PermissionsOptOut: opts.permissionsOptOut, + } + + a.StartProgressIndicatorWithLabel("Creating codespace") + codespace, err := a.apiClient.CreateCodespace(ctx, createParams) a.StopProgressIndicator() + if err != nil { - return fmt.Errorf("error creating codespace: %w", err) + var aerr api.AcceptPermissionsRequiredError + if !errors.As(err, &aerr) || aerr.AllowPermissionsURL == "" { + return fmt.Errorf("error creating codespace: %w", err) + } + + codespace, err = a.handleAdditionalPermissions(ctx, createParams, aerr.AllowPermissionsURL) + if err != nil { + // this error could be a cmdutil.SilentError (in the case that the user opened the browser) so we don't want to wrap it + return err + } } if opts.showStatus { @@ -131,6 +148,71 @@ return nil } +func (a *App) handleAdditionalPermissions(ctx context.Context, createParams *api.CreateCodespaceParams, allowPermissionsURL string) (*api.Codespace, error) { + var ( + isInteractive = a.io.CanPrompt() + cs = a.io.ColorScheme() + displayURL = utils.DisplayURL(allowPermissionsURL) + ) + + fmt.Fprintf(a.io.ErrOut, "You must authorize or deny additional permissions requested by this codespace before continuing.\n") + + if !isInteractive { + fmt.Fprintf(a.io.ErrOut, "%s in your browser to review and authorize additional permissions: %s\n", cs.Bold("Open this URL"), displayURL) + fmt.Fprintf(a.io.ErrOut, "Alternatively, you can run %q with the %q option to continue without authorizing additional permissions.\n", a.io.ColorScheme().Bold("create"), cs.Bold("--default-permissions")) + return nil, cmdutil.SilentError + } + + choices := []string{ + "Continue in browser to review and authorize additional permissions", + "Continue without authorizing additional permissions", + } + + permsSurvey := []*survey.Question{ + { + Name: "accept", + Prompt: &survey.Select{ + Message: "What would you like to do?", + Options: choices, + Default: choices[0], + }, + Validate: survey.Required, + }, + } + + var answers struct { + Accept string + } + + if err := ask(permsSurvey, &answers); err != nil { + return nil, fmt.Errorf("error getting answers: %w", err) + } + + // if the user chose to continue in the browser, open the URL + if answers.Accept == choices[0] { + if err := a.browser.Browse(allowPermissionsURL); err != nil { + return nil, fmt.Errorf("error opening browser: %w", err) + } + // browser opened successfully but we do not know if they accepted the permissions + // so we must exit and wait for the user to attempt the create again + return nil, cmdutil.SilentError + } + + // if the user chose to create the codespace without the permissions, + // we can continue with the create opting out of the additional permissions + createParams.PermissionsOptOut = true + + a.StartProgressIndicatorWithLabel("Creating codespace") + codespace, err := a.apiClient.CreateCodespace(ctx, createParams) + a.StopProgressIndicator() + + if err != nil { + return nil, fmt.Errorf("error creating codespace: %w", err) + } + + return codespace, nil +} + // showStatus polls the codespace for a list of post create states and their status. It will keep polling // until all states have finished. Once all states have finished, we poll once more to check if any new // states have been introduced and stop polling otherwise. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/create_test.go new/cli-2.5.2/pkg/cmd/codespace/create_test.go --- old/cli-2.5.1/pkg/cmd/codespace/create_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/create_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -7,6 +7,7 @@ "time" "github.com/cli/cli/v2/internal/codespaces/api" + "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" ) @@ -18,7 +19,7 @@ name string fields fields opts createOptions - wantErr bool + wantErr error wantStdout string wantStderr string }{ @@ -69,6 +70,57 @@ }, wantStdout: "monalisa-dotfiles-abcd1234\n", }, + { + name: "create codespace that requires accepting additional permissions", + fields: fields{ + apiClient: &apiClientMock{ + GetCodespaceRegionLocationFunc: func(ctx context.Context) (string, error) { + return "EUROPE", nil + }, + GetRepositoryFunc: func(ctx context.Context, nwo string) (*api.Repository, error) { + return &api.Repository{ + ID: 1234, + FullName: nwo, + DefaultBranch: "main", + }, nil + }, + GetCodespacesMachinesFunc: func(ctx context.Context, repoID int, branch, location string) ([]*api.Machine, error) { + return []*api.Machine{ + { + Name: "GIGA", + DisplayName: "Gigabits of a machine", + }, + }, nil + }, + 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) + } + return &api.Codespace{}, api.AcceptPermissionsRequiredError{ + AllowPermissionsURL: "https://example.com/permissions", + } + }, + GetCodespaceRepoSuggestionsFunc: func(ctx context.Context, partialSearch string, params api.RepoSearchParameters) ([]string, error) { + return nil, nil // We can't ask for suggestions without a terminal. + }, + }, + }, + opts: createOptions{ + repo: "monalisa/dotfiles", + branch: "", + machine: "GIGA", + showStatus: false, + idleTimeout: 30 * time.Minute, + }, + wantErr: cmdutil.SilentError, + wantStderr: `You must authorize or deny additional permissions requested by this codespace before continuing. +Open this URL in your browser to review and authorize additional permissions: example.com/permissions +Alternatively, you can run "create" with the "--default-permissions" option to continue without authorizing additional permissions. +`, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -77,7 +129,7 @@ io: io, apiClient: tt.fields.apiClient, } - if err := a.Create(context.Background(), tt.opts); (err != nil) != tt.wantErr { + if err := a.Create(context.Background(), tt.opts); err != tt.wantErr { t.Errorf("App.Create() error = %v, wantErr %v", err, tt.wantErr) } if got := stdout.String(); got != tt.wantStdout { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/delete_test.go new/cli-2.5.2/pkg/cmd/codespace/delete_test.go --- old/cli-2.5.1/pkg/cmd/codespace/delete_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/delete_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -190,7 +190,7 @@ io, _, stdout, stderr := iostreams.Test() io.SetStdinTTY(true) io.SetStdoutTTY(true) - app := NewApp(io, nil, apiMock) + app := NewApp(io, nil, apiMock, nil) err := app.Delete(context.Background(), opts) if (err != nil) != tt.wantErr { t.Errorf("delete() error = %v, wantErr %v", err, tt.wantErr) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/codespace/list.go new/cli-2.5.2/pkg/cmd/codespace/list.go --- old/cli-2.5.1/pkg/cmd/codespace/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/codespace/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -16,9 +16,10 @@ var exporter cmdutil.Exporter listCmd := &cobra.Command{ - Use: "list", - Short: "List your codespaces", - Args: noArgsConstraint, + Use: "list", + Short: "List your codespaces", + Aliases: []string{"ls"}, + Args: noArgsConstraint, RunE: func(cmd *cobra.Command, args []string) error { if limit < 1 { return cmdutil.FlagErrorf("invalid limit: %v", limit) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/completion/completion.go new/cli-2.5.2/pkg/cmd/completion/completion.go --- old/cli-2.5.1/pkg/cmd/completion/completion.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/completion/completion.go 2022-03-01 15:37:58.000000000 +0100 @@ -92,8 +92,7 @@ } cmdutil.DisableAuthCheck(cmd) - - cmd.Flags().StringVarP(&shellType, "shell", "s", "", "Shell type: {bash|zsh|fish|powershell}") + cmdutil.StringEnumFlag(cmd, &shellType, "shell", "s", "", []string{"bash", "zsh", "fish", "powershell"}, "Shell type") return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/completion/completion_test.go new/cli-2.5.2/pkg/cmd/completion/completion_test.go --- old/cli-2.5.1/pkg/cmd/completion/completion_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/completion/completion_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -39,7 +39,7 @@ { name: "unsupported shell", args: "completion -s csh", - wantErr: "unsupported shell type \"csh\"", + wantErr: "invalid argument \"csh\" for \"-s, --shell\" flag: valid values are {bash|zsh|fish|powershell}", }, } for _, tt := range tests { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/config/list/list.go new/cli-2.5.2/pkg/cmd/config/list/list.go --- old/cli-2.5.1/pkg/cmd/config/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/config/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -23,9 +23,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "Print a list of configuration keys and values", - Args: cobra.ExactArgs(0), + Use: "list", + Short: "Print a list of configuration keys and values", + Aliases: []string{"ls"}, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { if runF != nil { return runF(opts) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/extension/command.go new/cli-2.5.2/pkg/cmd/extension/command.go --- old/cli-2.5.1/pkg/cmd/extension/command.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/extension/command.go 2022-03-01 15:37:58.000000000 +0100 @@ -40,9 +40,10 @@ extCmd.AddCommand( &cobra.Command{ - Use: "list", - Short: "List installed extension commands", - Args: cobra.NoArgs, + Use: "list", + Short: "List installed extension commands", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { cmds := m.List(true) if len(cmds) == 0 { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/gist/list/list.go new/cli-2.5.2/pkg/cmd/gist/list/list.go --- old/cli-2.5.1/pkg/cmd/gist/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/gist/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -35,9 +35,10 @@ var flagSecret bool cmd := &cobra.Command{ - Use: "list", - Short: "List your gists", - Args: cobra.NoArgs, + Use: "list", + Short: "List your gists", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { if opts.Limit < 1 { return cmdutil.FlagErrorf("invalid limit: %v", opts.Limit) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/gpg-key/list/list.go new/cli-2.5.2/pkg/cmd/gpg-key/list/list.go --- old/cli-2.5.1/pkg/cmd/gpg-key/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/gpg-key/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -27,9 +27,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "Lists GPG keys in your GitHub account", - Args: cobra.ExactArgs(0), + Use: "list", + Short: "Lists GPG keys in your GitHub account", + Aliases: []string{"ls"}, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { if runF != nil { return runF(opts) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/issue/comment/comment.go new/cli-2.5.2/pkg/cmd/issue/comment/comment.go --- old/cli-2.5.1/pkg/cmd/issue/comment/comment.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/issue/comment/comment.go 2022-03-01 15:37:58.000000000 +0100 @@ -23,9 +23,15 @@ cmd := &cobra.Command{ Use: "comment {<number> | <url>}", - Short: "Create a new issue comment", + Short: "Add a comment to an issue", + Long: heredoc.Doc(` + Add a comment to a GitHub issue. + + Without the body text supplied through flags, the command will interactively + prompt for the comment text. + `), Example: heredoc.Doc(` - $ gh issue comment 22 --body "I was able to reproduce this issue, lets fix it." + $ gh issue comment 12 --body "Hi from GitHub CLI" `), Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { @@ -54,10 +60,10 @@ }, } - cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "Supply a body. Will prompt for one otherwise.") + cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "The comment body `text`") cmd.Flags().StringVarP(&bodyFile, "body-file", "F", "", "Read body text from `file` (use \"-\" to read from standard input)") - cmd.Flags().BoolP("editor", "e", false, "Add body using editor") - cmd.Flags().BoolP("web", "w", false, "Add body in browser") + cmd.Flags().BoolP("editor", "e", false, "Skip prompts and open the text editor to write the body in") + cmd.Flags().BoolP("web", "w", false, "Open the web browser to write the comment") return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/issue/list/list.go new/cli-2.5.2/pkg/cmd/issue/list/list.go --- old/cli-2.5.1/pkg/cmd/issue/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/issue/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -57,16 +57,22 @@ cmd := &cobra.Command{ Use: "list", - Short: "List and filter issues in this repository", + Short: "List issues in a repository", + Long: heredoc.Doc(` + List issues in a GitHub repository. + + The search query syntax is documented here: + <https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests> + `), Example: heredoc.Doc(` - $ gh issue list -l "bug" -l "help wanted" - $ gh issue list -A monalisa - $ gh issue list -a "@me" - $ gh issue list --web + $ gh issue list --label "bug" --label "help wanted" + $ gh issue list --author monalisa + $ gh issue list --assignee "@me" $ gh issue list --milestone "The big 1.0" $ gh issue list --search "error no:assignee sort:created-asc" `), - Args: cmdutil.NoArgsQuoteReminder, + Aliases: []string{"ls"}, + Args: cmdutil.NoArgsQuoteReminder, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo @@ -82,17 +88,14 @@ }, } - cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the browser to list the issue(s)") + cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List issues in the web browser") cmd.Flags().StringVarP(&opts.Assignee, "assignee", "a", "", "Filter by assignee") - cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Filter by labels") - cmd.Flags().StringVarP(&opts.State, "state", "s", "open", "Filter by state: {open|closed|all}") - _ = cmd.RegisterFlagCompletionFunc("state", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"open", "closed", "all"}, cobra.ShellCompDirectiveNoFileComp - }) + cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Filter by label") + cmdutil.StringEnumFlag(cmd, &opts.State, "state", "s", "open", []string{"open", "closed", "all"}, "Filter by state") cmd.Flags().IntVarP(&opts.LimitResults, "limit", "L", 30, "Maximum number of issues to fetch") cmd.Flags().StringVarP(&opts.Author, "author", "A", "", "Filter by author") cmd.Flags().StringVar(&opts.Mention, "mention", "", "Filter by mention") - cmd.Flags().StringVarP(&opts.Milestone, "milestone", "m", "", "Filter by milestone `number` or `title`") + cmd.Flags().StringVarP(&opts.Milestone, "milestone", "m", "", "Filter by milestone number or title") cmd.Flags().StringVarP(&opts.Search, "search", "S", "", "Search issues with `query`") cmdutil.AddJSONFlags(cmd, &opts.Exporter, api.IssueFields) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/comment/comment.go new/cli-2.5.2/pkg/cmd/pr/comment/comment.go --- old/cli-2.5.1/pkg/cmd/pr/comment/comment.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/comment/comment.go 2022-03-01 15:37:58.000000000 +0100 @@ -22,17 +22,15 @@ cmd := &cobra.Command{ Use: "comment [<number> | <url> | <branch>]", - Short: "Create a new pr comment", + Short: "Add a comment to a pull request", Long: heredoc.Doc(` - Create a new pr comment. + Add a comment to a GitHub pull request. - Without an argument, the pull request that belongs to the current branch - is selected. - - With '--web', comment on the pull request in a web browser instead. + Without the body text supplied through flags, the command will interactively + prompt for the comment text. `), Example: heredoc.Doc(` - $ gh pr comment 22 --body "This looks great, lets get it deployed." + $ gh pr comment 13 --body "Hi from GitHub CLI" `), Args: cobra.MaximumNArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { @@ -68,10 +66,10 @@ }, } - cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "Supply a body. Will prompt for one otherwise.") + cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "The comment body `text`") cmd.Flags().StringVarP(&bodyFile, "body-file", "F", "", "Read body text from `file` (use \"-\" to read from standard input)") - cmd.Flags().BoolP("editor", "e", false, "Add body using editor") - cmd.Flags().BoolP("web", "w", false, "Add body in browser") + cmd.Flags().BoolP("editor", "e", false, "Skip prompts and open the text editor to write the body in") + cmd.Flags().BoolP("web", "w", false, "Open the web browser to write the comment") return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/diff/diff.go new/cli-2.5.2/pkg/cmd/pr/diff/diff.go --- old/cli-2.5.1/pkg/cmd/pr/diff/diff.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/diff/diff.go 2022-03-01 15:37:58.000000000 +0100 @@ -66,7 +66,7 @@ case "never": opts.UseColor = false default: - return cmdutil.FlagErrorf("the value for `--color` must be one of \"auto\", \"always\", or \"never\"") + return fmt.Errorf("unsupported color %q", colorFlag) } if runF != nil { @@ -76,7 +76,7 @@ }, } - cmd.Flags().StringVar(&colorFlag, "color", "auto", "Use color in diff output: {always|never|auto}") + cmdutil.StringEnumFlag(cmd, &colorFlag, "color", "", "auto", []string{"always", "never", "auto"}, "Use color in diff output") cmd.Flags().BoolVar(&opts.Patch, "patch", false, "Display diff in patch format") return cmd diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/diff/diff_test.go new/cli-2.5.2/pkg/cmd/pr/diff/diff_test.go --- old/cli-2.5.1/pkg/cmd/pr/diff/diff_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/diff/diff_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -83,7 +83,7 @@ name: "invalid --color argument", args: "--color doublerainbow", isTTY: true, - wantErr: "the value for `--color` must be one of \"auto\", \"always\", or \"never\"", + wantErr: "invalid argument \"doublerainbow\" for \"--color\" flag: valid values are {always|never|auto}", }, } for _, tt := range tests { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/list/list.go new/cli-2.5.2/pkg/cmd/pr/list/list.go --- old/cli-2.5.1/pkg/cmd/pr/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -36,6 +36,7 @@ HeadBranch string Labels []string Author string + AppAuthor string Assignee string Search string Draft string @@ -52,24 +53,28 @@ cmd := &cobra.Command{ Use: "list", - Short: "List and filter pull requests in this repository", + Short: "List pull requests in a repository", + Long: heredoc.Doc(` + List pull requests in a GitHub repository. + + The search query syntax is documented here: + <https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests> + `), Example: heredoc.Doc(` List PRs authored by you $ gh pr list --author "@me" - List PRs assigned to you - $ gh pr list --assignee "@me" - - List PRs by label, combining multiple labels with AND + List only PRs with all of the given labels $ gh pr list --label bug --label "priority 1" - List PRs using search syntax + Filter PRs using search syntax $ gh pr list --search "status:success review:required" - Open the list of PRs in a web browser - $ gh pr list --web + Find a PR that introduced a given commit + $ gh pr list --search "<SHA>" --state merged `), - Args: cmdutil.NoArgsQuoteReminder, + Aliases: []string{"ls"}, + Args: cmdutil.NoArgsQuoteReminder, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo @@ -82,6 +87,14 @@ opts.Draft = strconv.FormatBool(draft) } + if err := cmdutil.MutuallyExclusive( + "specify only `--author` or `--app`", + opts.Author != "", + opts.AppAuthor != "", + ); err != nil { + return err + } + if runF != nil { return runF(opts) } @@ -89,16 +102,14 @@ }, } - cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the browser to list the pull requests") + cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "List pull requests in the web browser") cmd.Flags().IntVarP(&opts.LimitResults, "limit", "L", 30, "Maximum number of items to fetch") - cmd.Flags().StringVarP(&opts.State, "state", "s", "open", "Filter by state: {open|closed|merged|all}") - _ = cmd.RegisterFlagCompletionFunc("state", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{"open", "closed", "merged", "all"}, cobra.ShellCompDirectiveNoFileComp - }) + cmdutil.StringEnumFlag(cmd, &opts.State, "state", "s", "open", []string{"open", "closed", "merged", "all"}, "Filter by state") cmd.Flags().StringVarP(&opts.BaseBranch, "base", "B", "", "Filter by base branch") cmd.Flags().StringVarP(&opts.HeadBranch, "head", "H", "", "Filter by head branch") - cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Filter by labels") + cmd.Flags().StringSliceVarP(&opts.Labels, "label", "l", nil, "Filter by label") cmd.Flags().StringVarP(&opts.Author, "author", "A", "", "Filter by author") + cmd.Flags().StringVar(&opts.AppAuthor, "app", "", "Filter by GitHub App author") cmd.Flags().StringVarP(&opts.Assignee, "assignee", "a", "", "Filter by assignee") cmd.Flags().StringVarP(&opts.Search, "search", "S", "", "Search pull requests with `query`") cmd.Flags().BoolVarP(&draft, "draft", "d", false, "Filter by draft state") @@ -163,6 +174,10 @@ return opts.Browser.Browse(openURL) } + if opts.AppAuthor != "" { + filters.Author = fmt.Sprintf("app/%s", opts.AppAuthor) + } + listResult, err := listPullRequests(httpClient, baseRepo, filters, opts.LimitResults) if err != nil { return err diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/list/list_test.go new/cli-2.5.2/pkg/cmd/pr/list/list_test.go --- old/cli-2.5.1/pkg/cmd/pr/list/list_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/list/list_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -229,6 +229,53 @@ } } +func TestPRList_filteringAuthor(t *testing.T) { + tests := []struct { + name string + cli string + expectedQuery string + }{ + { + name: "author @me", + cli: `--author "@me"`, + expectedQuery: `repo:OWNER/REPO is:pr is:open author:@me`, + }, + { + name: "author user", + cli: `--author "monalisa"`, + expectedQuery: `repo:OWNER/REPO is:pr is:open author:monalisa`, + }, + { + name: "app author", + cli: `--author "app/dependabot"`, + expectedQuery: `repo:OWNER/REPO is:pr is:open author:app/dependabot`, + }, + { + name: "app author with app option", + cli: `--app "dependabot"`, + expectedQuery: `repo:OWNER/REPO is:pr is:open author:app/dependabot`, + }, + } + + 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)) + })) + + _, err := runCommand(http, true, test.cli) + if err != nil { + t.Fatal(err) + } + }) + } +} + func TestPRList_withInvalidLimitFlag(t *testing.T) { http := initFakeHTTP() defer http.Verify(t) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/pr/shared/commentable.go new/cli-2.5.2/pkg/cmd/pr/shared/commentable.go --- old/cli-2.5.1/pkg/cmd/pr/shared/commentable.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/pr/shared/commentable.go 2022-03-01 15:37:58.000000000 +0100 @@ -65,13 +65,9 @@ if inputFlags == 0 { if !opts.IO.CanPrompt() { - return cmdutil.FlagErrorf("`--body`, `--body-file` or `--web` required when not running interactively") + return cmdutil.FlagErrorf("flags required when not running interactively") } opts.Interactive = true - } else if inputFlags == 1 { - if !opts.IO.CanPrompt() && opts.InputType == InputTypeEditor { - return cmdutil.FlagErrorf("`--body`, `--body-file` or `--web` required when not running interactively") - } } else if inputFlags > 1 { return cmdutil.FlagErrorf("specify only one of `--body`, `--body-file`, `--editor`, or `--web`") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/release/list/list.go new/cli-2.5.2/pkg/cmd/release/list/list.go --- old/cli-2.5.1/pkg/cmd/release/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/release/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -28,9 +28,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "List releases in a repository", - Args: cobra.NoArgs, + Use: "list", + Short: "List releases in a repository", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/release/release.go new/cli-2.5.2/pkg/cmd/release/release.go --- old/cli-2.5.1/pkg/cmd/release/release.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/release/release.go 2022-03-01 15:37:58.000000000 +0100 @@ -15,7 +15,7 @@ func NewCmdRelease(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "release <command>", - Short: "Manage GitHub releases", + Short: "Manage releases", Annotations: map[string]string{ "IsCore": "true", }, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/repo/deploy-key/list/list.go new/cli-2.5.2/pkg/cmd/repo/deploy-key/list/list.go --- old/cli-2.5.1/pkg/cmd/repo/deploy-key/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/repo/deploy-key/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -26,9 +26,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "List deploy keys in a GitHub repository", - Args: cobra.ExactArgs(0), + Use: "list", + Short: "List deploy keys in a GitHub repository", + Aliases: []string{"ls"}, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { opts.BaseRepo = f.BaseRepo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/repo/fork/fork.go new/cli-2.5.2/pkg/cmd/repo/fork/fork.go --- old/cli-2.5.1/pkg/cmd/repo/fork/fork.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/repo/fork/fork.go 2022-03-01 15:37:58.000000000 +0100 @@ -7,6 +7,7 @@ "strings" "time" + "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/api" "github.com/cli/cli/v2/context" "github.com/cli/cli/v2/git" @@ -61,21 +62,23 @@ Use: "fork [<repository>] [-- <gitflags>...]", Args: func(cmd *cobra.Command, args []string) error { if cmd.ArgsLenAtDash() == 0 && len(args[1:]) > 0 { - return cmdutil.FlagErrorf("repository argument required when passing 'git clone' flags") + return cmdutil.FlagErrorf("repository argument required when passing git clone flags") } return nil }, Short: "Create a fork of a repository", - Long: `Create a fork of a repository. + Long: heredoc.Docf(` + Create a fork of a repository. -With no argument, creates a fork of the current repository. Otherwise, forks -the specified repository. + 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. + 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 %[1]s--remote-name%[1]s. -Additional 'git clone' flags can be passed in by listing them after '--'.`, + Additional git clone flags can be passed after %[1]s--%[1]s. + `, "`"), RunE: func(cmd *cobra.Command, args []string) error { promptOk := opts.IO.CanPrompt() if len(args) > 0 { @@ -109,14 +112,14 @@ if err == pflag.ErrHelp { return err } - return cmdutil.FlagErrorf("%w\nSeparate git clone flags with '--'.", err) + return cmdutil.FlagErrorf("%w\nSeparate git clone flags with `--`.", err) }) - cmd.Flags().BoolVar(&opts.Clone, "clone", false, "Clone the fork {true|false}") - cmd.Flags().BoolVar(&opts.Remote, "remote", false, "Add remote for fork {true|false}") - cmd.Flags().StringVar(&opts.RemoteName, "remote-name", defaultRemoteName, "Specify a name for a fork's new remote.") + cmd.Flags().BoolVar(&opts.Clone, "clone", false, "Clone the fork") + cmd.Flags().BoolVar(&opts.Remote, "remote", false, "Add a git remote for the fork") + cmd.Flags().StringVar(&opts.RemoteName, "remote-name", defaultRemoteName, "Specify the name for the new remote") cmd.Flags().StringVar(&opts.Organization, "org", "", "Create the fork in an organization") - cmd.Flags().StringVar(&opts.ForkName, "fork-name", "", "Specify a name for the forked repo") + cmd.Flags().StringVar(&opts.ForkName, "fork-name", "", "Rename the forked repository") return cmd } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/repo/fork/fork_test.go new/cli-2.5.2/pkg/cmd/repo/fork/fork_test.go --- old/cli-2.5.1/pkg/cmd/repo/fork/fork_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/repo/fork/fork_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -45,7 +45,7 @@ name: "git args without repo", cli: "-- --foo bar", wantErr: true, - errMsg: "repository argument required when passing 'git clone' flags", + errMsg: "repository argument required when passing git clone flags", }, { name: "repo", @@ -130,7 +130,7 @@ name: "git flags in wrong place", cli: "--depth 1 OWNER/REPO", wantErr: true, - errMsg: "unknown flag: --depth\nSeparate git clone flags with '--'.", + errMsg: "unknown flag: --depth\nSeparate git clone flags with `--`.", }, { name: "with fork name", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/repo/repo.go new/cli-2.5.2/pkg/cmd/repo/repo.go --- old/cli-2.5.1/pkg/cmd/repo/repo.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/repo/repo.go 2022-03-01 15:37:58.000000000 +0100 @@ -22,7 +22,7 @@ func NewCmdRepo(f *cmdutil.Factory) *cobra.Command { cmd := &cobra.Command{ Use: "repo <command>", - Short: "Create, clone, fork, and view repositories", + Short: "Manage repositories", Long: `Work with GitHub repositories.`, Example: heredoc.Doc(` $ gh repo create diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/root/help.go new/cli-2.5.2/pkg/cmd/root/help.go --- old/cli-2.5.1/pkg/cmd/root/help.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/root/help.go 2022-03-01 15:37:58.000000000 +0100 @@ -3,6 +3,7 @@ import ( "bytes" "fmt" + "sort" "strings" "github.com/cli/cli/v2/pkg/cmdutil" @@ -88,6 +89,7 @@ return } + namePadding := 12 coreCommands := []string{} actionsCommands := []string{} additionalCommands := []string{} @@ -99,7 +101,7 @@ continue } - s := rpad(c.Name()+":", c.NamePadding()) + c.Short + s := rpad(c.Name()+":", namePadding) + c.Short if _, ok := c.Annotations["IsCore"]; ok { coreCommands = append(coreCommands, s) } else if _, ok := c.Annotations["IsActions"]; ok { @@ -145,6 +147,16 @@ } if isRootCmd(command) { + var helpTopics []string + if c := findCommand(command, "actions"); c != nil { + helpTopics = append(helpTopics, rpad(c.Name()+":", namePadding)+c.Short) + } + for topic, params := range HelpTopics { + helpTopics = append(helpTopics, rpad(topic+":", namePadding)+params["short"]) + } + sort.Strings(helpTopics) + helpEntries = append(helpEntries, helpEntry{"HELP TOPICS", strings.Join(helpTopics, "\n")}) + if exts := f.ExtensionManager.List(false); len(exts) > 0 { var names []string for _, ext := range exts { @@ -192,6 +204,15 @@ } } +func findCommand(cmd *cobra.Command, name string) *cobra.Command { + for _, c := range cmd.Commands() { + if c.Name() == name { + return c + } + } + return nil +} + // rpad adds padding to the right of a string. func rpad(s string, padding int) string { template := fmt.Sprintf("%%-%ds ", padding) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/root/root.go new/cli-2.5.2/pkg/cmd/root/root.go --- old/cli-2.5.1/pkg/cmd/root/root.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/root/root.go 2022-03-01 15:37:58.000000000 +0100 @@ -50,9 +50,6 @@ "help:feedback": heredoc.Doc(` Open an issue using 'gh issue create -R github.com/cli/cli' `), - "help:environment": heredoc.Doc(` - See 'gh help environment' for the list of supported environment variables. - `), }, } @@ -142,6 +139,7 @@ vscsURL, &lazyLoadedHTTPClient{factory: f}, ), + f.Browser, ) cmd := codespaceCmd.NewRootCmd(app) cmd.Use = "codespace" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/run/list/list.go new/cli-2.5.2/pkg/cmd/run/list/list.go --- old/cli-2.5.1/pkg/cmd/run/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/run/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -40,9 +40,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "List recent workflow runs", - Args: cobra.NoArgs, + Use: "list", + Short: "List recent workflow runs", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/secret/list/list.go new/cli-2.5.2/pkg/cmd/secret/list/list.go --- old/cli-2.5.1/pkg/cmd/secret/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/secret/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -48,7 +48,8 @@ - organization: available to Actions runs within an organization - user: available to Codespaces for your user `), - Args: cobra.NoArgs, + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/secret/set/set.go new/cli-2.5.2/pkg/cmd/secret/set/set.go --- old/cli-2.5.1/pkg/cmd/secret/set/set.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/secret/set/set.go 2022-03-01 15:37:58.000000000 +0100 @@ -121,10 +121,6 @@ return cmdutil.FlagErrorf("`--visibility` is only supported with `--org`") } - if opts.Visibility != shared.All && opts.Visibility != shared.Private && opts.Visibility != shared.Selected { - return cmdutil.FlagErrorf("`--visibility` must be one of \"all\", \"private\", or \"selected\"") - } - if opts.Visibility != shared.Selected && len(opts.RepositoryNames) > 0 { return cmdutil.FlagErrorf("`--repos` is only supported with `--visibility=selected`") } @@ -149,7 +145,7 @@ cmd.Flags().StringVarP(&opts.OrgName, "org", "o", "", "Set `organization` secret") cmd.Flags().StringVarP(&opts.EnvName, "env", "e", "", "Set deployment `environment` secret") cmd.Flags().BoolVarP(&opts.UserSecrets, "user", "u", false, "Set a secret for your user") - cmd.Flags().StringVarP(&opts.Visibility, "visibility", "v", "private", "Set visibility for an organization secret: `{all|private|selected}`") + cmdutil.StringEnumFlag(cmd, &opts.Visibility, "visibility", "v", shared.Private, []string{shared.All, shared.Private, shared.Selected}, "Set visibility for an organization secret") cmd.Flags().StringSliceVarP(&opts.RepositoryNames, "repos", "r", []string{}, "List of `repositories` that can access an organization or user secret") cmd.Flags().StringVarP(&opts.Body, "body", "b", "", "The value for the secret (reads from standard input if not specified)") cmd.Flags().BoolVar(&opts.DoNotStore, "no-store", false, "Print the encrypted, base64-encoded value instead of storing it on Github") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/ssh-key/list/list.go new/cli-2.5.2/pkg/cmd/ssh-key/list/list.go --- old/cli-2.5.1/pkg/cmd/ssh-key/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/ssh-key/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -26,9 +26,10 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "Lists SSH keys in your GitHub account", - Args: cobra.ExactArgs(0), + Use: "list", + Short: "Lists SSH keys in your GitHub account", + Aliases: []string{"ls"}, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { if runF != nil { return runF(opts) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmd/workflow/list/list.go new/cli-2.5.2/pkg/cmd/workflow/list/list.go --- old/cli-2.5.1/pkg/cmd/workflow/list/list.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmd/workflow/list/list.go 2022-03-01 15:37:58.000000000 +0100 @@ -33,10 +33,11 @@ } cmd := &cobra.Command{ - Use: "list", - Short: "List workflows", - Long: "List workflow files, hiding disabled workflows by default.", - Args: cobra.NoArgs, + Use: "list", + Short: "List workflows", + Long: "List workflow files, hiding disabled workflows by default.", + Aliases: []string{"ls"}, + Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { // support `-R, --repo` override opts.BaseRepo = f.BaseRepo diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/cmdutil/flags.go new/cli-2.5.2/pkg/cmdutil/flags.go --- old/cli-2.5.1/pkg/cmdutil/flags.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/cmdutil/flags.go 2022-03-01 15:37:58.000000000 +0100 @@ -1,7 +1,9 @@ package cmdutil import ( + "fmt" "strconv" + "strings" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -21,6 +23,21 @@ return f } +// StringEnumFlag defines a new string flag that only allows values listed in options. +func StringEnumFlag(cmd *cobra.Command, p *string, name, shorthand, defaultValue string, options []string, usage string) *pflag.Flag { + *p = defaultValue + val := &enumValue{string: p, options: options} + f := cmd.Flags().VarPF(val, name, shorthand, fmt.Sprintf("%s: %s", usage, formatValuesForUsageDocs(options))) + _ = cmd.RegisterFlagCompletionFunc(name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + return options, cobra.ShellCompDirectiveNoFileComp + }) + return f +} + +func formatValuesForUsageDocs(values []string) string { + return fmt.Sprintf("{%s}", strings.Join(values, "|")) +} + type stringValue struct { string **string } @@ -75,3 +92,31 @@ func (b *boolValue) IsBoolFlag() bool { return true } + +type enumValue struct { + string *string + options []string +} + +func (e *enumValue) Set(value string) error { + found := false + for _, opt := range e.options { + if strings.EqualFold(opt, value) { + found = true + break + } + } + if !found { + return fmt.Errorf("valid values are %s", formatValuesForUsageDocs(e.options)) + } + *e.string = value + return nil +} + +func (e *enumValue) String() string { + return *e.string +} + +func (e *enumValue) Type() string { + return "string" +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/iostreams/color.go new/cli-2.5.2/pkg/iostreams/color.go --- old/cli-2.5.1/pkg/iostreams/color.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/iostreams/color.go 2022-03-01 15:37:58.000000000 +0100 @@ -211,7 +211,7 @@ } func (c *ColorScheme) HexToRGB(hex string, x string) string { - if !c.enabled || !c.hasTrueColor { + if !c.enabled || !c.hasTrueColor || len(hex) != 6 { return x } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cli-2.5.1/pkg/iostreams/color_test.go new/cli-2.5.2/pkg/iostreams/color_test.go --- old/cli-2.5.1/pkg/iostreams/color_test.go 2022-02-15 18:37:41.000000000 +0100 +++ new/cli-2.5.2/pkg/iostreams/color_test.go 2022-03-01 15:37:58.000000000 +0100 @@ -175,6 +175,13 @@ wants: "red", cs: NewColorScheme(false, false, false), }, + { + name: "invalid hex", + hex: "fc0", + text: "red", + wants: "red", + cs: NewColorScheme(false, false, false), + }, } for _, tt := range tests { ++++++ vendor.tar.gz ++++++ ++++ 2577 lines of diff (skipped)