X Tutup
Skip to content

Commit ef0672e

Browse files
authored
Merge branch 'trunk' into multi-devcontainer
2 parents 9554e52 + 6fbe6d9 commit ef0672e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+3148
-133
lines changed

api/client.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -316,41 +316,55 @@ func graphQLClient(h *http.Client, hostname string) *graphql.Client {
316316

317317
// REST performs a REST request and parses the response.
318318
func (c Client) REST(hostname string, method string, p string, body io.Reader, data interface{}) error {
319+
_, err := c.RESTWithNext(hostname, method, p, body, data)
320+
return err
321+
}
322+
323+
func (c Client) RESTWithNext(hostname string, method string, p string, body io.Reader, data interface{}) (string, error) {
319324
req, err := http.NewRequest(method, restURL(hostname, p), body)
320325
if err != nil {
321-
return err
326+
return "", err
322327
}
323328

324329
req.Header.Set("Content-Type", "application/json; charset=utf-8")
325330

326331
resp, err := c.http.Do(req)
327332
if err != nil {
328-
return err
333+
return "", err
329334
}
330335
defer resp.Body.Close()
331336

332337
success := resp.StatusCode >= 200 && resp.StatusCode < 300
333338
if !success {
334-
return HandleHTTPError(resp)
339+
return "", HandleHTTPError(resp)
335340
}
336341

337342
if resp.StatusCode == http.StatusNoContent {
338-
return nil
343+
return "", nil
339344
}
340345

341346
b, err := ioutil.ReadAll(resp.Body)
342347
if err != nil {
343-
return err
348+
return "", err
344349
}
345350

346351
err = json.Unmarshal(b, &data)
347352
if err != nil {
348-
return err
353+
return "", err
349354
}
350355

351-
return nil
356+
var next string
357+
for _, m := range linkRE.FindAllStringSubmatch(resp.Header.Get("Link"), -1) {
358+
if len(m) > 2 && m[2] == "next" {
359+
next = m[1]
360+
}
361+
}
362+
363+
return next, nil
352364
}
353365

366+
var linkRE = regexp.MustCompile(`<([^>]+)>;\s*rel="([^"]+)"`)
367+
354368
func restURL(hostname string, pathOrURL string) string {
355369
if strings.HasPrefix(pathOrURL, "https://") || strings.HasPrefix(pathOrURL, "http://") {
356370
return pathOrURL

cmd/gh/main.go

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func mainRun() exitCode {
5858
updateMessageChan <- rel
5959
}()
6060

61-
hasDebug := os.Getenv("DEBUG") != ""
61+
hasDebug, _ := utils.IsDebugEnabled()
6262

6363
cmdFactory := factory.New(buildVersion)
6464
stderr := cmdFactory.IOStreams.ErrOut
@@ -327,8 +327,10 @@ func checkForUpdate(currentVersion string) (*update.ReleaseInfo, error) {
327327
// does not depend on user configuration
328328
func basicClient(currentVersion string) (*api.Client, error) {
329329
var opts []api.ClientOption
330-
if verbose := os.Getenv("DEBUG"); verbose != "" {
331-
opts = append(opts, apiVerboseLog())
330+
if isVerbose, debugValue := utils.IsDebugEnabled(); isVerbose {
331+
colorize := utils.IsTerminal(os.Stderr)
332+
logTraffic := strings.Contains(debugValue, "api")
333+
opts = append(opts, api.VerboseLog(colorable.NewColorable(os.Stderr), logTraffic, colorize))
332334
}
333335
opts = append(opts, api.AddHeader("User-Agent", fmt.Sprintf("GitHub CLI %s", currentVersion)))
334336

@@ -344,12 +346,6 @@ func basicClient(currentVersion string) (*api.Client, error) {
344346
return api.NewClient(opts...), nil
345347
}
346348

347-
func apiVerboseLog() api.ClientOption {
348-
logTraffic := strings.Contains(os.Getenv("DEBUG"), "api")
349-
colorize := utils.IsTerminal(os.Stderr)
350-
return api.VerboseLog(colorable.NewColorable(os.Stderr), logTraffic, colorize)
351-
}
352-
353349
func isRecentRelease(publishedAt time.Time) bool {
354350
return !publishedAt.IsZero() && time.Since(publishedAt) < time.Hour*24
355351
}

go.mod

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ require (
77
github.com/MakeNowJust/heredoc v1.0.0
88
github.com/briandowns/spinner v1.18.1
99
github.com/charmbracelet/glamour v0.4.0
10+
github.com/charmbracelet/lipgloss v0.5.0
1011
github.com/cli/browser v1.1.0
1112
github.com/cli/oauth v0.9.0
1213
github.com/cli/safeexec v1.0.0
1314
github.com/cli/shurcooL-graphql v0.0.1
1415
github.com/cpuguy83/go-md2man/v2 v2.0.1
15-
github.com/creack/pty v1.1.17
16+
github.com/creack/pty v1.1.18
1617
github.com/gabriel-vasile/mimetype v1.4.0
1718
github.com/google/go-cmp v0.5.7
1819
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
@@ -27,7 +28,7 @@ require (
2728
github.com/mattn/go-isatty v0.0.14
2829
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d
2930
github.com/muesli/reflow v0.3.0
30-
github.com/muesli/termenv v0.9.0
31+
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0
3132
github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38
3233
github.com/opentracing/opentracing-go v1.1.0
3334
github.com/shurcooL/githubv4 v0.0.0-20200928013246-d292edc3691b

go.sum

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ github.com/briandowns/spinner v1.18.1/go.mod h1:mQak9GHqbspjC/5iUx3qMlIho8xBS/pp
4848
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
4949
github.com/charmbracelet/glamour v0.4.0 h1:scR+smyB7WdmrlIaff6IVlm48P48JaNM7JypM/VGl4k=
5050
github.com/charmbracelet/glamour v0.4.0/go.mod h1:9ZRtG19AUIzcTm7FGLGbq3D5WKQ5UyZBbQsMQN0XIqc=
51+
github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8=
52+
github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs=
5153
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
5254
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
5355
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -66,8 +68,9 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
6668
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
6769
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
6870
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
69-
github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
7071
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
72+
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
73+
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
7174
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7275
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
7376
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -181,6 +184,7 @@ github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
181184
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
182185
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
183186
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
187+
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
184188
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
185189
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
186190
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@@ -189,10 +193,12 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
189193
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
190194
github.com/microcosm-cc/bluemonday v1.0.17 h1:Z1a//hgsQ4yjC+8zEkV8IWySkXnsxmdSY642CTFQb5Y=
191195
github.com/microcosm-cc/bluemonday v1.0.17/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
196+
github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ=
192197
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
193198
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
194-
github.com/muesli/termenv v0.9.0 h1:wnbOaGz+LUR3jNT0zOzinPnyDaCZUQRZj9GxK8eRVl8=
195199
github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw=
200+
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 h1:STjmj0uFfRryL9fzRA/OupNppeAID6QJYPMavTL7jtY=
201+
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
196202
github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 h1:0FrBxrkJ0hVembTb/e4EU5Ml6vLcOusAqymmYISg5Uo=
197203
github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU=
198204
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=

internal/authflow/flow.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/cli/cli/v2/internal/ghinstance"
1313
"github.com/cli/cli/v2/pkg/cmdutil"
1414
"github.com/cli/cli/v2/pkg/iostreams"
15+
"github.com/cli/cli/v2/utils"
1516
"github.com/cli/oauth"
1617
)
1718

@@ -53,8 +54,9 @@ func authFlow(oauthHost string, IO *iostreams.IOStreams, notice string, addition
5354
cs := IO.ColorScheme()
5455

5556
httpClient := http.DefaultClient
56-
if envDebug := os.Getenv("DEBUG"); envDebug != "" {
57-
logTraffic := strings.Contains(envDebug, "api") || strings.Contains(envDebug, "oauth")
57+
debugEnabled, debugValue := utils.IsDebugEnabled()
58+
if debugEnabled {
59+
logTraffic := strings.Contains(debugValue, "api")
5860
httpClient.Transport = api.VerboseLog(IO.ErrOut, logTraffic, IO.ColorEnabled())(httpClient.Transport)
5961
}
6062

internal/run/run.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"os/exec"
99
"path/filepath"
1010
"strings"
11+
12+
"github.com/cli/cli/v2/utils"
1113
)
1214

1315
// Runnable is typically an exec.Cmd or its stub in tests
@@ -28,7 +30,7 @@ type cmdWithStderr struct {
2830
}
2931

3032
func (c cmdWithStderr) Output() ([]byte, error) {
31-
if os.Getenv("DEBUG") != "" {
33+
if isVerbose, _ := utils.IsDebugEnabled(); isVerbose {
3234
_ = printArgs(os.Stderr, c.Cmd.Args)
3335
}
3436
if c.Cmd.Stderr != nil {
@@ -44,7 +46,7 @@ func (c cmdWithStderr) Output() ([]byte, error) {
4446
}
4547

4648
func (c cmdWithStderr) Run() error {
47-
if os.Getenv("DEBUG") != "" {
49+
if isVerbose, _ := utils.IsDebugEnabled(); isVerbose {
4850
_ = printArgs(os.Stderr, c.Cmd.Args)
4951
}
5052
if c.Cmd.Stderr != nil {

pkg/cmd/codespace/ssh.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func newSSHCmd(app *App) *cobra.Command {
5959
$ gh codespace ssh
6060
6161
$ gh codespace ssh --config > ~/.ssh/codespaces
62-
$ echo 'include ~/.ssh/codespaces' >> ~/.ssh/config
62+
$ printf 'Match all\nInclude ~/.ssh/codespaces\n' >> ~/.ssh/config
6363
`),
6464
PreRunE: func(c *cobra.Command, args []string) error {
6565
if opts.stdio {

pkg/cmd/extension/command.go

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,13 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
6565
if !c.IsBinary() && len(version) > 8 {
6666
version = version[:8]
6767
}
68-
t.AddField(version, nil, nil)
68+
69+
if c.IsPinned() {
70+
t.AddField(version, nil, cs.Cyan)
71+
} else {
72+
t.AddField(version, nil, nil)
73+
}
74+
6975
var updateAvailable string
7076
if c.UpdateAvailable() {
7177
updateAvailable = "Upgrade available"
@@ -76,10 +82,12 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
7682
return t.Render()
7783
},
7884
},
79-
&cobra.Command{
80-
Use: "install <repository>",
81-
Short: "Install a gh extension from a repository",
82-
Long: heredoc.Doc(`
85+
func() *cobra.Command {
86+
var pinFlag string
87+
cmd := &cobra.Command{
88+
Use: "install <repository>",
89+
Short: "Install a gh extension from a repository",
90+
Long: heredoc.Doc(`
8391
Install a GitHub repository locally as a GitHub CLI extension.
8492
8593
The repository argument can be specified in "owner/repo" format as well as a full URL.
@@ -90,41 +98,57 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
9098
9199
See the list of available extensions at <https://github.com/topics/gh-extension>.
92100
`),
93-
Example: heredoc.Doc(`
101+
Example: heredoc.Doc(`
94102
$ gh extension install owner/gh-extension
95103
$ gh extension install https://git.example.com/owner/gh-extension
96104
$ gh extension install .
97105
`),
98-
Args: cmdutil.MinimumArgs(1, "must specify a repository to install from"),
99-
RunE: func(cmd *cobra.Command, args []string) error {
100-
if args[0] == "." {
101-
wd, err := os.Getwd()
106+
Args: cmdutil.MinimumArgs(1, "must specify a repository to install from"),
107+
RunE: func(cmd *cobra.Command, args []string) error {
108+
if args[0] == "." {
109+
if pinFlag != "" {
110+
return fmt.Errorf("local extensions cannot be pinned")
111+
}
112+
wd, err := os.Getwd()
113+
if err != nil {
114+
return err
115+
}
116+
return m.InstallLocal(wd)
117+
}
118+
119+
repo, err := ghrepo.FromFullName(args[0])
102120
if err != nil {
103121
return err
104122
}
105-
return m.InstallLocal(wd)
106-
}
107-
108-
repo, err := ghrepo.FromFullName(args[0])
109-
if err != nil {
110-
return err
111-
}
112-
113-
if err := checkValidExtension(cmd.Root(), m, repo.RepoName()); err != nil {
114-
return err
115-
}
116123

117-
if err := m.Install(repo); err != nil {
118-
return err
119-
}
124+
if err := checkValidExtension(cmd.Root(), m, repo.RepoName()); err != nil {
125+
return err
126+
}
120127

121-
if io.IsStdoutTTY() {
122128
cs := io.ColorScheme()
123-
fmt.Fprintf(io.Out, "%s Installed extension %s\n", cs.SuccessIcon(), args[0])
124-
}
125-
return nil
126-
},
127-
},
129+
if err := m.Install(repo, pinFlag); err != nil {
130+
if errors.Is(err, releaseNotFoundErr) {
131+
return fmt.Errorf("%s Could not find a release of %s for %s",
132+
cs.FailureIcon(), args[0], cs.Cyan(pinFlag))
133+
} else if errors.Is(err, commitNotFoundErr) {
134+
return fmt.Errorf("%s %s does not exist in %s",
135+
cs.FailureIcon(), cs.Cyan(pinFlag), args[0])
136+
}
137+
return err
138+
}
139+
140+
if io.IsStdoutTTY() {
141+
fmt.Fprintf(io.Out, "%s Installed extension %s\n", cs.SuccessIcon(), args[0])
142+
if pinFlag != "" {
143+
fmt.Fprintf(io.Out, "%s Pinned extension at %s\n", cs.SuccessIcon(), cs.Cyan(pinFlag))
144+
}
145+
}
146+
return nil
147+
},
148+
}
149+
cmd.Flags().StringVar(&pinFlag, "pin", "", "pin extension to a release tag or commit ref")
150+
return cmd
151+
}(),
128152
func() *cobra.Command {
129153
var flagAll bool
130154
var flagForce bool
@@ -153,6 +177,9 @@ func NewCmdExtension(f *cmdutil.Factory) *cobra.Command {
153177
if err != nil && !errors.Is(err, upToDateError) {
154178
if name != "" {
155179
fmt.Fprintf(io.ErrOut, "%s Failed upgrading extension %s: %s\n", cs.FailureIcon(), name, err)
180+
} else if errors.Is(err, noExtensionsInstalledError) {
181+
fmt.Fprintf(io.ErrOut, "%s No installed extensions found\n", cs.WarningIcon())
182+
return nil
156183
} else {
157184
fmt.Fprintf(io.ErrOut, "%s Failed upgrading extensions\n", cs.FailureIcon())
158185
}

pkg/cmd/extension/command_test.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func TestNewCmdExtension(t *testing.T) {
4444
em.ListFunc = func(bool) []extensions.Extension {
4545
return []extensions.Extension{}
4646
}
47-
em.InstallFunc = func(_ ghrepo.Interface) error {
47+
em.InstallFunc = func(_ ghrepo.Interface, _ string) error {
4848
return nil
4949
}
5050
return func(t *testing.T) {
@@ -86,6 +86,13 @@ func TestNewCmdExtension(t *testing.T) {
8686
}
8787
},
8888
},
89+
{
90+
name: "install local extension with pin",
91+
args: []string{"install", ".", "--pin", "v1.0.0"},
92+
wantErr: true,
93+
errMsg: "local extensions cannot be pinned",
94+
isTTY: true,
95+
},
8996
{
9097
name: "upgrade argument error",
9198
args: []string{"upgrade"},
@@ -207,6 +214,22 @@ func TestNewCmdExtension(t *testing.T) {
207214
isTTY: true,
208215
wantStdout: "✓ Successfully upgraded extensions\n",
209216
},
217+
{
218+
name: "upgrade all none installed",
219+
args: []string{"upgrade", "--all"},
220+
managerStubs: func(em *extensions.ExtensionManagerMock) func(*testing.T) {
221+
em.UpgradeFunc = func(name string, force bool) error {
222+
return noExtensionsInstalledError
223+
}
224+
return func(t *testing.T) {
225+
calls := em.UpgradeCalls()
226+
assert.Equal(t, 1, len(calls))
227+
assert.Equal(t, "", calls[0].Name)
228+
}
229+
},
230+
isTTY: true,
231+
wantStderr: "! No installed extensions found\n",
232+
},
210233
{
211234
name: "upgrade all notty",
212235
args: []string{"upgrade", "--all"},

0 commit comments

Comments
 (0)
X Tutup