From 01bbf5ea6940a1dc3793aaae2db2d85b1769432c Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 7 Feb 2019 20:00:52 +0800 Subject: [PATCH] Add API to list tags (#5850) * Add API to list tags * update dependency gitea sdk vendor * fix swagger generation * fix swagger * add tests * update code.gitea.io/git vendor --- Gopkg.lock | 4 +- integrations/api_repo_tags_test.go | 38 ++++++++++++++ models/repo_tag.go | 24 +++++++++ routers/api/v1/api.go | 3 ++ routers/api/v1/convert/convert.go | 16 ++++++ routers/api/v1/repo/tag.go | 47 +++++++++++++++++ routers/api/v1/swagger/repo.go | 7 +++ templates/swagger/v1_json.tmpl | 75 ++++++++++++++++++++++++++++ vendor/code.gitea.io/git/repo_tag.go | 16 ++---- 9 files changed, 216 insertions(+), 14 deletions(-) create mode 100644 integrations/api_repo_tags_test.go create mode 100644 models/repo_tag.go create mode 100644 routers/api/v1/repo/tag.go diff --git a/Gopkg.lock b/Gopkg.lock index 2b05b2181c..3419a83fde 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,11 +3,11 @@ [[projects]] branch = "master" - digest = "1:537ed734fb4869453583d9ce24d93bf68c88287082778efe55ae749970a1012a" + digest = "1:0a001725d6e1b35faccf15cbc4f782b67a0d77f4bbf56e51a4b244f92e7c60ca" name = "code.gitea.io/git" packages = ["."] pruneopts = "NUT" - revision = "fbe468c7a634991285eaa1f93e73431e3edfc471" + revision = "0aea7f12d36ed49bcac560b61301cff88e478e5c" [[projects]] branch = "master" diff --git a/integrations/api_repo_tags_test.go b/integrations/api_repo_tags_test.go new file mode 100644 index 0000000000..f9026b4d29 --- /dev/null +++ b/integrations/api_repo_tags_test.go @@ -0,0 +1,38 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package integrations + +import ( + "net/http" + "path" + "testing" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/sdk/gitea" + + "github.com/stretchr/testify/assert" +) + +func TestAPIReposGetTags(t *testing.T) { + prepareTestEnv(t) + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + // Login as User2. + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/tags?token="+token, user.Name) + resp := session.MakeRequest(t, req, http.StatusOK) + + var tags []*api.Tag + DecodeJSON(t, resp, &tags) + + assert.EqualValues(t, 1, len(tags)) + assert.Equal(t, "v1.1", tags[0].Name) + assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA) + assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/commit/65f1bf27bc3bf70f64657658635e66094edbcb4d"), tags[0].Commit.URL) + assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/archive/v1.1.zip"), tags[0].ZipballURL) + assert.Equal(t, path.Join(setting.AppSubURL, "/user2/repo1/archive/v1.1.tar.gz"), tags[0].TarballURL) +} diff --git a/models/repo_tag.go b/models/repo_tag.go new file mode 100644 index 0000000000..752eaf0282 --- /dev/null +++ b/models/repo_tag.go @@ -0,0 +1,24 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package models + +import ( + "code.gitea.io/git" +) + +// GetTagsByPath returns repo tags by it's path +func GetTagsByPath(path string) ([]*git.Tag, error) { + gitRepo, err := git.OpenRepository(path) + if err != nil { + return nil, err + } + + return gitRepo.GetTagInfos() +} + +// GetTags return repo's tags +func (repo *Repository) GetTags() ([]*git.Tag, error) { + return GetTagsByPath(repo.RepoPath()) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index c5a5488a81..89d277233f 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -513,6 +513,9 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("", repo.ListBranches) m.Get("/*", context.RepoRefByType(context.RepoRefBranch), repo.GetBranch) }, reqRepoReader(models.UnitTypeCode)) + m.Group("/tags", func() { + m.Get("", repo.ListTags) + }, reqRepoReader(models.UnitTypeCode)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go index 35416dea51..6569d21ab8 100644 --- a/routers/api/v1/convert/convert.go +++ b/routers/api/v1/convert/convert.go @@ -34,6 +34,22 @@ func ToBranch(repo *models.Repository, b *models.Branch, c *git.Commit) *api.Bra } } +// ToTag convert a tag to an api.Tag +func ToTag(repo *models.Repository, t *git.Tag) *api.Tag { + return &api.Tag{ + Name: t.Name, + Commit: struct { + SHA string `json:"sha"` + URL string `json:"url"` + }{ + SHA: t.ID.String(), + URL: util.URLJoin(repo.Link(), "commit", t.ID.String()), + }, + ZipballURL: util.URLJoin(repo.Link(), "archive", t.Name+".zip"), + TarballURL: util.URLJoin(repo.Link(), "archive", t.Name+".tar.gz"), + } +} + // ToCommit convert a commit to api.PayloadCommit func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit { authorUsername := "" diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go new file mode 100644 index 0000000000..505f5b40c9 --- /dev/null +++ b/routers/api/v1/repo/tag.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/routers/api/v1/convert" + + api "code.gitea.io/sdk/gitea" +) + +// 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 + // responses: + // "200": + // "$ref": "#/responses/TagList" + tags, err := ctx.Repo.Repository.GetTags() + if err != nil { + ctx.Error(500, "GetTags", err) + return + } + + apiTags := make([]*api.Tag, len(tags)) + for i := range tags { + apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i]) + } + + ctx.JSON(200, &apiTags) +} diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 5b930e295e..5ddd32d0b7 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -36,6 +36,13 @@ type swaggerResponseBranchList struct { Body []api.Branch `json:"body"` } +// TagList +// swagger:response TagList +type swaggerReponseTagList struct { + // in:body + Body []api.Tag `json:"body"` +} + // Reference // swagger:response Reference type swaggerResponseReference struct { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d45a3d6d1b..6ecaf861df 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4681,6 +4681,39 @@ } } }, + "/repos/{owner}/{repo}/tags": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "List a repository's tags", + "operationId": "repoListTags", + "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 + } + ], + "responses": { + "200": { + "$ref": "#/responses/TagList" + } + } + } + }, "/repos/{owner}/{repo}/times": { "get": { "produces": [ @@ -8419,6 +8452,39 @@ "type": "string", "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "Tag": { + "description": "Tag represents a repository tag", + "type": "object", + "properties": { + "commit": { + "type": "object", + "properties": { + "sha": { + "type": "string", + "x-go-name": "SHA" + }, + "url": { + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-name": "Commit" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "tarball_url": { + "type": "string", + "x-go-name": "TarballURL" + }, + "zipball_url": { + "type": "string", + "x-go-name": "ZipballURL" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "Team": { "description": "Team represents a team in an organization", "type": "object", @@ -8898,6 +8964,15 @@ } } }, + "TagList": { + "description": "TagList", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Tag" + } + } + }, "Team": { "description": "Team", "schema": { diff --git a/vendor/code.gitea.io/git/repo_tag.go b/vendor/code.gitea.io/git/repo_tag.go index 11f1f3da73..77867f46c1 100644 --- a/vendor/code.gitea.io/git/repo_tag.go +++ b/vendor/code.gitea.io/git/repo_tag.go @@ -103,26 +103,18 @@ func (repo *Repository) GetTagInfos() ([]*Tag, error) { } tagNames := strings.Split(stdout, "\n") - var tags []*Tag + var tags = make([]*Tag, 0, len(tagNames)) for _, tagName := range tagNames { tagName = strings.TrimSpace(tagName) if len(tagName) == 0 { continue } - commitID, err := NewCommand("rev-parse", tagName).RunInDir(repo.Path) + + tag, err := repo.GetTag(tagName) if err != nil { return nil, err } - commit, err := repo.GetCommit(commitID) - if err != nil { - return nil, err - } - tags = append(tags, &Tag{ - Name: tagName, - Message: commit.Message(), - Object: commit.ID, - Tagger: commit.Author, - }) + tags = append(tags, tag) } sortTagsByTime(tags) return tags, nil