]> source.dussan.org Git - gitea.git/commitdiff
[API] Add delete release by tag & fix unreleased inconsistency (#14563)
author6543 <6543@obermui.de>
Sun, 7 Feb 2021 18:32:18 +0000 (19:32 +0100)
committerGitHub <noreply@github.com>
Sun, 7 Feb 2021 18:32:18 +0000 (19:32 +0100)
* DeleteReleaseByTag delete release not git tags

* Add api to delete tag (without release)

* fix & extend tests

* fix swagger doc

integrations/api_releases_test.go
integrations/api_repo_git_tags_test.go
routers/api/v1/api.go
routers/api/v1/repo/release_tags.go
routers/api/v1/repo/tag.go
templates/swagger/v1_json.tmpl

index 2b310d11e0847e5dd6f75e7b2884ce72613d3faa..26bf752ccae9eb02a6db8f9baeece96701aeb7eb 100644 (file)
@@ -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)
 }
index ad710a45204da56f82656db8366722bc17043208..bf6fc7c85813799aefadae9b9043675f02edf469 100644 (file)
@@ -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)
+}
index 42b52db93657da66eae891917f7e5fc658e589dc..9c21107a2892c2b6752212a1daad47f6e94a6cef 100644 (file)
@@ -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).
@@ -862,8 +863,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)
index 70011a6a8edcc28ef0cfcb021be1d8c4be37a459..4b853d44bb956a3e93f1048087d7f0af0910b987 100644 (file)
@@ -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:
@@ -67,11 +66,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
@@ -85,7 +84,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:
@@ -93,27 +92,25 @@ func DeleteReleaseTag(ctx *context.APIContext) {
        //     "$ref": "#/responses/empty"
        //   "404":
        //     "$ref": "#/responses/notFound"
-       //   "409":
-       //     "$ref": "#/responses/conflict"
 
        tag := ctx.Params(":tag")
 
        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)
        }
 
index 76c612bea4205e59af09b97131cf2df0ff64a4e6..ec9b541bd41d35095ea367af86499e112bf428a0 100644 (file)
@@ -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.Status(http.StatusNoContent)
+}
index 5a3be37b4aec486bab7102b7bc83c5301d8060a1..45f396f2830f6f065d7b5a89ab161ff0ae674cf0 100644 (file)
           "repository"
         ],
         "summary": "Get a release by tag name",
-        "operationId": "repoGetReleaseTag",
+        "operationId": "repoGetReleaseByTag",
         "parameters": [
           {
             "type": "string",
           },
           {
             "type": "string",
-            "description": "tagname of the release to get",
+            "description": "tag name of the release to get",
             "name": "tag",
             "in": "path",
             "required": true
         "tags": [
           "repository"
         ],
-        "summary": "Delete a release tag",
-        "operationId": "repoDeleteReleaseTag",
+        "summary": "Delete a release by tag name",
+        "operationId": "repoDeleteReleaseByTag",
         "parameters": [
           {
             "type": "string",
           },
           {
             "type": "string",
-            "description": "name of the tag to delete",
+            "description": "tag name of the release to delete",
             "name": "tag",
             "in": "path",
             "required": true
           },
           "404": {
             "$ref": "#/responses/notFound"
-          },
-          "409": {
-            "$ref": "#/responses/conflict"
           }
         }
       }
         }
       }
     },
+    "/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"
+          },
+          "409": {
+            "$ref": "#/responses/conflict"
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/teams": {
       "get": {
         "produces": [