Merge pull request #3656 from cli/release-json-export

Add `release view --json` export support
This commit is contained in:
Mislav Marohnić 2021-05-18 19:54:44 +02:00 committed by GitHub
commit 001e92e3e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 114 additions and 31 deletions

View file

@ -108,7 +108,7 @@ func viewRun(opts *ViewOptions) error {
opts.IO.DetectTerminalTheme()
if err := opts.IO.StartPager(); err != nil {
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v", err)
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
}
defer opts.IO.StopPager()

View file

@ -313,7 +313,7 @@ func createRun(opts *CreateOptions) error {
}
}
fmt.Fprintf(opts.IO.Out, "%s\n", newRelease.HTMLURL)
fmt.Fprintf(opts.IO.Out, "%s\n", newRelease.URL)
return nil
}

View file

@ -6,6 +6,8 @@ import (
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strings"
"time"
"github.com/cli/cli/api"
@ -13,30 +15,106 @@ import (
"github.com/cli/cli/internal/ghrepo"
)
type Release struct {
TagName string `json:"tag_name"`
Name string `json:"name"`
Body string `json:"body"`
IsDraft bool `json:"draft"`
IsPrerelease bool `json:"prerelease"`
CreatedAt time.Time `json:"created_at"`
PublishedAt time.Time `json:"published_at"`
var ReleaseFields = []string{
"url",
"apiUrl",
"uploadUrl",
"tarballUrl",
"zipballUrl",
"id",
"tagName",
"name",
"body",
"isDraft",
"isPrerelease",
"createdAt",
"publishedAt",
"targetCommitish",
"author",
"assets",
}
APIURL string `json:"url"`
UploadURL string `json:"upload_url"`
HTMLURL string `json:"html_url"`
Assets []ReleaseAsset
type Release struct {
ID string `json:"node_id"`
TagName string `json:"tag_name"`
Name string `json:"name"`
Body string `json:"body"`
IsDraft bool `json:"draft"`
IsPrerelease bool `json:"prerelease"`
CreatedAt time.Time `json:"created_at"`
PublishedAt *time.Time `json:"published_at"`
TargetCommitish string `json:"target_commitish"`
APIURL string `json:"url"`
UploadURL string `json:"upload_url"`
TarballURL string `json:"tarball_url"`
ZipballURL string `json:"zipball_url"`
URL string `json:"html_url"`
Assets []ReleaseAsset
Author struct {
Login string
ID string `json:"node_id"`
Login string `json:"login"`
}
}
type ReleaseAsset struct {
ID string `json:"node_id"`
Name string
Label string
Size int64
State string
APIURL string `json:"url"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DownloadCount int `json:"download_count"`
ContentType string `json:"content_type"`
BrowserDownloadURL string `json:"browser_download_url"`
}
func (rel *Release) ExportData(fields []string) *map[string]interface{} {
v := reflect.ValueOf(rel).Elem()
fieldByName := func(v reflect.Value, field string) reflect.Value {
return v.FieldByNameFunc(func(s string) bool {
return strings.EqualFold(field, s)
})
}
data := map[string]interface{}{}
for _, f := range fields {
switch f {
case "author":
data[f] = map[string]interface{}{
"id": rel.Author.ID,
"login": rel.Author.Login,
}
case "assets":
assets := make([]interface{}, 0, len(rel.Assets))
for _, a := range rel.Assets {
assets = append(assets, map[string]interface{}{
"url": a.BrowserDownloadURL,
"apiUrl": a.APIURL,
"id": a.ID,
"name": a.Name,
"label": a.Label,
"size": a.Size,
"state": a.State,
"createdAt": a.CreatedAt,
"updatedAt": a.UpdatedAt,
"downloadCount": a.DownloadCount,
"contentType": a.ContentType,
})
}
data[f] = assets
default:
sf := fieldByName(v, f)
data[f] = sf.Interface()
}
}
return &data
}
// FetchRelease finds a repository release by its tagName.
@ -55,11 +133,7 @@ func FetchRelease(httpClient *http.Client, baseRepo ghrepo.Interface, tagName st
defer resp.Body.Close()
if resp.StatusCode == 404 {
if canPush, err := api.CanPushToRepo(httpClient, baseRepo); err == nil && canPush {
return FindDraftRelease(httpClient, baseRepo, tagName)
} else if err != nil {
return nil, err
}
return FindDraftRelease(httpClient, baseRepo, tagName)
}
if resp.StatusCode > 299 {
@ -152,11 +226,8 @@ func FindDraftRelease(httpClient *http.Client, baseRepo ghrepo.Interface, tagNam
return &r, nil
}
}
if len(releases) < perPage {
break
}
page++
//nolint:staticcheck
break
}
return nil, errors.New("release not found")

View file

@ -26,6 +26,7 @@ type ViewOptions struct {
IO *iostreams.IOStreams
BaseRepo func() (ghrepo.Interface, error)
Browser browser
Exporter cmdutil.Exporter
TagName string
WebMode bool
@ -64,6 +65,7 @@ func NewCmdView(f *cmdutil.Factory, runF func(*ViewOptions) error) *cobra.Comman
}
cmd.Flags().BoolVarP(&opts.WebMode, "web", "w", false, "Open the release in the browser")
cmdutil.AddJSONFlags(cmd, &opts.Exporter, shared.ReleaseFields)
return cmd
}
@ -95,9 +97,19 @@ func viewRun(opts *ViewOptions) error {
if opts.WebMode {
if opts.IO.IsStdoutTTY() {
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(release.HTMLURL))
fmt.Fprintf(opts.IO.ErrOut, "Opening %s in your browser.\n", utils.DisplayURL(release.URL))
}
return opts.Browser.Browse(release.HTMLURL)
return opts.Browser.Browse(release.URL)
}
opts.IO.DetectTerminalTheme()
if err := opts.IO.StartPager(); err != nil {
fmt.Fprintf(opts.IO.ErrOut, "error starting pager: %v\n", err)
}
defer opts.IO.StopPager()
if opts.Exporter != nil {
return opts.Exporter.Write(opts.IO.Out, release, opts.IO.ColorEnabled())
}
if opts.IO.IsStdoutTTY() {
@ -126,10 +138,10 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
if release.IsDraft {
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s created this %s", release.Author.Login, utils.FuzzyAgo(time.Since(release.CreatedAt)))))
} else {
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s released this %s", release.Author.Login, utils.FuzzyAgo(time.Since(release.PublishedAt)))))
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s released this %s", release.Author.Login, utils.FuzzyAgo(time.Since(*release.PublishedAt)))))
}
style := markdown.GetStyle(io.DetectTerminalTheme())
style := markdown.GetStyle(io.TerminalTheme())
renderedDescription, err := markdown.Render(release.Body, style)
if err != nil {
return err
@ -151,7 +163,7 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error {
fmt.Fprint(w, "\n")
}
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("View on GitHub: %s", release.HTMLURL)))
fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("View on GitHub: %s", release.URL)))
return nil
}
@ -165,7 +177,7 @@ func renderReleasePlain(w io.Writer, release *shared.Release) error {
if !release.IsDraft {
fmt.Fprintf(w, "published:\t%s\n", release.PublishedAt.Format(time.RFC3339))
}
fmt.Fprintf(w, "url:\t%s\n", release.HTMLURL)
fmt.Fprintf(w, "url:\t%s\n", release.URL)
for _, a := range release.Assets {
fmt.Fprintf(w, "asset:\t%s\n", a.Name)
}