X Tutup
The Wayback Machine - https://web.archive.org/web/20200911234223/https://github.com/cli/cli/pull/1552
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add commands for managing GitHub Releases #1552

Merged
merged 29 commits into from Sep 9, 2020
Merged

Add commands for managing GitHub Releases #1552

merged 29 commits into from Sep 9, 2020

Conversation

@mislav
Copy link
Member

mislav commented Aug 19, 2020

New commands:

  • gh release list
  • gh release view <tag>
  • gh release create <tag> [<files>...]
  • gh release upload <tag> <files>...
  • gh release download <tag> [<pattern>]

Example output:

Screen Shot 2020-08-26 at 12 33 55

Example help:

$ gh release create -h
Create a new release

USAGE
  gh release create <tag> [<files>...] [flags]

FLAGS
  -d, --draft             Save the release as a draft instead of publishing it
  -n, --notes string      Release notes
  -F, --notes-file file   Read release notes from file
  -p, --prerelease        Mark the release as a prerelease
      --target branch     Target branch or commit SHA (default: main branch)
  -t, --title string      Release title

INHERITED FLAGS
      --help                     Show help for command
  -R, --repo [HOST/]OWNER/REPO   Select another repository using the [HOST/]OWNER/REPO format

TODO:

  • TESTS
  • list: render draft/prerelease information
  • view: add metadata to rendered output
  • view: add ability to view latest release for a repository
  • create: add flags for metadata
  • create: enable interactive flow
  • create: accept a list of files for upload
  • create: offer options to pre-populate release notes
    • Use commit messages as a template
    • Use pull request titles as a template
    • Use annotated git tag as template
  • gh release upload command
    • retry uploads on HTTP 50x
  • gh release download command
  • gh release delete command
  • enable all commands to reference unpublished releases by tag name

Fixes #378

mislav added 6 commits Aug 19, 2020
We install an HTTP middleware that adds the "Authorization" header on
every HTTP request. However, our asset download process might redirect
to a 3rd-party host (Amazon S3) and we want to allow those requests but
not require that they are authenticated.

Furthermore, we need the ability to specify the `Accept` request header
without it being overwritten by middleware, so now middleware only adds
headers that are not present in a request.
@mislav mislav marked this pull request as ready for review Aug 21, 2020
@mislav mislav added this to Needs review 🤔 in The GitHub CLI Aug 24, 2020
@eddumelendez
Copy link
Contributor

eddumelendez commented Aug 26, 2020

looking forward to it 😍

@ampinsk
Copy link
Collaborator

ampinsk commented Aug 26, 2020

I'm so excited for this one, it looks great! Had one nit from going through things:

Should we autocomplete the title from the tag?

$ gh release create 3.4.2
? Title (3.4.2)
Copy link

bigcook12345 left a comment

So it works now..?

@vilmibm vilmibm self-requested a review Sep 1, 2020
@mislav
Copy link
Member Author

mislav commented Sep 1, 2020

Should we autocomplete the title from the tag?

$ gh release create 3.4.2
? Title (3.4.2)

@ampinsk We can consider this, but the release title is technically optional on the server and, if left blank, will inherit the title of the git tag (if any) or the subject of the git commit that the tag points to. If people are actively counting on this behavior by intentionally leaving the title blank, then by pre-filling the title to match the tag name, we effectively disallow that fallback on the client.

I'm on the fence about what the ideal behavior would be, but for the sake of predictability and parity with platform behavior, I'm currently leaning to allowing blank titles.

@ampinsk
Copy link
Collaborator

ampinsk commented Sep 1, 2020

@mislav was definitely just a nit, totally fine with dropping it!

Copy link
Member

vilmibm left a comment

this was a lot to review at once >_>

i didn't see any blockers to merging, but had some light requests here and there so i'm marking request changes. overall i found the new commands to be very comprehensible and well organized, thanks!

@@ -45,7 +45,9 @@ func NewClientFromHTTP(httpClient *http.Client) *Client {
func AddHeader(name, value string) ClientOption {
return func(tr http.RoundTripper) http.RoundTripper {
return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) {
req.Header.Add(name, value)
if len(req.Header.Values(name)) == 0 {

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

why forbid overriding, here? I would expect AddHeader to let me replace an existing header with a new value.

ResponseHeader: logTraffic,
ResponseBody: logTraffic,
Formatters: []httpretty.Formatter{&httpretty.JSONFormatter{}},
MaxResponseBody: 10000,

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

is this intentional? I do this a lot locally so I certainly wouldn't mind it being in trunk.

This comment has been minimized.

@mislav

mislav Sep 2, 2020

Author Member

Good spot! Yes, it's intentional— I realized that I wasn't able to debug large response payloads without this.

@@ -45,7 +45,9 @@ func NewClientFromHTTP(httpClient *http.Client) *Client {
func AddHeader(name, value string) ClientOption {
return func(tr http.RoundTripper) http.RoundTripper {
return &funcTripper{roundTrip: func(req *http.Request) (*http.Response, error) {
req.Header.Add(name, value)
if req.Header.Get(name) == "" {

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

why prevent overriding, here? I'd think that repeated calls to AddHeader would update a header value, not silently fail to update.

This comment has been minimized.

@mislav

mislav Sep 2, 2020

Author Member

It's a good question! The short answer is that I had to add the guard to allow file downloads. Without this change, a file download request would set an Accept: application/octet-stream, but additional Accept values would get added by our request middleware via AddHeader and AddHeaderFunc, and thus JSON payload would get returned and the download sabotaged.

When I then thought about our AddHeader and AddHeaderFunc utilities, I realized that they are more in the service of adding default request headers (e.g. default Accept, default User-Agent, default Authorization), but if a request already contained any of those headers explicitly, there would be no reason to apply the default.

You have a good point that maybe we should change the verb from "Add" to something else that reflects the current semantics better?

This comment has been minimized.

@vilmibm

vilmibm Sep 8, 2020

Member

ah, that makes sense -- in that case yeah I think we can rename from AddHeader to something like SetHeaderDefault or similar

}

func publishRelease(httpClient *http.Client, releaseURL string) error {
req, err := http.NewRequest("PATCH", releaseURL, bytes.NewBufferString(`{"draft":false}`))

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

what is your motivation for moving away from client.REST? I don't have an issue with it, just curious.

This comment has been minimized.

@mislav

mislav Sep 2, 2020

Author Member

First and foremost is because REST requires me to instantiate an api.Client, which is an outdated pattern that I would like us to move away from since it acted as a wrapper around http.Client, but did not provide any benefits.

An additional reason was that I was hoping to see how comfortable I was letting specific commands control most aspects of how they process HTTP requests. This is with the goal of decreasing inter-package dependencies. I'm satisfied by this approach, but I do believe that error handling should be shared between all commands, as well as authentication.

},
}

cmd.Flags().StringVarP(&opts.Destination, "dir", "C", ".", "The directory to download files into")

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

why C for this? D, d, or o all seem more intuitive

This comment has been minimized.

@mislav

mislav Sep 3, 2020

Author Member

Good question! I guess I was biased towards -C because commands like tar, make, and git support it. Wget does -P, --directory-prefix. I will change to -D 👍

}

cmd := &cobra.Command{
Use: "download <tag> [<pattern>]",

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

I think it's worth documenting what kind of pattern this can be; when I was testing this out I wasn't sure if it was glob, plain wildcard, or regular expression.

var downloadError error
for i := 0; i < len(toDownload); i++ {
if err := <-results; err != nil {
downloadError = err

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

I wonder if it's worth a break here if we're not going to collect errors per-download. Otherwise we're discarding errors for all but the last download which is confusing.

This comment has been minimized.

@mislav

mislav Sep 3, 2020

Author Member

Good thought, but we cannot break here since we need to invoke <-results as many times as there were download jobs. You're right that all but the latest error gets discarded. Right now I don't think that's such an issue, but if people notice this, we could collect all errors into one meta-error

}

// FindDraftRelease interates over all releases in a repository until it finds one that matches tagName.
func FindDraftRelease(httpClient *http.Client, baseRepo ghrepo.Interface, tagName string) (*Release, error) {

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

does this exist because draft releases aren't included when using releases/tag/:foo? I'm confused how the Draft part of this function's name relates to what it actually does.

This comment has been minimized.

@mislav

mislav Sep 3, 2020

Author Member

Yes, the API doesn't let us query draft releases by tag name (makes sense, since there can be multiple drafts referring to the same tag name), so I have to resort to listing all releases. You are right that this lookup returns any release that matches a tag, not necessarily draft. To stay true to the function name, I will add code that only scopes the lookup to draft releases.

bold = ansi.ColorFunc("default+b")
)

func NewColorScheme(enabled bool) *ColorScheme {

This comment has been minimized.

@vilmibm

vilmibm Sep 1, 2020

Member

i like this approach to color! but please open a tech debt issue to port the whole codebase to it so we don't end up with competing color systems.

@tierninho
Copy link
Contributor

tierninho commented Sep 4, 2020

Two questions, is this path error due to the test mode or a bug?

gh release create TAG1 -R tierninho/blah
? Title TRIAL
? Release notes Write my own
/var/folders/cn/b6t8mw1s6xqcfysnl8jfjdg80000gn/T/874767777.md:1: command not found: \ufeff
exit status 127

and is it ok to skip the title? It will create a Draft title more than once.

gh release create TAG1 -R tierninho/blah
? Title
? Release notes Leave blank
? Is this a prerelease? No
? Submit? Save as draft
https://github.com/tierninho/Blah/releases/tag/untagged-caba1054e408fa5fef50

Screen Shot 2020-09-04 at 8 16 32 AM


Questions:

  • where are the assets downloaded to if you use the -R flag?
  • is there a -w flag coming as it might be useful in gh release view [tag] -w for instance?
  • Should there be a success message if upload succeeded?
  • Is the -d and -p supposed to be allowed at same time? I looks life a draft supersedes a prerelease.
@vilmibm
Copy link
Member

vilmibm commented Sep 8, 2020

@tierninho

Two questions, is this path error due to the test mode or a bug?

that error looks related to a bad value set for editor?

The GitHub CLI automation moved this from Needs review 🤔 to Needs to be merged 🎉 Sep 8, 2020
@vilmibm
vilmibm approved these changes Sep 8, 2020
@mislav
Copy link
Member Author

mislav commented Sep 9, 2020

@tierninho All great questions!

  • where are the assets downloaded to if you use the -R flag?

The same as without the -R flag: the current directory, or the one specified by --dir.

  • is there a -w flag coming as it might be useful in gh release view [tag] -w for instance?

That's a good idea! I will add it.

  • Should there be a success message if upload succeeded?

We haven't talked about it, but a message like “6 files successfully uploaded” might be nice. I'll consider adding it!

  • Is the -d and -p supposed to be allowed at same time? I looks life a draft supersedes a prerelease.

Yes because a draft will usually get published, and when it does then the original "prerelease" bit will matter. The "draft" and "prerelease" bits are thus independent of each other.

@mislav mislav merged commit f30bc5b into trunk Sep 9, 2020
12 checks passed
12 checks passed
CodeQL-Build
Details
lint
Details
lint
Details
build (ubuntu-latest)
Details
build (ubuntu-latest)
Details
build (windows-latest)
Details
build (windows-latest)
Details
build (macos-latest)
Details
build (macos-latest)
Details
build-minimum
Details
build-minimum
Details
CodeQL No new alerts
Details
The GitHub CLI automation moved this from Needs to be merged 🎉 to Pending Release 🥚 Sep 9, 2020
@mislav mislav deleted the release-cmd branch Sep 9, 2020
@cyrilletuzi
Copy link

cyrilletuzi commented Sep 9, 2020

@mislav First, thank you for this feature! <3

But is there a way to use the latest created tag (for example gh release create latest), instead of having to write the tag number manually (ie. gh release create v1.0.1)?

It seems very important to me to really be usable in an automation process. For exemple:

  • for my VS Code extension, my release script is:
vsce publish patch && git push && git push --tags
  • for my npm packages, my release script is:
npm run build && npm version minor && npm publish dist && git push && git push --tags;

In both cases, the tag creation is automated (by vsce publish or npm version), so I don't have access to the tag number directly. So I would not be able to be able to add the last automation step with gh release create.

@mislav
Copy link
Member Author

mislav commented Sep 10, 2020

@cyrilletuzi That's an excellent feature request and something that I haven't considered; thank you! Would you mind opening a separate issue so we can discuss and track this?

In the meantime, would it be an option for you to use the output of git tag --sort=-creatordate | head -1?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
The GitHub CLI
  
Pending Release 🥚
Linked issues

Successfully merging this pull request may close these issues.

7 participants
You can’t perform that action at this time.
X Tutup