Merge pull request #3656 from cli/release-json-export
Add `release view --json` export support
This commit is contained in:
commit
001e92e3e6
4 changed files with 114 additions and 31 deletions
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue