diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index b6b42ff9f..78be7df09 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -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() diff --git a/pkg/cmd/release/create/create.go b/pkg/cmd/release/create/create.go index c80d47953..5c91eed9b 100644 --- a/pkg/cmd/release/create/create.go +++ b/pkg/cmd/release/create/create.go @@ -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 } diff --git a/pkg/cmd/release/shared/fetch.go b/pkg/cmd/release/shared/fetch.go index 4cc31ff48..a964f9068 100644 --- a/pkg/cmd/release/shared/fetch.go +++ b/pkg/cmd/release/shared/fetch.go @@ -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") diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go index 9bcccb675..a7b796bee 100644 --- a/pkg/cmd/release/view/view.go +++ b/pkg/cmd/release/view/view.go @@ -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) }