summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--integrations/api_releases_test.go23
-rw-r--r--integrations/api_repo_test.go2
-rw-r--r--integrations/release_test.go6
-rw-r--r--models/fixtures/release.yml16
-rw-r--r--modules/context/api.go4
-rw-r--r--routers/api/v1/api.go4
-rw-r--r--routers/api/v1/repo/release_tags.go55
-rw-r--r--templates/swagger/v1_json.tmpl44
8 files changed, 149 insertions, 5 deletions
diff --git a/integrations/api_releases_test.go b/integrations/api_releases_test.go
index 58c2e35440..8328b014d6 100644
--- a/integrations/api_releases_test.go
+++ b/integrations/api_releases_test.go
@@ -154,3 +154,26 @@ func TestAPIGetReleaseByTag(t *testing.T) {
DecodeJSON(t, resp, &err)
assert.True(t, strings.HasPrefix(err.Message, "release tag does not exist"))
}
+
+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/releases/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/releases/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/integrations/api_repo_test.go b/integrations/api_repo_test.go
index c8afa73ae6..fed63a9597 100644
--- a/integrations/api_repo_test.go
+++ b/integrations/api_repo_test.go
@@ -223,7 +223,7 @@ func TestAPIViewRepo(t *testing.T) {
DecodeJSON(t, resp, &repo)
assert.EqualValues(t, 1, repo.ID)
assert.EqualValues(t, "repo1", repo.Name)
- assert.EqualValues(t, 1, repo.Releases)
+ assert.EqualValues(t, 2, repo.Releases)
assert.EqualValues(t, 1, repo.OpenIssues)
assert.EqualValues(t, 3, repo.OpenPulls)
diff --git a/integrations/release_test.go b/integrations/release_test.go
index 4d2260d884..c817dcaecf 100644
--- a/integrations/release_test.go
+++ b/integrations/release_test.go
@@ -83,7 +83,7 @@ func TestCreateRelease(t *testing.T) {
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, false)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 2)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.stable"), 3)
}
func TestCreateReleasePreRelease(t *testing.T) {
@@ -92,7 +92,7 @@ func TestCreateReleasePreRelease(t *testing.T) {
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", true, false)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 2)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.prerelease"), 3)
}
func TestCreateReleaseDraft(t *testing.T) {
@@ -101,7 +101,7 @@ func TestCreateReleaseDraft(t *testing.T) {
session := loginUser(t, "user2")
createNewRelease(t, session, "/user2/repo1", "v0.0.1", "v0.0.1", false, true)
- checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 2)
+ checkLatestReleaseAndCount(t, session, "/user2/repo1", "v0.0.1", i18n.Tr("en", "repo.release.draft"), 3)
}
func TestCreateReleasePaging(t *testing.T) {
diff --git a/models/fixtures/release.yml b/models/fixtures/release.yml
index f95eb048be..8d3f5840ef 100644
--- a/models/fixtures/release.yml
+++ b/models/fixtures/release.yml
@@ -27,3 +27,19 @@
is_prerelease: false
is_tag: false
created_unix: 946684800
+
+-
+ id: 3
+ repo_id: 1
+ publisher_id: 2
+ tag_name: "delete-tag"
+ lower_tag_name: "delete-tag"
+ target: "master"
+ title: "delete-tag"
+ sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d"
+ num_commits: 10
+ is_draft: false
+ is_prerelease: false
+ is_tag: true
+ created_unix: 946684800
+
diff --git a/modules/context/api.go b/modules/context/api.go
index 772e1f8f50..9dad588c7f 100644
--- a/modules/context/api.go
+++ b/modules/context/api.go
@@ -61,6 +61,10 @@ type APIForbiddenError struct {
// swagger:response notFound
type APINotFound struct{}
+//APIConflict is a conflict empty response
+// swagger:response conflict
+type APIConflict struct{}
+
//APIRedirect is a redirect response
// swagger:response redirect
type APIRedirect struct{}
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 147cb8e276..42489cd4a6 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -798,7 +798,9 @@ func RegisterRoutes(m *macaron.Macaron) {
})
})
m.Group("/tags", func() {
- m.Get("/:tag", repo.GetReleaseTag)
+ m.Combo("/:tag").
+ Get(repo.GetReleaseTag).
+ Delete(reqToken(), reqRepoWriter(models.UnitTypeReleases), repo.DeleteReleaseTag)
})
}, 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 2a72e0000e..ef07ce5e1a 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -5,11 +5,13 @@
package repo
import (
+ "errors"
"net/http"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
+ releaseservice "code.gitea.io/gitea/services/release"
)
// GetReleaseTag get a single release of a repository by its tagname
@@ -59,3 +61,56 @@ 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
+ // ---
+ // summary: Delete a release tag
+ // 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 the tag to delete
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // "$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)
+ 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"))
+ return
+ }
+
+ if err := releaseservice.DeleteReleaseByID(release.ID, ctx.User, true); err != nil {
+ ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
+ }
+
+ ctx.Status(http.StatusNoContent)
+}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 90a76643dd..b8f81bb8f7 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -7834,6 +7834,47 @@
"$ref": "#/responses/notFound"
}
}
+ },
+ "delete": {
+ "tags": [
+ "repository"
+ ],
+ "summary": "Delete a release tag",
+ "operationId": "repoDeleteReleaseTag",
+ "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 the 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}/releases/{id}": {
@@ -16249,6 +16290,9 @@
"$ref": "#/definitions/WatchInfo"
}
},
+ "conflict": {
+ "description": "APIConflict is a conflict empty response"
+ },
"empty": {
"description": "APIEmpty is an empty response"
},