Skip to content

Commit 001e92e

Browse files
authored
Merge pull request cli#3656 from cli/release-json-export
Add `release view --json` export support
2 parents f30afce + c667a0b commit 001e92e

File tree

4 files changed

+114
-31
lines changed

4 files changed

+114
-31
lines changed

pkg/cmd/issue/view/view.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func viewRun(opts *ViewOptions) error {
108108

109109
opts.IO.DetectTerminalTheme()
110110
if err := opts.IO.StartPager(); err != nil {
111-
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v", err)
111+
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
112112
}
113113
defer opts.IO.StopPager()
114114

pkg/cmd/release/create/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ func createRun(opts *CreateOptions) error {
313313
}
314314
}
315315

316-
fmt.Fprintf(opts.IO.Out, "%s\n", newRelease.HTMLURL)
316+
fmt.Fprintf(opts.IO.Out, "%s\n", newRelease.URL)
317317

318318
return nil
319319
}

pkg/cmd/release/shared/fetch.go

Lines changed: 94 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,115 @@ import (
66
"fmt"
77
"io/ioutil"
88
"net/http"
9+
"reflect"
10+
"strings"
911
"time"
1012

1113
"github.com/cli/cli/api"
1214
"github.com/cli/cli/internal/ghinstance"
1315
"github.com/cli/cli/internal/ghrepo"
1416
)
1517

18+
var ReleaseFields = []string{
19+
"url",
20+
"apiUrl",
21+
"uploadUrl",
22+
"tarballUrl",
23+
"zipballUrl",
24+
"id",
25+
"tagName",
26+
"name",
27+
"body",
28+
"isDraft",
29+
"isPrerelease",
30+
"createdAt",
31+
"publishedAt",
32+
"targetCommitish",
33+
"author",
34+
"assets",
35+
}
36+
1637
type Release struct {
17-
TagName string `json:"tag_name"`
18-
Name string `json:"name"`
19-
Body string `json:"body"`
20-
IsDraft bool `json:"draft"`
21-
IsPrerelease bool `json:"prerelease"`
22-
CreatedAt time.Time `json:"created_at"`
23-
PublishedAt time.Time `json:"published_at"`
24-
25-
APIURL string `json:"url"`
26-
UploadURL string `json:"upload_url"`
27-
HTMLURL string `json:"html_url"`
28-
Assets []ReleaseAsset
38+
ID string `json:"node_id"`
39+
TagName string `json:"tag_name"`
40+
Name string `json:"name"`
41+
Body string `json:"body"`
42+
IsDraft bool `json:"draft"`
43+
IsPrerelease bool `json:"prerelease"`
44+
CreatedAt time.Time `json:"created_at"`
45+
PublishedAt *time.Time `json:"published_at"`
46+
47+
TargetCommitish string `json:"target_commitish"`
48+
49+
APIURL string `json:"url"`
50+
UploadURL string `json:"upload_url"`
51+
TarballURL string `json:"tarball_url"`
52+
ZipballURL string `json:"zipball_url"`
53+
URL string `json:"html_url"`
54+
Assets []ReleaseAsset
2955

3056
Author struct {
31-
Login string
57+
ID string `json:"node_id"`
58+
Login string `json:"login"`
3259
}
3360
}
3461

3562
type ReleaseAsset struct {
63+
ID string `json:"node_id"`
3664
Name string
65+
Label string
3766
Size int64
3867
State string
3968
APIURL string `json:"url"`
69+
70+
CreatedAt time.Time `json:"created_at"`
71+
UpdatedAt time.Time `json:"updated_at"`
72+
DownloadCount int `json:"download_count"`
73+
ContentType string `json:"content_type"`
74+
BrowserDownloadURL string `json:"browser_download_url"`
75+
}
76+
77+
func (rel *Release) ExportData(fields []string) *map[string]interface{} {
78+
v := reflect.ValueOf(rel).Elem()
79+
fieldByName := func(v reflect.Value, field string) reflect.Value {
80+
return v.FieldByNameFunc(func(s string) bool {
81+
return strings.EqualFold(field, s)
82+
})
83+
}
84+
data := map[string]interface{}{}
85+
86+
for _, f := range fields {
87+
switch f {
88+
case "author":
89+
data[f] = map[string]interface{}{
90+
"id": rel.Author.ID,
91+
"login": rel.Author.Login,
92+
}
93+
case "assets":
94+
assets := make([]interface{}, 0, len(rel.Assets))
95+
for _, a := range rel.Assets {
96+
assets = append(assets, map[string]interface{}{
97+
"url": a.BrowserDownloadURL,
98+
"apiUrl": a.APIURL,
99+
"id": a.ID,
100+
"name": a.Name,
101+
"label": a.Label,
102+
"size": a.Size,
103+
"state": a.State,
104+
"createdAt": a.CreatedAt,
105+
"updatedAt": a.UpdatedAt,
106+
"downloadCount": a.DownloadCount,
107+
"contentType": a.ContentType,
108+
})
109+
}
110+
data[f] = assets
111+
default:
112+
sf := fieldByName(v, f)
113+
data[f] = sf.Interface()
114+
}
115+
}
116+
117+
return &data
40118
}
41119

42120
// FetchRelease finds a repository release by its tagName.
@@ -55,11 +133,7 @@ func FetchRelease(httpClient *http.Client, baseRepo ghrepo.Interface, tagName st
55133
defer resp.Body.Close()
56134

57135
if resp.StatusCode == 404 {
58-
if canPush, err := api.CanPushToRepo(httpClient, baseRepo); err == nil && canPush {
59-
return FindDraftRelease(httpClient, baseRepo, tagName)
60-
} else if err != nil {
61-
return nil, err
62-
}
136+
return FindDraftRelease(httpClient, baseRepo, tagName)
63137
}
64138

65139
if resp.StatusCode > 299 {
@@ -152,11 +226,8 @@ func FindDraftRelease(httpClient *http.Client, baseRepo ghrepo.Interface, tagNam
152226
return &r, nil
153227
}
154228
}
155-
156-
if len(releases) < perPage {
157-
break
158-
}
159-
page++
229+
//nolint:staticcheck
230+
break
160231
}
161232

162233
return nil, errors.New("release not found")

pkg/cmd/release/view/view.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type ViewOptions struct {
2626
IO *iostreams.IOStreams
2727
BaseRepo func() (ghrepo.Interface, error)
2828
Browser browser
29+
Exporter cmdutil.Exporter
2930

3031
TagName string
3132
WebMode bool
@@ -64,6 +65,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
6465
}
6566

6667
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the release in the browser")
68+
cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.ReleaseFields)
6769

6870
return cmd
6971
}
@@ -95,9 +97,19 @@ func viewRun(opts *ViewOptions) error {
9597

9698
if opts.WebMode {
9799
if opts.IO.IsStdoutTTY() {
98-
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(release.HTMLURL))
100+
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(release.URL))
99101
}
100-
return opts.Browser.Browse(release.HTMLURL)
102+
return opts.Browser.Browse(release.URL)
103+
}
104+
105+
opts.IO.DetectTerminalTheme()
106+
if err := opts.IO.StartPager(); err != nil {
107+
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
108+
}
109+
defer opts.IO.StopPager()
110+
111+
if opts.Exporter != nil {
112+
return opts.Exporter.Write(opts.IO.Out, release, opts.IO.ColorEnabled())
101113
}
102114

103115
if opts.IO.IsStdoutTTY() {
@@ -126,10 +138,10 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
126138
if release.IsDraft {
127139
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s created this %s", release.Author.Login, utils.FuzzyAgo(time.Since(release.CreatedAt)))))
128140
} else {
129-
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s released this %s", release.Author.Login, utils.FuzzyAgo(time.Since(release.PublishedAt)))))
141+
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s released this %s", release.Author.Login, utils.FuzzyAgo(time.Since(*release.PublishedAt)))))
130142
}
131143

132-
style := markdown.GetStyle(io.DetectTerminalTheme())
144+
style := markdown.GetStyle(io.TerminalTheme())
133145
renderedDescription, err := markdown.Render(release.Body, style)
134146
if err != nil {
135147
return err
@@ -151,7 +163,7 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
151163
fmt.Fprint(w, "\n")
152164
}
153165

154-
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("View on GitHub: %s", release.HTMLURL)))
166+
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("View on GitHub: %s", release.URL)))
155167
return nil
156168
}
157169

@@ -165,7 +177,7 @@ func renderReleasePlain(w io.Writer, release *shared.Release) error {
165177
if !release.IsDraft {
166178
fmt.Fprintf(w, "published:\t%s\n", release.PublishedAt.Format(time.RFC3339))
167179
}
168-
fmt.Fprintf(w, "url:\t%s\n", release.HTMLURL)
180+
fmt.Fprintf(w, "url:\t%s\n", release.URL)
169181
for _, a := range release.Assets {
170182
fmt.Fprintf(w, "asset:\t%s\n", a.Name)
171183
}

0 commit comments

Comments
 (0)