[API] Add repoCreateTag (#16165)
* Add API CreateTag * Add Test * API: expose Tag Messagerelease/v1.15
parent
19dedc3fa5
commit
f7cd394680
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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"`
|
||||||
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
|
@ -158,4 +158,7 @@ type swaggerParameterBodies struct {
|
||||||
|
|
||||||
// in:body
|
// in:body
|
||||||
PullReviewRequestOptions api.PullReviewRequestOptions
|
PullReviewRequestOptions api.PullReviewRequestOptions
|
||||||
|
|
||||||
|
// in:body
|
||||||
|
CreateTagOption api.CreateTagOption
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
Loading…
Reference in New Issue