Skip to content

Commit b5e974c

Browse files
jolheiserlunny
andauthored
Delete tag API (#13358)
* Delete tag API Signed-off-by: jolheiser <[email protected]> * Wording Signed-off-by: jolheiser <[email protected]> * Add conflict response and fix API tests Signed-off-by: jolheiser <[email protected]> * Fix other test Signed-off-by: jolheiser <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent e16a5bb commit b5e974c

File tree

8 files changed

+149
-5
lines changed

8 files changed

+149
-5
lines changed

integrations/api_releases_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,26 @@ func TestAPIGetReleaseByTag(t *testing.T) {
154154
DecodeJSON(t, resp, &err)
155155
assert.True(t, strings.HasPrefix(err.Message, "release tag does not exist"))
156156
}
157+
158+
func TestAPIDeleteTagByName(t *testing.T) {
159+
defer prepareTestEnv(t)()
160+
161+
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
162+
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
163+
session := loginUser(t, owner.LowerName)
164+
token := getTokenForLoggedInUser(t, session)
165+
166+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag/?token=%s",
167+
owner.Name, repo.Name, token)
168+
169+
req := NewRequestf(t, http.MethodDelete, urlStr)
170+
_ = session.MakeRequest(t, req, http.StatusNoContent)
171+
172+
// Make sure that actual releases can't be deleted outright
173+
createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test")
174+
urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag/?token=%s",
175+
owner.Name, repo.Name, token)
176+
177+
req = NewRequestf(t, http.MethodDelete, urlStr)
178+
_ = session.MakeRequest(t, req, http.StatusConflict)
179+
}

integrations/api_repo_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ func TestAPIViewRepo(t *testing.T) {
223223
DecodeJSON(t, resp, &repo)
224224
assert.EqualValues(t, 1, repo.ID)
225225
assert.EqualValues(t, "repo1", repo.Name)
226-
assert.EqualValues(t, 1, repo.Releases)
226+
assert.EqualValues(t, 2, repo.Releases)
227227
assert.EqualValues(t, 1, repo.OpenIssues)
228228
assert.EqualValues(t, 3, repo.OpenPulls)
229229

integrations/release_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func TestCreateRelease(t *testing.T) {
8383
session := loginUser(t, "user2")
8484
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, false)
8585

86-
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 2)
86+
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 3)
8787
}
8888

8989
func TestCreateReleasePreRelease(t *testing.T) {
@@ -92,7 +92,7 @@ func TestCreateReleasePreRelease(t *testing.T) {
9292
session := loginUser(t, "user2")
9393
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", true, false)
9494

95-
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 2)
95+
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 3)
9696
}
9797

9898
func TestCreateReleaseDraft(t *testing.T) {
@@ -101,7 +101,7 @@ func TestCreateReleaseDraft(t *testing.T) {
101101
session := loginUser(t, "user2")
102102
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, true)
103103

104-
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 2)
104+
checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 3)
105105
}
106106

107107
func TestCreateReleasePaging(t *testing.T) {

models/fixtures/release.yml

+16
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,19 @@
2727
is_prerelease: false
2828
is_tag: false
2929
created_unix: 946684800
30+
31+
-
32+
id: 3
33+
repo_id: 1
34+
publisher_id: 2
35+
tag_name: "delete-tag"
36+
lower_tag_name: "delete-tag"
37+
target: "master"
38+
title: "delete-tag"
39+
sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d"
40+
num_commits: 10
41+
is_draft: false
42+
is_prerelease: false
43+
is_tag: true
44+
created_unix: 946684800
45+

modules/context/api.go

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ type APIForbiddenError struct {
6161
// swagger:response notFound
6262
type APINotFound struct{}
6363

64+
//APIConflict is a conflict empty response
65+
// swagger:response conflict
66+
type APIConflict struct{}
67+
6468
//APIRedirect is a redirect response
6569
// swagger:response redirect
6670
type APIRedirect struct{}

routers/api/v1/api.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,9 @@ func RegisterRoutes(m *macaron.Macaron) {
798798
})
799799
})
800800
m.Group("/tags", func() {
801-
m.Get("/:tag", repo.GetReleaseTag)
801+
m.Combo("/:tag").
802+
Get(repo.GetReleaseTag).
803+
Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag)
802804
})
803805
}, reqRepoReader(models.UnitTypeReleases))
804806
m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync)

routers/api/v1/repo/release_tags.go

+55
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
package repo
66

77
import (
8+
"errors"
89
"net/http"
910

1011
"code.gitea.io/gitea/models"
1112
"code.gitea.io/gitea/modules/context"
1213
"code.gitea.io/gitea/modules/convert"
14+
releaseservice "code.gitea.io/gitea/services/release"
1315
)
1416

1517
// GetReleaseTag get a single release of a repository by its tagname
@@ -59,3 +61,56 @@ func GetReleaseTag(ctx *context.APIContext) {
5961
}
6062
ctx.JSON(http.StatusOK, convert.ToRelease(release))
6163
}
64+
65+
// DeleteReleaseTag delete a tag from a repository
66+
func DeleteReleaseTag(ctx *context.APIContext) {
67+
// swagger:operation DELETE /repos/{owner}/{repo}/releases/tags/{tag} repository repoDeleteReleaseTag
68+
// ---
69+
// summary: Delete a release tag
70+
// parameters:
71+
// - name: owner
72+
// in: path
73+
// description: owner of the repo
74+
// type: string
75+
// required: true
76+
// - name: repo
77+
// in: path
78+
// description: name of the repo
79+
// type: string
80+
// required: true
81+
// - name: tag
82+
// in: path
83+
// description: name of the tag to delete
84+
// type: string
85+
// required: true
86+
// responses:
87+
// "204":
88+
// "$ref": "#/responses/empty"
89+
// "404":
90+
// "$ref": "#/responses/notFound"
91+
// "409":
92+
// "$ref": "#/responses/conflict"
93+
94+
tag := ctx.Params(":tag")
95+
96+
release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
97+
if err != nil {
98+
if models.IsErrReleaseNotExist(err) {
99+
ctx.Error(http.StatusNotFound, "GetRelease", err)
100+
return
101+
}
102+
ctx.Error(http.StatusInternalServerError, "GetRelease", err)
103+
return
104+
}
105+
106+
if !release.IsTag {
107+
ctx.Error(http.StatusConflict, "IsTag", errors.New("a tag attached to a release cannot be deleted directly"))
108+
return
109+
}
110+
111+
if err := releaseservice.DeleteReleaseByID(release.ID, ctx.User, true); err != nil {
112+
ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
113+
}
114+
115+
ctx.Status(http.StatusNoContent)
116+
}

templates/swagger/v1_json.tmpl

+44
Original file line numberDiff line numberDiff line change
@@ -7834,6 +7834,47 @@
78347834
"$ref": "#/responses/notFound"
78357835
}
78367836
}
7837+
},
7838+
"delete": {
7839+
"tags": [
7840+
"repository"
7841+
],
7842+
"summary": "Delete a release tag",
7843+
"operationId": "repoDeleteReleaseTag",
7844+
"parameters": [
7845+
{
7846+
"type": "string",
7847+
"description": "owner of the repo",
7848+
"name": "owner",
7849+
"in": "path",
7850+
"required": true
7851+
},
7852+
{
7853+
"type": "string",
7854+
"description": "name of the repo",
7855+
"name": "repo",
7856+
"in": "path",
7857+
"required": true
7858+
},
7859+
{
7860+
"type": "string",
7861+
"description": "name of the tag to delete",
7862+
"name": "tag",
7863+
"in": "path",
7864+
"required": true
7865+
}
7866+
],
7867+
"responses": {
7868+
"204": {
7869+
"$ref": "#/responses/empty"
7870+
},
7871+
"404": {
7872+
"$ref": "#/responses/notFound"
7873+
},
7874+
"409": {
7875+
"$ref": "#/responses/conflict"
7876+
}
7877+
}
78377878
}
78387879
},
78397880
"/repos/{owner}/{repo}/releases/{id}": {
@@ -16249,6 +16290,9 @@
1624916290
"$ref": "#/definitions/WatchInfo"
1625016291
}
1625116292
},
16293+
"conflict": {
16294+
"description": "APIConflict is a conflict empty response"
16295+
},
1625216296
"empty": {
1625316297
"description": "APIEmpty is an empty response"
1625416298
},

0 commit comments

Comments
 (0)