From d0ca3a93f09d78fe5a3f1d81d99602f2df76a2a6 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 4 Feb 2021 03:08:13 +0100 Subject: [PATCH 1/4] DeleteReleaseByTag delete release not git tags --- routers/api/v1/api.go | 4 ++-- routers/api/v1/repo/release_tags.go | 27 +++++++++++++-------------- templates/swagger/v1_json.tmpl | 10 +++++----- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 42b52db93657d..c560e359c198c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -862,8 +862,8 @@ func Routes() *web.Route { }) m.Group("/tags", func() { m.Combo("/{tag}"). - Get(repo.GetReleaseTag). - Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag) + Get(repo.GetReleaseByTag). + Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseByTag) }) }, reqRepoReader(models.UnitTypeReleases)) m.Post("/mirror-sync", reqToken(), reqRepoWriter(models.UnitTypeCode), repo.MirrorSync) diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index ef07ce5e1ad56..aedf1e0676537 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -5,7 +5,6 @@ package repo import ( - "errors" "net/http" "code.gitea.io/gitea/models" @@ -14,9 +13,9 @@ import ( releaseservice "code.gitea.io/gitea/services/release" ) -// GetReleaseTag get a single release of a repository by its tagname -func GetReleaseTag(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/releases/tags/{tag} repository repoGetReleaseTag +// GetReleaseByTag get a single release of a repository by tag name +func GetReleaseByTag(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/releases/tags/{tag} repository repoGetReleaseByTag // --- // summary: Get a release by tag name // produces: @@ -34,7 +33,7 @@ func GetReleaseTag(ctx *context.APIContext) { // required: true // - name: tag // in: path - // description: tagname of the release to get + // description: tag name of the release to get // type: string // required: true // responses: @@ -62,11 +61,11 @@ func GetReleaseTag(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToRelease(release)) } -// DeleteReleaseTag delete a tag from a repository -func DeleteReleaseTag(ctx *context.APIContext) { - // swagger:operation DELETE /repos/{owner}/{repo}/releases/tags/{tag} repository repoDeleteReleaseTag +// DeleteReleaseByTag delete a release from a repository by tag name +func DeleteReleaseByTag(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/releases/tags/{tag} repository repoDeleteReleaseByTag // --- - // summary: Delete a release tag + // summary: Delete a release by tag name // parameters: // - name: owner // in: path @@ -80,7 +79,7 @@ func DeleteReleaseTag(ctx *context.APIContext) { // required: true // - name: tag // in: path - // description: name of the tag to delete + // description: tag name of the release to delete // type: string // required: true // responses: @@ -96,19 +95,19 @@ func DeleteReleaseTag(ctx *context.APIContext) { release, err := models.GetRelease(ctx.Repo.Repository.ID, tag) if err != nil { if models.IsErrReleaseNotExist(err) { - ctx.Error(http.StatusNotFound, "GetRelease", err) + ctx.NotFound() return } ctx.Error(http.StatusInternalServerError, "GetRelease", err) return } - if !release.IsTag { - ctx.Error(http.StatusConflict, "IsTag", errors.New("a tag attached to a release cannot be deleted directly")) + if release.IsTag { + ctx.NotFound() return } - if err := releaseservice.DeleteReleaseByID(release.ID, ctx.User, true); err != nil { + if err = releaseservice.DeleteReleaseByID(release.ID, ctx.User, false); err != nil { ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index fd760a28e6c47..5ca9a6bba5037 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -7964,7 +7964,7 @@ "repository" ], "summary": "Get a release by tag name", - "operationId": "repoGetReleaseTag", + "operationId": "repoGetReleaseByTag", "parameters": [ { "type": "string", @@ -7982,7 +7982,7 @@ }, { "type": "string", - "description": "tagname of the release to get", + "description": "tag name of the release to get", "name": "tag", "in": "path", "required": true @@ -8001,8 +8001,8 @@ "tags": [ "repository" ], - "summary": "Delete a release tag", - "operationId": "repoDeleteReleaseTag", + "summary": "Delete a release by tag name", + "operationId": "repoDeleteReleaseByTag", "parameters": [ { "type": "string", @@ -8020,7 +8020,7 @@ }, { "type": "string", - "description": "name of the tag to delete", + "description": "tag name of the release to delete", "name": "tag", "in": "path", "required": true From e0a153709ba3510d9f7c628b874b4ce55b4c9689 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 4 Feb 2021 03:30:47 +0100 Subject: [PATCH 2/4] Add api to delete tag (without release) --- routers/api/v1/api.go | 1 + routers/api/v1/repo/release_tags.go | 2 -- routers/api/v1/repo/tag.go | 56 +++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 46 ++++++++++++++++++++++-- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index c560e359c198c..9c21107a2892c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -754,6 +754,7 @@ func Routes() *web.Route { }, reqToken(), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) + m.Delete("/{tag}", repo.DeleteTag) }, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index aedf1e0676537..36489a9679f9a 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -87,8 +87,6 @@ func DeleteReleaseByTag(ctx *context.APIContext) { // "$ref": "#/responses/empty" // "404": // "$ref": "#/responses/notFound" - // "409": - // "$ref": "#/responses/conflict" tag := ctx.Params(":tag") diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 76c612bea4205..31f3e9e851d71 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -5,12 +5,15 @@ package repo import ( + "errors" "net/http" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" + releaseservice "code.gitea.io/gitea/services/release" ) // ListTags list all the tags of a repository @@ -104,3 +107,56 @@ func GetTag(ctx *context.APIContext) { ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx.Repo.Repository, tag, commit)) } } + +// DeleteTag delete a specific tag of in a repository by name +func DeleteTag(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/tags/{tag} repository repoDeleteTag + // --- + // summary: Delete a repository's tag by name + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: tag + // in: path + // description: name of tag to delete + // type: string + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/notFound" + // "409": + // "$ref": "#/responses/conflict" + + tag, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("tag")) + if err != nil { + if models.IsErrReleaseNotExist(err) { + ctx.NotFound() + return + } + ctx.Error(http.StatusInternalServerError, "GetRelease", err) + return + } + + if !tag.IsTag { + ctx.Error(http.StatusConflict, "IsTag", errors.New("a tag attached to a release cannot be deleted directly")) + return + } + + if err = releaseservice.DeleteReleaseByID(tag.ID, ctx.User, true); err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) + } + + ctx.JSON(http.StatusOK, &tag) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 5ca9a6bba5037..de1fa75186802 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8032,9 +8032,6 @@ }, "404": { "$ref": "#/responses/notFound" - }, - "409": { - "$ref": "#/responses/conflict" } } } @@ -8815,6 +8812,49 @@ } } }, + "/repos/{owner}/{repo}/tags/{tag}": { + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Delete a repository's tag by name", + "operationId": "repoDeleteTag", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of tag to delete", + "name": "tag", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/teams": { "get": { "produces": [ From 8f95f782f5ed76e95fb31cc353f81d2ff850910c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 4 Feb 2021 04:32:23 +0100 Subject: [PATCH 3/4] fix & extend tests --- integrations/api_releases_test.go | 20 ++++++++++---------- integrations/api_repo_git_tags_test.go | 24 ++++++++++++++++++++++++ routers/api/v1/repo/tag.go | 2 +- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go index 2b310d11e0847..26bf752ccae9e 100644 --- a/integrations/api_releases_test.go +++ b/integrations/api_releases_test.go @@ -154,7 +154,7 @@ func TestAPIGetReleaseByTag(t *testing.T) { assert.EqualValues(t, "Not Found", err.Message) } -func TestAPIDeleteTagByName(t *testing.T) { +func TestAPIDeleteReleaseByTagName(t *testing.T) { defer prepareTestEnv(t)() repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) @@ -162,17 +162,17 @@ func TestAPIDeleteTagByName(t *testing.T) { session := loginUser(t, owner.LowerName) token := getTokenForLoggedInUser(t, session) - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/delete-tag?token=%s", - owner.Name, repo.Name, token) + createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") - req := NewRequestf(t, http.MethodDelete, urlStr) + // delete release + req := NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag?token=%s", owner.Name, repo.Name, token)) _ = session.MakeRequest(t, req, http.StatusNoContent) - // Make sure that actual releases can't be deleted outright - createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") - urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag?token=%s", - owner.Name, repo.Name, token) + // make sure release is deleted + req = NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag?token=%s", owner.Name, repo.Name, token)) + _ = session.MakeRequest(t, req, http.StatusNotFound) - req = NewRequestf(t, http.MethodDelete, urlStr) - _ = session.MakeRequest(t, req, http.StatusConflict) + // delete release tag too + req = NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag?token=%s", owner.Name, repo.Name, token)) + _ = session.MakeRequest(t, req, http.StatusNoContent) } diff --git a/integrations/api_repo_git_tags_test.go b/integrations/api_repo_git_tags_test.go index ad710a45204da..bf6fc7c858137 100644 --- a/integrations/api_repo_git_tags_test.go +++ b/integrations/api_repo_git_tags_test.go @@ -5,6 +5,7 @@ package integrations import ( + "fmt" "net/http" "testing" @@ -59,3 +60,26 @@ func TestAPIGitTags(t *testing.T) { badReq := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/git/tags/%s?token=%s", user.Name, repo.Name, commit.ID.String(), token) session.MakeRequest(t, badReq, http.StatusBadRequest) } + +func TestAPIDeleteTagByName(t *testing.T) { + defer prepareTestEnv(t)() + + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User) + session := loginUser(t, owner.LowerName) + token := getTokenForLoggedInUser(t, session) + + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags/delete-tag?token=%s", + owner.Name, repo.Name, token) + + req := NewRequestf(t, http.MethodDelete, urlStr) + _ = session.MakeRequest(t, req, http.StatusNoContent) + + // Make sure that actual releases can't be deleted outright + createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") + urlStr = fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag?token=%s", + owner.Name, repo.Name, token) + + req = NewRequestf(t, http.MethodDelete, urlStr) + _ = session.MakeRequest(t, req, http.StatusConflict) +} diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go index 31f3e9e851d71..ec9b541bd41d3 100644 --- a/routers/api/v1/repo/tag.go +++ b/routers/api/v1/repo/tag.go @@ -158,5 +158,5 @@ func DeleteTag(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err) } - ctx.JSON(http.StatusOK, &tag) + ctx.Status(http.StatusNoContent) } From d901db32db1f9ac6fc77e8e770d262264a06089d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 4 Feb 2021 04:55:02 +0100 Subject: [PATCH 4/4] fix swagger doc --- templates/swagger/v1_json.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index de1fa75186802..75cacdf122ad0 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -8851,6 +8851,9 @@ }, "404": { "$ref": "#/responses/notFound" + }, + "409": { + "$ref": "#/responses/conflict" } } }