feat(cmd/release): allow to delete release with its attached tag

This commit is contained in:
lktslionel 2022-10-06 00:35:06 +02:00
parent 4a2ca15350
commit 0b0ae1e673
No known key found for this signature in database
GPG key ID: 3ABF935806E4FCF3
2 changed files with 80 additions and 2 deletions

View file

@ -6,6 +6,7 @@ import (
"github.com/AlecAivazis/survey/v2"
"github.com/cli/cli/v2/api"
"github.com/cli/cli/v2/internal/ghinstance"
"github.com/cli/cli/v2/internal/ghrepo"
"github.com/cli/cli/v2/pkg/cmd/release/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
@ -21,6 +22,7 @@ type DeleteOptions struct {
TagName string
SkipConfirm bool
CleanupTag bool
}
func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Command {
@ -47,6 +49,7 @@ func NewCmdDelete(f *cmdutil.Factory, runF func(*DeleteOptions) error) *cobra.Co
}
cmd.Flags().BoolVarP(&opts.SkipConfirm, "yes", "y", false, "Skip the confirmation prompt")
cmd.Flags().BoolVar(&opts.CleanupTag, "cleanup-tag", false, "Delete the tag attached to the release")
return cmd
}
@ -88,13 +91,23 @@ func deleteRun(opts *DeleteOptions) error {
return err
}
var cleanupMessage string
mustCleanupTag := opts.CleanupTag
if mustCleanupTag {
err = deleteTag(httpClient, baseRepo, release.TagName)
if err != nil {
return err
}
cleanupMessage = " and cleanup the tag"
}
if !opts.IO.IsStdoutTTY() || !opts.IO.IsStderrTTY() {
return nil
}
iofmt := opts.IO.ColorScheme()
fmt.Fprintf(opts.IO.ErrOut, "%s Deleted release %s\n", iofmt.SuccessIconWithColor(iofmt.Red), release.TagName)
if !release.IsDraft {
fmt.Fprintf(opts.IO.ErrOut, "%s Deleted release %s%s\n", iofmt.SuccessIconWithColor(iofmt.Red), release.TagName, cleanupMessage)
if !release.IsDraft && !mustCleanupTag {
fmt.Fprintf(opts.IO.ErrOut, "%s Note that the %s git tag still remains in the repository\n", iofmt.WarningIcon(), release.TagName)
}
@ -118,3 +131,25 @@ func deleteRelease(httpClient *http.Client, releaseURL string) error {
}
return nil
}
func deleteTag(httpClient *http.Client, baseRepo ghrepo.Interface, tagName string) error {
path := fmt.Sprintf("repos/%s/%s/git/refs/tags/%s", baseRepo.RepoOwner(), baseRepo.RepoName(), tagName)
url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
return err
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode > 299 {
return api.HandleHTTPError(resp)
}
return nil
}

View file

@ -31,6 +31,7 @@ func Test_NewCmdDelete(t *testing.T) {
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: false,
},
},
{
@ -40,6 +41,17 @@ func Test_NewCmdDelete(t *testing.T) {
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: true,
CleanupTag: false,
},
},
{
name: "cleanup tag",
args: "v1.2.3 --cleanup-tag",
isTTY: true,
want: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: true,
},
},
{
@ -83,8 +95,10 @@ func Test_NewCmdDelete(t *testing.T) {
require.NoError(t, err)
}
assert.Equal(t, tt.want.TagName, opts.TagName)
assert.Equal(t, tt.want.SkipConfirm, opts.SkipConfirm)
assert.Equal(t, tt.want.CleanupTag, opts.CleanupTag)
})
}
}
@ -104,6 +118,7 @@ func Test_deleteRun(t *testing.T) {
opts: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: true,
CleanupTag: false,
},
wantStdout: ``,
wantStderr: heredoc.Doc(`
@ -117,6 +132,31 @@ func Test_deleteRun(t *testing.T) {
opts: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: false,
},
wantStdout: ``,
wantStderr: ``,
},
{
name: "cleanup-tag & skipping confirmation",
isTTY: true,
opts: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: true,
CleanupTag: true,
},
wantStdout: ``,
wantStderr: heredoc.Doc(`
Deleted release v1.2.3 and cleanup the tag
`),
},
{
name: "cleanup-tag",
isTTY: false,
opts: DeleteOptions{
TagName: "v1.2.3",
SkipConfirm: false,
CleanupTag: true,
},
wantStdout: ``,
wantStderr: ``,
@ -135,7 +175,10 @@ func Test_deleteRun(t *testing.T) {
"draft": false,
"url": "https://api.github.com/repos/OWNER/REPO/releases/23456"
}`))
fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/releases/23456"), httpmock.StatusStringResponse(204, ""))
fakeHTTP.Register(httpmock.REST("DELETE", "repos/OWNER/REPO/git/refs/tags/v1.2.3"), httpmock.StatusStringResponse(204, ""))
tt.opts.IO = ios
tt.opts.HttpClient = func() (*http.Client, error) {