Skip to content

Commit bdadb30

Browse files
author
Nate Smith
authored
Merge pull request cli#1699 from cli/more-gists
bunch of gist stuff
2 parents 6d0da07 + 7c986c0 commit bdadb30

File tree

14 files changed

+1268
-13
lines changed

14 files changed

+1268
-13
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ We'd love to hear your feedback about `gh`. If you spot bugs or have features th
1818
- `gh pr [status, list, view, checkout, create]`
1919
- `gh issue [status, list, view, create]`
2020
- `gh repo [view, create, clone, fork]`
21+
- `gh gist [create, list, view, edit]`
2122
- `gh auth [login, logout, refresh, status]`
2223
- `gh config [get, set]`
2324
- `gh help`

internal/ghinstance/host.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,10 @@ func RESTPrefix(hostname string) string {
5555
}
5656
return "https://api.github.com/"
5757
}
58+
59+
func GistPrefix(hostname string) string {
60+
if IsEnterprise(hostname) {
61+
return fmt.Sprintf("https://%s/gist/", hostname)
62+
}
63+
return fmt.Sprintf("https://gist.%s/", hostname)
64+
}

pkg/cmd/alias/list/list.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,7 @@ func listRun(opts *ListOptions) error {
6969
sort.Strings(keys)
7070

7171
for _, alias := range keys {
72-
if tp.IsTTY() {
73-
// ensure that screen readers pause
74-
tp.AddField(alias+":", nil, nil)
75-
} else {
76-
tp.AddField(alias, nil, nil)
77-
}
72+
tp.AddField(alias+":", nil, nil)
7873
tp.AddField(aliasMap[alias], nil, nil)
7974
tp.EndRow()
8075
}

pkg/cmd/gist/create/create.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import (
2323
type CreateOptions struct {
2424
IO *iostreams.IOStreams
2525

26-
Description string
27-
Public bool
28-
Filenames []string
26+
Description string
27+
Public bool
28+
Filenames []string
29+
FilenameOverride string
2930

3031
HttpClient func() (*http.Client, error)
3132
}
@@ -84,6 +85,7 @@ func NewCmdCreate(f *cmdutil.Factory, runF func(*CreateOptions) error) *cobra.Co
8485

8586
cmd.Flags().StringVarP(&opts.Description, "desc", "d", "", "A description for this gist")
8687
cmd.Flags().BoolVarP(&opts.Public, "public", "p", false, "List the gist publicly (default: private)")
88+
cmd.Flags().StringVarP(&opts.FilenameOverride, "filename", "f", "", "Provide a filename to be used when reading from STDIN")
8789
return cmd
8890
}
8991

@@ -93,7 +95,7 @@ func createRun(opts *CreateOptions) error {
9395
fileArgs = []string{"-"}
9496
}
9597

96-
files, err := processFiles(opts.IO.In, fileArgs)
98+
files, err := processFiles(opts.IO.In, opts.FilenameOverride, fileArgs)
9799
if err != nil {
98100
return fmt.Errorf("failed to collect files for posting: %w", err)
99101
}
@@ -137,7 +139,7 @@ func createRun(opts *CreateOptions) error {
137139
return nil
138140
}
139141

140-
func processFiles(stdin io.ReadCloser, filenames []string) (map[string]string, error) {
142+
func processFiles(stdin io.ReadCloser, filenameOverride string, filenames []string) (map[string]string, error) {
141143
fs := map[string]string{}
142144

143145
if len(filenames) == 0 {
@@ -149,7 +151,11 @@ func processFiles(stdin io.ReadCloser, filenames []string) (map[string]string, e
149151
var content []byte
150152
var err error
151153
if f == "-" {
152-
filename = fmt.Sprintf("gistfile%d.txt", i)
154+
if filenameOverride != "" {
155+
filename = filenameOverride
156+
} else {
157+
filename = fmt.Sprintf("gistfile%d.txt", i)
158+
}
153159
content, err = ioutil.ReadAll(stdin)
154160
if err != nil {
155161
return fs, fmt.Errorf("failed to read from stdin: %w", err)

pkg/cmd/gist/create/create_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const (
2121

2222
func Test_processFiles(t *testing.T) {
2323
fakeStdin := strings.NewReader("hey cool how is it going")
24-
files, err := processFiles(ioutil.NopCloser(fakeStdin), []string{"-"})
24+
files, err := processFiles(ioutil.NopCloser(fakeStdin), "", []string{"-"})
2525
if err != nil {
2626
t.Fatalf("unexpected error processing files: %s", err)
2727
}

pkg/cmd/gist/edit/edit.go

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package edit
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"net/http"
9+
"net/url"
10+
"sort"
11+
"strings"
12+
13+
"github.com/AlecAivazis/survey/v2"
14+
"github.com/cli/cli/api"
15+
"github.com/cli/cli/internal/config"
16+
"github.com/cli/cli/internal/ghinstance"
17+
"github.com/cli/cli/pkg/cmd/gist/shared"
18+
"github.com/cli/cli/pkg/cmdutil"
19+
"github.com/cli/cli/pkg/iostreams"
20+
"github.com/cli/cli/pkg/prompt"
21+
"github.com/cli/cli/pkg/surveyext"
22+
"github.com/spf13/cobra"
23+
)
24+
25+
type EditOptions struct {
26+
IO *iostreams.IOStreams
27+
HttpClient func() (*http.Client, error)
28+
Config func() (config.Config, error)
29+
30+
Edit func(string, string, string, *iostreams.IOStreams) (string, error)
31+
32+
Selector string
33+
Filename string
34+
}
35+
36+
func NewCmdEdit(f *cmdutil.Factory, runF func(*EditOptions) error) *cobra.Command {
37+
opts := EditOptions{
38+
IO: f.IOStreams,
39+
HttpClient: f.HttpClient,
40+
Config: f.Config,
41+
Edit: func(editorCmd, filename, defaultContent string, io *iostreams.IOStreams) (string, error) {
42+
return surveyext.Edit(
43+
editorCmd,
44+
"*."+filename,
45+
defaultContent,
46+
io.In, io.Out, io.ErrOut, nil)
47+
},
48+
}
49+
50+
cmd := &cobra.Command{
51+
Use: "edit {<gist ID> | <gist URL>}",
52+
Short: "Edit one of your gists",
53+
Args: cobra.ExactArgs(1),
54+
RunE: func(c *cobra.Command, args []string) error {
55+
opts.Selector = args[0]
56+
57+
if runF != nil {
58+
return runF(&opts)
59+
}
60+
61+
return editRun(&opts)
62+
},
63+
}
64+
cmd.Flags().StringVarP(&opts.Filename, "filename", "f", "", "a specific file to edit")
65+
66+
return cmd
67+
}
68+
69+
func editRun(opts *EditOptions) error {
70+
gistID := opts.Selector
71+
72+
u, err := url.Parse(opts.Selector)
73+
if err == nil {
74+
if strings.HasPrefix(u.Path, "/") {
75+
gistID = u.Path[1:]
76+
}
77+
}
78+
79+
client, err := opts.HttpClient()
80+
if err != nil {
81+
return err
82+
}
83+
84+
gist, err := shared.GetGist(client, ghinstance.OverridableDefault(), gistID)
85+
if err != nil {
86+
return err
87+
}
88+
89+
filesToUpdate := map[string]string{}
90+
91+
for {
92+
filename := opts.Filename
93+
candidates := []string{}
94+
for filename := range gist.Files {
95+
candidates = append(candidates, filename)
96+
}
97+
98+
sort.Strings(candidates)
99+
100+
if filename == "" {
101+
if len(candidates) == 1 {
102+
filename = candidates[0]
103+
} else {
104+
if !opts.IO.CanPrompt() {
105+
return errors.New("unsure what file to edit; either specify --filename or run interactively")
106+
}
107+
err = prompt.SurveyAskOne(&survey.Select{
108+
Message: "Edit which file?",
109+
Options: candidates,
110+
}, &filename)
111+
112+
if err != nil {
113+
return fmt.Errorf("could not prompt: %w", err)
114+
}
115+
}
116+
}
117+
118+
if _, ok := gist.Files[filename]; !ok {
119+
return fmt.Errorf("gist has no file %q", filename)
120+
}
121+
122+
editorCommand, err := cmdutil.DetermineEditor(opts.Config)
123+
if err != nil {
124+
return err
125+
}
126+
text, err := opts.Edit(editorCommand, filename, gist.Files[filename].Content, opts.IO)
127+
128+
if err != nil {
129+
return err
130+
}
131+
132+
if text != gist.Files[filename].Content {
133+
gistFile := gist.Files[filename]
134+
gistFile.Content = text // so it appears if they re-edit
135+
filesToUpdate[filename] = text
136+
}
137+
138+
if !opts.IO.CanPrompt() {
139+
break
140+
}
141+
142+
if len(candidates) == 1 {
143+
break
144+
}
145+
146+
choice := ""
147+
148+
err = prompt.SurveyAskOne(&survey.Select{
149+
Message: "What next?",
150+
Options: []string{
151+
"Edit another file",
152+
"Submit",
153+
"Cancel",
154+
},
155+
}, &choice)
156+
157+
if err != nil {
158+
return fmt.Errorf("could not prompt: %w", err)
159+
}
160+
161+
stop := false
162+
163+
switch choice {
164+
case "Edit another file":
165+
continue
166+
case "Submit":
167+
stop = true
168+
case "Cancel":
169+
return cmdutil.SilentError
170+
}
171+
172+
if stop {
173+
break
174+
}
175+
}
176+
177+
err = updateGist(client, ghinstance.OverridableDefault(), gist)
178+
if err != nil {
179+
return err
180+
}
181+
182+
return nil
183+
}
184+
185+
func updateGist(client *http.Client, hostname string, gist *shared.Gist) error {
186+
body := shared.Gist{
187+
Description: gist.Description,
188+
Files: gist.Files,
189+
}
190+
191+
path := "gists/" + gist.ID
192+
193+
requestByte, err := json.Marshal(body)
194+
if err != nil {
195+
return err
196+
}
197+
198+
requestBody := bytes.NewReader(requestByte)
199+
200+
result := shared.Gist{}
201+
202+
apiClient := api.NewClientFromHTTP(client)
203+
err = apiClient.REST(hostname, "POST", path, requestBody, &result)
204+
205+
if err != nil {
206+
return err
207+
}
208+
209+
return nil
210+
}

0 commit comments

Comments
 (0)