[API] Add repoCreateTag (#16165)

* Add API CreateTag

* Add Test

* API: expose Tag Message
release/v1.15
6543 2021-06-17 18:04:10 +02:00 committed by GitHub
parent 19dedc3fa5
commit f7cd394680
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 180 additions and 3 deletions

View File

@ -5,6 +5,7 @@
package integrations package integrations
import ( import (
"fmt"
"net/http" "net/http"
"testing" "testing"
@ -15,14 +16,16 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestAPIReposGetTags(t *testing.T) { func TestAPIRepoTags(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
// Login as User2. // Login as User2.
session := loginUser(t, user.Name) session := loginUser(t, user.Name)
token := getTokenForLoggedInUser(t, session) token := getTokenForLoggedInUser(t, session)
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/tags?token="+token, user.Name) repoName := "repo1"
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/tags?token=%s", user.Name, repoName, token)
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
var tags []*api.Tag var tags []*api.Tag
@ -30,8 +33,36 @@ func TestAPIReposGetTags(t *testing.T) {
assert.Len(t, tags, 1) assert.Len(t, tags, 1)
assert.Equal(t, "v1.1", tags[0].Name) assert.Equal(t, "v1.1", tags[0].Name)
assert.Equal(t, "Initial commit", tags[0].Message)
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA) assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA)
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.URL) assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.URL)
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL) assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL)
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL) assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL)
newTag := createNewTagUsingAPI(t, session, token, user.Name, repoName, "awesome-tag", "", "nice!\nand some text")
resp = session.MakeRequest(t, req, http.StatusOK)
DecodeJSON(t, resp, &tags)
assert.Len(t, tags, 2)
for _, tag := range tags {
if tag.Name != "v1.1" {
assert.EqualValues(t, newTag.Name, tag.Name)
assert.EqualValues(t, newTag.Message, tag.Message)
assert.EqualValues(t, "nice!\nand some text", tag.Message)
assert.EqualValues(t, newTag.Commit.SHA, tag.Commit.SHA)
}
}
}
func createNewTagUsingAPI(t *testing.T, session *TestSession, token string, ownerName, repoName, name, target, msg string) *api.Tag {
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags?token=%s", ownerName, repoName, token)
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{
TagName: name,
Message: msg,
Target: target,
})
resp := session.MakeRequest(t, req, http.StatusCreated)
var respObj api.Tag
DecodeJSON(t, resp, &respObj)
return &respObj
} }

View File

@ -8,6 +8,7 @@ package convert
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"time" "time"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@ -135,6 +136,7 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
func ToTag(repo *models.Repository, t *git.Tag) *api.Tag { func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
return &api.Tag{ return &api.Tag{
Name: t.Name, Name: t.Name,
Message: strings.TrimSpace(t.Message),
ID: t.ID.String(), ID: t.ID.String(),
Commit: ToCommitMeta(repo, t), Commit: ToCommitMeta(repo, t),
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"), ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),

View File

@ -7,6 +7,7 @@ package structs
// Tag represents a repository tag // Tag represents a repository tag
type Tag struct { type Tag struct {
Name string `json:"name"` Name string `json:"name"`
Message string `json:"message"`
ID string `json:"id"` ID string `json:"id"`
Commit *CommitMeta `json:"commit"` Commit *CommitMeta `json:"commit"`
ZipballURL string `json:"zipball_url"` ZipballURL string `json:"zipball_url"`
@ -30,3 +31,11 @@ type AnnotatedTagObject struct {
URL string `json:"url"` URL string `json:"url"`
SHA string `json:"sha"` SHA string `json:"sha"`
} }
// CreateTagOption options when creating a tag
type CreateTagOption struct {
// required: true
TagName string `json:"tag_name" binding:"Required"`
Message string `json:"message"`
Target string `json:"target"`
}

View File

@ -775,6 +775,7 @@ func Routes() *web.Route {
}, reqToken(), reqAdmin()) }, reqToken(), reqAdmin())
m.Group("/tags", func() { m.Group("/tags", func() {
m.Get("", repo.ListTags) m.Get("", repo.ListTags)
m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateTagOption{}), repo.CreateTag)
m.Delete("/{tag}", repo.DeleteTag) m.Delete("/{tag}", repo.DeleteTag)
}, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true)) }, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true))
m.Group("/keys", func() { m.Group("/keys", func() {

View File

@ -6,12 +6,14 @@ package repo
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" 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/routers/api/v1/utils"
releaseservice "code.gitea.io/gitea/services/release" releaseservice "code.gitea.io/gitea/services/release"
) )
@ -160,3 +162,62 @@ func DeleteTag(ctx *context.APIContext) {
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)
} }
// 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:
// "200":
// "$ref": "#/responses/AnnotatedTag"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// "$ref": "#/responses/conflict"
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: %v", err))
return
}
if err := releaseservice.CreateNewTag(ctx.User, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
if models.IsErrTagAlreadyExists(err) {
ctx.Error(http.StatusConflict, "tag exist", err)
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))
}

View File

@ -158,4 +158,7 @@ type swaggerParameterBodies struct {
// in:body // in:body
PullReviewRequestOptions api.PullReviewRequestOptions PullReviewRequestOptions api.PullReviewRequestOptions
// in:body
CreateTagOption api.CreateTagOption
} }

View File

@ -9082,6 +9082,50 @@
"$ref": "#/responses/TagList" "$ref": "#/responses/TagList"
} }
} }
},
"post": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Create a new git tag in a repository",
"operationId": "repoCreateTag",
"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
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateTagOption"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/AnnotatedTag"
},
"404": {
"$ref": "#/responses/notFound"
},
"409": {
"$ref": "#/responses/conflict"
}
}
} }
}, },
"/repos/{owner}/{repo}/tags/{tag}": { "/repos/{owner}/{repo}/tags/{tag}": {
@ -13092,6 +13136,28 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "x-go-package": "code.gitea.io/gitea/modules/structs"
}, },
"CreateTagOption": {
"description": "CreateTagOption options when creating a tag",
"type": "object",
"required": [
"tag_name"
],
"properties": {
"message": {
"type": "string",
"x-go-name": "Message"
},
"tag_name": {
"type": "string",
"x-go-name": "TagName"
},
"target": {
"type": "string",
"x-go-name": "Target"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"CreateTeamOption": { "CreateTeamOption": {
"description": "CreateTeamOption options for creating a team", "description": "CreateTeamOption options for creating a team",
"type": "object", "type": "object",
@ -16149,6 +16215,10 @@
"type": "string", "type": "string",
"x-go-name": "ID" "x-go-name": "ID"
}, },
"message": {
"type": "string",
"x-go-name": "Message"
},
"name": { "name": {
"type": "string", "type": "string",
"x-go-name": "Name" "x-go-name": "Name"
@ -17265,7 +17335,7 @@
"parameterBodies": { "parameterBodies": {
"description": "parameterBodies", "description": "parameterBodies",
"schema": { "schema": {
"$ref": "#/definitions/PullReviewRequestOptions" "$ref": "#/definitions/CreateTagOption"
} }
}, },
"redirect": { "redirect": {