- When there's a succesful POST operation, it should return a 201 status code (which is the status code for succesful created) and additionally the created object. - Currently for the `POST /repos/{owner}/{repo}/tags` endpoint an 200 status code was documented in the OpenAPI specification, while an 201 status code was actually being returned. In this case the code is correct and the documented status code needs to be adjusted. - Resolves #2200 (cherry picked from commit a2939116f5ce21295981a3a9aa84a73fe289b8b2) (cherry picked from commit 22cff4158564a3e69bef83c458cf1f129e1b688b) (cherry picked from commit b23a7f27bb10e782e70530cc8c37f5d11f7a684a)
285 lines
7.4 KiB
Go
285 lines
7.4 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"code.gitea.io/gitea/models"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/modules/context"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
"code.gitea.io/gitea/modules/web"
|
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
|
"code.gitea.io/gitea/services/convert"
|
|
releaseservice "code.gitea.io/gitea/services/release"
|
|
)
|
|
|
|
// ListTags list all the tags of a repository
|
|
func ListTags(ctx *context.APIContext) {
|
|
// swagger:operation GET /repos/{owner}/{repo}/tags repository repoListTags
|
|
// ---
|
|
// summary: List a repository's tags
|
|
// 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: page
|
|
// in: query
|
|
// description: page number of results to return (1-based)
|
|
// type: integer
|
|
// - name: limit
|
|
// in: query
|
|
// description: page size of results, default maximum page size is 50
|
|
// type: integer
|
|
// responses:
|
|
// "200":
|
|
// "$ref": "#/responses/TagList"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
|
|
listOpts := utils.GetListOptions(ctx)
|
|
|
|
tags, total, err := ctx.Repo.GitRepo.GetTagInfos(listOpts.Page, listOpts.PageSize)
|
|
if err != nil {
|
|
ctx.Error(http.StatusInternalServerError, "GetTags", err)
|
|
return
|
|
}
|
|
|
|
apiTags := make([]*api.Tag, len(tags))
|
|
for i := range tags {
|
|
apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i])
|
|
}
|
|
|
|
ctx.SetTotalCountHeader(int64(total))
|
|
ctx.JSON(http.StatusOK, &apiTags)
|
|
}
|
|
|
|
// GetAnnotatedTag get the tag of a repository.
|
|
func GetAnnotatedTag(ctx *context.APIContext) {
|
|
// swagger:operation GET /repos/{owner}/{repo}/git/tags/{sha} repository GetAnnotatedTag
|
|
// ---
|
|
// summary: Gets the tag object of an annotated tag (not lightweight tags)
|
|
// 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: sha
|
|
// in: path
|
|
// description: sha of the tag. The Git tags API only supports annotated tag objects, not lightweight tags.
|
|
// type: string
|
|
// required: true
|
|
// responses:
|
|
// "200":
|
|
// "$ref": "#/responses/AnnotatedTag"
|
|
// "400":
|
|
// "$ref": "#/responses/error"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
|
|
sha := ctx.Params("sha")
|
|
if len(sha) == 0 {
|
|
ctx.Error(http.StatusBadRequest, "", "SHA not provided")
|
|
return
|
|
}
|
|
|
|
if tag, err := ctx.Repo.GitRepo.GetAnnotatedTag(sha); err != nil {
|
|
ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err)
|
|
} else {
|
|
commit, err := tag.Commit(ctx.Repo.GitRepo)
|
|
if err != nil {
|
|
ctx.Error(http.StatusBadRequest, "GetAnnotatedTag", err)
|
|
}
|
|
ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx, ctx.Repo.Repository, tag, commit))
|
|
}
|
|
}
|
|
|
|
// GetTag get the tag of a repository
|
|
func GetTag(ctx *context.APIContext) {
|
|
// swagger:operation GET /repos/{owner}/{repo}/tags/{tag} repository repoGetTag
|
|
// ---
|
|
// summary: Get the tag of a repository by tag 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
|
|
// type: string
|
|
// required: true
|
|
// responses:
|
|
// "200":
|
|
// "$ref": "#/responses/Tag"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
tagName := ctx.Params("*")
|
|
|
|
tag, err := ctx.Repo.GitRepo.GetTag(tagName)
|
|
if err != nil {
|
|
ctx.NotFound(tagName)
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusOK, convert.ToTag(ctx.Repo.Repository, tag))
|
|
}
|
|
|
|
// CreateTag create a new git tag in a repository
|
|
func CreateTag(ctx *context.APIContext) {
|
|
// swagger:operation POST /repos/{owner}/{repo}/tags repository repoCreateTag
|
|
// ---
|
|
// summary: Create a new git tag in a repository
|
|
// 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: body
|
|
// in: body
|
|
// schema:
|
|
// "$ref": "#/definitions/CreateTagOption"
|
|
// responses:
|
|
// "201":
|
|
// "$ref": "#/responses/Tag"
|
|
// "404":
|
|
// "$ref": "#/responses/notFound"
|
|
// "405":
|
|
// "$ref": "#/responses/empty"
|
|
// "409":
|
|
// "$ref": "#/responses/conflict"
|
|
// "423":
|
|
// "$ref": "#/responses/repoArchivedError"
|
|
form := web.GetForm(ctx).(*api.CreateTagOption)
|
|
|
|
// If target is not provided use default branch
|
|
if len(form.Target) == 0 {
|
|
form.Target = ctx.Repo.Repository.DefaultBranch
|
|
}
|
|
|
|
commit, err := ctx.Repo.GitRepo.GetCommit(form.Target)
|
|
if err != nil {
|
|
ctx.Error(http.StatusNotFound, "target not found", fmt.Errorf("target not found: %w", err))
|
|
return
|
|
}
|
|
|
|
if err := releaseservice.CreateNewTag(ctx, ctx.Doer, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
|
|
if models.IsErrTagAlreadyExists(err) {
|
|
ctx.Error(http.StatusConflict, "tag exist", err)
|
|
return
|
|
}
|
|
if models.IsErrProtectedTagName(err) {
|
|
ctx.Error(http.StatusMethodNotAllowed, "CreateNewTag", "user not allowed to create protected tag")
|
|
return
|
|
}
|
|
|
|
ctx.InternalServerError(err)
|
|
return
|
|
}
|
|
|
|
tag, err := ctx.Repo.GitRepo.GetTag(form.TagName)
|
|
if err != nil {
|
|
ctx.InternalServerError(err)
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusCreated, convert.ToTag(ctx.Repo.Repository, tag))
|
|
}
|
|
|
|
// 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"
|
|
// "405":
|
|
// "$ref": "#/responses/empty"
|
|
// "409":
|
|
// "$ref": "#/responses/conflict"
|
|
// "423":
|
|
// "$ref": "#/responses/repoArchivedError"
|
|
tagName := ctx.Params("*")
|
|
|
|
tag, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, tagName)
|
|
if err != nil {
|
|
if repo_model.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(ctx, ctx.Repo.Repository, tag, ctx.Doer, true); err != nil {
|
|
if models.IsErrProtectedTagName(err) {
|
|
ctx.Error(http.StatusMethodNotAllowed, "delTag", "user not allowed to delete protected tag")
|
|
return
|
|
}
|
|
ctx.Error(http.StatusInternalServerError, "DeleteReleaseByID", err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusNoContent)
|
|
}
|