Skip to content

Commit 3009e3f

Browse files
committed
Merge branch 'master' into api_user-settings
2 parents 5ed38ae + f7cd394 commit 3009e3f

File tree

27 files changed

+1598
-13
lines changed

27 files changed

+1598
-13
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ require (
5959
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
6060
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
6161
github.com/hashicorp/go-version v1.3.1
62+
github.com/hashicorp/golang-lru v0.5.1
6263
github.com/huandu/xstrings v1.3.2
6364
github.com/issue9/identicon v1.2.0
6465
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,7 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
593593
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
594594
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
595595
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
596+
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
596597
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
597598
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
598599
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=

integrations/api_repo_tags_test.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package integrations
66

77
import (
8+
"fmt"
89
"net/http"
910
"testing"
1011

@@ -15,23 +16,53 @@ import (
1516
"github.com/stretchr/testify/assert"
1617
)
1718

18-
func TestAPIReposGetTags(t *testing.T) {
19+
func TestAPIRepoTags(t *testing.T) {
1920
defer prepareTestEnv(t)()
2021
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
2122
// Login as User2.
2223
session := loginUser(t, user.Name)
2324
token := getTokenForLoggedInUser(t, session)
2425

25-
req := NewRequestf(t, "GET", "/api/v1/repos/%s/repo1/tags?token="+token, user.Name)
26+
repoName := "repo1"
27+
28+
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/tags?token=%s", user.Name, repoName, token)
2629
resp := session.MakeRequest(t, req, http.StatusOK)
2730

2831
var tags []*api.Tag
2932
DecodeJSON(t, resp, &tags)
3033

3134
assert.Len(t, tags, 1)
3235
assert.Equal(t, "v1.1", tags[0].Name)
36+
assert.Equal(t, "Initial commit", tags[0].Message)
3337
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.SHA)
3438
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/git/commits/65f1bf27bc3bf70f64657658635e66094edbcb4d", tags[0].Commit.URL)
3539
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.zip", tags[0].ZipballURL)
3640
assert.Equal(t, setting.AppURL+"user2/repo1/archive/v1.1.tar.gz", tags[0].TarballURL)
41+
42+
newTag := createNewTagUsingAPI(t, session, token, user.Name, repoName, "awesome-tag", "", "nice!\nand some text")
43+
resp = session.MakeRequest(t, req, http.StatusOK)
44+
DecodeJSON(t, resp, &tags)
45+
assert.Len(t, tags, 2)
46+
for _, tag := range tags {
47+
if tag.Name != "v1.1" {
48+
assert.EqualValues(t, newTag.Name, tag.Name)
49+
assert.EqualValues(t, newTag.Message, tag.Message)
50+
assert.EqualValues(t, "nice!\nand some text", tag.Message)
51+
assert.EqualValues(t, newTag.Commit.SHA, tag.Commit.SHA)
52+
}
53+
}
54+
}
55+
56+
func createNewTagUsingAPI(t *testing.T, session *TestSession, token string, ownerName, repoName, name, target, msg string) *api.Tag {
57+
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/tags?token=%s", ownerName, repoName, token)
58+
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateTagOption{
59+
TagName: name,
60+
Message: msg,
61+
Target: target,
62+
})
63+
resp := session.MakeRequest(t, req, http.StatusCreated)
64+
65+
var respObj api.Tag
66+
DecodeJSON(t, resp, &respObj)
67+
return &respObj
3768
}

integrations/api_repo_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,31 @@ func TestAPIRepoTransfer(t *testing.T) {
494494
repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository)
495495
_ = models.DeleteRepository(user, repo.OwnerID, repo.ID)
496496
}
497+
498+
func TestAPIRepoGetReviewers(t *testing.T) {
499+
defer prepareTestEnv(t)()
500+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
501+
session := loginUser(t, user.Name)
502+
token := getTokenForLoggedInUser(t, session)
503+
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
504+
505+
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token)
506+
resp := session.MakeRequest(t, req, http.StatusOK)
507+
var reviewers []*api.User
508+
DecodeJSON(t, resp, &reviewers)
509+
assert.Len(t, reviewers, 4)
510+
}
511+
512+
func TestAPIRepoGetAssignees(t *testing.T) {
513+
defer prepareTestEnv(t)()
514+
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
515+
session := loginUser(t, user.Name)
516+
token := getTokenForLoggedInUser(t, session)
517+
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
518+
519+
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token)
520+
resp := session.MakeRequest(t, req, http.StatusOK)
521+
var assignees []*api.User
522+
DecodeJSON(t, resp, &assignees)
523+
assert.Len(t, assignees, 1)
524+
}

modules/convert/convert.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package convert
88
import (
99
"fmt"
1010
"strconv"
11+
"strings"
1112
"time"
1213

1314
"code.gitea.io/gitea/models"
@@ -135,6 +136,7 @@ func ToBranchProtection(bp *models.ProtectedBranch) *api.BranchProtection {
135136
func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
136137
return &api.Tag{
137138
Name: t.Name,
139+
Message: strings.TrimSpace(t.Message),
138140
ID: t.ID.String(),
139141
Commit: ToCommitMeta(repo, t),
140142
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),

modules/convert/user.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@ func ToUser(user, doer *models.User) *api.User {
2525
return toUser(user, signed, authed)
2626
}
2727

28+
// ToUsers convert list of models.User to list of api.User
29+
func ToUsers(doer *models.User, users []*models.User) []*api.User {
30+
result := make([]*api.User, len(users))
31+
for i := range users {
32+
result[i] = ToUser(users[i], doer)
33+
}
34+
return result
35+
}
36+
2837
// ToUserWithAccessMode convert models.User to api.User
2938
// AccessMode is not none show add some more information
3039
func ToUserWithAccessMode(user *models.User, accessMode models.AccessMode) *api.User {

modules/highlight/highlight.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package highlight
88
import (
99
"bufio"
1010
"bytes"
11+
"fmt"
1112
gohtml "html"
1213
"path/filepath"
1314
"strings"
@@ -20,6 +21,7 @@ import (
2021
"github.com/alecthomas/chroma/formatters/html"
2122
"github.com/alecthomas/chroma/lexers"
2223
"github.com/alecthomas/chroma/styles"
24+
lru "github.com/hashicorp/golang-lru"
2325
)
2426

2527
// don't index files larger than this many bytes for performance purposes
@@ -30,6 +32,8 @@ var (
3032
highlightMapping = map[string]string{}
3133

3234
once sync.Once
35+
36+
cache *lru.ARCCache
3337
)
3438

3539
// NewContext loads custom highlight map from local config
@@ -39,6 +43,13 @@ func NewContext() {
3943
for i := range keys {
4044
highlightMapping[keys[i].Name()] = keys[i].Value()
4145
}
46+
47+
// The size 512 is simply a conservative rule of thumb
48+
c, err := lru.NewARC(512)
49+
if err != nil {
50+
panic(fmt.Sprintf("failed to initialize LRU cache for highlighter: %s", err))
51+
}
52+
cache = c
4253
})
4354
}
4455

@@ -73,11 +84,18 @@ func Code(fileName, code string) string {
7384
lexer = lexers.Get(val)
7485
}
7586

87+
if lexer == nil {
88+
if l, ok := cache.Get(fileName); ok {
89+
lexer = l.(chroma.Lexer)
90+
}
91+
}
92+
7693
if lexer == nil {
7794
lexer = lexers.Match(fileName)
7895
if lexer == nil {
7996
lexer = lexers.Fallback
8097
}
98+
cache.Add(fileName, lexer)
8199
}
82100

83101
iterator, err := lexer.Tokenise(nil, string(code))

modules/structs/repo_tag.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package structs
77
// Tag represents a repository tag
88
type Tag struct {
99
Name string `json:"name"`
10+
Message string `json:"message"`
1011
ID string `json:"id"`
1112
Commit *CommitMeta `json:"commit"`
1213
ZipballURL string `json:"zipball_url"`
@@ -30,3 +31,11 @@ type AnnotatedTagObject struct {
3031
URL string `json:"url"`
3132
SHA string `json:"sha"`
3233
}
34+
35+
// CreateTagOption options when creating a tag
36+
type CreateTagOption struct {
37+
// required: true
38+
TagName string `json:"tag_name" binding:"Required"`
39+
Message string `json:"message"`
40+
Target string `json:"target"`
41+
}

routers/api/v1/api.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,8 @@ func Routes() *web.Route {
750750
Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator).
751751
Delete(reqAdmin(), repo.DeleteCollaborator)
752752
}, reqToken())
753+
m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees)
754+
m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers)
753755
m.Group("/teams", func() {
754756
m.Get("", reqAnyRepoReader(), repo.ListTeams)
755757
m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam).
@@ -777,6 +779,7 @@ func Routes() *web.Route {
777779
}, reqToken(), reqAdmin())
778780
m.Group("/tags", func() {
779781
m.Get("", repo.ListTags)
782+
m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateTagOption{}), repo.CreateTag)
780783
m.Delete("/{tag}", repo.DeleteTag)
781784
}, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true))
782785
m.Group("/keys", func() {

routers/api/v1/notify/repo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
6565
// - name: all
6666
// in: query
6767
// description: If true, show notifications marked as read. Default value is false
68-
// type: string
68+
// type: boolean
6969
// - name: status-types
7070
// in: query
7171
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned"

routers/api/v1/notify/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func ListNotifications(ctx *context.APIContext) {
2727
// - name: all
2828
// in: query
2929
// description: If true, show notifications marked as read. Default value is false
30-
// type: string
30+
// type: boolean
3131
// - name: status-types
3232
// in: query
3333
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned."

routers/api/v1/repo/collaborators.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,3 +221,63 @@ func DeleteCollaborator(ctx *context.APIContext) {
221221
}
222222
ctx.Status(http.StatusNoContent)
223223
}
224+
225+
// GetReviewers return all users that can be requested to review in this repo
226+
func GetReviewers(ctx *context.APIContext) {
227+
// swagger:operation GET /repos/{owner}/{repo}/reviewers repository repoGetReviewers
228+
// ---
229+
// summary: Return all users that can be requested to review in this repo
230+
// produces:
231+
// - application/json
232+
// parameters:
233+
// - name: owner
234+
// in: path
235+
// description: owner of the repo
236+
// type: string
237+
// required: true
238+
// - name: repo
239+
// in: path
240+
// description: name of the repo
241+
// type: string
242+
// required: true
243+
// responses:
244+
// "200":
245+
// "$ref": "#/responses/UserList"
246+
247+
reviewers, err := ctx.Repo.Repository.GetReviewers(ctx.User.ID, 0)
248+
if err != nil {
249+
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
250+
return
251+
}
252+
ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, reviewers))
253+
}
254+
255+
// GetAssignees return all users that have write access and can be assigned to issues
256+
func GetAssignees(ctx *context.APIContext) {
257+
// swagger:operation GET /repos/{owner}/{repo}/assignees repository repoGetAssignees
258+
// ---
259+
// summary: Return all users that have write access and can be assigned to issues
260+
// produces:
261+
// - application/json
262+
// parameters:
263+
// - name: owner
264+
// in: path
265+
// description: owner of the repo
266+
// type: string
267+
// required: true
268+
// - name: repo
269+
// in: path
270+
// description: name of the repo
271+
// type: string
272+
// required: true
273+
// responses:
274+
// "200":
275+
// "$ref": "#/responses/UserList"
276+
277+
assignees, err := ctx.Repo.Repository.GetAssignees()
278+
if err != nil {
279+
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
280+
return
281+
}
282+
ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, assignees))
283+
}

routers/api/v1/repo/tag.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ package repo
66

77
import (
88
"errors"
9+
"fmt"
910
"net/http"
1011

1112
"code.gitea.io/gitea/models"
1213
"code.gitea.io/gitea/modules/context"
1314
"code.gitea.io/gitea/modules/convert"
1415
api "code.gitea.io/gitea/modules/structs"
16+
"code.gitea.io/gitea/modules/web"
1517
"code.gitea.io/gitea/routers/api/v1/utils"
1618
releaseservice "code.gitea.io/gitea/services/release"
1719
)
@@ -160,3 +162,62 @@ func DeleteTag(ctx *context.APIContext) {
160162

161163
ctx.Status(http.StatusNoContent)
162164
}
165+
166+
// CreateTag create a new git tag in a repository
167+
func CreateTag(ctx *context.APIContext) {
168+
// swagger:operation POST /repos/{owner}/{repo}/tags repository repoCreateTag
169+
// ---
170+
// summary: Create a new git tag in a repository
171+
// produces:
172+
// - application/json
173+
// parameters:
174+
// - name: owner
175+
// in: path
176+
// description: owner of the repo
177+
// type: string
178+
// required: true
179+
// - name: repo
180+
// in: path
181+
// description: name of the repo
182+
// type: string
183+
// required: true
184+
// - name: body
185+
// in: body
186+
// schema:
187+
// "$ref": "#/definitions/CreateTagOption"
188+
// responses:
189+
// "200":
190+
// "$ref": "#/responses/AnnotatedTag"
191+
// "404":
192+
// "$ref": "#/responses/notFound"
193+
// "409":
194+
// "$ref": "#/responses/conflict"
195+
form := web.GetForm(ctx).(*api.CreateTagOption)
196+
197+
// If target is not provided use default branch
198+
if len(form.Target) == 0 {
199+
form.Target = ctx.Repo.Repository.DefaultBranch
200+
}
201+
202+
commit, err := ctx.Repo.GitRepo.GetCommit(form.Target)
203+
if err != nil {
204+
ctx.Error(http.StatusNotFound, "target not found", fmt.Errorf("target not found: %v", err))
205+
return
206+
}
207+
208+
if err := releaseservice.CreateNewTag(ctx.User, ctx.Repo.Repository, commit.ID.String(), form.TagName, form.Message); err != nil {
209+
if models.IsErrTagAlreadyExists(err) {
210+
ctx.Error(http.StatusConflict, "tag exist", err)
211+
return
212+
}
213+
ctx.InternalServerError(err)
214+
return
215+
}
216+
217+
tag, err := ctx.Repo.GitRepo.GetTag(form.TagName)
218+
if err != nil {
219+
ctx.InternalServerError(err)
220+
return
221+
}
222+
ctx.JSON(http.StatusCreated, convert.ToTag(ctx.Repo.Repository, tag))
223+
}

0 commit comments

Comments
 (0)