mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'master' into fix-6409
This commit is contained in:
@@ -45,6 +45,11 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
return
|
||||
}
|
||||
|
||||
visibility := api.VisibleTypePublic
|
||||
if form.Visibility != "" {
|
||||
visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
|
||||
org := &models.User{
|
||||
Name: form.UserName,
|
||||
FullName: form.FullName,
|
||||
@@ -53,7 +58,9 @@ func CreateOrg(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
Location: form.Location,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: visibility,
|
||||
}
|
||||
|
||||
if err := models.CreateOrganization(org, u); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
models.IsErrNameReserved(err) ||
|
||||
|
||||
+29
-8
@@ -74,7 +74,8 @@ import (
|
||||
"code.gitea.io/gitea/routers/api/v1/user"
|
||||
|
||||
"github.com/go-macaron/binding"
|
||||
"gopkg.in/macaron.v1"
|
||||
"github.com/go-macaron/cors"
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
)
|
||||
|
||||
func sudo() macaron.Handler {
|
||||
@@ -500,6 +501,12 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/swagger", misc.Swagger) //Render V1 by default
|
||||
}
|
||||
|
||||
var handlers []macaron.Handler
|
||||
if setting.EnableCORS {
|
||||
handlers = append(handlers, cors.CORS(setting.CORSConfig))
|
||||
}
|
||||
handlers = append(handlers, securityHeaders(), context.APIContexter(), sudo())
|
||||
|
||||
m.Group("/v1", func() {
|
||||
// Miscellaneous
|
||||
if setting.API.EnableSwagger {
|
||||
@@ -601,7 +608,8 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
|
||||
m.Group("/:username/:reponame", func() {
|
||||
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
|
||||
Delete(reqToken(), reqOwner(), repo.Delete)
|
||||
Delete(reqToken(), reqOwner(), repo.Delete).
|
||||
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(repo.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||
@@ -743,6 +751,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/commits/:ref", func() {
|
||||
// TODO: Add m.Get("") for single commit (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
|
||||
m.Get("/status", repo.GetCombinedCommitStatusByRef)
|
||||
m.Get("/statuses", repo.GetCommitStatusesByRef)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
@@ -754,9 +763,11 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Get("/refs/*", repo.GetGitRefs)
|
||||
m.Get("/trees/:sha", context.RepoRef(), repo.GetTree)
|
||||
m.Get("/blobs/:sha", context.RepoRef(), repo.GetBlob)
|
||||
m.Get("/tags/:sha", context.RepoRef(), repo.GetTag)
|
||||
}, reqRepoReader(models.UnitTypeCode))
|
||||
m.Group("/contents", func() {
|
||||
m.Get("/*", repo.GetFileContents)
|
||||
m.Get("", repo.GetContentsList)
|
||||
m.Get("/*", repo.GetContents)
|
||||
m.Group("/*", func() {
|
||||
m.Post("", bind(api.CreateFileOptions{}), repo.CreateFile)
|
||||
m.Put("", bind(api.UpdateFileOptions{}), repo.UpdateFile)
|
||||
@@ -787,14 +798,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
Delete(reqToken(), reqOrgMembership(), org.ConcealMember)
|
||||
})
|
||||
m.Combo("/teams", reqToken(), reqOrgMembership()).Get(org.ListTeams).
|
||||
Post(bind(api.CreateTeamOption{}), org.CreateTeam)
|
||||
Post(reqOrgOwnership(), bind(api.CreateTeamOption{}), org.CreateTeam)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(org.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), org.CreateHook)
|
||||
m.Combo("/:id").Get(org.GetHook).
|
||||
Patch(reqOrgOwnership(), bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(reqOrgOwnership(), org.DeleteHook)
|
||||
}, reqToken(), reqOrgMembership())
|
||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(org.DeleteHook)
|
||||
}, reqToken(), reqOrgOwnership())
|
||||
}, orgAssignment(true))
|
||||
m.Group("/teams/:teamid", func() {
|
||||
m.Combo("").Get(org.GetTeam).
|
||||
@@ -841,5 +852,15 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||
m.Group("/topics", func() {
|
||||
m.Get("/search", repo.TopicSearch)
|
||||
})
|
||||
}, context.APIContexter(), sudo())
|
||||
}, handlers...)
|
||||
}
|
||||
|
||||
func securityHeaders() macaron.Handler {
|
||||
return func(ctx *macaron.Context) {
|
||||
ctx.Resp.Before(func(w macaron.ResponseWriter) {
|
||||
// CORB: https://www.chromium.org/Home/chromium-security/corb-for-developers
|
||||
// http://stackoverflow.com/a/3146618/244009
|
||||
w.Header().Set("x-content-type-options", "nosniff")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
@@ -26,7 +27,7 @@ func ToEmail(email *models.EmailAddress) *api.Email {
|
||||
}
|
||||
}
|
||||
|
||||
// ToBranch convert a commit and branch to an api.Branch
|
||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch {
|
||||
return &api.Branch{
|
||||
Name: b.Name,
|
||||
@@ -34,23 +35,18 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit) *api.Branch
|
||||
}
|
||||
}
|
||||
|
||||
// ToTag convert a tag to an api.Tag
|
||||
// ToTag convert a git.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"),
|
||||
Name: t.Name,
|
||||
ID: t.ID.String(),
|
||||
Commit: ToCommitMeta(repo, t),
|
||||
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
|
||||
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommit convert a commit to api.PayloadCommit
|
||||
// ToCommit convert a git.Commit to api.PayloadCommit
|
||||
func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
authorUsername := ""
|
||||
if author, err := models.GetUserByEmail(c.Author.Email); err == nil {
|
||||
@@ -66,17 +62,10 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
log.Error("GetUserByEmail: %v", err)
|
||||
}
|
||||
|
||||
verif := models.ParseCommitWithSignature(c)
|
||||
var signature, payload string
|
||||
if c.Signature != nil {
|
||||
signature = c.Signature.Signature
|
||||
payload = c.Signature.Payload
|
||||
}
|
||||
|
||||
return &api.PayloadCommit{
|
||||
ID: c.ID.String(),
|
||||
Message: c.Message(),
|
||||
URL: util.URLJoin(repo.Link(), "commit", c.ID.String()),
|
||||
URL: util.URLJoin(repo.HTMLURL(), "commit", c.ID.String()),
|
||||
Author: &api.PayloadUser{
|
||||
Name: c.Author.Name,
|
||||
Email: c.Author.Email,
|
||||
@@ -87,13 +76,24 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
|
||||
Email: c.Committer.Email,
|
||||
UserName: committerUsername,
|
||||
},
|
||||
Timestamp: c.Author.When,
|
||||
Verification: &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
Signature: signature,
|
||||
Payload: payload,
|
||||
},
|
||||
Timestamp: c.Author.When,
|
||||
Verification: ToVerification(c),
|
||||
}
|
||||
}
|
||||
|
||||
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
|
||||
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
|
||||
verif := models.ParseCommitWithSignature(c)
|
||||
var signature, payload string
|
||||
if c.Signature != nil {
|
||||
signature = c.Signature.Signature
|
||||
payload = c.Signature.Payload
|
||||
}
|
||||
return &api.PayloadCommitVerification{
|
||||
Verified: verif.Verified,
|
||||
Reason: verif.Reason,
|
||||
Signature: signature,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ func ToOrganization(org *models.User) *api.Organization {
|
||||
Description: org.Description,
|
||||
Website: org.Website,
|
||||
Location: org.Location,
|
||||
Visibility: org.Visibility.String(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,9 +237,53 @@ func ToUser(user *models.User, signed, admin bool) *api.User {
|
||||
AvatarURL: user.AvatarLink(),
|
||||
FullName: markup.Sanitize(user.FullName),
|
||||
IsAdmin: user.IsAdmin,
|
||||
LastLogin: user.LastLoginUnix.AsTime(),
|
||||
Created: user.CreatedUnix.AsTime(),
|
||||
}
|
||||
if signed && (!user.KeepEmailPrivate || admin) {
|
||||
result.Email = user.Email
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ToAnnotatedTag convert git.Tag to api.AnnotatedTag
|
||||
func ToAnnotatedTag(repo *models.Repository, t *git.Tag, c *git.Commit) *api.AnnotatedTag {
|
||||
return &api.AnnotatedTag{
|
||||
Tag: t.Name,
|
||||
SHA: t.ID.String(),
|
||||
Object: ToAnnotatedTagObject(repo, c),
|
||||
Message: t.Message,
|
||||
URL: util.URLJoin(repo.APIURL(), "git/tags", t.ID.String()),
|
||||
Tagger: ToCommitUser(t.Tagger),
|
||||
Verification: ToVerification(c),
|
||||
}
|
||||
}
|
||||
|
||||
// ToAnnotatedTagObject convert a git.Commit to an api.AnnotatedTagObject
|
||||
func ToAnnotatedTagObject(repo *models.Repository, commit *git.Commit) *api.AnnotatedTagObject {
|
||||
return &api.AnnotatedTagObject{
|
||||
SHA: commit.ID.String(),
|
||||
Type: string(git.ObjectCommit),
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", commit.ID.String()),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommitUser convert a git.Signature to an api.CommitUser
|
||||
func ToCommitUser(sig *git.Signature) *api.CommitUser {
|
||||
return &api.CommitUser{
|
||||
Identity: api.Identity{
|
||||
Name: sig.Name,
|
||||
Email: sig.Email,
|
||||
},
|
||||
Date: sig.When.UTC().Format(time.RFC3339),
|
||||
}
|
||||
}
|
||||
|
||||
// ToCommitMeta convert a git.Tag to an api.CommitMeta
|
||||
func ToCommitMeta(repo *models.Repository, tag *git.Tag) *api.CommitMeta {
|
||||
return &api.CommitMeta{
|
||||
SHA: tag.Object.String(),
|
||||
// TODO: Add the /commits API endpoint and use it here (https://developer.github.com/v3/repos/commits/#get-a-single-commit)
|
||||
URL: util.URLJoin(repo.APIURL(), "git/commits", tag.ID.String()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
@@ -42,7 +43,7 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
||||
}
|
||||
|
||||
if len(form.Text) == 0 {
|
||||
ctx.Write([]byte(""))
|
||||
_, _ = ctx.Write([]byte(""))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,12 +64,24 @@ func Markdown(ctx *context.APIContext, form api.MarkdownOption) {
|
||||
meta = ctx.Repo.Repository.ComposeMetas()
|
||||
}
|
||||
if form.Wiki {
|
||||
ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
|
||||
_, err := ctx.Write([]byte(markdown.RenderWiki(md, urlPrefix, meta)))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
ctx.Write(markdown.Render(md, urlPrefix, meta))
|
||||
_, err := ctx.Write(markdown.Render(md, urlPrefix, meta))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
default:
|
||||
ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))
|
||||
_, err := ctx.Write(markdown.RenderRaw([]byte(form.Text), "", false))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,5 +111,9 @@ func MarkdownRaw(ctx *context.APIContext) {
|
||||
ctx.Error(422, "", err)
|
||||
return
|
||||
}
|
||||
ctx.Write(markdown.RenderRaw(body, "", false))
|
||||
_, err = ctx.Write(markdown.RenderRaw(body, "", false))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,11 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
return
|
||||
}
|
||||
|
||||
visibility := api.VisibleTypePublic
|
||||
if form.Visibility != "" {
|
||||
visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
|
||||
org := &models.User{
|
||||
Name: form.UserName,
|
||||
FullName: form.FullName,
|
||||
@@ -98,6 +103,7 @@ func Create(ctx *context.APIContext, form api.CreateOrgOption) {
|
||||
Location: form.Location,
|
||||
IsActive: true,
|
||||
Type: models.UserTypeOrganization,
|
||||
Visibility: visibility,
|
||||
}
|
||||
if err := models.CreateOrganization(org, ctx.User); err != nil {
|
||||
if models.IsErrUserAlreadyExist(err) ||
|
||||
@@ -153,6 +159,7 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EditOrgOption"
|
||||
// responses:
|
||||
@@ -163,8 +170,11 @@ func Edit(ctx *context.APIContext, form api.EditOrgOption) {
|
||||
org.Description = form.Description
|
||||
org.Website = form.Website
|
||||
org.Location = form.Location
|
||||
if err := models.UpdateUserCols(org, "full_name", "description", "website", "location"); err != nil {
|
||||
ctx.Error(500, "UpdateUser", err)
|
||||
if form.Visibility != "" {
|
||||
org.Visibility = api.VisibilityModes[form.Visibility]
|
||||
}
|
||||
if err := models.UpdateUserCols(org, "full_name", "description", "website", "location", "visibility"); err != nil {
|
||||
ctx.Error(500, "EditOrganization", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+58
-14
@@ -181,7 +181,7 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'content' must be base64 encoded\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'sha' is the SHA for the file that already exists\n\n 'new_branch' (optional) will make a new branch from 'branch' before creating the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/CreateFileOptions"
|
||||
// responses:
|
||||
@@ -204,6 +204,11 @@ func CreateFile(ctx *context.APIContext, apiOpts api.CreateFileOptions) {
|
||||
Email: apiOpts.Author.Email,
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.add", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := createOrUpdateFile(ctx, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateFile", err)
|
||||
} else {
|
||||
@@ -238,7 +243,7 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'content' must be base64 encoded\n\n 'sha' is the SHA for the file that already exists\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'new_branch' (optional) will make a new branch from 'branch' before updating the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateFileOptions"
|
||||
// responses:
|
||||
@@ -264,6 +269,10 @@ func UpdateFile(ctx *context.APIContext, apiOpts api.UpdateFileOptions) {
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.update", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := createOrUpdateFile(ctx, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
|
||||
} else {
|
||||
@@ -316,7 +325,7 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "'sha' is the SHA for the file to be deleted\n\n 'author' and 'committer' are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)\n\n If 'branch' is not given, default branch will be used\n\n 'new_branch' (optional) will make a new branch from 'branch' before deleting the file"
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/DeleteFileOptions"
|
||||
// responses:
|
||||
@@ -346,6 +355,10 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
},
|
||||
}
|
||||
|
||||
if opts.Message == "" {
|
||||
opts.Message = ctx.Tr("repo.editor.delete", opts.TreePath)
|
||||
}
|
||||
|
||||
if fileResponse, err := repofiles.DeleteRepoFile(ctx.Repo.Repository, ctx.User, opts); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "DeleteFile", err)
|
||||
} else {
|
||||
@@ -353,11 +366,11 @@ func DeleteFile(ctx *context.APIContext, apiOpts api.DeleteFileOptions) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetFileContents Get the contents of a fle in a repository
|
||||
func GetFileContents(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetFileContents
|
||||
// GetContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
|
||||
func GetContents(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetContents
|
||||
// ---
|
||||
// summary: Gets the contents of a file or directory in a repository
|
||||
// summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
@@ -373,20 +386,20 @@ func GetFileContents(ctx *context.APIContext) {
|
||||
// required: true
|
||||
// - name: filepath
|
||||
// in: path
|
||||
// description: path of the file to delete
|
||||
// description: path of the dir, file, symlink or submodule in the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: ref
|
||||
// in: query
|
||||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||
// required: false
|
||||
// type: string
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/FileContentResponse"
|
||||
// "$ref": "#/responses/ContentsResponse"
|
||||
|
||||
if !CanReadFiles(ctx.Repo) {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFileContents", models.ErrUserDoesNotHaveAccessToRepo{
|
||||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", models.ErrUserDoesNotHaveAccessToRepo{
|
||||
UserID: ctx.User.ID,
|
||||
RepoName: ctx.Repo.Repository.LowerName,
|
||||
})
|
||||
@@ -396,9 +409,40 @@ func GetFileContents(ctx *context.APIContext) {
|
||||
treePath := ctx.Params("*")
|
||||
ref := ctx.QueryTrim("ref")
|
||||
|
||||
if fileContents, err := repofiles.GetFileContents(ctx.Repo.Repository, treePath, ref); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFileContents", err)
|
||||
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, treePath, ref); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err)
|
||||
} else {
|
||||
ctx.JSON(http.StatusOK, fileContents)
|
||||
ctx.JSON(http.StatusOK, fileList)
|
||||
}
|
||||
}
|
||||
|
||||
// GetContentsList Get the metadata of all the entries of the root dir
|
||||
func GetContentsList(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/contents repository repoGetContentsList
|
||||
// ---
|
||||
// summary: Gets the metadata of all the entries of the root dir
|
||||
// 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: ref
|
||||
// in: query
|
||||
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||
// type: string
|
||||
// required: false
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ContentsListResponse"
|
||||
|
||||
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface
|
||||
GetContents(ctx)
|
||||
}
|
||||
|
||||
@@ -100,8 +100,7 @@ func getGitRefsInternal(ctx *context.APIContext, filter string) {
|
||||
Object: &api.GitObject{
|
||||
SHA: refs[i].Object.String(),
|
||||
Type: refs[i].Type,
|
||||
// TODO: Add commit/tag info URL
|
||||
//URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
|
||||
URL: ctx.Repo.Repository.APIURL() + "/git/" + refs[i].Type + "s/" + refs[i].Object.String(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,9 +125,10 @@ func CreateLabel(ctx *context.APIContext, form api.CreateLabelOption) {
|
||||
// "201":
|
||||
// "$ref": "#/responses/Label"
|
||||
label := &models.Label{
|
||||
Name: form.Name,
|
||||
Color: form.Color,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Name: form.Name,
|
||||
Color: form.Color,
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
Description: form.Description,
|
||||
}
|
||||
if err := models.NewLabel(label); err != nil {
|
||||
ctx.Error(500, "NewLabel", err)
|
||||
@@ -185,6 +186,9 @@ func EditLabel(ctx *context.APIContext, form api.EditLabelOption) {
|
||||
if form.Color != nil {
|
||||
label.Color = *form.Color
|
||||
}
|
||||
if form.Description != nil {
|
||||
label.Description = *form.Description
|
||||
}
|
||||
if err := models.UpdateLabel(label); err != nil {
|
||||
ctx.ServerError("UpdateLabel", err)
|
||||
return
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ListMilestones list all the opened milestones for a repository
|
||||
// ListMilestones list milestones for a repository
|
||||
func ListMilestones(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/milestones issue issueGetMilestonesList
|
||||
// ---
|
||||
@@ -32,10 +32,14 @@ func ListMilestones(ctx *context.APIContext) {
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: state
|
||||
// in: query
|
||||
// description: Milestone state, Recognised values are open, closed and all. Defaults to "open"
|
||||
// type: string
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/MilestoneList"
|
||||
milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID)
|
||||
milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")))
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetMilestonesByRepoID", err)
|
||||
return
|
||||
|
||||
+26
-11
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/notification"
|
||||
"code.gitea.io/gitea/modules/pull"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
@@ -188,7 +189,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
)
|
||||
|
||||
// Get repo/branch information
|
||||
headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
@@ -240,7 +241,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
milestoneID = milestone.ID
|
||||
}
|
||||
|
||||
patch, err := headGitRepo.GetPatch(prInfo.MergeBase, headBranch)
|
||||
patch, err := headGitRepo.GetPatch(compareInfo.MergeBase, headBranch)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetPatch", err)
|
||||
return
|
||||
@@ -251,9 +252,15 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
deadlineUnix = util.TimeStamp(form.Deadline.Unix())
|
||||
}
|
||||
|
||||
maxIndex, err := models.GetMaxIndexOfIssue(repo.ID)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetPatch", err)
|
||||
return
|
||||
}
|
||||
|
||||
prIssue := &models.Issue{
|
||||
RepoID: repo.ID,
|
||||
Index: repo.NextIssueIndex(),
|
||||
Index: maxIndex + 1,
|
||||
Title: form.Title,
|
||||
PosterID: ctx.User.ID,
|
||||
Poster: ctx.User,
|
||||
@@ -271,7 +278,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
|
||||
BaseBranch: baseBranch,
|
||||
HeadRepo: headRepo,
|
||||
BaseRepo: repo,
|
||||
MergeBase: prInfo.MergeBase,
|
||||
MergeBase: compareInfo.MergeBase,
|
||||
Type: models.PullRequestGitea,
|
||||
}
|
||||
|
||||
@@ -347,7 +354,11 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||
return
|
||||
}
|
||||
|
||||
pr.LoadIssue()
|
||||
err = pr.LoadIssue()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
issue := pr.Issue
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
|
||||
@@ -541,7 +552,11 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
return
|
||||
}
|
||||
|
||||
pr.LoadIssue()
|
||||
err = pr.LoadIssue()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
pr.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
if ctx.IsSigned {
|
||||
@@ -581,7 +596,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
}
|
||||
|
||||
if err := pr.Merge(ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if err := pull.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Status(405)
|
||||
return
|
||||
@@ -594,7 +609,7 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
ctx.Status(200)
|
||||
}
|
||||
|
||||
func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.PullRequestInfo, string, string) {
|
||||
func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption) (*models.User, *models.Repository, *git.Repository, *git.CompareInfo, string, string) {
|
||||
baseRepo := ctx.Repo.Repository
|
||||
|
||||
// Get compared branches information
|
||||
@@ -706,11 +721,11 @@ func parseCompareInfo(ctx *context.APIContext, form api.CreatePullRequestOption)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
prInfo, err := headGitRepo.GetPullRequestInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
compareInfo, err := headGitRepo.GetCompareInfo(models.RepoPath(baseRepo.Owner.Name, baseRepo.Name), baseBranch, headBranch)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetPullRequestInfo", err)
|
||||
ctx.Error(500, "GetCompareInfo", err)
|
||||
return nil, nil, nil, nil, "", ""
|
||||
}
|
||||
|
||||
return headUser, headRepo, headGitRepo, prInfo, baseBranch, headBranch
|
||||
return headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch
|
||||
}
|
||||
|
||||
@@ -5,13 +5,12 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -177,20 +176,9 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// Check if the filetype is allowed by the settings
|
||||
fileType := http.DetectContentType(buf)
|
||||
|
||||
allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",")
|
||||
allowed := false
|
||||
for _, t := range allowedTypes {
|
||||
t := strings.Trim(t, " ")
|
||||
if t == "*/*" || t == fileType {
|
||||
allowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !allowed {
|
||||
ctx.Error(400, "DetectContentType", errors.New("File type is not allowed"))
|
||||
err = upload.VerifyAllowedContentType(buf, strings.Split(setting.AttachmentAllowedTypes, ","))
|
||||
if err != nil {
|
||||
ctx.Error(400, "DetectContentType", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+282
-36
@@ -56,6 +56,15 @@ func Search(ctx *context.APIContext) {
|
||||
// description: search only for repos that the user with the given id owns or contributes to
|
||||
// type: integer
|
||||
// format: int64
|
||||
// - name: starredBy
|
||||
// in: query
|
||||
// description: search only for repos that the user with the given id has starred
|
||||
// type: integer
|
||||
// format: int64
|
||||
// - name: private
|
||||
// in: query
|
||||
// description: include private repositories this user has access to (defaults to true)
|
||||
// type: boolean
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
@@ -96,6 +105,10 @@ func Search(ctx *context.APIContext) {
|
||||
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
|
||||
TopicOnly: ctx.QueryBool("topic"),
|
||||
Collaborate: util.OptionalBoolNone,
|
||||
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
|
||||
UserIsAdmin: ctx.IsUserSiteAdmin(),
|
||||
UserID: ctx.Data["SignedUserID"].(int64),
|
||||
StarredByID: ctx.QueryInt64("starredBy"),
|
||||
}
|
||||
|
||||
if ctx.QueryBool("exclusive") {
|
||||
@@ -140,42 +153,6 @@ func Search(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
var err error
|
||||
if opts.OwnerID > 0 {
|
||||
var repoOwner *models.User
|
||||
if ctx.User != nil && ctx.User.ID == opts.OwnerID {
|
||||
repoOwner = ctx.User
|
||||
} else {
|
||||
repoOwner, err = models.GetUserByID(opts.OwnerID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if repoOwner.IsOrganization() {
|
||||
opts.Collaborate = util.OptionalBoolFalse
|
||||
}
|
||||
|
||||
// Check visibility.
|
||||
if ctx.IsSigned {
|
||||
if ctx.User.ID == repoOwner.ID {
|
||||
opts.Private = true
|
||||
} else if repoOwner.IsOrganization() {
|
||||
opts.Private, err = repoOwner.IsOwnedBy(ctx.User.ID)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repos, count, err := models.SearchRepositoryByName(opts)
|
||||
if err != nil {
|
||||
ctx.JSON(500, api.SearchError{
|
||||
@@ -263,6 +240,10 @@ func Create(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "409":
|
||||
// description: The repository with the same name already exists.
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
if ctx.User.IsOrganization() {
|
||||
// Shouldn't reach this condition, but just in case.
|
||||
ctx.Error(422, "", "not allowed creating repository for organization")
|
||||
@@ -523,6 +504,271 @@ func GetByID(ctx *context.APIContext) {
|
||||
ctx.JSON(200, repo.APIFormat(perm.AccessMode))
|
||||
}
|
||||
|
||||
// Edit edit repository properties
|
||||
func Edit(ctx *context.APIContext, opts api.EditRepoOption) {
|
||||
// swagger:operation PATCH /repos/{owner}/{repo} repository repoEdit
|
||||
// ---
|
||||
// summary: Edit a repository's properties. Only fields that are set will be changed.
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo to edit
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo to edit
|
||||
// type: string
|
||||
// required: true
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// description: "Properties of a repo that you can edit"
|
||||
// schema:
|
||||
// "$ref": "#/definitions/EditRepoOption"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Repository"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
if err := updateBasicProperties(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := updateRepoUnits(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if opts.Archived != nil {
|
||||
if err := updateRepoArchivedState(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, ctx.Repo.Repository.APIFormat(ctx.Repo.AccessMode))
|
||||
}
|
||||
|
||||
// updateBasicProperties updates the basic properties of a repo: Name, Description, Website and Visibility
|
||||
func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
oldRepoName := repo.Name
|
||||
newRepoName := repo.Name
|
||||
if opts.Name != nil {
|
||||
newRepoName = *opts.Name
|
||||
}
|
||||
// Check if repository name has been changed and not just a case change
|
||||
if repo.LowerName != strings.ToLower(newRepoName) {
|
||||
if err := models.ChangeRepositoryName(ctx.Repo.Owner, repo.Name, newRepoName); err != nil {
|
||||
switch {
|
||||
case models.IsErrRepoAlreadyExist(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err)
|
||||
case models.IsErrNameReserved(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is reserved [name: %s]", newRepoName), err)
|
||||
case models.IsErrNamePatternNotAllowed(err):
|
||||
ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name's pattern is not allowed [name: %s, pattern: %s]", newRepoName, err.(models.ErrNamePatternNotAllowed).Pattern), err)
|
||||
default:
|
||||
ctx.Error(http.StatusUnprocessableEntity, "ChangeRepositoryName", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err := models.NewRepoRedirect(ctx.Repo.Owner.ID, repo.ID, repo.Name, newRepoName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "NewRepoRedirect", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := models.RenameRepoAction(ctx.User, oldRepoName, repo); err != nil {
|
||||
log.Error("RenameRepoAction: %v", err)
|
||||
ctx.Error(http.StatusInternalServerError, "RenameRepoActions", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
|
||||
}
|
||||
// Update the name in the repo object for the response
|
||||
repo.Name = newRepoName
|
||||
repo.LowerName = strings.ToLower(newRepoName)
|
||||
|
||||
if opts.Description != nil {
|
||||
repo.Description = *opts.Description
|
||||
}
|
||||
|
||||
if opts.Website != nil {
|
||||
repo.Website = *opts.Website
|
||||
}
|
||||
|
||||
visibilityChanged := false
|
||||
if opts.Private != nil {
|
||||
// Visibility of forked repository is forced sync with base repository.
|
||||
if repo.IsFork {
|
||||
*opts.Private = repo.BaseRepo.IsPrivate
|
||||
}
|
||||
|
||||
visibilityChanged = repo.IsPrivate != *opts.Private
|
||||
// when ForcePrivate enabled, you could change public repo to private, but only admin users can change private to public
|
||||
if visibilityChanged && setting.Repository.ForcePrivate && !*opts.Private && !ctx.User.IsAdmin {
|
||||
err := fmt.Errorf("cannot change private repository to public")
|
||||
ctx.Error(http.StatusUnprocessableEntity, "Force Private enabled", err)
|
||||
return err
|
||||
}
|
||||
|
||||
repo.IsPrivate = *opts.Private
|
||||
}
|
||||
|
||||
if err := models.UpdateRepository(repo, visibilityChanged); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepository", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository basic settings updated: %s/%s", owner.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRepoUnits updates repo units: Issue settings, Wiki settings, PR settings
|
||||
func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
owner := ctx.Repo.Owner
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
var units []models.RepoUnit
|
||||
|
||||
for _, tp := range models.MustRepoUnits {
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: tp,
|
||||
Config: new(models.UnitConfig),
|
||||
})
|
||||
}
|
||||
|
||||
if opts.HasIssues != nil {
|
||||
if *opts.HasIssues {
|
||||
// We don't currently allow setting individual issue settings through the API,
|
||||
// only can enable/disable issues, so when enabling issues,
|
||||
// we either get the existing config which means it was already enabled,
|
||||
// or create a new config since it doesn't exist.
|
||||
unit, err := repo.GetUnit(models.UnitTypeIssues)
|
||||
var config *models.IssuesConfig
|
||||
if err != nil {
|
||||
// Unit type doesn't exist so we make a new config file with default values
|
||||
config = &models.IssuesConfig{
|
||||
EnableTimetracker: true,
|
||||
AllowOnlyContributorsToTrackTime: true,
|
||||
EnableDependencies: true,
|
||||
}
|
||||
} else {
|
||||
config = unit.IssuesConfig()
|
||||
}
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypeIssues,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.HasWiki != nil {
|
||||
if *opts.HasWiki {
|
||||
// We don't currently allow setting individual wiki settings through the API,
|
||||
// only can enable/disable the wiki, so when enabling the wiki,
|
||||
// we either get the existing config which means it was already enabled,
|
||||
// or create a new config since it doesn't exist.
|
||||
config := &models.UnitConfig{}
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypeWiki,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if opts.HasPullRequests != nil {
|
||||
if *opts.HasPullRequests {
|
||||
// We do allow setting individual PR settings through the API, so
|
||||
// we get the config settings and then set them
|
||||
// if those settings were provided in the opts.
|
||||
unit, err := repo.GetUnit(models.UnitTypePullRequests)
|
||||
var config *models.PullRequestsConfig
|
||||
if err != nil {
|
||||
// Unit type doesn't exist so we make a new config file with default values
|
||||
config = &models.PullRequestsConfig{
|
||||
IgnoreWhitespaceConflicts: false,
|
||||
AllowMerge: true,
|
||||
AllowRebase: true,
|
||||
AllowRebaseMerge: true,
|
||||
AllowSquash: true,
|
||||
}
|
||||
} else {
|
||||
config = unit.PullRequestsConfig()
|
||||
}
|
||||
|
||||
if opts.IgnoreWhitespaceConflicts != nil {
|
||||
config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts
|
||||
}
|
||||
if opts.AllowMerge != nil {
|
||||
config.AllowMerge = *opts.AllowMerge
|
||||
}
|
||||
if opts.AllowRebase != nil {
|
||||
config.AllowRebase = *opts.AllowRebase
|
||||
}
|
||||
if opts.AllowRebaseMerge != nil {
|
||||
config.AllowRebaseMerge = *opts.AllowRebaseMerge
|
||||
}
|
||||
if opts.AllowSquash != nil {
|
||||
config.AllowSquash = *opts.AllowSquash
|
||||
}
|
||||
|
||||
units = append(units, models.RepoUnit{
|
||||
RepoID: repo.ID,
|
||||
Type: models.UnitTypePullRequests,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if err := models.UpdateRepositoryUnits(repo, units); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("Repository advanced settings updated: %s/%s", owner.Name, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateRepoArchivedState updates repo's archive state
|
||||
func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
repo := ctx.Repo.Repository
|
||||
// archive / un-archive
|
||||
if opts.Archived != nil {
|
||||
if repo.IsMirror {
|
||||
err := fmt.Errorf("repo is a mirror, cannot archive/un-archive")
|
||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
return err
|
||||
}
|
||||
if *opts.Archived {
|
||||
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||
log.Error("Tried to archive a repo: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("Repository was archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
} else {
|
||||
if err := repo.SetArchiveRepoState(*opts.Archived); err != nil {
|
||||
log.Error("Tried to un-archive a repo: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "ArchiveRepoState", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("Repository was un-archived: %s/%s", ctx.Repo.Owner.Name, repo.Name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete one repository
|
||||
func Delete(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo} repository repoDelete
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// 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 (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRepoEdit(t *testing.T) {
|
||||
models.PrepareTestEnv(t)
|
||||
|
||||
ctx := test.MockContext(t, "user2/repo1")
|
||||
test.LoadRepo(t, ctx, 1)
|
||||
test.LoadUser(t, ctx, 2)
|
||||
ctx.Repo.Owner = ctx.User
|
||||
description := "new description"
|
||||
website := "http://wwww.newwebsite.com"
|
||||
private := true
|
||||
hasIssues := false
|
||||
hasWiki := false
|
||||
defaultBranch := "master"
|
||||
hasPullRequests := true
|
||||
ignoreWhitespaceConflicts := true
|
||||
allowMerge := false
|
||||
allowRebase := false
|
||||
allowRebaseMerge := false
|
||||
allowSquashMerge := false
|
||||
archived := true
|
||||
opts := api.EditRepoOption{
|
||||
Name: &ctx.Repo.Repository.Name,
|
||||
Description: &description,
|
||||
Website: &website,
|
||||
Private: &private,
|
||||
HasIssues: &hasIssues,
|
||||
HasWiki: &hasWiki,
|
||||
DefaultBranch: &defaultBranch,
|
||||
HasPullRequests: &hasPullRequests,
|
||||
IgnoreWhitespaceConflicts: &ignoreWhitespaceConflicts,
|
||||
AllowMerge: &allowMerge,
|
||||
AllowRebase: &allowRebase,
|
||||
AllowRebaseMerge: &allowRebaseMerge,
|
||||
AllowSquash: &allowSquashMerge,
|
||||
Archived: &archived,
|
||||
}
|
||||
|
||||
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||
ID: 1,
|
||||
}, models.Cond("name = ? AND is_archived = 1", *opts.Name))
|
||||
}
|
||||
|
||||
func TestRepoEditNameChange(t *testing.T) {
|
||||
models.PrepareTestEnv(t)
|
||||
|
||||
ctx := test.MockContext(t, "user2/repo1")
|
||||
test.LoadRepo(t, ctx, 1)
|
||||
test.LoadUser(t, ctx, 2)
|
||||
ctx.Repo.Owner = ctx.User
|
||||
name := "newname"
|
||||
opts := api.EditRepoOption{
|
||||
Name: &name,
|
||||
}
|
||||
|
||||
Edit(&context.APIContext{Context: ctx, Org: nil}, opts)
|
||||
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
|
||||
|
||||
models.AssertExistsAndLoadBean(t, &models.Repository{
|
||||
ID: 1,
|
||||
}, models.Cond("name = ?", opts.Name))
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -57,17 +58,12 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
|
||||
Description: form.Description,
|
||||
Context: form.Context,
|
||||
}
|
||||
if err := models.NewCommitStatus(ctx.Repo.Repository, ctx.User, sha, status); err != nil {
|
||||
ctx.Error(500, "NewCommitStatus", err)
|
||||
if err := repofiles.CreateCommitStatus(ctx.Repo.Repository, ctx.User, sha, status); err != nil {
|
||||
ctx.Error(500, "CreateCommitStatus", err)
|
||||
return
|
||||
}
|
||||
|
||||
newStatus, err := models.GetCommitStatus(ctx.Repo.Repository, sha, status)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetCommitStatus", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(201, newStatus.APIFormat())
|
||||
ctx.JSON(201, status.APIFormat())
|
||||
}
|
||||
|
||||
// GetCommitStatuses returns all statuses for any given commit hash
|
||||
@@ -140,6 +136,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||
statuses, err := models.GetCommitStatuses(repo, sha, page)
|
||||
if err != nil {
|
||||
ctx.Error(500, "GetCommitStatuses", fmt.Errorf("GetCommitStatuses[%s, %s, %d]: %v", repo.FullName(), sha, page, err))
|
||||
return
|
||||
}
|
||||
|
||||
apiStatuses := make([]*api.Status, 0, len(statuses))
|
||||
|
||||
@@ -7,6 +7,7 @@ package repo
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||
"net/http"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
@@ -45,3 +46,47 @@ func ListTags(ctx *context.APIContext) {
|
||||
|
||||
ctx.JSON(200, &apiTags)
|
||||
}
|
||||
|
||||
// GetTag get the tag of a repository.
|
||||
func GetTag(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/git/tags/{sha} repository GetTag
|
||||
// ---
|
||||
// summary: Gets the tag of 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: sha
|
||||
// in: path
|
||||
// description: sha of the tag
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/AnnotatedTag"
|
||||
|
||||
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, "GetTag", err)
|
||||
} else {
|
||||
commit, err := tag.Commit()
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusBadRequest, "GetTag", err)
|
||||
}
|
||||
ctx.JSON(http.StatusOK, convert.ToAnnotatedTag(ctx.Repo.Repository, tag, commit))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,8 @@ type swaggerParameterBodies struct {
|
||||
// in:body
|
||||
CreateRepoOption api.CreateRepoOption
|
||||
// in:body
|
||||
EditRepoOption api.EditRepoOption
|
||||
// in:body
|
||||
CreateForkOption api.CreateForkOption
|
||||
|
||||
// in:body
|
||||
|
||||
@@ -38,11 +38,25 @@ type swaggerResponseBranchList struct {
|
||||
|
||||
// TagList
|
||||
// swagger:response TagList
|
||||
type swaggerReponseTagList struct {
|
||||
type swaggerResponseTagList struct {
|
||||
// in:body
|
||||
Body []api.Tag `json:"body"`
|
||||
}
|
||||
|
||||
// Tag
|
||||
// swagger:response Tag
|
||||
type swaggerResponseTag struct {
|
||||
// in:body
|
||||
Body api.Tag `json:"body"`
|
||||
}
|
||||
|
||||
// AnnotatedTag
|
||||
// swagger:response AnnotatedTag
|
||||
type swaggerResponseAnnotatedTag struct {
|
||||
// in:body
|
||||
Body api.AnnotatedTag `json:"body"`
|
||||
}
|
||||
|
||||
// Reference
|
||||
// swagger:response Reference
|
||||
type swaggerResponseReference struct {
|
||||
@@ -183,11 +197,18 @@ type swaggerFileResponse struct {
|
||||
Body api.FileResponse `json:"body"`
|
||||
}
|
||||
|
||||
// FileContentResponse
|
||||
// swagger:response FileContentResponse
|
||||
type swaggerFileContentResponse struct {
|
||||
// ContentsResponse
|
||||
// swagger:response ContentsResponse
|
||||
type swaggerContentsResponse struct {
|
||||
//in: body
|
||||
Body api.FileContentResponse `json:"body"`
|
||||
Body api.ContentsResponse `json:"body"`
|
||||
}
|
||||
|
||||
// ContentsListResponse
|
||||
// swagger:response ContentsListResponse
|
||||
type swaggerContentsListResponse struct {
|
||||
// in:body
|
||||
Body []api.ContentsResponse `json:"body"`
|
||||
}
|
||||
|
||||
// FileDeleteResponse
|
||||
|
||||
@@ -36,6 +36,9 @@ func ListAccessTokens(ctx *context.APIContext) {
|
||||
|
||||
apiTokens := make([]*api.AccessToken, len(tokens))
|
||||
for i := range tokens {
|
||||
if tokens[i].Name == "drone" {
|
||||
tokens[i].Name = "drone-legacy-use-oauth2-instead"
|
||||
}
|
||||
apiTokens[i] = &api.AccessToken{
|
||||
ID: tokens[i].ID,
|
||||
Name: tokens[i].Name,
|
||||
@@ -76,14 +79,18 @@ func CreateAccessToken(ctx *context.APIContext, form api.CreateAccessTokenOption
|
||||
UID: ctx.User.ID,
|
||||
Name: form.Name,
|
||||
}
|
||||
if t.Name == "drone" {
|
||||
t.Name = "drone-legacy-use-oauth2-instead"
|
||||
}
|
||||
if err := models.NewAccessToken(t); err != nil {
|
||||
ctx.Error(500, "NewAccessToken", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(201, &api.AccessToken{
|
||||
Name: t.Name,
|
||||
Token: t.Token,
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Token: t.Token,
|
||||
ID: t.ID,
|
||||
TokenLastEight: t.TokenLastEight,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,9 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/routers/api/v1/convert"
|
||||
)
|
||||
|
||||
func composePublicGPGKeysAPILink() string {
|
||||
return setting.AppURL + "api/v1/user/gpg_keys/"
|
||||
}
|
||||
|
||||
func listGPGKeys(ctx *context.APIContext, uid int64) {
|
||||
keys, err := models.ListGPGKeys(uid)
|
||||
if err != nil {
|
||||
|
||||
@@ -98,6 +98,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
|
||||
URL: form.Config["url"],
|
||||
ContentType: models.ToHookContentType(form.Config["content_type"]),
|
||||
Secret: form.Config["secret"],
|
||||
HTTPMethod: "POST",
|
||||
HookEvent: &models.HookEvent{
|
||||
ChooseEvents: true,
|
||||
HookEvents: models.HookEvents{
|
||||
|
||||
Reference in New Issue
Block a user