From 4c0ec221faaed7ea6cea0ad5c19d3f79473a7c40 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sat, 18 Nov 2023 18:30:30 +0530 Subject: [PATCH 01/44] test: add 2 fixtures for project --- models/fixtures/project.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/models/fixtures/project.yml b/models/fixtures/project.yml index 1bf8030f6aa57..c07a4002a4d9c 100644 --- a/models/fixtures/project.yml +++ b/models/fixtures/project.yml @@ -45,3 +45,29 @@ type: 2 created_unix: 1688973000 updated_unix: 1688973000 + +# user projects +- + id: 5 + title: project on user2 + owner_id: 2 + repo_id: 0 + is_closed: false + creator_id: 2 + board_type: 1 + type: 1 + created_unix: 1688973000 + updated_unix: 1688973000 + +# org projects +- + id: 6 + title: project on org17 + owner_id: 17 + repo_id: 0 + is_closed: false + creator_id: 17 + board_type: 1 + type: 3 + created_unix: 1688973000 + updated_unix: 1688973000 From 6092b81563b5a7f3c9614512216ed4644c4c0fc4 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sat, 18 Nov 2023 18:31:43 +0530 Subject: [PATCH 02/44] refactor: add structs for project --- modules/structs/project.go | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 modules/structs/project.go diff --git a/modules/structs/project.go b/modules/structs/project.go new file mode 100644 index 0000000000000..e70659e5ceeed --- /dev/null +++ b/modules/structs/project.go @@ -0,0 +1,40 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +import "time" + +type NewProjectPayload struct { + // required:true + Title string `json:"title" binding:"Required"` + // required:true + BoardType uint8 `json:"board_type"` + // required:true + CardType uint8 `json:"card_type"` + Description string `json:"description"` +} + +type UpdateProjectPayload struct { + // required:true + Title string `json:"title" binding:"Required"` + Description string `json:"description"` +} + +type Project struct { + ID int64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + BoardType uint8 `json:"board_type"` + IsClosed bool `json:"is_closed"` + // swagger:strfmt date-time + Created time.Time `json:"created_at"` + // swagger:strfmt date-time + Updated time.Time `json:"updated_at"` + // swagger:strfmt date-time + Closed time.Time `json:"closed_at"` + + Repo *RepositoryMeta `json:"repository"` + Creator *User `json:"creator"` + Owner *User `json:"owner"` +} From b6f9d0518032ffd86b32b851f804aa29ed8e3617 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sat, 18 Nov 2023 18:32:22 +0530 Subject: [PATCH 03/44] feat: api for projects --- models/project/project.go | 21 +- routers/api/v1/api.go | 372 +++++++++++++++++++---- routers/api/v1/projects/project.go | 407 ++++++++++++++++++++++++++ routers/api/v1/swagger/project.go | 22 ++ services/convert/project.go | 66 +++++ templates/swagger/v1_json.tmpl | 338 +++++++++++++++++++++ tests/integration/api_project_test.go | 228 +++++++++++++++ 7 files changed, 1396 insertions(+), 58 deletions(-) create mode 100644 routers/api/v1/projects/project.go create mode 100644 routers/api/v1/swagger/project.go create mode 100644 services/convert/project.go create mode 100644 tests/integration/api_project_test.go diff --git a/models/project/project.go b/models/project/project.go index 3a1bfe1dbd3ff..670ae39f87c66 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -95,6 +95,7 @@ type Project struct { RepoID int64 `xorm:"INDEX"` Repo *repo_model.Repository `xorm:"-"` CreatorID int64 `xorm:"NOT NULL"` + Creator *user_model.User `xorm:"-"` IsClosed bool `xorm:"INDEX"` BoardType BoardType CardType CardType @@ -115,6 +116,14 @@ func (p *Project) LoadOwner(ctx context.Context) (err error) { return err } +func (p *Project) LoadCreator(ctx context.Context) (err error) { + if p.Creator != nil { + return nil + } + p.Creator, err = user_model.GetUserByID(ctx, p.CreatorID) + return err +} + func (p *Project) LoadRepo(ctx context.Context) (err error) { if p.RepoID == 0 || p.Repo != nil { return nil @@ -348,7 +357,11 @@ func updateRepositoryProjectCount(ctx context.Context, repoID int64) error { } // ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed -func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error { +func ChangeProjectStatusByRepoIDAndID( + ctx context.Context, + repoID, projectID int64, + isClosed bool, +) error { ctx, committer, err := db.TxContext(ctx) if err != nil { return err @@ -389,7 +402,11 @@ func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { p.IsClosed = isClosed p.ClosedDateUnix = timeutil.TimeStampNow() - count, err := db.GetEngine(ctx).ID(p.ID).Where("repo_id = ? AND is_closed = ?", p.RepoID, !isClosed).Cols("is_closed", "closed_date_unix").Update(p) + count, err := db.GetEngine(ctx). + ID(p.ID). + Where("repo_id = ? AND is_closed = ?", p.RepoID, !isClosed). + Cols("is_closed", "closed_date_unix"). + Update(p) if err != nil { return err } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index cadddb44c39ef..30b1c2a07fc13 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -88,6 +88,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/notify" "code.gitea.io/gitea/routers/api/v1/org" "code.gitea.io/gitea/routers/api/v1/packages" + "code.gitea.io/gitea/routers/api/v1/projects" "code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/settings" "code.gitea.io/gitea/routers/api/v1/user" @@ -231,7 +232,11 @@ func repoAssignment() func(ctx *context.APIContext) { func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() { - ctx.Error(http.StatusForbidden, "reqPackageAccess", "user should have specific permission or be a site admin") + ctx.Error( + http.StatusForbidden, + "reqPackageAccess", + "user should have specific permission or be a site admin", + ) return } } @@ -239,7 +244,9 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) // if a token is being used for auth, we check that it contains the required scope // if a token is not being used, reqToken will enforce other sign in methods -func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) { +func tokenRequiresScopes( + requiredScopeCategories ...auth_model.AccessTokenScopeCategory, +) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { // no scope required if len(requiredScopeCategories) == 0 { @@ -257,27 +264,46 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC // use the http method to determine the access level requiredScopeLevel := auth_model.Read - if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { + if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || + ctx.Req.Method == "DELETE" { requiredScopeLevel = auth_model.Write } // get the required scope for the given access level and category - requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...) + requiredScopes := auth_model.GetRequiredScopes( + requiredScopeLevel, + requiredScopeCategories...) // check if scope only applies to public resources publicOnly, err := scope.PublicOnly() if err != nil { - ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) + ctx.Error( + http.StatusForbidden, + "tokenRequiresScope", + "parsing public resource scope failed: "+err.Error(), + ) return } // this context is used by the middleware in the specific route - ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository) - ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization) + ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && + auth_model.ContainsCategory( + requiredScopeCategories, + auth_model.AccessTokenScopeCategoryRepository, + ) + ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && + auth_model.ContainsCategory( + requiredScopeCategories, + auth_model.AccessTokenScopeCategoryOrganization, + ) allow, err := scope.HasScope(requiredScopes...) if err != nil { - ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) + ctx.Error( + http.StatusForbidden, + "tokenRequiresScope", + "checking scope failed: "+err.Error(), + ) return } @@ -285,7 +311,14 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC return } - ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) + ctx.Error( + http.StatusForbidden, + "tokenRequiresScope", + fmt.Sprintf( + "token does not have at least one of required scope(s): %v", + requiredScopes, + ), + ) } } @@ -303,7 +336,11 @@ func reqToken() func(ctx *context.APIContext) { if pubRepoExists && publicRepo.(bool) && ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { - ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") + ctx.Error( + http.StatusForbidden, + "reqToken", + "token scope is limited to public repos", + ) return } @@ -326,14 +363,19 @@ func reqToken() func(ctx *context.APIContext) { func reqExploreSignIn() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { - ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") + ctx.Error( + http.StatusUnauthorized, + "reqExploreSignIn", + "you must be signed in to search for users", + ) } } } func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { + if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && + ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { return } if !ctx.IsBasicAuth { @@ -367,7 +409,11 @@ func reqOwner() func(ctx *context.APIContext) { func reqSelfOrAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer { - ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser") + ctx.Error( + http.StatusForbidden, + "reqSelfOrAdmin", + "doer should be the site admin or be same as the contextUser", + ) return } } @@ -377,7 +423,11 @@ func reqSelfOrAdmin() func(ctx *context.APIContext) { func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository") + ctx.Error( + http.StatusForbidden, + "reqAdmin", + "user should be an owner or a collaborator with admin write of a repository", + ) return } } @@ -387,7 +437,11 @@ func reqAdmin() func(ctx *context.APIContext) { func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") + ctx.Error( + http.StatusForbidden, + "reqRepoWriter", + "user should have a permission to write to a repo", + ) return } } @@ -396,8 +450,13 @@ func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { // reqRepoBranchWriter user should have a permission to write to a branch, or be a site admin func reqRepoBranchWriter(ctx *context.APIContext) { options, ok := web.GetForm(ctx).(api.FileOptionInterface) - if !ok || (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) { - ctx.Error(http.StatusForbidden, "reqRepoBranchWriter", "user should have a permission to write to this branch") + if !ok || + (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) { + ctx.Error( + http.StatusForbidden, + "reqRepoBranchWriter", + "user should have a permission to write to this branch", + ) return } } @@ -406,7 +465,11 @@ func reqRepoBranchWriter(ctx *context.APIContext) { func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Repo.CanRead(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") + ctx.Error( + http.StatusForbidden, + "reqRepoReader", + "user should have specific read permission or be a repo admin or a site admin", + ) return } } @@ -416,7 +479,11 @@ func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { func reqAnyRepoReader() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Repo.HasAccess() && !ctx.IsUserSiteAdmin() { - ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin") + ctx.Error( + http.StatusForbidden, + "reqAnyRepoReader", + "user should have any permission to read repository or permissions of site admin", + ) return } } @@ -671,7 +738,11 @@ func mustEnableWiki(ctx *context.APIContext) { func mustNotBeArchived(ctx *context.APIContext) { if ctx.Repo.Repository.IsArchived { - ctx.Error(http.StatusLocked, "RepoArchived", fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString())) + ctx.Error( + http.StatusLocked, + "RepoArchived", + fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString()), + ) return } } @@ -689,7 +760,11 @@ func bind[T any](_ T) any { theObj := new(T) // create a new form obj for every request but not use obj directly errs := binding.Bind(ctx.Req, theObj) if len(errs) > 0 { - ctx.Error(http.StatusUnprocessableEntity, "validationError", fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error())) + ctx.Error( + http.StatusUnprocessableEntity, + "validationError", + fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error()), + ) return } web.SetForm(ctx, theObj) @@ -739,7 +814,11 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC return } if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { - log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) + log.Info( + "Failed authentication attempt for %s from %s", + ctx.Doer.Name, + ctx.RemoteAddr(), + ) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.JSON(http.StatusForbidden, map[string]string{ "message": "This account is prohibited from signing in, please contact your site administrator.", @@ -800,8 +879,10 @@ func Routes() *web.Route { // setting.CORSConfig.AllowSubdomain // FIXME: the cors middleware needs allowSubdomain option AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, - AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP"}, setting.CORSConfig.Headers...), - MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + AllowedHeaders: append( + []string{"Authorization", "X-Gitea-OTP"}, + setting.CORSConfig.Headers...), + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), })) } m.Use(context.APIContexter()) @@ -880,7 +961,12 @@ func Routes() *web.Route { m.Get("/heatmap", user.GetUserHeatmapData) } - m.Get("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), reqExploreSignIn(), user.ListUserRepos) + m.Get( + "/repos", + tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), + reqExploreSignIn(), + user.ListUserRepos, + ) m.Group("/tokens", func() { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) @@ -968,7 +1054,8 @@ func Routes() *web.Route { m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) // (repo scope) - m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(user.ListMyRepos). + m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)). + Get(user.ListMyRepos). Post(bind(api.CreateRepoOption{}), repo.Create) // (repo scope) @@ -996,17 +1083,27 @@ func Routes() *web.Route { m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar) m.Delete("", user.DeleteAvatar) }, reqToken()) + + m.Combo("/projects", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)). + Get(projects.ListUserProjects). + Post(bind(api.NewProjectPayload{}), projects.CreateUserProject) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Repositories (requires repo scope, org scope) - m.Post("/org/{org}/repos", - tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), + m.Post( + "/org/{org}/repos", + tokenRequiresScopes( + auth_model.AccessTokenScopeCategoryOrganization, + auth_model.AccessTokenScopeCategoryRepository, + ), reqToken(), bind(api.CreateRepoOption{}), - repo.CreateOrgRepoDeprecated) + repo.CreateOrgRepoDeprecated, + ) // requires repo scope - m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)). + Get(repo.GetByID) // Repos (requires repo scope) m.Group("/repos", func() { @@ -1019,7 +1116,13 @@ func Routes() *web.Route { m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) - m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) + m.Post( + "/generate", + reqToken(), + reqRepoReader(unit.TypeCode), + bind(api.GenerateRepoOption{}), + repo.Generate, + ) m.Group("/transfer", func() { m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) m.Post("/accept", repo.AcceptTransfer) @@ -1045,7 +1148,12 @@ func Routes() *web.Route { m.Combo("").Get(repo.GetHook). Patch(bind(api.EditHookOption{}), repo.EditHook). Delete(repo.DeleteHook) - m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) + m.Post( + "/tests", + context.ReferencesGitRepo(), + context.RepoRefForAPI, + repo.TestHook, + ) }) }, reqToken(), reqAdmin(), reqWebhooksEnabled()) m.Group("/collaborators", func() { @@ -1065,31 +1173,79 @@ func Routes() *web.Route { Put(reqAdmin(), repo.AddTeam). Delete(reqAdmin(), repo.DeleteTeam) }, reqToken()) - m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) - m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) + m.Get( + "/raw/*", + context.ReferencesGitRepo(), + context.RepoRefForAPI, + reqRepoReader(unit.TypeCode), + repo.GetRawFile, + ) + m.Get( + "/media/*", + context.ReferencesGitRepo(), + context.RepoRefForAPI, + reqRepoReader(unit.TypeCode), + repo.GetRawFileOrLFS, + ) m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { m.Get("", repo.ListBranches) m.Get("/*", repo.GetBranch) - m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch) - m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch) + m.Delete( + "/*", + reqToken(), + reqRepoWriter(unit.TypeCode), + mustNotBeArchived, + repo.DeleteBranch, + ) + m.Post( + "", + reqToken(), + reqRepoWriter(unit.TypeCode), + mustNotBeArchived, + bind(api.CreateBranchRepoOption{}), + repo.CreateBranch, + ) }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) - m.Post("", bind(api.CreateBranchProtectionOption{}), mustNotBeArchived, repo.CreateBranchProtection) + m.Post( + "", + bind(api.CreateBranchProtectionOption{}), + mustNotBeArchived, + repo.CreateBranchProtection, + ) m.Group("/{name}", func() { m.Get("", repo.GetBranchProtection) - m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection) + m.Patch( + "", + bind(api.EditBranchProtectionOption{}), + mustNotBeArchived, + repo.EditBranchProtection, + ) m.Delete("", repo.DeleteBranchProtection) }) }, reqToken(), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) m.Get("/*", repo.GetTag) - m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateTagOption{}), repo.CreateTag) - m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteTag) + m.Post( + "", + reqToken(), + reqRepoWriter(unit.TypeCode), + mustNotBeArchived, + bind(api.CreateTagOption{}), + repo.CreateTag, + ) + m.Delete( + "/*", + reqToken(), + reqRepoWriter(unit.TypeCode), + mustNotBeArchived, + repo.DeleteTag, + ) }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). @@ -1107,7 +1263,14 @@ func Routes() *web.Route { Patch(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). Delete(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) m.Get("/revisions/{pageName}", repo.ListPageRevisions) - m.Post("/new", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) + m.Post( + "/new", + reqToken(), + mustNotBeArchived, + reqRepoWriter(unit.TypeWiki), + bind(api.CreateWikiPageOptions{}), + repo.NewWikiPage, + ) m.Get("/pages", repo.ListWikiPages) }, mustEnableWiki) m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) @@ -1152,7 +1315,13 @@ func Routes() *web.Route { Get(repo.GetPushMirrorByName) }, reqAdmin(), reqToken()) - m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) + m.Get( + "/editorconfig/{filename}", + context.ReferencesGitRepo(), + context.RepoRefForAPI, + reqRepoReader(unit.TypeCode), + repo.GetEditorconfig, + ) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) @@ -1178,7 +1347,12 @@ func Routes() *web.Route { Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) m.Combo("/comments"). Get(repo.GetPullReviewComments) - m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) + m.Post( + "/dismissals", + reqToken(), + bind(api.DismissPullReviewOptions{}), + repo.DismissPullReview, + ) m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) }) }) @@ -1210,15 +1384,47 @@ func Routes() *web.Route { m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Get("/notes/{sha}", repo.GetNote) }, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) - m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), mustNotBeArchived, repo.ApplyDiffPatch) + m.Post( + "/diffpatch", + reqRepoWriter(unit.TypeCode), + reqToken(), + bind(api.ApplyDiffPatchFileOptions{}), + mustNotBeArchived, + repo.ApplyDiffPatch, + ) m.Group("/contents", func() { m.Get("", repo.GetContentsList) - m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles) + m.Post( + "", + reqToken(), + bind(api.ChangeFilesOptions{}), + reqRepoBranchWriter, + mustNotBeArchived, + repo.ChangeFiles, + ) m.Get("/*", repo.GetContents) m.Group("/*", func() { - m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile) - m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile) - m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile) + m.Post( + "", + bind(api.CreateFileOptions{}), + reqRepoBranchWriter, + mustNotBeArchived, + repo.CreateFile, + ) + m.Put( + "", + bind(api.UpdateFileOptions{}), + reqRepoBranchWriter, + mustNotBeArchived, + repo.UpdateFile, + ) + m.Delete( + "", + bind(api.DeleteFileOptions{}), + reqRepoBranchWriter, + mustNotBeArchived, + repo.DeleteFile, + ) }, reqToken()) }, reqRepoReader(unit.TypeCode)) m.Get("/signing-key.gpg", misc.SigningKey) @@ -1232,7 +1438,11 @@ func Routes() *web.Route { }, reqAnyRepoReader()) m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig) - m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig) + m.Get( + "/issue_config/validate", + context.ReferencesGitRepo(), + repo.ValidateIssueConfig, + ) m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages) m.Get("/activities/feeds", repo.ListRepoActivityFeeds) m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed) @@ -1290,7 +1500,8 @@ func Routes() *web.Route { m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + m.Combo("/{id}", reqToken()). + Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) @@ -1308,7 +1519,8 @@ func Routes() *web.Route { Delete(repo.ResetIssueTime) m.Delete("/{id}", repo.DeleteTime) }, reqToken()) - m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) + m.Combo("/deadline"). + Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { m.Post("/start", repo.StartIssueStopwatch) m.Post("/stop", repo.StopIssueStopwatch) @@ -1363,6 +1575,12 @@ func Routes() *web.Route { Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) + m.Group("/projects", func() { + m. + Combo(""). + Get(projects.ListRepoProjects). + Post(bind(api.NewProjectPayload{}), projects.CreateRepoProject) + }, mustEnableIssues) }, repoAssignment()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) @@ -1370,20 +1588,43 @@ func Routes() *web.Route { m.Group("/packages/{username}", func() { m.Group("/{type}/{name}/{version}", func() { m.Get("", reqToken(), packages.GetPackage) - m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) + m.Delete( + "", + reqToken(), + reqPackageAccess(perm.AccessModeWrite), + packages.DeletePackage, + ) m.Get("/files", reqToken(), packages.ListPackageFiles) }) m.Get("/", reqToken(), packages.ListPackages) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) // Organizations - m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) + m.Get( + "/user/orgs", + reqToken(), + tokenRequiresScopes( + auth_model.AccessTokenScopeCategoryUser, + auth_model.AccessTokenScopeCategoryOrganization, + ), + org.ListMyOrgs, + ) m.Group("/users/{username}/orgs", func() { m.Get("", reqToken(), org.ListUserOrgs) m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context_service.UserAssignmentAPI()) - m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) - m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) + m.Post( + "/orgs", + tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), + reqToken(), + bind(api.CreateOrgOption{}), + org.Create, + ) + m.Get( + "/orgs", + org.GetAll, + tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), + ) m.Group("/orgs/{org}", func() { m.Combo("").Get(org.Get). Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). @@ -1414,7 +1655,13 @@ func Routes() *web.Route { }, reqToken(), reqOrgMembership()) m.Group("/labels", func() { m.Get("", org.ListLabels) - m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) + m.Post( + "", + reqToken(), + reqOrgOwnership(), + bind(api.CreateLabelOption{}), + org.CreateLabel, + ) m.Combo("/{id}").Get(reqToken(), org.GetLabel). Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) @@ -1431,6 +1678,12 @@ func Routes() *web.Route { m.Delete("", org.DeleteAvatar) }, reqToken(), reqOrgOwnership()) m.Get("/activities/feeds", org.ListOrgActivityFeeds) + + m.Group("/projects", func() { + m.Combo(""). + Get(projects.ListOrgProjects). + Post(bind(api.NewProjectPayload{}), projects.CreateOrgProject) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue), reqToken(), reqOrgMembership()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true)) m.Group("/teams/{teamid}", func() { m.Combo("").Get(reqToken(), org.GetTeam). @@ -1493,6 +1746,13 @@ func Routes() *web.Route { }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) + m.Group("/projects", func() { + m. + Combo("/{id}"). + Get(projects.GetProject). + Patch(bind(api.UpdateProjectPayload{}), projects.UpdateProject). + Delete(projects.DeleteProject) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue), reqToken()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go new file mode 100644 index 0000000000000..d5c5cbfe0ec09 --- /dev/null +++ b/routers/api/v1/projects/project.go @@ -0,0 +1,407 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package projects + +import ( + "net/http" + + project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/convert" +) + +func innerCreateProject( + ctx *context.APIContext, + project_type project_model.Type, +) { + form := web.GetForm(ctx).(*api.NewProjectPayload) + project := &project_model.Project{ + RepoID: 0, + OwnerID: ctx.Doer.ID, + Title: form.Title, + Description: form.Description, + CreatorID: ctx.Doer.ID, + BoardType: project_model.BoardType(form.BoardType), + Type: project_type, + } + + if ctx.ContextUser != nil { + project.OwnerID = ctx.ContextUser.ID + } + + if project_type == project_model.TypeRepository { + project.RepoID = ctx.Repo.Repository.ID + } + + if err := project_model.NewProject(ctx, project); err != nil { + ctx.Error(http.StatusInternalServerError, "NewProject", err) + return + } + + project, err := project_model.GetProjectByID(ctx, project.ID) + if err != nil { + ctx.Error(http.StatusInternalServerError, "NewProject", err) + return + } + + ctx.JSON(http.StatusCreated, convert.ToAPIProject(ctx, project)) +} + +func CreateUserProject(ctx *context.APIContext) { + // swagger:operation POST /user/projects project projectCreateUserProject + // --- + // summary: Create a user project + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: project + // in: body + // required: true + // schema: { "$ref": "#/definitions/NewProjectPayload" } + // responses: + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + innerCreateProject(ctx, project_model.TypeIndividual) +} + +func CreateOrgProject(ctx *context.APIContext) { + // swagger:operation POST /orgs/{org}/projects project projectCreateOrgProject + // --- + // summary: Create a organization project + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: org + // in: path + // description: owner of repo + // type: string + // required: true + // - name: project + // in: body + // required: true + // schema: { "$ref": "#/definitions/NewProjectPayload" } + // responses: + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + innerCreateProject(ctx, project_model.TypeOrganization) +} + +func CreateRepoProject(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/projects project projectCreateRepositoryProject + // --- + // summary: Create a repository project + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of repo + // type: string + // required: true + // - name: repo + // in: path + // description: repo + // type: string + // required: true + // - name: project + // in: body + // required: true + // schema: { "$ref": "#/definitions/NewProjectPayload" } + // responses: + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + innerCreateProject(ctx, project_model.TypeRepository) +} + +func GetProject(ctx *context.APIContext) { + // swagger:operation GET /projects/{id} project projectGetProject + // --- + // summary: Get project + // produces: + // - application/json + // parameters: + // - name: id + // in: path + // description: id of the project + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetProjectByID", err) + } + return + } + + ctx.JSON(http.StatusOK, convert.ToAPIProject(ctx, project)) +} + +func UpdateProject(ctx *context.APIContext) { + // swagger:operation PATCH /projects/{id} project projectUpdateProject + // --- + // summary: Update project + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: id + // in: path + // description: id of the project + // type: string + // required: true + // - name: project + // in: body + // required: true + // schema: { "$ref": "#/definitions/UpdateProjectPayload" } + // responses: + // "200": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + form := web.GetForm(ctx).(*api.UpdateProjectPayload) + project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64("id")) + if err != nil { + if project_model.IsErrProjectNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + } + return + } + if project.Title != form.Title { + project.Title = form.Title + } + if project.Description != form.Description { + project.Description = form.Description + } + + err = project_model.UpdateProject(ctx, project) + if err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + return + } + ctx.JSON(http.StatusOK, convert.ToAPIProject(ctx, project)) +} + +func DeleteProject(ctx *context.APIContext) { + // swagger:operation DELETE /projects/{id} project projectDeleteProject + // --- + // summary: Delete project + // parameters: + // - name: id + // in: path + // description: id of the project + // type: string + // required: true + // responses: + // "204": + // "description": "Deleted the project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + + if err := project_model.DeleteProjectByID(ctx, ctx.ParamsInt64(":id")); err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteProjectByID", err) + return + } + + ctx.Status(http.StatusNoContent) + +} + +func ListUserProjects(ctx *context.APIContext) { + // swagger:operation GET /user/projects project projectListUserProjects + // --- + // summary: List repository projects + // produces: + // - application/json + // parameters: + // - name: closed + // in: query + // description: include closed issues or not + // type: boolean + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: ctx.Doer.ID, + Page: ctx.FormInt("page"), + IsClosed: ctx.FormOptionalBool("closed"), + Type: project_model.TypeIndividual, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.SetLinkHeader(int(count), setting.UI.IssuePagingNum) + ctx.SetTotalCountHeader(count) + + apiProjects, err := convert.ToAPIProjectList(ctx, projects) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.JSON(http.StatusOK, apiProjects) +} + +func ListOrgProjects(ctx *context.APIContext) { + // swagger:operation GET /orgs/{org}/projects project projectListOrgProjects + // --- + // summary: List repository projects + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: closed + // in: query + // description: include closed issues or not + // type: boolean + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + OwnerID: ctx.Org.Organization.AsUser().ID, + Page: ctx.FormInt("page"), + IsClosed: ctx.FormOptionalBool("closed"), + Type: project_model.TypeOrganization, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.SetLinkHeader(int(count), setting.UI.IssuePagingNum) + ctx.SetTotalCountHeader(count) + + apiProjects, err := convert.ToAPIProjectList(ctx, projects) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.JSON(http.StatusOK, apiProjects) +} + +func ListRepoProjects(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/projects project projectListRepositoryProjects + // --- + // summary: List repository projects + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: repo + // in: path + // description: repo + // type: string + // required: true + // - name: closed + // in: query + // description: include closed issues or not + // type: boolean + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results + // type: integer + // responses: + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + RepoID: ctx.Repo.Repository.ID, + Page: ctx.FormInt("page"), + IsClosed: ctx.FormOptionalBool("closed"), + Type: project_model.TypeRepository, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.SetLinkHeader(int(count), setting.UI.IssuePagingNum) + ctx.SetTotalCountHeader(count) + + apiProjects, err := convert.ToAPIProjectList(ctx, projects) + if err != nil { + ctx.Error(http.StatusInternalServerError, "Projects", err) + return + } + + ctx.JSON(http.StatusOK, apiProjects) +} diff --git a/routers/api/v1/swagger/project.go b/routers/api/v1/swagger/project.go new file mode 100644 index 0000000000000..cfa00c7faeab3 --- /dev/null +++ b/routers/api/v1/swagger/project.go @@ -0,0 +1,22 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package swagger + +import ( + api "code.gitea.io/gitea/modules/structs" +) + +// Project +// swagger:response Project +type swaggerResponseProject struct { + // in:body + Body api.Project `json:"body"` +} + +// ProjectList +// swagger:response ProjectList +type swaggerResponseProjectList struct { + // in:body + Body []api.Project `json:"body"` +} diff --git a/services/convert/project.go b/services/convert/project.go new file mode 100644 index 0000000000000..281c1c1a236f7 --- /dev/null +++ b/services/convert/project.go @@ -0,0 +1,66 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "context" + + project_model "code.gitea.io/gitea/models/project" + api "code.gitea.io/gitea/modules/structs" +) + +func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Project { + + apiProject := &api.Project{ + Title: project.Title, + Description: project.Description, + BoardType: uint8(project.BoardType), + IsClosed: project.IsClosed, + Created: project.CreatedUnix.AsTime(), + Updated: project.UpdatedUnix.AsTime(), + Closed: project.ClosedDateUnix.AsTime(), + } + + // try to laod the repo + project.LoadRepo(ctx) + if project.Repo != nil { + apiProject.Repo = &api.RepositoryMeta{ + ID: project.RepoID, + Name: project.Repo.Name, + Owner: project.Repo.OwnerName, + FullName: project.Repo.FullName(), + } + } + + project.LoadCreator(ctx) + if project.Creator != nil { + apiProject.Creator = &api.User{ + ID: project.Creator.ID, + UserName: project.Creator.Name, + FullName: project.Creator.FullName, + } + } + + project.LoadOwner(ctx) + if project.Owner != nil { + apiProject.Owner = &api.User{ + ID: project.Owner.ID, + UserName: project.Owner.Name, + FullName: project.Owner.FullName, + } + } + + return apiProject +} + +func ToAPIProjectList( + ctx context.Context, + projects []*project_model.Project, +) ([]*api.Project, error) { + result := make([]*api.Project, len(projects)) + for i := range projects { + result[i] = ToAPIProject(ctx, projects[i]) + } + return result, nil +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 75a45dc68ac56..43b31534a286b 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2311,6 +2311,75 @@ } } }, + "/orgs/{org}/projects": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "List repository projects", + "operationId": "projectListOrgProjects", + "parameters": [ + { + "type": "string", + "description": "owner of the repository", + "name": "org", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "include closed issues or not", + "name": "closed", + "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" + } + ] + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Create a organization project", + "operationId": "projectCreateOrgProject", + "parameters": [ + { + "type": "string", + "description": "owner of repo", + "name": "org", + "in": "path", + "required": true + }, + { + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/NewProjectPayload" + } + } + ] + } + }, "/orgs/{org}/public_members": { "get": { "produces": [ @@ -2912,6 +2981,73 @@ } } }, + "/projects/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Get project", + "operationId": "projectGetProject", + "parameters": [ + { + "type": "string", + "description": "id of the project", + "name": "id", + "in": "path", + "required": true + } + ] + }, + "delete": { + "tags": [ + "project" + ], + "summary": "Delete project", + "operationId": "projectDeleteProject", + "parameters": [ + { + "type": "string", + "description": "id of the project", + "name": "id", + "in": "path", + "required": true + } + ] + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Update project", + "operationId": "projectUpdateProject", + "parameters": [ + { + "type": "string", + "description": "id of the project", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/UpdateProjectPayload" + } + } + ] + } + }, "/repos/issues/search": { "get": { "produces": [ @@ -10140,6 +10276,89 @@ } } }, + "/repos/{owner}/{repo}/projects": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "List repository projects", + "operationId": "projectListRepositoryProjects", + "parameters": [ + { + "type": "string", + "description": "owner of the repository", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "boolean", + "description": "include closed issues or not", + "name": "closed", + "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" + } + ] + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Create a repository project", + "operationId": "projectCreateRepositoryProject", + "parameters": [ + { + "type": "string", + "description": "owner of repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/NewProjectPayload" + } + } + ] + } + }, "/repos/{owner}/{repo}/pulls": { "get": { "produces": [ @@ -15481,6 +15700,61 @@ } } }, + "/user/projects": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "List repository projects", + "operationId": "projectListUserProjects", + "parameters": [ + { + "type": "boolean", + "description": "include closed issues or not", + "name": "closed", + "in": "query" + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results", + "name": "limit", + "in": "query" + } + ] + }, + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "project" + ], + "summary": "Create a user project", + "operationId": "projectCreateUserProject", + "parameters": [ + { + "name": "project", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/NewProjectPayload" + } + } + ] + } + }, "/user/repos": { "get": { "produces": [ @@ -21310,6 +21584,55 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "Project": { + "type": "object", + "properties": { + "board_type": { + "type": "integer", + "format": "uint8", + "x-go-name": "BoardType" + }, + "closed_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Closed" + }, + "created_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Created" + }, + "creator": { + "$ref": "#/definitions/User" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "id": { + "type": "integer", + "format": "int64", + "x-go-name": "ID" + }, + "is_closed": { + "type": "boolean", + "x-go-name": "IsClosed" + }, + "repository": { + "$ref": "#/definitions/RepositoryMeta" + }, + "title": { + "type": "string", + "x-go-name": "Title" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Updated" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "PublicKey": { "description": "PublicKey publickey is a user key to push code to repository", "type": "object", @@ -23628,6 +23951,21 @@ } } }, + "Project": { + "description": "Project", + "schema": { + "$ref": "#/definitions/Project" + } + }, + "ProjectList": { + "description": "ProjectList", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Project" + } + } + }, "PublicKey": { "description": "PublicKey", "schema": { diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go new file mode 100644 index 0000000000000..75cb480c282b3 --- /dev/null +++ b/tests/integration/api_project_test.go @@ -0,0 +1,228 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "net/url" + "testing" + + auth_model "code.gitea.io/gitea/models/auth" + project_model "code.gitea.io/gitea/models/project" + "code.gitea.io/gitea/models/unittest" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/tests" + "github.com/stretchr/testify/assert" +) + +func TestAPICreateUserProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeWriteIssue, + auth_model.AccessTokenScopeWriteUser, + ) + urlStr := fmt.Sprintf("/api/v1/user/projects?token=%s", token) + + req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ + Title: title, + Description: description, + BoardType: board_type, + }) + resp := MakeRequest(t, req, http.StatusCreated) + var apiProject api.Project + DecodeJSON(t, resp, &apiProject) + assert.Equal(t, title, apiProject.Title) + assert.Equal(t, description, apiProject.Description) + assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, "user2", apiProject.Creator.UserName) +} + +func TestAPICreateOrgProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + + orgName := "org17" + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeWriteIssue, + auth_model.AccessTokenScopeWriteOrganization, + ) + urlStr := fmt.Sprintf("/api/v1/orgs/%s/projects?token=%s", orgName, token) + + req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ + Title: title, + Description: description, + BoardType: board_type, + }) + resp := MakeRequest(t, req, http.StatusCreated) + var apiProject api.Project + DecodeJSON(t, resp, &apiProject) + assert.Equal(t, title, apiProject.Title) + assert.Equal(t, description, apiProject.Description) + assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, "org17", apiProject.Creator.UserName) +} + +func TestAPICreateRepoProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + + ownerName := "user2" + repoName := "repo1" + token := getUserToken( + t, + ownerName, + auth_model.AccessTokenScopeWriteIssue, + auth_model.AccessTokenScopeWriteOrganization, + ) + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/projects?token=%s", ownerName, repoName, token) + + req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ + Title: title, + Description: description, + BoardType: board_type, + }) + resp := MakeRequest(t, req, http.StatusCreated) + var apiProject api.Project + DecodeJSON(t, resp, &apiProject) + assert.Equal(t, title, apiProject.Title) + assert.Equal(t, description, apiProject.Description) + assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, "repo1", apiProject.Repo.Name) +} + +func TestAPIListUserProjects(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeReadUser, + auth_model.AccessTokenScopeReadIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/user/projects")) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequest(t, "GET", link.String()) + var apiProjects []*api.Project + + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiProjects) + assert.Len(t, apiProjects, 1) +} + +func TestAPIListOrgProjects(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + orgName := "org17" + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeReadOrganization, + auth_model.AccessTokenScopeReadIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/orgs/%s/projects", orgName)) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequest(t, "GET", link.String()) + var apiProjects []*api.Project + + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiProjects) + assert.Len(t, apiProjects, 1) +} + +func TestAPIListRepoProjects(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + ownerName := "user2" + repoName := "repo1" + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeReadRepository, + auth_model.AccessTokenScopeReadIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName)) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequest(t, "GET", link.String()) + var apiProjects []*api.Project + + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiProjects) + assert.Len(t, apiProjects, 1) +} + +func TestAPIGetProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeReadUser, + auth_model.AccessTokenScopeReadIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequest(t, "GET", link.String()) + var apiProject *api.Project + + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiProject) + assert.Equal(t, "First project", apiProject.Title) + assert.Equal(t, "repo1", apiProject.Repo.Name) + assert.Equal(t, "user2", apiProject.Creator.UserName) +} + +func TestAPIUpdateProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeWriteUser, + auth_model.AccessTokenScopeWriteIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequestWithJSON(t, "PATCH", link.String(), &api.UpdateProjectPayload{ + Title: "First project updated", + }) + + var apiProject *api.Project + + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiProject) + assert.Equal(t, "First project updated", apiProject.Title) +} + +func TestAPIDeleteProject(t *testing.T) { + defer tests.PrepareTestEnv(t)() + token := getUserToken( + t, + "user2", + auth_model.AccessTokenScopeWriteUser, + auth_model.AccessTokenScopeWriteIssue, + ) + link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) + + link.RawQuery = url.Values{"token": {token}}.Encode() + + req := NewRequest(t, "DELETE", link.String()) + + MakeRequest(t, req, http.StatusNoContent) + unittest.AssertNotExistsBean(t, &project_model.Project{ID: 1}) +} From a0f8dbd928b972ba585cf7d18ba2918a918d0e94 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Tue, 16 Jan 2024 20:38:15 +0530 Subject: [PATCH 04/44] chore: remove unnecessary go lines formatting --- models/project/project.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/models/project/project.go b/models/project/project.go index 670ae39f87c66..c63b83061276c 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -357,11 +357,7 @@ func updateRepositoryProjectCount(ctx context.Context, repoID int64) error { } // ChangeProjectStatusByRepoIDAndID toggles a project between opened and closed -func ChangeProjectStatusByRepoIDAndID( - ctx context.Context, - repoID, projectID int64, - isClosed bool, -) error { +func ChangeProjectStatusByRepoIDAndID(ctx context.Context, repoID, projectID int64, isClosed bool) error { ctx, committer, err := db.TxContext(ctx) if err != nil { return err @@ -402,11 +398,7 @@ func ChangeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { p.IsClosed = isClosed p.ClosedDateUnix = timeutil.TimeStampNow() - count, err := db.GetEngine(ctx). - ID(p.ID). - Where("repo_id = ? AND is_closed = ?", p.RepoID, !isClosed). - Cols("is_closed", "closed_date_unix"). - Update(p) + count, err := db.GetEngine(ctx).ID(p.ID).Where("repo_id = ? AND is_closed = ?", p.RepoID, !isClosed).Cols("is_closed", "closed_date_unix").Update(p) if err != nil { return err } From df22b6dd128edd2ced9d63b77bd1bbebb7f1a14a Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Tue, 16 Jan 2024 20:44:55 +0530 Subject: [PATCH 05/44] chore: remove the goline formatting --- services/convert/project.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/services/convert/project.go b/services/convert/project.go index 281c1c1a236f7..0e25d6c10dd87 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -54,10 +54,7 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Proj return apiProject } -func ToAPIProjectList( - ctx context.Context, - projects []*project_model.Project, -) ([]*api.Project, error) { +func ToAPIProjectList(ctx context.Context, projects []*project_model.Project) ([]*api.Project, error) { result := make([]*api.Project, len(projects)) for i := range projects { result[i] = ToAPIProject(ctx, projects[i]) From 5bbbade0268fffb1dde50d90b13d41ecd34e22a0 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Tue, 16 Jan 2024 21:04:27 +0530 Subject: [PATCH 06/44] chore: remove unwanted indentation on the line --- tests/integration/api_project_test.go | 63 ++++----------------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 75cb480c282b3..017a5bf3f1a35 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -21,12 +21,7 @@ func TestAPICreateUserProject(t *testing.T) { defer tests.PrepareTestEnv(t)() const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeWriteIssue, - auth_model.AccessTokenScopeWriteUser, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteUser) urlStr := fmt.Sprintf("/api/v1/user/projects?token=%s", token) req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ @@ -48,12 +43,7 @@ func TestAPICreateOrgProject(t *testing.T) { const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) orgName := "org17" - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeWriteIssue, - auth_model.AccessTokenScopeWriteOrganization, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) urlStr := fmt.Sprintf("/api/v1/orgs/%s/projects?token=%s", orgName, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ @@ -76,12 +66,7 @@ func TestAPICreateRepoProject(t *testing.T) { ownerName := "user2" repoName := "repo1" - token := getUserToken( - t, - ownerName, - auth_model.AccessTokenScopeWriteIssue, - auth_model.AccessTokenScopeWriteOrganization, - ) + token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/projects?token=%s", ownerName, repoName, token) req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ @@ -101,12 +86,7 @@ func TestAPICreateRepoProject(t *testing.T) { func TestAPIListUserProjects(t *testing.T) { defer tests.PrepareTestEnv(t)() - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeReadUser, - auth_model.AccessTokenScopeReadIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/user/projects")) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -123,12 +103,7 @@ func TestAPIListOrgProjects(t *testing.T) { defer tests.PrepareTestEnv(t)() orgName := "org17" - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeReadOrganization, - auth_model.AccessTokenScopeReadIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/orgs/%s/projects", orgName)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -146,12 +121,7 @@ func TestAPIListRepoProjects(t *testing.T) { ownerName := "user2" repoName := "repo1" - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeReadRepository, - auth_model.AccessTokenScopeReadIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -166,12 +136,7 @@ func TestAPIListRepoProjects(t *testing.T) { func TestAPIGetProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeReadUser, - auth_model.AccessTokenScopeReadIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -188,12 +153,7 @@ func TestAPIGetProject(t *testing.T) { func TestAPIUpdateProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeWriteUser, - auth_model.AccessTokenScopeWriteIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) link.RawQuery = url.Values{"token": {token}}.Encode() @@ -211,12 +171,7 @@ func TestAPIUpdateProject(t *testing.T) { func TestAPIDeleteProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - token := getUserToken( - t, - "user2", - auth_model.AccessTokenScopeWriteUser, - auth_model.AccessTokenScopeWriteIssue, - ) + token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) link.RawQuery = url.Values{"token": {token}}.Encode() From aa32ad74af97507e6c268dc95bf95a65e75046e0 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 19:55:42 +0530 Subject: [PATCH 07/44] refactor: update for db find method usage --- routers/api/v1/projects/project.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index d5c5cbfe0ec09..e258d6e760733 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -6,6 +6,7 @@ package projects import ( "net/http" + "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" @@ -270,11 +271,13 @@ func ListUserProjects(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: ctx.Doer.ID, - Page: ctx.FormInt("page"), - IsClosed: ctx.FormOptionalBool("closed"), + projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ Type: project_model.TypeIndividual, + IsClosed: ctx.FormOptionalBool("closed"), + OwnerID: ctx.Doer.ID, + ListOptions: db.ListOptions{ + Page: ctx.FormInt("page"), + }, }) if err != nil { ctx.Error(http.StatusInternalServerError, "Projects", err) @@ -324,9 +327,11 @@ func ListOrgProjects(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ - OwnerID: ctx.Org.Organization.AsUser().ID, - Page: ctx.FormInt("page"), + projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ + OwnerID: ctx.Org.Organization.AsUser().ID, + ListOptions: db.ListOptions{ + Page: ctx.FormInt("page"), + }, IsClosed: ctx.FormOptionalBool("closed"), Type: project_model.TypeOrganization, }) @@ -383,11 +388,13 @@ func ListRepoProjects(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - projects, count, err := project_model.FindProjects(ctx, project_model.SearchOptions{ + projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ RepoID: ctx.Repo.Repository.ID, - Page: ctx.FormInt("page"), IsClosed: ctx.FormOptionalBool("closed"), Type: project_model.TypeRepository, + ListOptions: db.ListOptions{ + Page: ctx.FormInt("page"), + }, }) if err != nil { ctx.Error(http.StatusInternalServerError, "Projects", err) From 81c3d0ce34ec988fa2a5197a29ffc7cc32b566ec Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 20:16:37 +0530 Subject: [PATCH 08/44] fix: add missing swagger tags --- modules/structs/project.go | 2 ++ routers/api/v1/swagger/options.go | 6 ++++ templates/swagger/v1_json.tmpl | 51 ++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/modules/structs/project.go b/modules/structs/project.go index e70659e5ceeed..4f1683e96c116 100644 --- a/modules/structs/project.go +++ b/modules/structs/project.go @@ -5,6 +5,7 @@ package structs import "time" +// swagger:model type NewProjectPayload struct { // required:true Title string `json:"title" binding:"Required"` @@ -15,6 +16,7 @@ type NewProjectPayload struct { Description string `json:"description"` } +// swagger:model type UpdateProjectPayload struct { // required:true Title string `json:"title" binding:"Required"` diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 6f7859df62ed4..4937d5e99a352 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -190,4 +190,10 @@ type swaggerParameterBodies struct { // in:body CreateOrUpdateSecretOption api.CreateOrUpdateSecretOption + + // in:body + NewProjectPayload api.NewProjectPayload + + // in:body + UpdateProjectPayload api.UpdateProjectPayload } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 2c5e0108a95be..044a1703a31cb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -21119,6 +21119,35 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "NewProjectPayload": { + "type": "object", + "required": [ + "title", + "board_type", + "card_type" + ], + "properties": { + "board_type": { + "type": "integer", + "format": "uint8", + "x-go-name": "BoardType" + }, + "card_type": { + "type": "integer", + "format": "uint8", + "x-go-name": "CardType" + }, + "description": { + "type": "string", + "x-go-name": "Description" + }, + "title": { + "type": "string", + "x-go-name": "Title" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "NodeInfo": { "description": "NodeInfo contains standardized way of exposing metadata about a server running one of the distributed social networks", "type": "object", @@ -21719,6 +21748,9 @@ "type": "boolean", "x-go-name": "IsClosed" }, + "owner": { + "$ref": "#/definitions/User" + }, "repository": { "$ref": "#/definitions/RepositoryMeta" }, @@ -23111,6 +23143,23 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "UpdateProjectPayload": { + "type": "object", + "required": [ + "title" + ], + "properties": { + "description": { + "type": "string", + "x-go-name": "Description" + }, + "title": { + "type": "string", + "x-go-name": "Title" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "UpdateRepoAvatarOption": { "description": "UpdateRepoAvatarUserOption options when updating the repo avatar", "type": "object", @@ -24462,7 +24511,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/CreateOrUpdateSecretOption" + "$ref": "#/definitions/UpdateProjectPayload" } }, "redirect": { From 852c0df3825d906a5de5bff040f6da561916f91b Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 21:44:47 +0530 Subject: [PATCH 09/44] chore: fix lint issues --- tests/integration/api_project_test.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 017a5bf3f1a35..c001bc9f08d9a 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -14,12 +14,13 @@ import ( "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/tests" + "github.com/stretchr/testify/assert" ) func TestAPICreateUserProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteUser) urlStr := fmt.Sprintf("/api/v1/user/projects?token=%s", token) @@ -27,20 +28,20 @@ func TestAPICreateUserProject(t *testing.T) { req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ Title: title, Description: description, - BoardType: board_type, + BoardType: boardType, }) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.BoardType) assert.Equal(t, "user2", apiProject.Creator.UserName) } func TestAPICreateOrgProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) orgName := "org17" token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) @@ -49,20 +50,20 @@ func TestAPICreateOrgProject(t *testing.T) { req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ Title: title, Description: description, - BoardType: board_type, + BoardType: boardType, }) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.BoardType) assert.Equal(t, "org17", apiProject.Creator.UserName) } func TestAPICreateRepoProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, board_type = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) ownerName := "user2" repoName := "repo1" @@ -72,14 +73,14 @@ func TestAPICreateRepoProject(t *testing.T) { req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ Title: title, Description: description, - BoardType: board_type, + BoardType: boardType, }) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, board_type, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.BoardType) assert.Equal(t, "repo1", apiProject.Repo.Name) } @@ -87,7 +88,7 @@ func TestAPIListUserProjects(t *testing.T) { defer tests.PrepareTestEnv(t)() token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue) - link, _ := url.Parse(fmt.Sprintf("/api/v1/user/projects")) + link, _ := url.Parse("/api/v1/user/projects") link.RawQuery = url.Values{"token": {token}}.Encode() From dbfecced25d63c9191e19d57bd60691563d955aa Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 21:45:36 +0530 Subject: [PATCH 10/44] fix: return error when converting project to api project --- routers/api/v1/projects/project.go | 29 ++++++++++++++++++++++------- services/convert/project.go | 29 ++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index e258d6e760733..8b24e4d488632 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -17,7 +17,7 @@ import ( func innerCreateProject( ctx *context.APIContext, - project_type project_model.Type, + projectType project_model.Type, ) { form := web.GetForm(ctx).(*api.NewProjectPayload) project := &project_model.Project{ @@ -27,14 +27,14 @@ func innerCreateProject( Description: form.Description, CreatorID: ctx.Doer.ID, BoardType: project_model.BoardType(form.BoardType), - Type: project_type, + Type: projectType, } if ctx.ContextUser != nil { project.OwnerID = ctx.ContextUser.ID } - if project_type == project_model.TypeRepository { + if projectType == project_model.TypeRepository { project.RepoID = ctx.Repo.Repository.ID } @@ -49,7 +49,13 @@ func innerCreateProject( return } - ctx.JSON(http.StatusCreated, convert.ToAPIProject(ctx, project)) + projectResponse, err := convert.ToAPIProject(ctx, project) + if err != nil { + ctx.Error(http.StatusInternalServerError, "NewProject", err) + return + } + + ctx.JSON(http.StatusCreated, projectResponse) } func CreateUserProject(ctx *context.APIContext) { @@ -165,7 +171,12 @@ func GetProject(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIProject(ctx, project)) + projectResponse, err := convert.ToAPIProject(ctx, project) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetProjectByID", err) + return + } + ctx.JSON(http.StatusOK, projectResponse) } func UpdateProject(ctx *context.APIContext) { @@ -215,7 +226,12 @@ func UpdateProject(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "UpdateProject", err) return } - ctx.JSON(http.StatusOK, convert.ToAPIProject(ctx, project)) + projectResponse, err := convert.ToAPIProject(ctx, project) + if err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + return + } + ctx.JSON(http.StatusOK, projectResponse) } func DeleteProject(ctx *context.APIContext) { @@ -242,7 +258,6 @@ func DeleteProject(ctx *context.APIContext) { } ctx.Status(http.StatusNoContent) - } func ListUserProjects(ctx *context.APIContext) { diff --git a/services/convert/project.go b/services/convert/project.go index 0e25d6c10dd87..4292037f9e9ea 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -10,8 +10,7 @@ import ( api "code.gitea.io/gitea/modules/structs" ) -func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Project { - +func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Project, error) { apiProject := &api.Project{ Title: project.Title, Description: project.Description, @@ -23,7 +22,10 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Proj } // try to laod the repo - project.LoadRepo(ctx) + err := project.LoadRepo(ctx) + if err != nil { + return nil, err + } if project.Repo != nil { apiProject.Repo = &api.RepositoryMeta{ ID: project.RepoID, @@ -33,7 +35,10 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Proj } } - project.LoadCreator(ctx) + err = project.LoadCreator(ctx) + if err != nil { + return nil, err + } if project.Creator != nil { apiProject.Creator = &api.User{ ID: project.Creator.ID, @@ -42,7 +47,10 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Proj } } - project.LoadOwner(ctx) + err = project.LoadOwner(ctx) + if err != nil { + return nil, err + } if project.Owner != nil { apiProject.Owner = &api.User{ ID: project.Owner.ID, @@ -51,13 +59,20 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) *api.Proj } } - return apiProject + return apiProject, nil } func ToAPIProjectList(ctx context.Context, projects []*project_model.Project) ([]*api.Project, error) { result := make([]*api.Project, len(projects)) + var err error for i := range projects { - result[i] = ToAPIProject(ctx, projects[i]) + result[i], err = ToAPIProject(ctx, projects[i]) + if err != nil { + break + } + } + if err != nil { + return nil, err } return result, nil } From fec855e323330ee6cf1078525a6acf0cfe41aa0b Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 22:14:39 +0530 Subject: [PATCH 11/44] chore: fix swagger documentation --- modules/structs/project.go | 1 + routers/api/v1/projects/project.go | 108 +++++++++++++-------------- templates/swagger/v1_json.tmpl | 114 ++++++++++++++++++++++++++--- 3 files changed, 160 insertions(+), 63 deletions(-) diff --git a/modules/structs/project.go b/modules/structs/project.go index 4f1683e96c116..46e7bebfcd9b8 100644 --- a/modules/structs/project.go +++ b/modules/structs/project.go @@ -23,6 +23,7 @@ type UpdateProjectPayload struct { Description string `json:"description"` } +// swagger:model type Project struct { ID int64 `json:"id"` Title string `json:"title"` diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 8b24e4d488632..4aed86d7de4dd 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -72,12 +72,12 @@ func CreateUserProject(ctx *context.APIContext) { // required: true // schema: { "$ref": "#/definitions/NewProjectPayload" } // responses: - // "201": - // "$ref": "#/responses/Project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" innerCreateProject(ctx, project_model.TypeIndividual) } @@ -100,12 +100,12 @@ func CreateOrgProject(ctx *context.APIContext) { // required: true // schema: { "$ref": "#/definitions/NewProjectPayload" } // responses: - // "201": - // "$ref": "#/responses/Project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" innerCreateProject(ctx, project_model.TypeOrganization) } @@ -133,12 +133,12 @@ func CreateRepoProject(ctx *context.APIContext) { // required: true // schema: { "$ref": "#/definitions/NewProjectPayload" } // responses: - // "201": - // "$ref": "#/responses/Project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "201": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" innerCreateProject(ctx, project_model.TypeRepository) } @@ -155,12 +155,12 @@ func GetProject(ctx *context.APIContext) { // type: string // required: true // responses: - // "200": - // "$ref": "#/responses/Project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "200": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) if err != nil { if project_model.IsErrProjectNotExist(err) { @@ -198,12 +198,12 @@ func UpdateProject(ctx *context.APIContext) { // required: true // schema: { "$ref": "#/definitions/UpdateProjectPayload" } // responses: - // "200": - // "$ref": "#/responses/Project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "200": + // "$ref": "#/responses/Project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" form := web.GetForm(ctx).(*api.UpdateProjectPayload) project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64("id")) if err != nil { @@ -245,12 +245,12 @@ func DeleteProject(ctx *context.APIContext) { // type: string // required: true // responses: - // "204": - // "description": "Deleted the project" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "204": + // "description": "Deleted the project" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" if err := project_model.DeleteProjectByID(ctx, ctx.ParamsInt64(":id")); err != nil { ctx.Error(http.StatusInternalServerError, "DeleteProjectByID", err) @@ -280,12 +280,12 @@ func ListUserProjects(ctx *context.APIContext) { // description: page size of results // type: integer // responses: - // "200": - // "$ref": "#/responses/ProjectList" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ Type: project_model.TypeIndividual, IsClosed: ctx.FormOptionalBool("closed"), @@ -336,12 +336,12 @@ func ListOrgProjects(ctx *context.APIContext) { // description: page size of results // type: integer // responses: - // "200": - // "$ref": "#/responses/ProjectList" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ OwnerID: ctx.Org.Organization.AsUser().ID, ListOptions: db.ListOptions{ @@ -397,12 +397,12 @@ func ListRepoProjects(ctx *context.APIContext) { // description: page size of results // type: integer // responses: - // "200": - // "$ref": "#/responses/ProjectList" - // "403": - // "$ref": "#/responses/forbidden" - // "404": - // "$ref": "#/responses/notFound" + // "200": + // "$ref": "#/responses/ProjectList" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ RepoID: ctx.Repo.Repository.ID, IsClosed: ctx.FormOptionalBool("closed"), diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 044a1703a31cb..8f49333daf112 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2393,7 +2393,18 @@ "name": "limit", "in": "query" } - ] + ], + "responses": { + "200": { + "$ref": "#/responses/ProjectList" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } }, "post": { "consumes": [ @@ -2423,7 +2434,15 @@ "$ref": "#/definitions/NewProjectPayload" } } - ] + ], + "responses": { + "201": { + "$ref": "#/responses/Project" + }, + "403": { + "$ref": "#/responses/forbidden" + } + } } }, "/orgs/{org}/public_members": { @@ -3045,7 +3064,18 @@ "in": "path", "required": true } - ] + ], + "responses": { + "200": { + "$ref": "#/responses/Project" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } }, "delete": { "tags": [ @@ -3061,7 +3091,18 @@ "in": "path", "required": true } - ] + ], + "responses": { + "204": { + "description": "Deleted the project" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } }, "patch": { "consumes": [ @@ -3091,7 +3132,18 @@ "$ref": "#/definitions/UpdateProjectPayload" } } - ] + ], + "responses": { + "200": { + "$ref": "#/responses/Project" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } } }, "/repos/issues/search": { @@ -10365,7 +10417,18 @@ "name": "limit", "in": "query" } - ] + ], + "responses": { + "200": { + "$ref": "#/responses/ProjectList" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } }, "post": { "consumes": [ @@ -10402,7 +10465,18 @@ "$ref": "#/definitions/NewProjectPayload" } } - ] + ], + "responses": { + "201": { + "$ref": "#/responses/Project" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } } }, "/repos/{owner}/{repo}/pulls": { @@ -15819,7 +15893,18 @@ "name": "limit", "in": "query" } - ] + ], + "responses": { + "200": { + "$ref": "#/responses/ProjectList" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } }, "post": { "consumes": [ @@ -15842,7 +15927,18 @@ "$ref": "#/definitions/NewProjectPayload" } } - ] + ], + "responses": { + "201": { + "$ref": "#/responses/Project" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + } + } } }, "/user/repos": { From 6238b9e7465a4ce1ee06a5fb4c3cfd7228f24d38 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 22:55:07 +0530 Subject: [PATCH 12/44] fix: ignore errors when loading data for converting project to response --- services/convert/project.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/services/convert/project.go b/services/convert/project.go index 4292037f9e9ea..42ce611e15bbe 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -22,10 +22,7 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro } // try to laod the repo - err := project.LoadRepo(ctx) - if err != nil { - return nil, err - } + _ = project.LoadRepo(ctx) if project.Repo != nil { apiProject.Repo = &api.RepositoryMeta{ ID: project.RepoID, @@ -35,10 +32,7 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro } } - err = project.LoadCreator(ctx) - if err != nil { - return nil, err - } + _ = project.LoadCreator(ctx) if project.Creator != nil { apiProject.Creator = &api.User{ ID: project.Creator.ID, @@ -47,10 +41,7 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro } } - err = project.LoadOwner(ctx) - if err != nil { - return nil, err - } + _ = project.LoadOwner(ctx) if project.Owner != nil { apiProject.Owner = &api.User{ ID: project.Owner.ID, From 8819a0a4fc8c1e6ebd489b313fefa3f94e3741d2 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 22:55:31 +0530 Subject: [PATCH 13/44] refactor: use the add token auth method for token in tests --- tests/integration/api_project_test.go | 33 +++++++++------------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index c001bc9f08d9a..8904170f51fe6 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -23,13 +23,12 @@ func TestAPICreateUserProject(t *testing.T) { const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteUser) - urlStr := fmt.Sprintf("/api/v1/user/projects?token=%s", token) - req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ + req := NewRequestWithJSON(t, "POST", "/api/v1/user/projects", &api.NewProjectPayload{ Title: title, Description: description, BoardType: boardType, - }) + }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) @@ -45,13 +44,13 @@ func TestAPICreateOrgProject(t *testing.T) { orgName := "org17" token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) - urlStr := fmt.Sprintf("/api/v1/orgs/%s/projects?token=%s", orgName, token) + urlStr := fmt.Sprintf("/api/v1/orgs/%s/projects", orgName) req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ Title: title, Description: description, BoardType: boardType, - }) + }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) @@ -68,13 +67,13 @@ func TestAPICreateRepoProject(t *testing.T) { ownerName := "user2" repoName := "repo1" token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) - urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/projects?token=%s", ownerName, repoName, token) + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName) req := NewRequestWithJSON(t, "POST", urlStr, &api.NewProjectPayload{ Title: title, Description: description, BoardType: boardType, - }) + }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusCreated) var apiProject api.Project DecodeJSON(t, resp, &apiProject) @@ -107,9 +106,7 @@ func TestAPIListOrgProjects(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadOrganization, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/orgs/%s/projects", orgName)) - link.RawQuery = url.Values{"token": {token}}.Encode() - - req := NewRequest(t, "GET", link.String()) + req := NewRequest(t, "GET", link.String()).AddTokenAuth(token) var apiProjects []*api.Project resp := MakeRequest(t, req, http.StatusOK) @@ -125,9 +122,7 @@ func TestAPIListRepoProjects(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/projects", ownerName, repoName)) - link.RawQuery = url.Values{"token": {token}}.Encode() - - req := NewRequest(t, "GET", link.String()) + req := NewRequest(t, "GET", link.String()).AddTokenAuth(token) var apiProjects []*api.Project resp := MakeRequest(t, req, http.StatusOK) @@ -140,9 +135,7 @@ func TestAPIGetProject(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) - link.RawQuery = url.Values{"token": {token}}.Encode() - - req := NewRequest(t, "GET", link.String()) + req := NewRequest(t, "GET", link.String()).AddTokenAuth(token) var apiProject *api.Project resp := MakeRequest(t, req, http.StatusOK) @@ -157,11 +150,9 @@ func TestAPIUpdateProject(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) - link.RawQuery = url.Values{"token": {token}}.Encode() - req := NewRequestWithJSON(t, "PATCH", link.String(), &api.UpdateProjectPayload{ Title: "First project updated", - }) + }).AddTokenAuth(token) var apiProject *api.Project @@ -175,9 +166,7 @@ func TestAPIDeleteProject(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) - link.RawQuery = url.Values{"token": {token}}.Encode() - - req := NewRequest(t, "DELETE", link.String()) + req := NewRequest(t, "DELETE", link.String()).AddTokenAuth(token) MakeRequest(t, req, http.StatusNoContent) unittest.AssertNotExistsBean(t, &project_model.Project{ID: 1}) From 3b2943a0023c52374c8807d317a23d0aae00fe53 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Wed, 17 Jan 2024 23:20:02 +0530 Subject: [PATCH 14/44] test: use the add token auth method for token --- tests/integration/api_project_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 8904170f51fe6..2754e1f3a4baf 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -89,9 +89,7 @@ func TestAPIListUserProjects(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopeReadIssue) link, _ := url.Parse("/api/v1/user/projects") - link.RawQuery = url.Values{"token": {token}}.Encode() - - req := NewRequest(t, "GET", link.String()) + req := NewRequest(t, "GET", link.String()).AddTokenAuth(token) var apiProjects []*api.Project resp := MakeRequest(t, req, http.StatusOK) From d2fd13836c2d6a1d72e70d858f37325d1b0c5fe1 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Sat, 20 Jan 2024 10:18:05 +0530 Subject: [PATCH 15/44] test: use the correct fields for the test case --- tests/integration/api_project_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 2754e1f3a4baf..03ada63336729 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -57,7 +57,8 @@ func TestAPICreateOrgProject(t *testing.T) { assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) assert.Equal(t, boardType, apiProject.BoardType) - assert.Equal(t, "org17", apiProject.Creator.UserName) + assert.Equal(t, "user2", apiProject.Creator.UserName) + assert.Equal(t, "org17", apiProject.Owner.UserName) } func TestAPICreateRepoProject(t *testing.T) { From 897c67b5555dcc3f6b371f35df94183dfde95c67 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 21 Jan 2024 15:29:23 +0100 Subject: [PATCH 16/44] revert formatting changes --- routers/api/v1/api.go | 360 +++++++----------------------------------- 1 file changed, 58 insertions(+), 302 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index fb9ea820c913b..df2222acb8b48 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -234,11 +234,7 @@ func repoAssignment() func(ctx *context.APIContext) { func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if ctx.Package.AccessMode < accessMode && !ctx.IsUserSiteAdmin() { - ctx.Error( - http.StatusForbidden, - "reqPackageAccess", - "user should have specific permission or be a site admin", - ) + ctx.Error(http.StatusForbidden, "reqPackageAccess", "user should have specific permission or be a site admin") return } } @@ -246,9 +242,7 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) // if a token is being used for auth, we check that it contains the required scope // if a token is not being used, reqToken will enforce other sign in methods -func tokenRequiresScopes( - requiredScopeCategories ...auth_model.AccessTokenScopeCategory, -) func(ctx *context.APIContext) { +func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { // no scope required if len(requiredScopeCategories) == 0 { @@ -266,46 +260,27 @@ func tokenRequiresScopes( // use the http method to determine the access level requiredScopeLevel := auth_model.Read - if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || - ctx.Req.Method == "DELETE" { + if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { requiredScopeLevel = auth_model.Write } // get the required scope for the given access level and category - requiredScopes := auth_model.GetRequiredScopes( - requiredScopeLevel, - requiredScopeCategories...) + requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...) // check if scope only applies to public resources publicOnly, err := scope.PublicOnly() if err != nil { - ctx.Error( - http.StatusForbidden, - "tokenRequiresScope", - "parsing public resource scope failed: "+err.Error(), - ) + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) return } // this context is used by the middleware in the specific route - ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && - auth_model.ContainsCategory( - requiredScopeCategories, - auth_model.AccessTokenScopeCategoryRepository, - ) - ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && - auth_model.ContainsCategory( - requiredScopeCategories, - auth_model.AccessTokenScopeCategoryOrganization, - ) + ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository) + ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization) allow, err := scope.HasScope(requiredScopes...) if err != nil { - ctx.Error( - http.StatusForbidden, - "tokenRequiresScope", - "checking scope failed: "+err.Error(), - ) + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) return } @@ -313,14 +288,7 @@ func tokenRequiresScopes( return } - ctx.Error( - http.StatusForbidden, - "tokenRequiresScope", - fmt.Sprintf( - "token does not have at least one of required scope(s): %v", - requiredScopes, - ), - ) + ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) } } @@ -338,11 +306,7 @@ func reqToken() func(ctx *context.APIContext) { if pubRepoExists && publicRepo.(bool) && ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { - ctx.Error( - http.StatusForbidden, - "reqToken", - "token scope is limited to public repos", - ) + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") return } @@ -365,19 +329,14 @@ func reqToken() func(ctx *context.APIContext) { func reqExploreSignIn() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if setting.Service.Explore.RequireSigninView && !ctx.IsSigned { - ctx.Error( - http.StatusUnauthorized, - "reqExploreSignIn", - "you must be signed in to search for users", - ) + ctx.Error(http.StatusUnauthorized, "reqExploreSignIn", "you must be signed in to search for users") } } } func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { - if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && - ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { + if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { return } if !ctx.IsBasicAuth { @@ -411,11 +370,7 @@ func reqOwner() func(ctx *context.APIContext) { func reqSelfOrAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer { - ctx.Error( - http.StatusForbidden, - "reqSelfOrAdmin", - "doer should be the site admin or be same as the contextUser", - ) + ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser") return } } @@ -425,11 +380,7 @@ func reqSelfOrAdmin() func(ctx *context.APIContext) { func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error( - http.StatusForbidden, - "reqAdmin", - "user should be an owner or a collaborator with admin write of a repository", - ) + ctx.Error(http.StatusForbidden, "reqAdmin", "user should be an owner or a collaborator with admin write of a repository") return } } @@ -439,11 +390,7 @@ func reqAdmin() func(ctx *context.APIContext) { func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error( - http.StatusForbidden, - "reqRepoWriter", - "user should have a permission to write to a repo", - ) + ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") return } } @@ -452,13 +399,8 @@ func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { // reqRepoBranchWriter user should have a permission to write to a branch, or be a site admin func reqRepoBranchWriter(ctx *context.APIContext) { options, ok := web.GetForm(ctx).(api.FileOptionInterface) - if !ok || - (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) { - ctx.Error( - http.StatusForbidden, - "reqRepoBranchWriter", - "user should have a permission to write to this branch", - ) + if !ok || (!ctx.Repo.CanWriteToBranch(ctx, ctx.Doer, options.Branch()) && !ctx.IsUserSiteAdmin()) { + ctx.Error(http.StatusForbidden, "reqRepoBranchWriter", "user should have a permission to write to this branch") return } } @@ -467,11 +409,7 @@ func reqRepoBranchWriter(ctx *context.APIContext) { func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Repo.CanRead(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { - ctx.Error( - http.StatusForbidden, - "reqRepoReader", - "user should have specific read permission or be a repo admin or a site admin", - ) + ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") return } } @@ -481,11 +419,7 @@ func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { func reqAnyRepoReader() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { if !ctx.Repo.HasAccess() && !ctx.IsUserSiteAdmin() { - ctx.Error( - http.StatusForbidden, - "reqAnyRepoReader", - "user should have any permission to read repository or permissions of site admin", - ) + ctx.Error(http.StatusForbidden, "reqAnyRepoReader", "user should have any permission to read repository or permissions of site admin") return } } @@ -740,11 +674,7 @@ func mustEnableWiki(ctx *context.APIContext) { func mustNotBeArchived(ctx *context.APIContext) { if ctx.Repo.Repository.IsArchived { - ctx.Error( - http.StatusLocked, - "RepoArchived", - fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString()), - ) + ctx.Error(http.StatusLocked, "RepoArchived", fmt.Errorf("%s is archived", ctx.Repo.Repository.LogString())) return } } @@ -762,11 +692,7 @@ func bind[T any](_ T) any { theObj := new(T) // create a new form obj for every request but not use obj directly errs := binding.Bind(ctx.Req, theObj) if len(errs) > 0 { - ctx.Error( - http.StatusUnprocessableEntity, - "validationError", - fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error()), - ) + ctx.Error(http.StatusUnprocessableEntity, "validationError", fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error())) return } web.SetForm(ctx, theObj) @@ -816,11 +742,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC return } if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { - log.Info( - "Failed authentication attempt for %s from %s", - ctx.Doer.Name, - ctx.RemoteAddr(), - ) + log.Info("Failed authentication attempt for %s from %s", ctx.Doer.Name, ctx.RemoteAddr()) ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") ctx.JSON(http.StatusForbidden, map[string]string{ "message": "This account is prohibited from signing in, please contact your site administrator.", @@ -904,10 +826,8 @@ func Routes() *web.Route { AllowedOrigins: setting.CORSConfig.AllowDomain, AllowedMethods: setting.CORSConfig.Methods, AllowCredentials: setting.CORSConfig.AllowCredentials, - AllowedHeaders: append( - []string{"Authorization", "X-Gitea-OTP"}, - setting.CORSConfig.Headers...), - MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), + AllowedHeaders: append([]string{"Authorization", "X-Gitea-OTP"}, setting.CORSConfig.Headers...), + MaxAge: int(setting.CORSConfig.MaxAge.Seconds()), })) } m.Use(context.APIContexter()) @@ -988,12 +908,7 @@ func Routes() *web.Route { m.Get("/heatmap", user.GetUserHeatmapData) } - m.Get( - "/repos", - tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), - reqExploreSignIn(), - user.ListUserRepos, - ) + m.Get("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository), reqExploreSignIn(), user.ListUserRepos) m.Group("/tokens", func() { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) @@ -1087,8 +1002,7 @@ func Routes() *web.Route { m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey) // (repo scope) - m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)). - Get(user.ListMyRepos). + m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(user.ListMyRepos). Post(bind(api.CreateRepoOption{}), repo.Create) // (repo scope) @@ -1123,20 +1037,13 @@ func Routes() *web.Route { }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Repositories (requires repo scope, org scope) - m.Post( - "/org/{org}/repos", - tokenRequiresScopes( - auth_model.AccessTokenScopeCategoryOrganization, - auth_model.AccessTokenScopeCategoryRepository, - ), + m.Post("/org/{org}/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), reqToken(), bind(api.CreateRepoOption{}), - repo.CreateOrgRepoDeprecated, - ) + repo.CreateOrgRepoDeprecated) // requires repo scope - m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)). - Get(repo.GetByID) + m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID) // Repos (requires repo scope) m.Group("/repos", func() { @@ -1149,13 +1056,7 @@ func Routes() *web.Route { m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) - m.Post( - "/generate", - reqToken(), - reqRepoReader(unit.TypeCode), - bind(api.GenerateRepoOption{}), - repo.Generate, - ) + m.Post("/generate", reqToken(), reqRepoReader(unit.TypeCode), bind(api.GenerateRepoOption{}), repo.Generate) m.Group("/transfer", func() { m.Post("", reqOwner(), bind(api.TransferRepoOption{}), repo.Transfer) m.Post("/accept", repo.AcceptTransfer) @@ -1187,12 +1088,7 @@ func Routes() *web.Route { m.Combo("").Get(repo.GetHook). Patch(bind(api.EditHookOption{}), repo.EditHook). Delete(repo.DeleteHook) - m.Post( - "/tests", - context.ReferencesGitRepo(), - context.RepoRefForAPI, - repo.TestHook, - ) + m.Post("/tests", context.ReferencesGitRepo(), context.RepoRefForAPI, repo.TestHook) }) }, reqToken(), reqAdmin(), reqWebhooksEnabled()) m.Group("/collaborators", func() { @@ -1212,79 +1108,31 @@ func Routes() *web.Route { Put(reqAdmin(), repo.AddTeam). Delete(reqAdmin(), repo.DeleteTeam) }, reqToken()) - m.Get( - "/raw/*", - context.ReferencesGitRepo(), - context.RepoRefForAPI, - reqRepoReader(unit.TypeCode), - repo.GetRawFile, - ) - m.Get( - "/media/*", - context.ReferencesGitRepo(), - context.RepoRefForAPI, - reqRepoReader(unit.TypeCode), - repo.GetRawFileOrLFS, - ) + m.Get("/raw/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFile) + m.Get("/media/*", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetRawFileOrLFS) m.Get("/archive/*", reqRepoReader(unit.TypeCode), repo.GetArchive) m.Combo("/forks").Get(repo.ListForks). Post(reqToken(), reqRepoReader(unit.TypeCode), bind(api.CreateForkOption{}), repo.CreateFork) m.Group("/branches", func() { m.Get("", repo.ListBranches) m.Get("/*", repo.GetBranch) - m.Delete( - "/*", - reqToken(), - reqRepoWriter(unit.TypeCode), - mustNotBeArchived, - repo.DeleteBranch, - ) - m.Post( - "", - reqToken(), - reqRepoWriter(unit.TypeCode), - mustNotBeArchived, - bind(api.CreateBranchRepoOption{}), - repo.CreateBranch, - ) + m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch) + m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), repo.CreateBranch) }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) m.Group("/branch_protections", func() { m.Get("", repo.ListBranchProtections) - m.Post( - "", - bind(api.CreateBranchProtectionOption{}), - mustNotBeArchived, - repo.CreateBranchProtection, - ) + m.Post("", bind(api.CreateBranchProtectionOption{}), mustNotBeArchived, repo.CreateBranchProtection) m.Group("/{name}", func() { m.Get("", repo.GetBranchProtection) - m.Patch( - "", - bind(api.EditBranchProtectionOption{}), - mustNotBeArchived, - repo.EditBranchProtection, - ) + m.Patch("", bind(api.EditBranchProtectionOption{}), mustNotBeArchived, repo.EditBranchProtection) m.Delete("", repo.DeleteBranchProtection) }) }, reqToken(), reqAdmin()) m.Group("/tags", func() { m.Get("", repo.ListTags) m.Get("/*", repo.GetTag) - m.Post( - "", - reqToken(), - reqRepoWriter(unit.TypeCode), - mustNotBeArchived, - bind(api.CreateTagOption{}), - repo.CreateTag, - ) - m.Delete( - "/*", - reqToken(), - reqRepoWriter(unit.TypeCode), - mustNotBeArchived, - repo.DeleteTag, - ) + m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateTagOption{}), repo.CreateTag) + m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteTag) }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). @@ -1302,14 +1150,7 @@ func Routes() *web.Route { Patch(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.EditWikiPage). Delete(mustNotBeArchived, reqToken(), reqRepoWriter(unit.TypeWiki), repo.DeleteWikiPage) m.Get("/revisions/{pageName}", repo.ListPageRevisions) - m.Post( - "/new", - reqToken(), - mustNotBeArchived, - reqRepoWriter(unit.TypeWiki), - bind(api.CreateWikiPageOptions{}), - repo.NewWikiPage, - ) + m.Post("/new", reqToken(), mustNotBeArchived, reqRepoWriter(unit.TypeWiki), bind(api.CreateWikiPageOptions{}), repo.NewWikiPage) m.Get("/pages", repo.ListWikiPages) }, mustEnableWiki) m.Post("/markup", reqToken(), bind(api.MarkupOption{}), misc.Markup) @@ -1354,13 +1195,7 @@ func Routes() *web.Route { Get(repo.GetPushMirrorByName) }, reqAdmin(), reqToken()) - m.Get( - "/editorconfig/{filename}", - context.ReferencesGitRepo(), - context.RepoRefForAPI, - reqRepoReader(unit.TypeCode), - repo.GetEditorconfig, - ) + m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig) m.Group("/pulls", func() { m.Combo("").Get(repo.ListPullRequests). Post(reqToken(), mustNotBeArchived, bind(api.CreatePullRequestOption{}), repo.CreatePullRequest) @@ -1386,12 +1221,7 @@ func Routes() *web.Route { Post(reqToken(), bind(api.SubmitPullReviewOptions{}), repo.SubmitPullReview) m.Combo("/comments"). Get(repo.GetPullReviewComments) - m.Post( - "/dismissals", - reqToken(), - bind(api.DismissPullReviewOptions{}), - repo.DismissPullReview, - ) + m.Post("/dismissals", reqToken(), bind(api.DismissPullReviewOptions{}), repo.DismissPullReview) m.Post("/undismissals", reqToken(), repo.UnDismissPullReview) }) }) @@ -1423,47 +1253,15 @@ func Routes() *web.Route { m.Get("/tags/{sha}", repo.GetAnnotatedTag) m.Get("/notes/{sha}", repo.GetNote) }, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode)) - m.Post( - "/diffpatch", - reqRepoWriter(unit.TypeCode), - reqToken(), - bind(api.ApplyDiffPatchFileOptions{}), - mustNotBeArchived, - repo.ApplyDiffPatch, - ) + m.Post("/diffpatch", reqRepoWriter(unit.TypeCode), reqToken(), bind(api.ApplyDiffPatchFileOptions{}), mustNotBeArchived, repo.ApplyDiffPatch) m.Group("/contents", func() { m.Get("", repo.GetContentsList) - m.Post( - "", - reqToken(), - bind(api.ChangeFilesOptions{}), - reqRepoBranchWriter, - mustNotBeArchived, - repo.ChangeFiles, - ) + m.Post("", reqToken(), bind(api.ChangeFilesOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.ChangeFiles) m.Get("/*", repo.GetContents) m.Group("/*", func() { - m.Post( - "", - bind(api.CreateFileOptions{}), - reqRepoBranchWriter, - mustNotBeArchived, - repo.CreateFile, - ) - m.Put( - "", - bind(api.UpdateFileOptions{}), - reqRepoBranchWriter, - mustNotBeArchived, - repo.UpdateFile, - ) - m.Delete( - "", - bind(api.DeleteFileOptions{}), - reqRepoBranchWriter, - mustNotBeArchived, - repo.DeleteFile, - ) + m.Post("", bind(api.CreateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.CreateFile) + m.Put("", bind(api.UpdateFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.UpdateFile) + m.Delete("", bind(api.DeleteFileOptions{}), reqRepoBranchWriter, mustNotBeArchived, repo.DeleteFile) }, reqToken()) }, reqRepoReader(unit.TypeCode)) m.Get("/signing-key.gpg", misc.SigningKey) @@ -1477,11 +1275,7 @@ func Routes() *web.Route { }, reqAnyRepoReader()) m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates) m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig) - m.Get( - "/issue_config/validate", - context.ReferencesGitRepo(), - repo.ValidateIssueConfig, - ) + m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig) m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages) m.Get("/activities/feeds", repo.ListRepoActivityFeeds) m.Get("/new_pin_allowed", repo.AreNewIssuePinsAllowed) @@ -1539,8 +1333,7 @@ func Routes() *web.Route { m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/{id}", reqToken()). - Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) @@ -1558,8 +1351,7 @@ func Routes() *web.Route { Delete(repo.ResetIssueTime) m.Delete("/{id}", repo.DeleteTime) }, reqToken()) - m.Combo("/deadline"). - Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) + m.Combo("/deadline").Post(reqToken(), bind(api.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Group("/stopwatch", func() { m.Post("/start", repo.StartIssueStopwatch) m.Post("/stop", repo.StopIssueStopwatch) @@ -1615,9 +1407,7 @@ func Routes() *web.Route { Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) m.Group("/projects", func() { - m. - Combo(""). - Get(projects.ListRepoProjects). + m.Combo("").Get(projects.ListRepoProjects). Post(bind(api.NewProjectPayload{}), projects.CreateRepoProject) }, mustEnableIssues) }, repoAssignment()) @@ -1627,43 +1417,20 @@ func Routes() *web.Route { m.Group("/packages/{username}", func() { m.Group("/{type}/{name}/{version}", func() { m.Get("", reqToken(), packages.GetPackage) - m.Delete( - "", - reqToken(), - reqPackageAccess(perm.AccessModeWrite), - packages.DeletePackage, - ) + m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) m.Get("/files", reqToken(), packages.ListPackageFiles) }) m.Get("/", reqToken(), packages.ListPackages) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context_service.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) // Organizations - m.Get( - "/user/orgs", - reqToken(), - tokenRequiresScopes( - auth_model.AccessTokenScopeCategoryUser, - auth_model.AccessTokenScopeCategoryOrganization, - ), - org.ListMyOrgs, - ) + m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) m.Group("/users/{username}/orgs", func() { m.Get("", reqToken(), org.ListUserOrgs) m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context_service.UserAssignmentAPI()) - m.Post( - "/orgs", - tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), - reqToken(), - bind(api.CreateOrgOption{}), - org.Create, - ) - m.Get( - "/orgs", - org.GetAll, - tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), - ) + m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) + m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) m.Group("/orgs/{org}", func() { m.Combo("").Get(org.Get). Patch(reqToken(), reqOrgOwnership(), bind(api.EditOrgOption{}), org.Edit). @@ -1700,13 +1467,7 @@ func Routes() *web.Route { }, reqToken(), reqOrgMembership()) m.Group("/labels", func() { m.Get("", org.ListLabels) - m.Post( - "", - reqToken(), - reqOrgOwnership(), - bind(api.CreateLabelOption{}), - org.CreateLabel, - ) + m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateLabelOption{}), org.CreateLabel) m.Combo("/{id}").Get(reqToken(), org.GetLabel). Patch(reqToken(), reqOrgOwnership(), bind(api.EditLabelOption{}), org.EditLabel). Delete(reqToken(), reqOrgOwnership(), org.DeleteLabel) @@ -1723,10 +1484,8 @@ func Routes() *web.Route { m.Delete("", org.DeleteAvatar) }, reqToken(), reqOrgOwnership()) m.Get("/activities/feeds", org.ListOrgActivityFeeds) - m.Group("/projects", func() { - m.Combo(""). - Get(projects.ListOrgProjects). + m.Combo("").Get(projects.ListOrgProjects). Post(bind(api.NewProjectPayload{}), projects.CreateOrgProject) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue), reqToken(), reqOrgMembership()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true)) @@ -1793,11 +1552,8 @@ func Routes() *web.Route { m.Get("/registration-token", admin.GetRegistrationToken) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) - m.Group("/projects", func() { - m. - Combo("/{id}"). - Get(projects.GetProject). + m.Combo("/{id}").Get(projects.GetProject). Patch(bind(api.UpdateProjectPayload{}), projects.UpdateProject). Delete(projects.DeleteProject) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue), reqToken()) From 065001c0815d3efdaa4fc8519ef1d1f89a27ba28 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 21 Jan 2024 15:32:42 +0100 Subject: [PATCH 17/44] more reverts --- routers/api/v1/api.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index df2222acb8b48..408952edb0390 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1037,7 +1037,8 @@ func Routes() *web.Route { }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Repositories (requires repo scope, org scope) - m.Post("/org/{org}/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), + m.Post("/org/{org}/repos", + tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) @@ -1552,6 +1553,7 @@ func Routes() *web.Route { m.Get("/registration-token", admin.GetRegistrationToken) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) + m.Group("/projects", func() { m.Combo("/{id}").Get(projects.GetProject). Patch(bind(api.UpdateProjectPayload{}), projects.UpdateProject). From 58e56cd0c67b9945194ffe338f8c7efeca2b205c Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 21 Jan 2024 15:39:55 +0100 Subject: [PATCH 18/44] more formatting fixes --- routers/api/v1/projects/project.go | 35 ++++++++++----------------- tests/integration/api_project_test.go | 4 +-- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 4aed86d7de4dd..4bcf241082801 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -15,10 +15,7 @@ import ( "code.gitea.io/gitea/services/convert" ) -func innerCreateProject( - ctx *context.APIContext, - projectType project_model.Type, -) { +func innerCreateProject(ctx *context.APIContext, projectType project_model.Type) { form := web.GetForm(ctx).(*api.NewProjectPayload) project := &project_model.Project{ RepoID: 0, @@ -287,12 +284,10 @@ func ListUserProjects(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ - Type: project_model.TypeIndividual, - IsClosed: ctx.FormOptionalBool("closed"), - OwnerID: ctx.Doer.ID, - ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), - }, + Type: project_model.TypeIndividual, + IsClosed: ctx.FormOptionalBool("closed"), + OwnerID: ctx.Doer.ID, + ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, }) if err != nil { ctx.Error(http.StatusInternalServerError, "Projects", err) @@ -343,12 +338,10 @@ func ListOrgProjects(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ - OwnerID: ctx.Org.Organization.AsUser().ID, - ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), - }, - IsClosed: ctx.FormOptionalBool("closed"), - Type: project_model.TypeOrganization, + OwnerID: ctx.Org.Organization.AsUser().ID, + ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, + IsClosed: ctx.FormOptionalBool("closed"), + Type: project_model.TypeOrganization, }) if err != nil { ctx.Error(http.StatusInternalServerError, "Projects", err) @@ -404,12 +397,10 @@ func ListRepoProjects(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ - RepoID: ctx.Repo.Repository.ID, - IsClosed: ctx.FormOptionalBool("closed"), - Type: project_model.TypeRepository, - ListOptions: db.ListOptions{ - Page: ctx.FormInt("page"), - }, + RepoID: ctx.Repo.Repository.ID, + IsClosed: ctx.FormOptionalBool("closed"), + Type: project_model.TypeRepository, + ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, }) if err != nil { ctx.Error(http.StatusInternalServerError, "Projects", err) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 03ada63336729..15f431683c55c 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -149,9 +149,7 @@ func TestAPIUpdateProject(t *testing.T) { token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteUser, auth_model.AccessTokenScopeWriteIssue) link, _ := url.Parse(fmt.Sprintf("/api/v1/projects/%d", 1)) - req := NewRequestWithJSON(t, "PATCH", link.String(), &api.UpdateProjectPayload{ - Title: "First project updated", - }).AddTokenAuth(token) + req := NewRequestWithJSON(t, "PATCH", link.String(), &api.UpdateProjectPayload{Title: "First project updated"}).AddTokenAuth(token) var apiProject *api.Project From 74043f7b4c1b9048c9be2d6ce587315b85b0ef31 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Sun, 21 Jan 2024 15:48:45 +0100 Subject: [PATCH 19/44] fix project sort test --- models/project/project_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/models/project/project_test.go b/models/project/project_test.go index 7a37c1faf2908..8a6dcfee9b52d 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -92,19 +92,19 @@ func TestProjectsSort(t *testing.T) { }{ { sortType: "default", - wants: []int64{1, 3, 2, 4}, + wants: []int64{1, 3, 2, 4, 5, 6}, }, { sortType: "oldest", - wants: []int64{4, 2, 3, 1}, + wants: []int64{4, 5, 6, 2, 3, 1}, }, { sortType: "recentupdate", - wants: []int64{1, 3, 2, 4}, + wants: []int64{1, 3, 2, 4, 5, 6}, }, { sortType: "leastupdate", - wants: []int64{4, 2, 3, 1}, + wants: []int64{4, 5, 6, 2, 3, 1}, }, } @@ -113,8 +113,8 @@ func TestProjectsSort(t *testing.T) { OrderBy: GetSearchOrderByBySortType(tt.sortType), }) assert.NoError(t, err) - assert.EqualValues(t, int64(4), count) - if assert.Len(t, projects, 4) { + assert.EqualValues(t, int64(6), count) + if assert.Len(t, projects, 6) { for i := range projects { assert.EqualValues(t, tt.wants[i], projects[i].ID) } From 4475c4f6fc43d988d50868a0aa062c1d2bbbc339 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Mon, 22 Jan 2024 13:43:10 +0100 Subject: [PATCH 20/44] actually fix test! --- models/project/project_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/models/project/project_test.go b/models/project/project_test.go index 8a6dcfee9b52d..8fbbdedecf012 100644 --- a/models/project/project_test.go +++ b/models/project/project_test.go @@ -92,7 +92,7 @@ func TestProjectsSort(t *testing.T) { }{ { sortType: "default", - wants: []int64{1, 3, 2, 4, 5, 6}, + wants: []int64{1, 3, 2, 6, 5, 4}, }, { sortType: "oldest", @@ -100,7 +100,7 @@ func TestProjectsSort(t *testing.T) { }, { sortType: "recentupdate", - wants: []int64{1, 3, 2, 4, 5, 6}, + wants: []int64{1, 3, 2, 6, 5, 4}, }, { sortType: "leastupdate", From 927da10445beb48a0c558c87d9c6ef8c82f2a478 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Fri, 26 Jan 2024 10:07:47 +0530 Subject: [PATCH 21/44] chore: swagger doc typo corrections --- routers/api/v1/projects/project.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 4bcf241082801..8758f538e740d 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -260,13 +260,13 @@ func DeleteProject(ctx *context.APIContext) { func ListUserProjects(ctx *context.APIContext) { // swagger:operation GET /user/projects project projectListUserProjects // --- - // summary: List repository projects + // summary: List user projects // produces: // - application/json // parameters: // - name: closed // in: query - // description: include closed issues or not + // description: include closed projects or not // type: boolean // - name: page // in: query @@ -290,7 +290,7 @@ func ListUserProjects(ctx *context.APIContext) { ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListUserProjets", err) return } @@ -299,7 +299,7 @@ func ListUserProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListUserProjects", err) return } @@ -309,7 +309,7 @@ func ListUserProjects(ctx *context.APIContext) { func ListOrgProjects(ctx *context.APIContext) { // swagger:operation GET /orgs/{org}/projects project projectListOrgProjects // --- - // summary: List repository projects + // summary: List org projects // produces: // - application/json // parameters: @@ -320,7 +320,7 @@ func ListOrgProjects(ctx *context.APIContext) { // required: true // - name: closed // in: query - // description: include closed issues or not + // description: include closed projects or not // type: boolean // - name: page // in: query @@ -344,7 +344,7 @@ func ListOrgProjects(ctx *context.APIContext) { Type: project_model.TypeOrganization, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListOrgProjects", err) return } @@ -353,7 +353,7 @@ func ListOrgProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListOrgProjects", err) return } @@ -379,7 +379,7 @@ func ListRepoProjects(ctx *context.APIContext) { // required: true // - name: closed // in: query - // description: include closed issues or not + // description: include closed projects or not // type: boolean // - name: page // in: query @@ -403,7 +403,7 @@ func ListRepoProjects(ctx *context.APIContext) { ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListRepoProjects", err) return } @@ -412,7 +412,7 @@ func ListRepoProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "Projects", err) + ctx.Error(http.StatusInternalServerError, "ListRepoProjects", err) return } From 975d98d8f1645c91045875d07b0fab718e18405c Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Fri, 26 Jan 2024 10:08:00 +0530 Subject: [PATCH 22/44] chore: remove redundant comment --- services/convert/project.go | 1 - 1 file changed, 1 deletion(-) diff --git a/services/convert/project.go b/services/convert/project.go index 42ce611e15bbe..f721dc9ca2fcf 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -21,7 +21,6 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro Closed: project.ClosedDateUnix.AsTime(), } - // try to laod the repo _ = project.LoadRepo(ctx) if project.Repo != nil { apiProject.Repo = &api.RepositoryMeta{ From d4ea5a57d465cab30ab71feb35b3fe9363068abb Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Fri, 26 Jan 2024 10:08:24 +0530 Subject: [PATCH 23/44] fix: use the page field from parameters --- routers/api/v1/projects/project.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 8758f538e740d..04ed55f22363d 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -396,18 +396,20 @@ func ListRepoProjects(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" + + page := ctx.FormInt("page") projects, count, err := db.FindAndCount[project_model.Project](ctx, project_model.SearchOptions{ RepoID: ctx.Repo.Repository.ID, IsClosed: ctx.FormOptionalBool("closed"), Type: project_model.TypeRepository, - ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, + ListOptions: db.ListOptions{Page: page}, }) if err != nil { ctx.Error(http.StatusInternalServerError, "ListRepoProjects", err) return } - ctx.SetLinkHeader(int(count), setting.UI.IssuePagingNum) + ctx.SetLinkHeader(int(count), page) ctx.SetTotalCountHeader(count) apiProjects, err := convert.ToAPIProjectList(ctx, projects) From 0a0837f47b67180311556636e7fe98530b76f618 Mon Sep 17 00:00:00 2001 From: dineshsalunke Date: Fri, 26 Jan 2024 11:15:37 +0530 Subject: [PATCH 24/44] chore: update the swagger file --- templates/swagger/v1_json.tmpl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index ee320f6d621ac..3c14c36da44ce 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2365,7 +2365,7 @@ "tags": [ "project" ], - "summary": "List repository projects", + "summary": "List org projects", "operationId": "projectListOrgProjects", "parameters": [ { @@ -2377,7 +2377,7 @@ }, { "type": "boolean", - "description": "include closed issues or not", + "description": "include closed projects or not", "name": "closed", "in": "query" }, @@ -10401,7 +10401,7 @@ }, { "type": "boolean", - "description": "include closed issues or not", + "description": "include closed projects or not", "name": "closed", "in": "query" }, @@ -15872,12 +15872,12 @@ "tags": [ "project" ], - "summary": "List repository projects", + "summary": "List user projects", "operationId": "projectListUserProjects", "parameters": [ { "type": "boolean", - "description": "include closed issues or not", + "description": "include closed projects or not", "name": "closed", "in": "query" }, From a7aabc5349b2e7c322b0d78da39a3ff196d3925c Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sun, 22 Sep 2024 09:17:06 +0530 Subject: [PATCH 25/44] refactor: add the missing parameter bodies for project --- routers/api/v1/swagger/options.go | 6 ++++++ templates/swagger/v1_json.tmpl | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 1de58632d57fa..f717a49094b4e 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -205,4 +205,10 @@ type swaggerParameterBodies struct { // in:body UpdateVariableOption api.UpdateVariableOption + + // in:body + NewProjectPayload api.NewProjectPayload + + // in:body + UpdateProjectPayload api.UpdateProjectPayload } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 2fb80388ad776..f30c8b70e85e4 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -26745,7 +26745,7 @@ "parameterBodies": { "description": "parameterBodies", "schema": { - "$ref": "#/definitions/UpdateVariableOption" + "$ref": "#/definitions/UpdateProjectPayload" } }, "redirect": { @@ -26844,4 +26844,4 @@ "TOTPHeader": [] } ] -} +} \ No newline at end of file From 7383e7bf324feb07110c331cb54b691de995268b Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sun, 22 Sep 2024 11:59:05 +0530 Subject: [PATCH 26/44] refactor: rename BoardType to TemplateType --- services/convert/project.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/services/convert/project.go b/services/convert/project.go index f721dc9ca2fcf..f5e5ecf41e946 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -12,13 +12,13 @@ import ( func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Project, error) { apiProject := &api.Project{ - Title: project.Title, - Description: project.Description, - BoardType: uint8(project.BoardType), - IsClosed: project.IsClosed, - Created: project.CreatedUnix.AsTime(), - Updated: project.UpdatedUnix.AsTime(), - Closed: project.ClosedDateUnix.AsTime(), + Title: project.Title, + Description: project.Description, + TemplateType: uint8(project.TemplateType), + IsClosed: project.IsClosed, + Created: project.CreatedUnix.AsTime(), + Updated: project.UpdatedUnix.AsTime(), + Closed: project.ClosedDateUnix.AsTime(), } _ = project.LoadRepo(ctx) From 016aa727c05302d61197b4583ed66091316df08e Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sun, 22 Sep 2024 11:59:23 +0530 Subject: [PATCH 27/44] refactor: add creator property to the project --- models/project/project.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/project/project.go b/models/project/project.go index d6f6db2b5a29d..5c1fafa20505a 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -91,6 +91,7 @@ type Project struct { RepoID int64 `xorm:"INDEX"` Repo *repo_model.Repository `xorm:"-"` CreatorID int64 `xorm:"NOT NULL"` + Creator *user_model.User `xorm:"-"` IsClosed bool `xorm:"INDEX"` TemplateType TemplateType `xorm:"'board_type'"` // TODO: rename the column to template_type CardType CardType From d067b1d34feed6e857292b3174f46f9a40000005 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sun, 22 Sep 2024 11:59:41 +0530 Subject: [PATCH 28/44] refactor: add BoardType to TemplateType property --- modules/structs/project.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/structs/project.go b/modules/structs/project.go index 46e7bebfcd9b8..26f23d6fd6740 100644 --- a/modules/structs/project.go +++ b/modules/structs/project.go @@ -25,11 +25,11 @@ type UpdateProjectPayload struct { // swagger:model type Project struct { - ID int64 `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - BoardType uint8 `json:"board_type"` - IsClosed bool `json:"is_closed"` + ID int64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + TemplateType uint8 `json:"board_type"` + IsClosed bool `json:"is_closed"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time From eeca89efb8217864683f46bba96df1b6dcb931ec Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Sun, 22 Sep 2024 12:00:14 +0530 Subject: [PATCH 29/44] refactor: handle errors when converting to APIProject --- services/convert/project.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/services/convert/project.go b/services/convert/project.go index f5e5ecf41e946..1cb00696abd15 100644 --- a/services/convert/project.go +++ b/services/convert/project.go @@ -21,7 +21,9 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro Closed: project.ClosedDateUnix.AsTime(), } - _ = project.LoadRepo(ctx) + if err := project.LoadRepo(ctx); err != nil { + return nil, err + } if project.Repo != nil { apiProject.Repo = &api.RepositoryMeta{ ID: project.RepoID, @@ -31,7 +33,9 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro } } - _ = project.LoadCreator(ctx) + if err := project.LoadCreator(ctx); err != nil { + return nil, err + } if project.Creator != nil { apiProject.Creator = &api.User{ ID: project.Creator.ID, @@ -40,7 +44,9 @@ func ToAPIProject(ctx context.Context, project *project_model.Project) (*api.Pro } } - _ = project.LoadOwner(ctx) + if err := project.LoadOwner(ctx); err != nil { + return nil, err + } if project.Owner != nil { apiProject.Owner = &api.User{ ID: project.Owner.ID, From 185a594d329719a583668ff2d912a0b772d984da Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Mon, 23 Sep 2024 07:49:48 +0530 Subject: [PATCH 30/44] refactor: update the context --- routers/api/v1/projects/project.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 04ed55f22363d..277ca6b58386b 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -8,23 +8,23 @@ import ( "code.gitea.io/gitea/models/db" project_model "code.gitea.io/gitea/models/project" - "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" ) func innerCreateProject(ctx *context.APIContext, projectType project_model.Type) { form := web.GetForm(ctx).(*api.NewProjectPayload) project := &project_model.Project{ - RepoID: 0, - OwnerID: ctx.Doer.ID, - Title: form.Title, - Description: form.Description, - CreatorID: ctx.Doer.ID, - BoardType: project_model.BoardType(form.BoardType), - Type: projectType, + RepoID: 0, + OwnerID: ctx.Doer.ID, + Title: form.Title, + Description: form.Description, + CreatorID: ctx.Doer.ID, + TemplateType: project_model.TemplateType(form.BoardType), + Type: projectType, } if ctx.ContextUser != nil { @@ -158,7 +158,7 @@ func GetProject(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" - project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id")) + project, err := project_model.GetProjectByID(ctx, ctx.FormInt64(":id")) if err != nil { if project_model.IsErrProjectNotExist(err) { ctx.NotFound() @@ -202,7 +202,7 @@ func UpdateProject(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" form := web.GetForm(ctx).(*api.UpdateProjectPayload) - project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64("id")) + project, err := project_model.GetProjectByID(ctx, ctx.FormInt64("id")) if err != nil { if project_model.IsErrProjectNotExist(err) { ctx.NotFound() @@ -249,7 +249,7 @@ func DeleteProject(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if err := project_model.DeleteProjectByID(ctx, ctx.ParamsInt64(":id")); err != nil { + if err := project_model.DeleteProjectByID(ctx, ctx.FormInt64(":id")); err != nil { ctx.Error(http.StatusInternalServerError, "DeleteProjectByID", err) return } From 30027ac0bddf9b90720abd0956e7d7ecbc8ceef2 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Mon, 23 Sep 2024 07:50:11 +0530 Subject: [PATCH 31/44] refactor: update the board type enums --- tests/integration/api_project_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/integration/api_project_test.go b/tests/integration/api_project_test.go index 15f431683c55c..740610050a4cd 100644 --- a/tests/integration/api_project_test.go +++ b/tests/integration/api_project_test.go @@ -20,7 +20,7 @@ import ( func TestAPICreateUserProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.TemplateTypeBasicKanban) token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteUser) @@ -34,13 +34,13 @@ func TestAPICreateUserProject(t *testing.T) { DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, boardType, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.TemplateType) assert.Equal(t, "user2", apiProject.Creator.UserName) } func TestAPICreateOrgProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.TemplateTypeBasicKanban) orgName := "org17" token := getUserToken(t, "user2", auth_model.AccessTokenScopeWriteIssue, auth_model.AccessTokenScopeWriteOrganization) @@ -56,14 +56,14 @@ func TestAPICreateOrgProject(t *testing.T) { DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, boardType, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.TemplateType) assert.Equal(t, "user2", apiProject.Creator.UserName) assert.Equal(t, "org17", apiProject.Owner.UserName) } func TestAPICreateRepoProject(t *testing.T) { defer tests.PrepareTestEnv(t)() - const title, description, boardType = "project_name", "project_description", uint8(project_model.BoardTypeBasicKanban) + const title, description, boardType = "project_name", "project_description", uint8(project_model.TemplateTypeBasicKanban) ownerName := "user2" repoName := "repo1" @@ -80,7 +80,7 @@ func TestAPICreateRepoProject(t *testing.T) { DecodeJSON(t, resp, &apiProject) assert.Equal(t, title, apiProject.Title) assert.Equal(t, description, apiProject.Description) - assert.Equal(t, boardType, apiProject.BoardType) + assert.Equal(t, boardType, apiProject.TemplateType) assert.Equal(t, "repo1", apiProject.Repo.Name) } From 596dcc01b5944856c5a3f791ca1a30c96e90bf53 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Tue, 24 Jun 2025 07:40:08 +0530 Subject: [PATCH 32/44] refactor: return ApiError instead of Error from projects methods --- routers/api/v1/projects/project.go | 34 +++++++++++++++--------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/routers/api/v1/projects/project.go b/routers/api/v1/projects/project.go index 277ca6b58386b..22264038e741f 100644 --- a/routers/api/v1/projects/project.go +++ b/routers/api/v1/projects/project.go @@ -36,19 +36,19 @@ func innerCreateProject(ctx *context.APIContext, projectType project_model.Type) } if err := project_model.NewProject(ctx, project); err != nil { - ctx.Error(http.StatusInternalServerError, "NewProject", err) + ctx.APIErrorInternal(err) return } project, err := project_model.GetProjectByID(ctx, project.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, "NewProject", err) + ctx.APIErrorInternal(err) return } projectResponse, err := convert.ToAPIProject(ctx, project) if err != nil { - ctx.Error(http.StatusInternalServerError, "NewProject", err) + ctx.APIErrorInternal(err) return } @@ -161,16 +161,16 @@ func GetProject(ctx *context.APIContext) { project, err := project_model.GetProjectByID(ctx, ctx.FormInt64(":id")) if err != nil { if project_model.IsErrProjectNotExist(err) { - ctx.NotFound() + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "GetProjectByID", err) + ctx.APIErrorInternal(err) } return } projectResponse, err := convert.ToAPIProject(ctx, project) if err != nil { - ctx.Error(http.StatusInternalServerError, "GetProjectByID", err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, projectResponse) @@ -205,9 +205,9 @@ func UpdateProject(ctx *context.APIContext) { project, err := project_model.GetProjectByID(ctx, ctx.FormInt64("id")) if err != nil { if project_model.IsErrProjectNotExist(err) { - ctx.NotFound() + ctx.APIError(http.StatusNotFound, err) } else { - ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + ctx.APIErrorInternal(err) } return } @@ -220,12 +220,12 @@ func UpdateProject(ctx *context.APIContext) { err = project_model.UpdateProject(ctx, project) if err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + ctx.APIErrorInternal(err) return } projectResponse, err := convert.ToAPIProject(ctx, project) if err != nil { - ctx.Error(http.StatusInternalServerError, "UpdateProject", err) + ctx.APIErrorInternal(err) return } ctx.JSON(http.StatusOK, projectResponse) @@ -250,7 +250,7 @@ func DeleteProject(ctx *context.APIContext) { // "$ref": "#/responses/notFound" if err := project_model.DeleteProjectByID(ctx, ctx.FormInt64(":id")); err != nil { - ctx.Error(http.StatusInternalServerError, "DeleteProjectByID", err) + ctx.APIErrorInternal(err) return } @@ -290,7 +290,7 @@ func ListUserProjects(ctx *context.APIContext) { ListOptions: db.ListOptions{Page: ctx.FormInt("page")}, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListUserProjets", err) + ctx.APIErrorInternal(err) return } @@ -299,7 +299,7 @@ func ListUserProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListUserProjects", err) + ctx.APIErrorInternal(err) return } @@ -344,7 +344,7 @@ func ListOrgProjects(ctx *context.APIContext) { Type: project_model.TypeOrganization, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListOrgProjects", err) + ctx.APIErrorInternal(err) return } @@ -353,7 +353,7 @@ func ListOrgProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListOrgProjects", err) + ctx.APIErrorInternal(err) return } @@ -405,7 +405,7 @@ func ListRepoProjects(ctx *context.APIContext) { ListOptions: db.ListOptions{Page: page}, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListRepoProjects", err) + ctx.APIErrorInternal(err) return } @@ -414,7 +414,7 @@ func ListRepoProjects(ctx *context.APIContext) { apiProjects, err := convert.ToAPIProjectList(ctx, projects) if err != nil { - ctx.Error(http.StatusInternalServerError, "ListRepoProjects", err) + ctx.APIErrorInternal(err) return } From fbe7e8e99456b8a4af531005a040792bcd7a8024 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Tue, 24 Jun 2025 07:40:33 +0530 Subject: [PATCH 33/44] refactor: create projects method for usr, org and repo --- routers/api/v1/api.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index d112b3015cccb..dac37f560aaa1 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1163,6 +1163,11 @@ func Routes() *web.Router { m.Delete("", user.UnblockUser) }, context.UserAssignmentAPI(), checkTokenPublicOnly()) }) + + m.Group("/projects", func() { + m.Get("", projects.ListUserProjects) + m.Post("", bind(api.NewProjectPayload{}), projects.CreateUserProject) + }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Repositories (requires repo scope, org scope) @@ -1462,6 +1467,10 @@ func Routes() *web.Router { }, reqAdmin(), reqToken()) m.Get("/{ball_type:tarball|zipball|bundle}/*", reqRepoReader(unit.TypeCode), repo.DownloadArchive) + + m.Group("/projects", func() { + m.Post("", bind(api.NewProjectPayload{}), projects.CreateRepoProject) + }) }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) @@ -1594,10 +1603,6 @@ func Routes() *web.Router { Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) - m.Group("/projects", func() { - m.Combo("").Get(projects.ListRepoProjects). - Post(bind(api.NewProjectPayload{}), projects.CreateRepoProject) - }, mustEnableIssues) }, repoAssignment()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) @@ -1686,6 +1691,10 @@ func Routes() *web.Router { m.Delete("", org.UnblockUser) }) }, reqToken(), reqOrgOwnership()) + + m.Group("/projects", func() { + m.Post("", bind(api.NewProjectPayload{}), projects.CreateOrgProject) + }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly()) m.Group("/teams/{teamid}", func() { m.Combo("").Get(reqToken(), org.GetTeam). @@ -1764,11 +1773,6 @@ func Routes() *web.Router { }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryAdmin), reqToken(), reqSiteAdmin()) - m.Group("/projects", func() { - m.Combo("/{id}").Get(projects.GetProject). - Patch(bind(api.UpdateProjectPayload{}), projects.UpdateProject). - Delete(projects.DeleteProject) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue), reqToken()) m.Group("/topics", func() { m.Get("/search", repo.TopicSearch) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) From 20a697eefda136389ae05e00af6f7d58db271b46 Mon Sep 17 00:00:00 2001 From: Denys Konovalov Date: Thu, 26 Jun 2025 22:11:29 +0200 Subject: [PATCH 34/44] Fix formatting and swagger --- routers/api/v1/swagger/options.go | 2 +- templates/swagger/v1_json.tmpl | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index e6168afdad2ff..cf2dc951153ca 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -225,5 +225,5 @@ type swaggerParameterBodies struct { // in:body UpdateProjectPayload api.UpdateProjectPayload - LockIssueOption api.LockIssueOption + LockIssueOption api.LockIssueOption } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 7e7c0b683799b..5f7f8d6e846b3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -26878,7 +26878,7 @@ "board_type": { "type": "integer", "format": "uint8", - "x-go-name": "BoardType" + "x-go-name": "TemplateType" }, "closed_at": { "type": "string", @@ -30035,6 +30035,9 @@ "description": "parameterBodies", "schema": { "$ref": "#/definitions/LockIssueOption" + }, + "headers": { + "LockIssueOption": {} } }, "redirect": { @@ -30133,3 +30136,4 @@ "TOTPHeader": [] } ] +} \ No newline at end of file From dd7edfbafaa79fb55d2a2c3143aa76ca90558b27 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Mon, 7 Jul 2025 00:33:00 +0530 Subject: [PATCH 35/44] refactor: add missing middleware --- routers/api/v1/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index dac37f560aaa1..0f3533e2f47fc 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1603,7 +1603,7 @@ func Routes() *web.Router { Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) - }, repoAssignment()) + }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs From 8358eef602eb787204711506b9e88e5a117e06a2 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Mon, 7 Jul 2025 00:34:10 +0530 Subject: [PATCH 36/44] refactor: add list projects get method --- routers/api/v1/api.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0f3533e2f47fc..fbbf4757be910 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1694,6 +1694,7 @@ func Routes() *web.Router { m.Group("/projects", func() { m.Post("", bind(api.NewProjectPayload{}), projects.CreateOrgProject) + m.Get("", projects.ListOrgProjects) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly()) m.Group("/teams/{teamid}", func() { From ef3fdec3ae9212580aa4b5198e433d548eceb3c9 Mon Sep 17 00:00:00 2001 From: Dinesh Salunke Date: Mon, 7 Jul 2025 00:37:55 +0530 Subject: [PATCH 37/44] chore: merge upstream --- .eslintrc.cjs | 3 +- .golangci.yml | 10 + CODE_OF_CONDUCT.md | 4 +- Makefile | 15 +- README.md | 4 +- SECURITY.md | 54 +- cmd/embedded.go | 8 +- contrib/backport/backport.go | 2 +- models/actions/run_job.go | 4 +- models/actions/run_job_status_test.go | 6 +- models/actions/task.go | 9 +- models/asymkey/gpg_key_add.go | 2 +- models/asymkey/gpg_key_verify.go | 2 +- models/asymkey/ssh_key_parse.go | 2 +- models/asymkey/ssh_key_verify.go | 2 +- models/auth/auth_token.go | 2 +- models/db/context.go | 9 + models/db/sql_postgres_with_schema.go | 4 +- models/git/protected_branch.go | 2 +- models/issues/comment.go | 10 +- models/issues/issue_search.go | 2 +- models/issues/stopwatch.go | 167 +- models/issues/stopwatch_test.go | 61 +- models/migrations/base/tests.go | 5 +- models/migrations/v1_10/v100.go | 2 +- models/migrations/v1_10/v101.go | 2 +- models/migrations/v1_10/v88.go | 2 +- models/migrations/v1_10/v89.go | 2 +- models/migrations/v1_10/v90.go | 2 +- models/migrations/v1_10/v91.go | 2 +- models/migrations/v1_10/v92.go | 2 +- models/migrations/v1_10/v93.go | 2 +- models/migrations/v1_10/v94.go | 2 +- models/migrations/v1_10/v95.go | 2 +- models/migrations/v1_10/v96.go | 2 +- models/migrations/v1_10/v97.go | 2 +- models/migrations/v1_10/v98.go | 2 +- models/migrations/v1_10/v99.go | 2 +- models/migrations/v1_11/v102.go | 2 +- models/migrations/v1_11/v103.go | 2 +- models/migrations/v1_11/v104.go | 2 +- models/migrations/v1_11/v105.go | 2 +- models/migrations/v1_11/v106.go | 2 +- models/migrations/v1_11/v107.go | 2 +- models/migrations/v1_11/v108.go | 2 +- models/migrations/v1_11/v109.go | 2 +- models/migrations/v1_11/v110.go | 2 +- models/migrations/v1_11/v111.go | 2 +- models/migrations/v1_11/v112.go | 6 +- models/migrations/v1_11/v113.go | 2 +- models/migrations/v1_11/v114.go | 2 +- models/migrations/v1_11/v115.go | 2 +- models/migrations/v1_11/v116.go | 2 +- models/migrations/v1_12/v117.go | 2 +- models/migrations/v1_12/v118.go | 2 +- models/migrations/v1_12/v119.go | 2 +- models/migrations/v1_12/v120.go | 2 +- models/migrations/v1_12/v121.go | 2 +- models/migrations/v1_12/v122.go | 2 +- models/migrations/v1_12/v123.go | 2 +- models/migrations/v1_12/v124.go | 2 +- models/migrations/v1_12/v125.go | 2 +- models/migrations/v1_12/v126.go | 2 +- models/migrations/v1_12/v127.go | 2 +- models/migrations/v1_12/v128.go | 2 +- models/migrations/v1_12/v129.go | 2 +- models/migrations/v1_12/v130.go | 2 +- models/migrations/v1_12/v131.go | 2 +- models/migrations/v1_12/v132.go | 2 +- models/migrations/v1_12/v133.go | 2 +- models/migrations/v1_12/v134.go | 2 +- models/migrations/v1_12/v135.go | 2 +- models/migrations/v1_12/v136.go | 2 +- models/migrations/v1_12/v137.go | 2 +- models/migrations/v1_12/v138.go | 2 +- models/migrations/v1_12/v139.go | 2 +- models/migrations/v1_13/v140.go | 9 +- models/migrations/v1_13/v141.go | 2 +- models/migrations/v1_13/v142.go | 2 +- models/migrations/v1_13/v143.go | 2 +- models/migrations/v1_13/v144.go | 2 +- models/migrations/v1_13/v145.go | 2 +- models/migrations/v1_13/v146.go | 2 +- models/migrations/v1_13/v147.go | 2 +- models/migrations/v1_13/v148.go | 2 +- models/migrations/v1_13/v149.go | 2 +- models/migrations/v1_13/v150.go | 2 +- models/migrations/v1_13/v151.go | 2 +- models/migrations/v1_13/v152.go | 2 +- models/migrations/v1_13/v153.go | 2 +- models/migrations/v1_13/v154.go | 2 +- models/migrations/v1_14/main_test.go | 2 +- models/migrations/v1_14/v155.go | 2 +- models/migrations/v1_14/v156.go | 2 +- models/migrations/v1_14/v157.go | 13 +- models/migrations/v1_14/v158.go | 2 +- models/migrations/v1_14/v159.go | 2 +- models/migrations/v1_14/v160.go | 2 +- models/migrations/v1_14/v161.go | 2 +- models/migrations/v1_14/v162.go | 2 +- models/migrations/v1_14/v163.go | 2 +- models/migrations/v1_14/v164.go | 2 +- models/migrations/v1_14/v165.go | 12 +- models/migrations/v1_14/v166.go | 2 +- models/migrations/v1_14/v167.go | 2 +- models/migrations/v1_14/v168.go | 2 +- models/migrations/v1_14/v169.go | 2 +- models/migrations/v1_14/v170.go | 2 +- models/migrations/v1_14/v171.go | 2 +- models/migrations/v1_14/v172.go | 2 +- models/migrations/v1_14/v173.go | 2 +- models/migrations/v1_14/v174.go | 2 +- models/migrations/v1_14/v175.go | 2 +- models/migrations/v1_14/v176.go | 2 +- models/migrations/v1_14/v176_test.go | 2 +- models/migrations/v1_14/v177.go | 2 +- models/migrations/v1_14/v177_test.go | 2 +- models/migrations/v1_15/main_test.go | 2 +- models/migrations/v1_15/v178.go | 2 +- models/migrations/v1_15/v179.go | 2 +- models/migrations/v1_15/v180.go | 2 +- models/migrations/v1_15/v181.go | 2 +- models/migrations/v1_15/v181_test.go | 2 +- models/migrations/v1_15/v182.go | 2 +- models/migrations/v1_15/v182_test.go | 2 +- models/migrations/v1_15/v183.go | 2 +- models/migrations/v1_15/v184.go | 2 +- models/migrations/v1_15/v185.go | 2 +- models/migrations/v1_15/v186.go | 2 +- models/migrations/v1_15/v187.go | 2 +- models/migrations/v1_15/v188.go | 2 +- models/migrations/v1_16/main_test.go | 2 +- models/migrations/v1_16/v189.go | 2 +- models/migrations/v1_16/v189_test.go | 2 +- models/migrations/v1_16/v190.go | 2 +- models/migrations/v1_16/v191.go | 2 +- models/migrations/v1_16/v192.go | 2 +- models/migrations/v1_16/v193.go | 2 +- models/migrations/v1_16/v193_test.go | 2 +- models/migrations/v1_16/v194.go | 2 +- models/migrations/v1_16/v195.go | 2 +- models/migrations/v1_16/v195_test.go | 2 +- models/migrations/v1_16/v196.go | 2 +- models/migrations/v1_16/v197.go | 2 +- models/migrations/v1_16/v198.go | 2 +- models/migrations/v1_16/v199.go | 2 +- models/migrations/v1_16/v200.go | 2 +- models/migrations/v1_16/v201.go | 2 +- models/migrations/v1_16/v202.go | 2 +- models/migrations/v1_16/v203.go | 2 +- models/migrations/v1_16/v204.go | 2 +- models/migrations/v1_16/v205.go | 2 +- models/migrations/v1_16/v206.go | 2 +- models/migrations/v1_16/v207.go | 2 +- models/migrations/v1_16/v208.go | 2 +- models/migrations/v1_16/v209.go | 2 +- models/migrations/v1_16/v210.go | 2 +- models/migrations/v1_16/v210_test.go | 2 +- models/migrations/v1_17/main_test.go | 2 +- models/migrations/v1_17/v211.go | 2 +- models/migrations/v1_17/v212.go | 2 +- models/migrations/v1_17/v213.go | 2 +- models/migrations/v1_17/v214.go | 2 +- models/migrations/v1_17/v215.go | 2 +- models/migrations/v1_17/v216.go | 2 +- models/migrations/v1_17/v217.go | 2 +- models/migrations/v1_17/v218.go | 2 +- models/migrations/v1_17/v219.go | 2 +- models/migrations/v1_17/v220.go | 2 +- models/migrations/v1_17/v221.go | 2 +- models/migrations/v1_17/v221_test.go | 2 +- models/migrations/v1_17/v222.go | 2 +- models/migrations/v1_17/v223.go | 2 +- models/migrations/v1_18/main_test.go | 2 +- models/migrations/v1_18/v224.go | 2 +- models/migrations/v1_18/v225.go | 2 +- models/migrations/v1_18/v226.go | 2 +- models/migrations/v1_18/v227.go | 2 +- models/migrations/v1_18/v228.go | 2 +- models/migrations/v1_18/v229.go | 2 +- models/migrations/v1_18/v229_test.go | 2 +- models/migrations/v1_18/v230.go | 2 +- models/migrations/v1_18/v230_test.go | 2 +- models/migrations/v1_19/main_test.go | 2 +- models/migrations/v1_19/v231.go | 2 +- models/migrations/v1_19/v232.go | 2 +- models/migrations/v1_19/v233.go | 2 +- models/migrations/v1_19/v233_test.go | 2 +- models/migrations/v1_19/v234.go | 2 +- models/migrations/v1_19/v235.go | 2 +- models/migrations/v1_19/v236.go | 2 +- models/migrations/v1_19/v237.go | 2 +- models/migrations/v1_19/v238.go | 2 +- models/migrations/v1_19/v239.go | 2 +- models/migrations/v1_19/v240.go | 2 +- models/migrations/v1_19/v241.go | 2 +- models/migrations/v1_19/v242.go | 2 +- models/migrations/v1_19/v243.go | 2 +- models/migrations/v1_20/main_test.go | 2 +- models/migrations/v1_20/v244.go | 2 +- models/migrations/v1_20/v245.go | 2 +- models/migrations/v1_20/v246.go | 2 +- models/migrations/v1_20/v247.go | 2 +- models/migrations/v1_20/v248.go | 2 +- models/migrations/v1_20/v249.go | 2 +- models/migrations/v1_20/v250.go | 2 +- models/migrations/v1_20/v251.go | 2 +- models/migrations/v1_20/v252.go | 2 +- models/migrations/v1_20/v253.go | 2 +- models/migrations/v1_20/v254.go | 2 +- models/migrations/v1_20/v255.go | 2 +- models/migrations/v1_20/v256.go | 2 +- models/migrations/v1_20/v257.go | 2 +- models/migrations/v1_20/v258.go | 2 +- models/migrations/v1_20/v259.go | 2 +- models/migrations/v1_20/v259_test.go | 2 +- models/migrations/v1_21/main_test.go | 2 +- models/migrations/v1_21/v260.go | 2 +- models/migrations/v1_21/v261.go | 2 +- models/migrations/v1_21/v262.go | 2 +- models/migrations/v1_21/v263.go | 2 +- models/migrations/v1_21/v264.go | 2 +- models/migrations/v1_21/v265.go | 2 +- models/migrations/v1_21/v266.go | 2 +- models/migrations/v1_21/v267.go | 2 +- models/migrations/v1_21/v268.go | 2 +- models/migrations/v1_21/v269.go | 2 +- models/migrations/v1_21/v270.go | 2 +- models/migrations/v1_21/v271.go | 3 +- models/migrations/v1_21/v272.go | 3 +- models/migrations/v1_21/v273.go | 3 +- models/migrations/v1_21/v274.go | 3 +- models/migrations/v1_21/v275.go | 2 +- models/migrations/v1_21/v276.go | 2 +- models/migrations/v1_21/v277.go | 2 +- models/migrations/v1_21/v278.go | 2 +- models/migrations/v1_21/v279.go | 2 +- models/migrations/v1_22/main_test.go | 2 +- models/migrations/v1_22/v280.go | 2 +- models/migrations/v1_22/v281.go | 2 +- models/migrations/v1_22/v282.go | 2 +- models/migrations/v1_22/v283.go | 2 +- models/migrations/v1_22/v283_test.go | 2 +- models/migrations/v1_22/v284.go | 3 +- models/migrations/v1_22/v285.go | 2 +- models/migrations/v1_22/v286.go | 2 +- models/migrations/v1_22/v286_test.go | 2 +- models/migrations/v1_22/v287.go | 2 +- models/migrations/v1_22/v287_test.go | 2 +- models/migrations/v1_22/v288.go | 2 +- models/migrations/v1_22/v289.go | 2 +- models/migrations/v1_22/v290.go | 2 +- models/migrations/v1_22/v291.go | 2 +- models/migrations/v1_22/v292.go | 2 +- models/migrations/v1_22/v293.go | 2 +- models/migrations/v1_22/v293_test.go | 2 +- models/migrations/v1_22/v294.go | 2 +- models/migrations/v1_22/v294_test.go | 2 +- models/migrations/v1_22/v295.go | 2 +- models/migrations/v1_22/v296.go | 2 +- models/migrations/v1_22/v297.go | 2 +- models/migrations/v1_22/v298.go | 2 +- models/migrations/v1_23/main_test.go | 2 +- models/migrations/v1_23/v299.go | 2 +- models/migrations/v1_23/v300.go | 2 +- models/migrations/v1_23/v301.go | 2 +- models/migrations/v1_23/v302.go | 2 +- models/migrations/v1_23/v302_test.go | 2 +- models/migrations/v1_23/v303.go | 2 +- models/migrations/v1_23/v304.go | 2 +- models/migrations/v1_23/v304_test.go | 2 +- models/migrations/v1_23/v305.go | 2 +- models/migrations/v1_23/v306.go | 2 +- models/migrations/v1_23/v307.go | 2 +- models/migrations/v1_23/v308.go | 2 +- models/migrations/v1_23/v309.go | 2 +- models/migrations/v1_23/v310.go | 2 +- models/migrations/v1_23/v311.go | 2 +- models/migrations/v1_24/v312.go | 2 +- models/migrations/v1_24/v313.go | 2 +- models/migrations/v1_24/v314.go | 2 +- models/migrations/v1_24/v315.go | 2 +- models/migrations/v1_24/v316.go | 2 +- models/migrations/v1_24/v317.go | 2 +- models/migrations/v1_24/v318.go | 2 +- models/migrations/v1_24/v319.go | 2 +- models/migrations/v1_24/v320.go | 2 +- models/migrations/v1_6/v70.go | 2 +- models/migrations/v1_6/v71.go | 2 +- models/migrations/v1_6/v72.go | 2 +- models/migrations/v1_7/v73.go | 2 +- models/migrations/v1_7/v74.go | 2 +- models/migrations/v1_7/v75.go | 2 +- models/migrations/v1_8/v76.go | 2 +- models/migrations/v1_8/v77.go | 2 +- models/migrations/v1_8/v78.go | 2 +- models/migrations/v1_8/v79.go | 2 +- models/migrations/v1_8/v80.go | 2 +- models/migrations/v1_8/v81.go | 2 +- models/migrations/v1_9/v82.go | 2 +- models/migrations/v1_9/v83.go | 2 +- models/migrations/v1_9/v84.go | 2 +- models/migrations/v1_9/v85.go | 2 +- models/migrations/v1_9/v86.go | 2 +- models/migrations/v1_9/v87.go | 2 +- models/organization/org.go | 5 - models/organization/team_repo.go | 33 +- models/organization/team_repo_test.go | 2 +- models/perm/access/repo_permission.go | 43 +- models/perm/access/repo_permission_test.go | 46 + models/project/project.go | 4 +- models/repo/language_stats.go | 9 +- models/repo/transfer.go | 2 +- models/repo/update.go | 18 +- models/user/badge.go | 2 +- modules/auth/httpauth/httpauth.go | 47 + modules/auth/httpauth/httpauth_test.go | 43 + modules/auth/password/hash/common.go | 2 +- modules/base/tool.go | 16 - modules/base/tool_test.go | 19 - modules/cache/cache_redis.go | 2 +- modules/cache/cache_twoqueue.go | 2 +- modules/cache/string_cache.go | 2 +- modules/commitstatus/commit_status.go | 4 +- modules/fileicon/entry.go | 10 +- modules/fileicon/material.go | 3 +- modules/fileicon/material_test.go | 8 +- modules/git/blob.go | 64 +- modules/git/commit.go | 3 +- modules/git/error.go | 16 - modules/git/tree_blob_nogogit.go | 36 +- modules/git/tree_entry.go | 84 +- modules/git/tree_entry_common_test.go | 76 + modules/git/tree_entry_gogit.go | 10 +- modules/git/tree_entry_mode.go | 4 +- modules/git/tree_entry_nogogit.go | 4 +- modules/git/tree_entry_test.go | 47 - modules/git/tree_gogit.go | 3 +- modules/graceful/manager_windows.go | 3 +- modules/htmlutil/html.go | 2 + modules/htmlutil/html_test.go | 9 + modules/json/json.go | 3 +- modules/lfs/pointer.go | 13 +- modules/lfs/pointer_scanner_gogit.go | 2 +- modules/lfstransfer/backend/util.go | 1 + modules/log/logger.go | 2 +- modules/markup/common/footnote.go | 4 +- modules/markup/console/console.go | 38 +- modules/markup/console/console_test.go | 32 +- .../markup/markdown/transform_blockquote.go | 2 +- modules/markup/markdown/transform_codespan.go | 2 +- modules/markup/markdown/transform_heading.go | 2 +- modules/markup/mdstripper/mdstripper.go | 5 +- modules/markup/renderer.go | 12 +- modules/optional/serialization_test.go | 2 +- modules/packages/pub/metadata.go | 2 +- modules/repository/branch.go | 9 +- modules/setting/actions.go | 4 +- modules/setting/config_env.go | 2 +- modules/setting/config_env_test.go | 3 + modules/setting/config_provider.go | 2 +- modules/setting/security.go | 2 +- modules/setting/storage.go | 6 +- modules/structs/admin_user.go | 7 +- modules/structs/git_blob.go | 3 + modules/structs/hook.go | 5 +- modules/structs/issue_tracked_time.go | 5 +- modules/structs/org.go | 2 + modules/structs/repo_file.go | 85 +- modules/structs/user.go | 4 +- modules/structs/user_email.go | 1 + modules/templates/helper.go | 5 - modules/templates/helper_test.go | 4 - modules/templates/htmlrenderer.go | 4 +- modules/templates/scopedtmpl/scopedtmpl.go | 25 +- modules/templates/util_json.go | 4 +- modules/templates/util_render.go | 43 +- modules/templates/util_render_test.go | 11 + modules/typesniffer/typesniffer.go | 65 +- modules/typesniffer/typesniffer_test.go | 16 +- modules/util/error.go | 4 +- modules/util/slice.go | 3 +- modules/util/string.go | 21 + modules/util/time_str.go | 2 +- modules/web/router_path.go | 7 +- modules/web/router_test.go | 39 +- options/fileicon/material-icon-rules.json | 86 +- options/fileicon/material-icon-svgs.json | 354 ++-- options/locale/locale_cs-CZ.ini | 5 - options/locale/locale_de-DE.ini | 5 - options/locale/locale_el-GR.ini | 4 - options/locale/locale_en-US.ini | 17 +- options/locale/locale_es-ES.ini | 4 - options/locale/locale_fa-IR.ini | 4 - options/locale/locale_fi-FI.ini | 2 - options/locale/locale_fr-FR.ini | 50 +- options/locale/locale_ga-IE.ini | 15 +- options/locale/locale_id-ID.ini | 1 - options/locale/locale_is-IS.ini | 2 - options/locale/locale_it-IT.ini | 4 - options/locale/locale_ja-JP.ini | 5 - options/locale/locale_ko-KR.ini | 2 - options/locale/locale_lv-LV.ini | 4 - options/locale/locale_nl-NL.ini | 4 - options/locale/locale_pl-PL.ini | 4 - options/locale/locale_pt-BR.ini | 4 - options/locale/locale_pt-PT.ini | 19 +- options/locale/locale_ru-RU.ini | 4 - options/locale/locale_si-LK.ini | 4 - options/locale/locale_sk-SK.ini | 1 - options/locale/locale_sv-SE.ini | 2 - options/locale/locale_tr-TR.ini | 51 +- options/locale/locale_uk-UA.ini | 8 +- options/locale/locale_zh-CN.ini | 65 +- options/locale/locale_zh-HK.ini | 1 - options/locale/locale_zh-TW.ini | 5 - package-lock.json | 1647 ++++++++--------- package.json | 58 +- public/assets/img/svg/gitea-chef.svg | 2 +- public/assets/img/svg/gitea-codecommit.svg | 2 +- public/assets/img/svg/gitea-debian.svg | 2 +- public/assets/img/svg/gitea-gitbucket.svg | 2 +- public/assets/img/svg/gitea-gitlab.svg | 2 +- public/assets/img/svg/gitea-google.svg | 2 +- public/assets/img/svg/gitea-maven.svg | 2 +- .../assets/img/svg/gitea-microsoftonline.svg | 2 +- public/assets/img/svg/gitea-npm.svg | 2 +- public/assets/img/svg/gitea-onedev.svg | 2 +- public/assets/img/svg/gitea-openid.svg | 2 +- public/assets/img/svg/gitea-rubygems.svg | 2 +- public/assets/img/svg/gitea-swift.svg | 2 +- public/assets/img/svg/gitea-vagrant.svg | 2 +- public/assets/img/svg/octicon-sparkle.svg | 1 + routers/api/actions/artifacts_utils.go | 2 +- routers/api/packages/api.go | 2 + routers/api/packages/conda/conda.go | 2 +- routers/api/packages/container/auth.go | 2 +- routers/api/packages/container/blob.go | 19 +- routers/api/packages/container/container.go | 28 +- routers/api/packages/container/manifest.go | 167 +- routers/api/packages/nuget/nuget.go | 2 +- routers/api/v1/admin/org.go | 2 +- routers/api/v1/admin/repo.go | 2 +- routers/api/v1/admin/user.go | 12 +- routers/api/v1/admin/user_badge.go | 6 +- routers/api/v1/api.go | 50 +- routers/api/v1/misc/markup.go | 4 +- routers/api/v1/org/block.go | 6 +- routers/api/v1/org/member.go | 10 +- routers/api/v1/org/org.go | 4 +- routers/api/v1/org/team.go | 6 +- routers/api/v1/repo/blob.go | 2 +- routers/api/v1/repo/branch.go | 8 +- routers/api/v1/repo/collaborators.go | 8 +- routers/api/v1/repo/file.go | 417 ++--- routers/api/v1/repo/issue_stopwatch.go | 56 +- routers/api/v1/repo/issue_subscription.go | 4 +- routers/api/v1/repo/issue_tracked_time.go | 2 +- routers/api/v1/repo/patch.go | 68 +- routers/api/v1/repo/repo.go | 2 +- routers/api/v1/repo/wiki.go | 2 +- routers/api/v1/swagger/repo.go | 6 + routers/api/v1/user/app.go | 6 +- routers/api/v1/user/block.go | 6 +- routers/api/v1/user/follower.go | 14 +- routers/api/v1/user/gpg_key.go | 2 +- routers/api/v1/user/key.go | 2 +- routers/api/v1/user/repo.go | 2 +- routers/api/v1/user/star.go | 2 +- routers/api/v1/user/user.go | 6 +- routers/api/v1/user/watch.go | 2 +- routers/common/pagetmpl.go | 28 +- routers/install/install.go | 2 +- routers/private/hook_post_receive.go | 26 +- routers/web/admin/config.go | 2 +- routers/web/auth/oauth2_provider.go | 41 +- routers/web/misc/markup.go | 2 +- routers/web/org/teams.go | 15 +- routers/web/repo/actions/view.go | 8 +- routers/web/repo/commit.go | 7 +- routers/web/repo/editor.go | 10 +- routers/web/repo/issue_stopwatch.go | 40 +- routers/web/repo/issue_view.go | 4 + routers/web/repo/pull.go | 9 +- routers/web/repo/setting/lfs.go | 4 +- routers/web/repo/setting/protected_branch.go | 3 +- routers/web/repo/setting/protected_tag.go | 3 +- routers/web/repo/setting/setting.go | 29 +- routers/web/repo/setting/webhook.go | 7 +- routers/web/repo/treelist.go | 3 +- routers/web/repo/view.go | 58 +- routers/web/repo/view_file.go | 350 ++-- routers/web/repo/view_home.go | 27 +- routers/web/repo/view_readme.go | 72 +- routers/web/swagger_json.go | 5 + routers/web/user/package.go | 10 +- routers/web/web.go | 9 +- services/actions/notifier_helper.go | 7 +- services/agit/agit.go | 27 + services/auth/basic.go | 15 +- services/auth/oauth2.go | 7 +- services/auth/source/ldap/source_search.go | 6 +- services/context/context_response.go | 2 +- services/context/org.go | 2 +- services/context/pagination.go | 8 +- services/context/private.go | 9 +- services/context/repo.go | 2 +- services/context/user.go | 2 +- services/convert/convert.go | 4 +- services/doctor/paths.go | 5 +- services/forms/repo_form.go | 3 +- services/gitdiff/gitdiff.go | 51 +- services/issue/assignee.go | 2 +- services/issue/status.go | 4 +- services/lfs/server.go | 19 +- services/migrations/migrate.go | 8 +- services/notify/notify.go | 23 + services/oauth2_provider/access_token.go | 25 +- .../oauth2_provider/additional_scopes_test.go | 2 +- services/oauth2_provider/init.go | 2 +- services/oauth2_provider/jwtsigningkey.go | 2 +- services/oauth2_provider/token.go | 2 +- services/packages/container/common.go | 2 +- services/packages/rpm/repository.go | 4 +- services/pull/merge.go | 2 +- services/pull/reviewer.go | 2 +- services/repository/files/content.go | 226 ++- services/repository/files/content_test.go | 190 +- services/repository/files/file.go | 17 +- services/repository/files/file_test.go | 19 +- services/repository/files/patch.go | 1 - services/repository/files/tree.go | 2 +- services/repository/files/update.go | 4 +- services/user/update.go | 2 +- services/versioned_migration/migration.go | 2 +- services/webhook/notifier.go | 37 + tailwind.config.js | 2 +- templates/admin/auth/edit.tmpl | 17 +- templates/admin/hooks.tmpl | 3 - templates/admin/packages/list.tmpl | 19 +- templates/admin/repo/list.tmpl | 21 +- templates/base/footer.tmpl | 8 +- templates/base/head_navbar.tmpl | 55 +- templates/base/head_navbar_icons.tmpl | 25 + templates/base/head_script.tmpl | 2 +- templates/devtest/flex-list.tmpl | 2 +- templates/org/settings/hooks.tmpl | 2 +- templates/projects/list.tmpl | 13 +- templates/projects/view.tmpl | 6 +- templates/repo/blame.tmpl | 2 + templates/repo/branch/list.tmpl | 12 +- templates/repo/commits_list.tmpl | 2 +- templates/repo/editor/common_breadcrumb.tmpl | 2 +- templates/repo/graph.tmpl | 39 +- templates/repo/graph/commits.tmpl | 9 +- templates/repo/graph/div.tmpl | 10 +- templates/repo/home_sidebar_bottom.tmpl | 4 +- templates/repo/issue/card.tmpl | 15 +- templates/repo/issue/filter_item_label.tmpl | 2 +- templates/repo/issue/filter_list.tmpl | 2 +- templates/repo/issue/label_precolors.tmpl | 43 +- .../repo/issue/labels/label_edit_modal.tmpl | 2 +- templates/repo/issue/labels/label_list.tmpl | 6 +- templates/repo/issue/milestones.tmpl | 14 +- .../repo/issue/sidebar/assignee_list.tmpl | 2 +- templates/repo/issue/sidebar/label_list.tmpl | 2 +- .../repo/issue/sidebar/reviewer_list.tmpl | 2 +- .../issue/sidebar/stopwatch_timetracker.tmpl | 4 +- templates/repo/issue/sidebar/wip_switch.tmpl | 2 +- .../repo/issue/view_content/comments.tmpl | 2 +- .../issue/view_content/pull_merge_box.tmpl | 2 +- templates/repo/release/list.tmpl | 2 +- templates/repo/release/new.tmpl | 14 +- templates/repo/settings/branches.tmpl | 15 +- templates/repo/settings/collaboration.tmpl | 41 +- templates/repo/settings/deploy_keys.tmpl | 13 +- templates/repo/settings/lfs_file.tmpl | 2 - templates/repo/settings/webhook/base.tmpl | 2 +- .../repo/settings/webhook/base_list.tmpl | 5 +- .../repo/settings/webhook/delete_modal.tmpl | 10 - templates/repo/settings/webhook/dingtalk.tmpl | 3 +- templates/repo/settings/webhook/discord.tmpl | 3 +- templates/repo/settings/webhook/feishu.tmpl | 8 +- templates/repo/settings/webhook/gitea.tmpl | 11 +- templates/repo/settings/webhook/gogs.tmpl | 11 +- templates/repo/settings/webhook/history.tmpl | 2 +- templates/repo/settings/webhook/list.tmpl | 4 - templates/repo/settings/webhook/matrix.tmpl | 2 +- templates/repo/settings/webhook/msteams.tmpl | 3 +- .../repo/settings/webhook/packagist.tmpl | 3 +- templates/repo/settings/webhook/settings.tmpl | 82 +- templates/repo/settings/webhook/slack.tmpl | 3 +- templates/repo/settings/webhook/telegram.tmpl | 3 +- .../repo/settings/webhook/wechatwork.tmpl | 3 +- templates/repo/tag/list.tmpl | 85 +- templates/repo/tag/name.tmpl | 2 +- templates/repo/view_file.tmpl | 88 +- templates/repo/view_list.tmpl | 3 + templates/repo/wiki/view.tmpl | 13 +- templates/shared/issuelist.tmpl | 15 +- templates/swagger/ui.tmpl | 1 + templates/swagger/v1_input.json | 4 +- templates/swagger/v1_json.tmpl | 254 ++- templates/user/auth/oidc_wellknown.tmpl | 14 +- .../user/notification/notification_div.tmpl | 2 +- templates/user/settings/hooks.tmpl | 2 +- tests/e2e/e2e_test.go | 2 +- tests/integration/api_packages_rpm_test.go | 8 +- tests/integration/api_repo_archive_test.go | 17 +- .../integration/api_repo_file_create_test.go | 11 +- .../integration/api_repo_file_delete_test.go | 28 +- .../integration/api_repo_file_update_test.go | 9 +- .../api_repo_get_contents_list_test.go | 26 +- .../integration/api_repo_get_contents_test.go | 138 +- tests/integration/git_misc_test.go | 102 + tests/integration/integration_test.go | 5 +- tests/integration/issue_test.go | 7 + tests/integration/lfs_view_test.go | 9 +- tests/integration/links_test.go | 69 +- tests/integration/oauth_test.go | 46 +- tests/integration/org_test.go | 33 + tests/integration/repo_test.go | 17 + tests/integration/repo_webhook_test.go | 72 + tests/integration/repofiles_change_test.go | 26 +- tests/integration/timetracking_test.go | 10 +- tests/test_utils.go | 3 +- tools/generate-svg.js | 1 + web_src/css/base.css | 24 +- web_src/css/features/colorpicker.css | 32 +- web_src/css/features/gitgraph.css | 71 +- web_src/css/features/projects.css | 3 +- web_src/css/modules/animations.css | 3 +- web_src/css/modules/label.css | 80 +- web_src/css/modules/list.css | 1 - web_src/css/modules/navbar.css | 13 - web_src/css/repo.css | 94 +- web_src/css/repo/file-view.css | 30 + web_src/css/repo/issue-card.css | 9 +- web_src/css/repo/issue-label.css | 25 +- web_src/css/repo/release-tag.css | 10 +- web_src/css/shared/flex-list.css | 13 +- web_src/js/bootstrap.ts | 7 +- web_src/js/components/DashboardRepoList.vue | 19 +- web_src/js/components/DiffCommitSelector.vue | 6 +- web_src/js/components/ViewFileTreeStore.ts | 3 +- web_src/js/features/colorpicker.ts | 36 +- web_src/js/features/common-button.ts | 33 +- web_src/js/features/common-fetch-action.ts | 42 +- .../js/features/comp/ComboMarkdownEditor.ts | 16 +- web_src/js/features/comp/ConfirmModal.ts | 37 +- web_src/js/features/comp/EditorUpload.ts | 2 +- web_src/js/features/comp/LabelEdit.ts | 3 +- web_src/js/features/comp/SearchUserBox.ts | 2 +- web_src/js/features/copycontent.ts | 14 +- web_src/js/features/dropzone.ts | 6 +- web_src/js/features/emoji.ts | 6 +- web_src/js/features/file-view.ts | 76 + web_src/js/features/repo-editor.ts | 13 +- web_src/js/features/repo-graph.ts | 143 +- web_src/js/features/repo-issue-list.ts | 8 +- web_src/js/features/repo-issue.ts | 27 +- web_src/js/features/repo-new.ts | 2 +- web_src/js/features/repo-projects.ts | 3 +- web_src/js/features/repo-wiki.ts | 3 +- web_src/js/features/stopwatch.ts | 2 +- web_src/js/features/tribute.ts | 13 +- web_src/js/globals.d.ts | 12 +- web_src/js/globals.ts | 5 +- web_src/js/htmx.ts | 33 +- web_src/js/index-domready.ts | 177 ++ web_src/js/index.ts | 179 +- web_src/js/markup/html2markdown.ts | 8 +- web_src/js/markup/mermaid.ts | 3 +- web_src/js/modules/fomantic/base.ts | 8 +- web_src/js/modules/fomantic/dropdown.ts | 9 +- web_src/js/modules/fomantic/modal.ts | 28 +- web_src/js/modules/tippy.ts | 3 +- web_src/js/modules/toast.ts | 2 +- web_src/js/render/pdf.ts | 17 - web_src/js/render/plugin.ts | 10 + web_src/js/render/plugins/3d-viewer.ts | 60 + web_src/js/render/plugins/pdf-viewer.ts | 20 + web_src/js/svg.ts | 3 +- web_src/js/utils/dom.ts | 21 +- web_src/js/utils/html.test.ts | 8 + web_src/js/utils/html.ts | 32 + webpack.config.js | 6 - 687 files changed, 5525 insertions(+), 4893 deletions(-) create mode 100644 modules/auth/httpauth/httpauth.go create mode 100644 modules/auth/httpauth/httpauth_test.go create mode 100644 modules/git/tree_entry_common_test.go create mode 100644 public/assets/img/svg/octicon-sparkle.svg create mode 100644 templates/base/head_navbar_icons.tmpl delete mode 100644 templates/repo/settings/webhook/delete_modal.tmpl delete mode 100644 templates/repo/settings/webhook/list.tmpl create mode 100644 web_src/js/features/file-view.ts create mode 100644 web_src/js/index-domready.ts delete mode 100644 web_src/js/render/pdf.ts create mode 100644 web_src/js/render/plugin.ts create mode 100644 web_src/js/render/plugins/3d-viewer.ts create mode 100644 web_src/js/render/plugins/pdf-viewer.ts create mode 100644 web_src/js/utils/html.test.ts create mode 100644 web_src/js/utils/html.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index f9e1050240814..57c6b19600b4b 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -91,6 +91,7 @@ module.exports = { plugins: ['@vitest/eslint-plugin'], globals: vitestPlugin.environments.env.globals, rules: { + 'github/unescaped-html-literal': [0], '@vitest/consistent-test-filename': [0], '@vitest/consistent-test-it': [0], '@vitest/expect-expect': [0], @@ -423,7 +424,7 @@ module.exports = { 'github/no-useless-passive': [2], 'github/prefer-observers': [2], 'github/require-passive-events': [2], - 'github/unescaped-html-literal': [0], + 'github/unescaped-html-literal': [2], 'grouped-accessor-pairs': [2], 'guard-for-in': [0], 'id-blacklist': [0], diff --git a/.golangci.yml b/.golangci.yml index c176d2115cc3e..2ad39fbae2cba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -45,7 +45,13 @@ linters: desc: do not use the ini package, use gitea's config system instead - pkg: gitea.com/go-chi/cache desc: do not use the go-chi cache package, use gitea's cache system + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true gocritic: + enabled-checks: + - equalFold disabled-checks: - ifElseChain - singleCaseSwitch # Every time this occurred in the code, there was no other way. @@ -83,6 +89,10 @@ linters: - name: unreachable-code - name: var-declaration - name: var-naming + arguments: + - [] # AllowList - do not remove as args for the rule are positional and won't work without lists first + - [] # DenyList + - - skip-package-name-checks: true # supress errors from underscore in migration packages staticcheck: checks: - all diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 979831eb9b8b2..6a7126388ef7a 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -30,7 +30,7 @@ These are the values to which people in the Gitea community should aspire. - **Be constructive.** - Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation. - Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved. - - Avoid snarking (pithy, unproductive, sniping comments) + - Avoid snarking (pithy, unproductive, sniping comments). - Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict. - Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group). - **Be responsible.** @@ -42,7 +42,7 @@ People are complicated. You should expect to be misunderstood and to misundersta ### Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ### Our Standards diff --git a/Makefile b/Makefile index c868ef4463804..6a3fa60e495ce 100644 --- a/Makefile +++ b/Makefile @@ -26,18 +26,18 @@ COMMA := , XGO_VERSION := go-1.24.x AIR_PACKAGE ?= github.com/air-verse/air@v1 -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.1 -GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.0.2 +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3 +GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.8.0 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.12 -MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 +MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0 +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.32.3 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1 GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1 -GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.0 -GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.0 +GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.19.1 +GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.19.1 DOCKER_IMAGE ?= gitea/gitea DOCKER_TAG ?= latest @@ -81,7 +81,6 @@ ifeq ($(RACE_ENABLED),true) endif STORED_VERSION_FILE := VERSION -HUGO_VERSION ?= 0.111.3 GITHUB_REF_TYPE ?= branch GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) diff --git a/README.md b/README.md index 017ca629d01a2..aa2c6010fa372 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,9 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request [![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com) -Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there. +Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there. -You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up. +You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty, but we hope to fill it as questions pop up. Get more information from [documentation](https://docs.gitea.com/contributing/localization). diff --git a/SECURITY.md b/SECURITY.md index c9dbf859f5803..d7c27ea61365c 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -14,12 +14,12 @@ Please **DO NOT** file a public issue, instead send your report privately to `se Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body. -The PGP key is valid until July 9, 2025. +The PGP key is valid until July 4, 2026. ``` Key ID: 6FCD2D5B Key Type: RSA -Expires: 7/9/2025 +Expires: 7/4/2026 Key Size: 4096/4096 Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B ``` @@ -42,18 +42,18 @@ lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N -LVsFAmaMse0FCQW4fW8ACgkQqv0jgW/NLVtXLg/+PF4G9Jhlui15BTNlEBJAV2P/ -1QlAV2krk0fP7tykn0FR9RfGIfVV/kwC1f+ouosYPQDDevl9LWdUIM+g94DtNo2o -7ACpcL3morvt5lVGpIZHL8TbX0qmFRXL/pB/cB+K6IwYvh2mrbp2zH+r4SCRyFYq -BjgXYFTI1MylJ1ShAjU6Z+m3oJ+2xs5LzHS0X6zkTjzA2Zl4zQzciQ9T+wJcE7Zi -HXdM1+YMF8KGNP8J9Rpug5oNDJ98lgZirRY7c3A/1xmYBiPnULwuuymdqEZO7l70 -SeAlE1RWYX8kbOBnBb/KY4XwE3Vic1oEzc9DiPWVH1ElX86WNNsFzuyULiwoBoWg -pqZGhL9x1p5+46RGQSDczsHM7YGVtfYOiDo2PAVrmwsT0BnXnK8Oe3YIkvmUPEJu -OkLt0Z6A5n8pz8zhQzuApwBsK4ncJ8zTCpvz/pfKKqZC/Vnoh3gKGhDGvOZ+b5IJ -0kUTe2JsbnwFixDUMDtacQ1op8XOyLoLVmgqLn0+Pws4XPBlMof2bioFir3yHKnP -gNchsF1agrlSIo5GA8u4ga+IlCSfvFIKrl7+cxacKcJYt/vbOU5KcvVJI5EtHKCG -xfHjHY2ah1Qww7SxW6IXiRZZzPpsL2mBM2CD7N3qh9bV2s27wxYCdUodsIZbiyHe -oWPzfBnkmiAN8KlZxHm5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen +LVsFAmhoHmkFCQeT6esACgkQqv0jgW/NLVuFLRAAmjBQSKRAgs2bFIEj7HLAbDp4 +f+XkdH+GsT3jRPOZ9QZgmtM+TfoE4yNgIVfOl+s4RdjM/W4QzqZuPQ55hbEHd056 +cJmm7B+6GsHFcdrPmh65sOCEIyh4+t45dUfeWpFsDPqm9j1UHXAJQIpB8vDEVAPH +t+3wLCk8GMPJs1o5tIyMmaO23ngvkwn8eG7KgY+rp2PzObrb5g7ppci0ILzILkrp +HVjZsEfUWRgSVF7LuU5ppqDKrlcqwUpQq6n3kGMZcLrCp6ACKP04TBmTfUxNwdL7 +I0N7apI2Pbct9T1Gv/lYAUFWyU2c3gh/EBLbO6BukaLOFRQHrtNfdJV/YnMPlcXr +LUJjK9K4eAH9DsrZqrisz/LthsC2BaNIN3KRMTk5YTYgmIh8GXzSgihORmtDFELC +RroID3pTuS0zjXh+wpY9GuPTh7UW23p42Daxca4fAT4k5EclvDRUrL21xMopPMiL +HuNdELz4FVchRTy05PjzKVyjVInDNojE2KUxnjxZDzYJ6aT/g+coD5yfntYm8BEj ++ZzL0ndZES54hzKLpv7zwBQwFzam68clZYmDPILOPTflQDfpGEWmJK4undFU5obz +ZsQRz0R3ulspChATbZxO0d5LX2obLpKO9X3b5VoO1KF+R8Vjw1Y0KxrNZ6rIcfqH +Z50QVQKSe9dm08K0ON+5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46 XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E @@ -65,19 +65,19 @@ s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/ axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t -WwUCZoyyjQUJBbh+DwAKCRCq/SOBb80tW18XD/9MXztmf01MT+1kZdBouZ/7Rp/7 -9kuqo//B1G+RXau4oFtPqb67kNe2WaIc3u5B73PUHsMf3i6z4ib2KbMhZZerLn0O -dRglcuPeNWmsASY3dH/XVG0cT0zvvWegagd12TJEl3Vs+7XNrOw4cwDj9L1+GH9m -kSt4uaANWn/6a3RvMRhiVEYuNwhAzcKaactPmYqrLJgoVLbRSDkgyHaMQ2jKgLxk -ifS/fvluGV0ub2Po6DJiqfRpd1tDvPhe9y1+r1WFDZsOcvTcZUfSt/7dXMGfqGu0 -2daVFlfeSXSALrDE5uc0UxodHCpP3sqRYDZevGLBRaaTkIjYXG/+N898+7K5WJF4 -xXOLWxM2cwGkG7eC9pugcDnBp9XlF7O+GBiZ05JUe5flXDQFZ+h3exjopu6KHF1B -RnzNy8LC0UKb+AuvRIOLV92a9Q9wGWU/jaVDu6nZ0umAeuSzxiHoDsonm0Fl9QAz -2/xCokebuoeLrEK7R2af3X86mqq3sVO4ax+HPYChzOaVQBiHUW/TAldWcldYYphR -/e2WsbmQfvCRtz/bZfo+aUVnrHNjzVMtF2SszdVmA/04Y8pS28MqtuRqhm5DPOOd -g1YeUywK5jRZ1twyo1kzJEFPLaoeaXaycsR1PMVBW0Urik5mrR/pOWq7PPoZoKb2 -lXYLE8bwkuQTmsyL1g== -=9i7d +WwUCaGgeJAUJB5PppgAKCRCq/SOBb80tW/NWEACB6Jrf0gWlk7e+hNCdnbM0ZVWU +f2sHNFfXxxsdhpcDgKbNHtkZb8nZgv8AX+5fTtUwMVa3vKcdw30xFiIM5N7cCIPV +vg/5z5BtfEaitnabEUG2iiVDIy8IHXIcK10rX+7BosA3QDl2PsiBHwyi5G13lRk8 +zGTSNDuOalug33h5/lr2dPigamkq74Aoy29q8Rjad6GfWHipL2bFimgtY+Zdi0BH +NLk4EJXxj1SgVx5dtkQzWJReBA5M+FQ4QYQZBO+f4TDoOLmjui152uhkoLBQbGAa +WWJFTVxm0bG5MXloEL3gA8DfU7XDwuW/sHJC5pBko8RpQViooOhckMepZV3Y83DK +bwLYa3JmPgj2rEv4993dvrJbQhpGd082HOxOsllCs8pgNq1SnXpWYfcGTgGKC3ts +U8YZUUJUQ7mi2L8Tv3ix20c9EiGmA30JAmA8eZTC3cWup91ZkkVBFRml2czTXajd +RWZ6GbHV5503ueDQcB8yBVgF3CSixs67+dGSbD3p86OqGrjAcJzM5TFbNKcnGLdE +kGbZpNwAISy750lXzXKmyrh5RTCeTOQerbwCMBvHZO+HAevA/LXDTw2OAiSIQlP5 +sYA4sFYLQ30OAkgJcmdp/pSgVj/erNtSN07ClrOpDb/uFpQymO6K2h0Pst3feNVK +9M2VbqL9C51z/wyHLg== +=SfZA -----END PGP PUBLIC KEY BLOCK----- ``` diff --git a/cmd/embedded.go b/cmd/embedded.go index 6a2fa07a93238..1908352453ff8 100644 --- a/cmd/embedded.go +++ b/cmd/embedded.go @@ -295,16 +295,14 @@ func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, } } -func compileCollectPatterns(args []string) ([]glob.Glob, error) { +func compileCollectPatterns(args []string) (_ []glob.Glob, err error) { if len(args) == 0 { args = []string{"**"} } pat := make([]glob.Glob, len(args)) for i := range args { - if g, err := glob.Compile(args[i], '/'); err != nil { - return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err) - } else { //nolint:revive - pat[i] = g + if pat[i], err = glob.Compile(args[i], '/'); err != nil { + return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err) } } return pat, nil diff --git a/contrib/backport/backport.go b/contrib/backport/backport.go index 6fbd610e62fda..2052295fb143c 100644 --- a/contrib/backport/backport.go +++ b/contrib/backport/backport.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//nolint:forbidigo +//nolint:forbidigo // use of print functions is allowed in cli package main import ( diff --git a/models/actions/run_job.go b/models/actions/run_job.go index c0df19b020c2a..bad895036d16f 100644 --- a/models/actions/run_job.go +++ b/models/actions/run_job.go @@ -185,10 +185,10 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status { return StatusSuccess case hasCancelled: return StatusCancelled - case hasFailure: - return StatusFailure case hasRunning: return StatusRunning + case hasFailure: + return StatusFailure case hasWaiting: return StatusWaiting case hasBlocked: diff --git a/models/actions/run_job_status_test.go b/models/actions/run_job_status_test.go index 523d38327e4cc..2a5eb00a6f808 100644 --- a/models/actions/run_job_status_test.go +++ b/models/actions/run_job_status_test.go @@ -58,14 +58,14 @@ func TestAggregateJobStatus(t *testing.T) { {[]Status{StatusCancelled, StatusRunning}, StatusCancelled}, {[]Status{StatusCancelled, StatusBlocked}, StatusCancelled}, - // failure with other status, fail fast - // Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast. + // failure with other status, usually fail fast, but "running" wins to match GitHub's behavior + // another reason that we can't make "failure" wins over "running": it would cause a weird behavior that user cannot cancel a workflow or get current running workflows correctly by filter after a job fail. {[]Status{StatusFailure}, StatusFailure}, {[]Status{StatusFailure, StatusSuccess}, StatusFailure}, {[]Status{StatusFailure, StatusSkipped}, StatusFailure}, {[]Status{StatusFailure, StatusCancelled}, StatusCancelled}, {[]Status{StatusFailure, StatusWaiting}, StatusFailure}, - {[]Status{StatusFailure, StatusRunning}, StatusFailure}, + {[]Status{StatusFailure, StatusRunning}, StatusRunning}, {[]Status{StatusFailure, StatusBlocked}, StatusFailure}, // skipped with other status diff --git a/models/actions/task.go b/models/actions/task.go index 63259582f6374..e0756b10c2a58 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -278,14 +278,13 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask return nil, false, err } - var workflowJob *jobparser.Job - if gots, err := jobparser.Parse(job.WorkflowPayload); err != nil { + parsedWorkflows, err := jobparser.Parse(job.WorkflowPayload) + if err != nil { return nil, false, fmt.Errorf("parse workflow of job %d: %w", job.ID, err) - } else if len(gots) != 1 { + } else if len(parsedWorkflows) != 1 { return nil, false, fmt.Errorf("workflow of job %d: not single workflow", job.ID) - } else { //nolint:revive - _, workflowJob = gots[0].Job() } + _, workflowJob := parsedWorkflows[0].Job() if _, err := e.Insert(task); err != nil { return nil, false, err diff --git a/models/asymkey/gpg_key_add.go b/models/asymkey/gpg_key_add.go index ec2031088ae44..1c7d2c1da2631 100644 --- a/models/asymkey/gpg_key_add.go +++ b/models/asymkey/gpg_key_add.go @@ -91,7 +91,7 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil) } if err != nil { - log.Error("Unable to validate token signature. Error: %v", err) + log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err) return nil, ErrGPGInvalidTokenSignature{ ID: ekeys[0].PrimaryKey.KeyIdString(), Wrapped: err, diff --git a/models/asymkey/gpg_key_verify.go b/models/asymkey/gpg_key_verify.go index 6eedb5b7baaf9..5ab2fd808175b 100644 --- a/models/asymkey/gpg_key_verify.go +++ b/models/asymkey/gpg_key_verify.go @@ -85,7 +85,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st } if signer == nil { - log.Error("Unable to validate token signature. Error: %v", err) + log.Debug("VerifyGPGKey failed: no signer") return "", ErrGPGInvalidTokenSignature{ ID: key.KeyID, } diff --git a/models/asymkey/ssh_key_parse.go b/models/asymkey/ssh_key_parse.go index 00d75b8e82fe1..fc39f28624d87 100644 --- a/models/asymkey/ssh_key_parse.go +++ b/models/asymkey/ssh_key_parse.go @@ -208,7 +208,7 @@ func SSHNativeParsePublicKey(keyLine string) (string, int, error) { // The ssh library can parse the key, so next we find out what key exactly we have. switch pkey.Type() { - case ssh.KeyAlgoDSA: //nolint + case ssh.KeyAlgoDSA: //nolint:staticcheck // it's deprecated rawPub := struct { Name string P, Q, G, Y *big.Int diff --git a/models/asymkey/ssh_key_verify.go b/models/asymkey/ssh_key_verify.go index 605ffe9096c2d..0cf29ca9f1084 100644 --- a/models/asymkey/ssh_key_verify.go +++ b/models/asymkey/ssh_key_verify.go @@ -35,7 +35,7 @@ func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signat // edge case for Windows based shells that will add CR LF if piped to ssh-keygen command // see https://github.com/PowerShell/PowerShell/issues/5974 if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil { - log.Error("Unable to validate token signature. Error: %v", err) + log.Debug("VerifySSHKey sshsig.Verify failed: %v", err) return "", ErrSSHInvalidTokenSignature{ Fingerprint: key.Fingerprint, } diff --git a/models/auth/auth_token.go b/models/auth/auth_token.go index 81f07d1a8382c..54ff5a0d75483 100644 --- a/models/auth/auth_token.go +++ b/models/auth/auth_token.go @@ -15,7 +15,7 @@ import ( var ErrAuthTokenNotExist = util.NewNotExistErrorf("auth token does not exist") -type AuthToken struct { //nolint:revive +type AuthToken struct { //nolint:revive // export stutter ID string `xorm:"pk"` TokenHash string UserID int64 `xorm:"INDEX"` diff --git a/models/db/context.go b/models/db/context.go index 05d7d72daa93f..ad99ada8c8c6e 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -178,6 +178,15 @@ func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error return txWithNoCheck(parentCtx, f) } +// WithTx2 is similar to WithTx, but it has two return values: result and error. +func WithTx2[T any](parentCtx context.Context, f func(ctx context.Context) (T, error)) (ret T, errRet error) { + errRet = WithTx(parentCtx, func(ctx context.Context) (errInner error) { + ret, errInner = f(ctx) + return errInner + }) + return ret, errRet +} + func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) error { sess := xormEngine.NewSession() defer sess.Close() diff --git a/models/db/sql_postgres_with_schema.go b/models/db/sql_postgres_with_schema.go index 64b61b2ef3444..812fe4a6a6164 100644 --- a/models/db/sql_postgres_with_schema.go +++ b/models/db/sql_postgres_with_schema.go @@ -39,7 +39,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { // golangci lint is incorrect here - there is no benefit to using driver.ExecerContext here // and in any case pq does not implement it - if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck + if execer, ok := conn.(driver.Execer); ok { //nolint:staticcheck // see above _, err := execer.Exec(`SELECT set_config( 'search_path', $1 || ',' || current_setting('search_path'), @@ -64,7 +64,7 @@ func (d *postgresSchemaDriver) Open(name string) (driver.Conn, error) { // driver.String.ConvertValue will never return err for string // golangci lint is incorrect here - there is no benefit to using stmt.ExecWithContext here - _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck + _, err = stmt.Exec([]driver.Value{schemaValue}) //nolint:staticcheck // see above if err != nil { _ = conn.Close() return nil, err diff --git a/models/git/protected_branch.go b/models/git/protected_branch.go index 19b02ccab9f39..55bbe6938cdc4 100644 --- a/models/git/protected_branch.go +++ b/models/git/protected_branch.go @@ -518,7 +518,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre return currentWhitelist, nil } - teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead) + teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests) if err != nil { return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v", repo.OwnerID, repo.ID, err) } diff --git a/models/issues/comment.go b/models/issues/comment.go index 9bef96d0ddfcc..4fdb0c1808fb4 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -715,7 +715,8 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository return nil } -func (c *Comment) loadReview(ctx context.Context) (err error) { +// LoadReview loads the associated review +func (c *Comment) LoadReview(ctx context.Context) (err error) { if c.ReviewID == 0 { return nil } @@ -732,11 +733,6 @@ func (c *Comment) loadReview(ctx context.Context) (err error) { return nil } -// LoadReview loads the associated review -func (c *Comment) LoadReview(ctx context.Context) error { - return c.loadReview(ctx) -} - // DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes. func (c *Comment) DiffSide() string { if c.Line < 0 { @@ -856,7 +852,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment } if comment.ReviewID != 0 { if comment.Review == nil { - if err := comment.loadReview(ctx); err != nil { + if err := comment.LoadReview(ctx); err != nil { return err } } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index 84d5948640513..79bd6a19b0daa 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -24,7 +24,7 @@ import ( const ScopeSortPrefix = "scope-" // IssuesOptions represents options of an issue. -type IssuesOptions struct { //nolint +type IssuesOptions struct { //nolint:revive // export stutter Paginator *db.ListOptions RepoIDs []int64 // overwrites RepoCond if the length is not 0 AllPublic bool // include also all public repositories diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go index 7c05a3a883d19..761b8f91a0166 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -5,7 +5,6 @@ package issues import ( "context" - "fmt" "time" "code.gitea.io/gitea/models/db" @@ -15,20 +14,6 @@ import ( "code.gitea.io/gitea/modules/util" ) -// ErrIssueStopwatchNotExist represents an error that stopwatch is not exist -type ErrIssueStopwatchNotExist struct { - UserID int64 - IssueID int64 -} - -func (err ErrIssueStopwatchNotExist) Error() string { - return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID) -} - -func (err ErrIssueStopwatchNotExist) Unwrap() error { - return util.ErrNotExist -} - // Stopwatch represents a stopwatch for time tracking. type Stopwatch struct { ID int64 `xorm:"pk autoincr"` @@ -55,13 +40,11 @@ func getStopwatch(ctx context.Context, userID, issueID int64) (sw *Stopwatch, ex return sw, exists, err } -// UserIDCount is a simple coalition of UserID and Count type UserStopwatch struct { UserID int64 StopWatches []*Stopwatch } -// GetUIDsAndNotificationCounts between the two provided times func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) { sws := []*Stopwatch{} if err := db.GetEngine(ctx).Where("issue_id != 0").Find(&sws); err != nil { @@ -87,7 +70,7 @@ func GetUIDsAndStopwatch(ctx context.Context) ([]*UserStopwatch, error) { return res, nil } -// GetUserStopwatches return list of all stopwatches of a user +// GetUserStopwatches return list of the user's all stopwatches func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOptions) ([]*Stopwatch, error) { sws := make([]*Stopwatch, 0, 8) sess := db.GetEngine(ctx).Where("stopwatch.user_id = ?", userID) @@ -102,7 +85,7 @@ func GetUserStopwatches(ctx context.Context, userID int64, listOptions db.ListOp return sws, nil } -// CountUserStopwatches return count of all stopwatches of a user +// CountUserStopwatches return count of the user's all stopwatches func CountUserStopwatches(ctx context.Context, userID int64) (int64, error) { return db.GetEngine(ctx).Where("user_id = ?", userID).Count(&Stopwatch{}) } @@ -136,43 +119,21 @@ func HasUserStopwatch(ctx context.Context, userID int64) (exists bool, sw *Stopw return exists, sw, issue, err } -// FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore -func FinishIssueStopwatchIfPossible(ctx context.Context, user *user_model.User, issue *Issue) error { - _, exists, err := getStopwatch(ctx, user.ID, issue.ID) - if err != nil { - return err - } - if !exists { - return nil - } - return FinishIssueStopwatch(ctx, user, issue) -} - -// CreateOrStopIssueStopwatch create an issue stopwatch if it's not exist, otherwise finish it -func CreateOrStopIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { - _, exists, err := getStopwatch(ctx, user.ID, issue.ID) - if err != nil { - return err - } - if exists { - return FinishIssueStopwatch(ctx, user, issue) - } - return CreateIssueStopwatch(ctx, user, issue) -} - -// FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error -func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { +// FinishIssueStopwatch if stopwatch exists, then finish it. +func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) { sw, exists, err := getStopwatch(ctx, user.ID, issue.ID) if err != nil { - return err + return false, err + } else if !exists { + return false, nil } - if !exists { - return ErrIssueStopwatchNotExist{ - UserID: user.ID, - IssueID: issue.ID, - } + if err = finishIssueStopwatch(ctx, user, issue, sw); err != nil { + return false, err } + return true, nil +} +func finishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue, sw *Stopwatch) error { // Create tracked time out of the time difference between start date and actual date timediff := time.Now().Unix() - int64(sw.CreatedUnix) @@ -184,14 +145,12 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss Time: timediff, } - if err := db.Insert(ctx, tt); err != nil { + if err := issue.LoadRepo(ctx); err != nil { return err } - - if err := issue.LoadRepo(ctx); err != nil { + if err := db.Insert(ctx, tt); err != nil { return err } - if _, err := CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, @@ -202,83 +161,65 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss }); err != nil { return err } - _, err = db.DeleteByBean(ctx, sw) + _, err := db.DeleteByBean(ctx, sw) return err } -// CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error -func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { - if err := issue.LoadRepo(ctx); err != nil { - return err - } - - // if another stopwatch is running: stop it - exists, _, otherIssue, err := HasUserStopwatch(ctx, user.ID) - if err != nil { - return err - } - if exists { - if err := FinishIssueStopwatch(ctx, user, otherIssue); err != nil { - return err +// CreateIssueStopwatch creates a stopwatch if the issue doesn't have the user's stopwatch. +// It also stops any other stopwatch that might be running for the user. +func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) { + { // if another issue's stopwatch is running: stop it; if this issue has a stopwatch: return an error. + exists, otherStopWatch, otherIssue, err := HasUserStopwatch(ctx, user.ID) + if err != nil { + return false, err + } + if exists { + if otherStopWatch.IssueID == issue.ID { + // don't allow starting stopwatch for the same issue + return false, nil + } + // stop the other issue's stopwatch + if err = finishIssueStopwatch(ctx, user, otherIssue, otherStopWatch); err != nil { + return false, err + } } } - // Create stopwatch - sw := &Stopwatch{ - UserID: user.ID, - IssueID: issue.ID, + if err = issue.LoadRepo(ctx); err != nil { + return false, err } - - if err := db.Insert(ctx, sw); err != nil { - return err + if err = db.Insert(ctx, &Stopwatch{UserID: user.ID, IssueID: issue.ID}); err != nil { + return false, err } - - if err := issue.LoadRepo(ctx); err != nil { - return err - } - - if _, err := CreateComment(ctx, &CreateCommentOptions{ + if _, err = CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, Type: CommentTypeStartTracking, }); err != nil { - return err + return false, err } - - return nil + return true, nil } // CancelStopwatch removes the given stopwatch and logs it into issue's timeline. -func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return err - } - defer committer.Close() - if err := cancelStopwatch(ctx, user, issue); err != nil { - return err - } - return committer.Commit() -} - -func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) error { - e := db.GetEngine(ctx) - sw, exists, err := getStopwatch(ctx, user.ID, issue.ID) - if err != nil { - return err - } - - if exists { - if _, err := e.Delete(sw); err != nil { +func CancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) (ok bool, err error) { + err = db.WithTx(ctx, func(ctx context.Context) error { + e := db.GetEngine(ctx) + sw, exists, err := getStopwatch(ctx, user.ID, issue.ID) + if err != nil { return err + } else if !exists { + return nil } - if err := issue.LoadRepo(ctx); err != nil { + if err = issue.LoadRepo(ctx); err != nil { return err } - - if _, err := CreateComment(ctx, &CreateCommentOptions{ + if _, err = e.Delete(sw); err != nil { + return err + } + if _, err = CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -286,6 +227,8 @@ func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) e }); err != nil { return err } - } - return nil + ok = true + return nil + }) + return ok, err } diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go index a1bf9dc931fb8..6333c10234b4e 100644 --- a/models/issues/stopwatch_test.go +++ b/models/issues/stopwatch_test.go @@ -10,7 +10,6 @@ import ( issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/timeutil" "github.com/stretchr/testify/assert" ) @@ -18,26 +17,22 @@ import ( func TestCancelStopwatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user1, err := user_model.GetUserByID(db.DefaultContext, 1) - assert.NoError(t, err) - - issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) - assert.NoError(t, err) - issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2) - assert.NoError(t, err) + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) - err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1) + ok, err := issues_model.CancelStopwatch(db.DefaultContext, user1, issue1) assert.NoError(t, err) + assert.True(t, ok) unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user1.ID, IssueID: issue1.ID}) + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID}) - _ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID}) - - assert.NoError(t, issues_model.CancelStopwatch(db.DefaultContext, user1, issue2)) + ok, err = issues_model.CancelStopwatch(db.DefaultContext, user1, issue1) + assert.NoError(t, err) + assert.False(t, ok) } func TestStopwatchExists(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - assert.True(t, issues_model.StopwatchExists(db.DefaultContext, 1, 1)) assert.False(t, issues_model.StopwatchExists(db.DefaultContext, 1, 2)) } @@ -58,21 +53,35 @@ func TestHasUserStopwatch(t *testing.T) { func TestCreateOrStopIssueStopwatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) - user2, err := user_model.GetUserByID(db.DefaultContext, 2) + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}) + issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}) + + // create a new stopwatch + ok, err := issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1) assert.NoError(t, err) - org3, err := user_model.GetUserByID(db.DefaultContext, 3) + assert.True(t, ok) + unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID}) + // should not create a second stopwatch for the same issue + ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue1) assert.NoError(t, err) - - issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) + assert.False(t, ok) + // on a different issue, it will finish the existing stopwatch and create a new one + ok, err = issues_model.CreateIssueStopwatch(db.DefaultContext, user4, issue3) assert.NoError(t, err) - issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2) + assert.True(t, ok) + unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue1.ID}) + unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: user4.ID, IssueID: issue3.ID}) + + // user2 already has a stopwatch in test fixture + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) + ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2) assert.NoError(t, err) - - assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, org3, issue1)) - sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1}) - assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow()) - - assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(db.DefaultContext, user2, issue2)) - unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2}) - unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2}) + assert.True(t, ok) + unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user2.ID, IssueID: issue2.ID}) + unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: user2.ID, IssueID: issue2.ID}) + ok, err = issues_model.FinishIssueStopwatch(db.DefaultContext, user2, issue2) + assert.NoError(t, err) + assert.False(t, ok) } diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 7da426fef0890..33fd1df707d88 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -1,7 +1,6 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//nolint:forbidigo package base import ( @@ -106,7 +105,7 @@ func MainTest(m *testing.M) { giteaConf := os.Getenv("GITEA_CONF") if giteaConf == "" { giteaConf = filepath.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini") - fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf) + _, _ = fmt.Fprintf(os.Stderr, "Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf) } if !filepath.IsAbs(giteaConf) { @@ -134,7 +133,7 @@ func MainTest(m *testing.M) { exitStatus := m.Run() if err := removeAllWithRetry(setting.RepoRootPath); err != nil { - fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err) } os.Exit(exitStatus) } diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go index 5d2fd8e244942..1742bea2963af 100644 --- a/models/migrations/v1_10/v100.go +++ b/models/migrations/v1_10/v100.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "net/url" diff --git a/models/migrations/v1_10/v101.go b/models/migrations/v1_10/v101.go index f023a2a0e779e..6c8dfe24860d1 100644 --- a/models/migrations/v1_10/v101.go +++ b/models/migrations/v1_10/v101.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_10/v88.go b/models/migrations/v1_10/v88.go index 7e86ac364f61a..eb8e81c19ee87 100644 --- a/models/migrations/v1_10/v88.go +++ b/models/migrations/v1_10/v88.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "crypto/sha1" diff --git a/models/migrations/v1_10/v89.go b/models/migrations/v1_10/v89.go index d5f27ffdc65db..0df2a6e17b6d1 100644 --- a/models/migrations/v1_10/v89.go +++ b/models/migrations/v1_10/v89.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v90.go b/models/migrations/v1_10/v90.go index 295d4b1c1bab8..5521a97e32773 100644 --- a/models/migrations/v1_10/v90.go +++ b/models/migrations/v1_10/v90.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v91.go b/models/migrations/v1_10/v91.go index 48cac2de7071f..08db6c274210d 100644 --- a/models/migrations/v1_10/v91.go +++ b/models/migrations/v1_10/v91.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v92.go b/models/migrations/v1_10/v92.go index 9080108594cd0..b6c04a9234884 100644 --- a/models/migrations/v1_10/v92.go +++ b/models/migrations/v1_10/v92.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "xorm.io/builder" diff --git a/models/migrations/v1_10/v93.go b/models/migrations/v1_10/v93.go index ee59a8db394f4..c131be9a8d88c 100644 --- a/models/migrations/v1_10/v93.go +++ b/models/migrations/v1_10/v93.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v94.go b/models/migrations/v1_10/v94.go index c131af162b3cf..13b7d7b303ef2 100644 --- a/models/migrations/v1_10/v94.go +++ b/models/migrations/v1_10/v94.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v95.go b/models/migrations/v1_10/v95.go index 3b1f67fd9c964..86b52026bfa11 100644 --- a/models/migrations/v1_10/v95.go +++ b/models/migrations/v1_10/v95.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go index 34c8240031c20..ca35a169c4ad9 100644 --- a/models/migrations/v1_10/v96.go +++ b/models/migrations/v1_10/v96.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "path/filepath" diff --git a/models/migrations/v1_10/v97.go b/models/migrations/v1_10/v97.go index dee45b32e35ea..5872bb63e57fd 100644 --- a/models/migrations/v1_10/v97.go +++ b/models/migrations/v1_10/v97.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v98.go b/models/migrations/v1_10/v98.go index bdd9aed0891ad..d21c326459598 100644 --- a/models/migrations/v1_10/v98.go +++ b/models/migrations/v1_10/v98.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import "xorm.io/xorm" diff --git a/models/migrations/v1_10/v99.go b/models/migrations/v1_10/v99.go index ebe6597f7c99b..223c188057b44 100644 --- a/models/migrations/v1_10/v99.go +++ b/models/migrations/v1_10/v99.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_10 //nolint +package v1_10 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_11/v102.go b/models/migrations/v1_11/v102.go index 9358e4cef3344..e52290afb0aac 100644 --- a/models/migrations/v1_11/v102.go +++ b/models/migrations/v1_11/v102.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_11/v103.go b/models/migrations/v1_11/v103.go index 53527dac586ff..a5157101605fb 100644 --- a/models/migrations/v1_11/v103.go +++ b/models/migrations/v1_11/v103.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v104.go b/models/migrations/v1_11/v104.go index 3e8ee64bc1112..3b0d3c64b2fbc 100644 --- a/models/migrations/v1_11/v104.go +++ b/models/migrations/v1_11/v104.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_11/v105.go b/models/migrations/v1_11/v105.go index b91340c30a944..d86973a0f6826 100644 --- a/models/migrations/v1_11/v105.go +++ b/models/migrations/v1_11/v105.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go index ecb11cdd1e34c..edffe18683195 100644 --- a/models/migrations/v1_11/v106.go +++ b/models/migrations/v1_11/v106.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v107.go b/models/migrations/v1_11/v107.go index f0bfe5862c9f0..a158e3bb5024b 100644 --- a/models/migrations/v1_11/v107.go +++ b/models/migrations/v1_11/v107.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v108.go b/models/migrations/v1_11/v108.go index a85096234d262..8f14504cebfb8 100644 --- a/models/migrations/v1_11/v108.go +++ b/models/migrations/v1_11/v108.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v109.go b/models/migrations/v1_11/v109.go index ea565ccda37e4..f7616aec7b5fb 100644 --- a/models/migrations/v1_11/v109.go +++ b/models/migrations/v1_11/v109.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v110.go b/models/migrations/v1_11/v110.go index 81afa1331d4ff..512f728c035e8 100644 --- a/models/migrations/v1_11/v110.go +++ b/models/migrations/v1_11/v110.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index 1c8527b2aab3f..c27465f05180f 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v112.go b/models/migrations/v1_11/v112.go index 0857663119535..fe45cf922218a 100644 --- a/models/migrations/v1_11/v112.go +++ b/models/migrations/v1_11/v112.go @@ -1,12 +1,12 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( - "fmt" "path/filepath" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -31,7 +31,7 @@ func RemoveAttachmentMissedRepo(x *xorm.Engine) error { for i := 0; i < len(attachments); i++ { uuid := attachments[i].UUID if err = util.RemoveAll(filepath.Join(setting.Attachment.Storage.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { - fmt.Printf("Error: %v", err) //nolint:forbidigo + log.Warn("Unable to remove attachment file by UUID %s: %v", uuid, err) } } diff --git a/models/migrations/v1_11/v113.go b/models/migrations/v1_11/v113.go index dea344a44f268..a4d54f66fb469 100644 --- a/models/migrations/v1_11/v113.go +++ b/models/migrations/v1_11/v113.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "fmt" diff --git a/models/migrations/v1_11/v114.go b/models/migrations/v1_11/v114.go index 95adcee989c1f..9467a8a90c9c7 100644 --- a/models/migrations/v1_11/v114.go +++ b/models/migrations/v1_11/v114.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "net/url" diff --git a/models/migrations/v1_11/v115.go b/models/migrations/v1_11/v115.go index c44c6d88e4d96..5933c0520f6fe 100644 --- a/models/migrations/v1_11/v115.go +++ b/models/migrations/v1_11/v115.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "crypto/md5" diff --git a/models/migrations/v1_11/v116.go b/models/migrations/v1_11/v116.go index 85aa76c1e0217..729fbad18b843 100644 --- a/models/migrations/v1_11/v116.go +++ b/models/migrations/v1_11/v116.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_11 //nolint +package v1_11 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v117.go b/models/migrations/v1_12/v117.go index 8eadcdef2b331..73b58ca34b25f 100644 --- a/models/migrations/v1_12/v117.go +++ b/models/migrations/v1_12/v117.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v118.go b/models/migrations/v1_12/v118.go index eb022dc5e487e..e8b4249743d69 100644 --- a/models/migrations/v1_12/v118.go +++ b/models/migrations/v1_12/v118.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v119.go b/models/migrations/v1_12/v119.go index 60bfe6a57da2b..b4bf29a9359c0 100644 --- a/models/migrations/v1_12/v119.go +++ b/models/migrations/v1_12/v119.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v120.go b/models/migrations/v1_12/v120.go index 3f7ed8d373153..14d515f5a7f1a 100644 --- a/models/migrations/v1_12/v120.go +++ b/models/migrations/v1_12/v120.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v121.go b/models/migrations/v1_12/v121.go index 175ec9164dddb..a28ae4e1c9f42 100644 --- a/models/migrations/v1_12/v121.go +++ b/models/migrations/v1_12/v121.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v122.go b/models/migrations/v1_12/v122.go index 6e31d863a1259..bc1b175f6ae1c 100644 --- a/models/migrations/v1_12/v122.go +++ b/models/migrations/v1_12/v122.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v123.go b/models/migrations/v1_12/v123.go index b0c3af07a3a4e..52b10bb85062a 100644 --- a/models/migrations/v1_12/v123.go +++ b/models/migrations/v1_12/v123.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v124.go b/models/migrations/v1_12/v124.go index d2ba03ffe03d7..9a93f436d43ce 100644 --- a/models/migrations/v1_12/v124.go +++ b/models/migrations/v1_12/v124.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v125.go b/models/migrations/v1_12/v125.go index ec4ffaab254be..7f582ecff5e96 100644 --- a/models/migrations/v1_12/v125.go +++ b/models/migrations/v1_12/v125.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v126.go b/models/migrations/v1_12/v126.go index ca9ec3aa3f340..64fd7f747875a 100644 --- a/models/migrations/v1_12/v126.go +++ b/models/migrations/v1_12/v126.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/builder" diff --git a/models/migrations/v1_12/v127.go b/models/migrations/v1_12/v127.go index 00e391dc875b8..9bd78db95e44e 100644 --- a/models/migrations/v1_12/v127.go +++ b/models/migrations/v1_12/v127.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index cba64711d0976..e7dbff3766083 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v129.go b/models/migrations/v1_12/v129.go index cf228242b9dfd..3e4d3aca6859c 100644 --- a/models/migrations/v1_12/v129.go +++ b/models/migrations/v1_12/v129.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v130.go b/models/migrations/v1_12/v130.go index 391810c7cadea..107bb756fd69c 100644 --- a/models/migrations/v1_12/v130.go +++ b/models/migrations/v1_12/v130.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "code.gitea.io/gitea/modules/json" diff --git a/models/migrations/v1_12/v131.go b/models/migrations/v1_12/v131.go index 5184bc3590323..1266c2f185998 100644 --- a/models/migrations/v1_12/v131.go +++ b/models/migrations/v1_12/v131.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v132.go b/models/migrations/v1_12/v132.go index 3b2b28f7abb4e..8b1ae6db935d8 100644 --- a/models/migrations/v1_12/v132.go +++ b/models/migrations/v1_12/v132.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v133.go b/models/migrations/v1_12/v133.go index c9087fc8c143e..69e20597d8504 100644 --- a/models/migrations/v1_12/v133.go +++ b/models/migrations/v1_12/v133.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import "xorm.io/xorm" diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index a918d38757919..09d743964dc26 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v135.go b/models/migrations/v1_12/v135.go index 8898011df56c8..5df0ad7fc4f92 100644 --- a/models/migrations/v1_12/v135.go +++ b/models/migrations/v1_12/v135.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v136.go b/models/migrations/v1_12/v136.go index d91ff92feb00a..0f53278b4623f 100644 --- a/models/migrations/v1_12/v136.go +++ b/models/migrations/v1_12/v136.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v137.go b/models/migrations/v1_12/v137.go index 0d86b72010923..9d384834882ee 100644 --- a/models/migrations/v1_12/v137.go +++ b/models/migrations/v1_12/v137.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_12/v138.go b/models/migrations/v1_12/v138.go index 8c8d353f405c8..4485adeb2dccb 100644 --- a/models/migrations/v1_12/v138.go +++ b/models/migrations/v1_12/v138.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "fmt" diff --git a/models/migrations/v1_12/v139.go b/models/migrations/v1_12/v139.go index 279aa7df87dc4..a3799841ac5f6 100644 --- a/models/migrations/v1_12/v139.go +++ b/models/migrations/v1_12/v139.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_12 //nolint +package v1_12 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_13/v140.go b/models/migrations/v1_13/v140.go index f3719e16f62a4..a9a047bca9d5b 100644 --- a/models/migrations/v1_13/v140.go +++ b/models/migrations/v1_13/v140.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" @@ -21,12 +21,7 @@ func FixLanguageStatsToSaveSize(x *xorm.Engine) error { // RepoIndexerType specifies the repository indexer type type RepoIndexerType int - const ( - // RepoIndexerTypeCode code indexer - 0 - RepoIndexerTypeCode RepoIndexerType = iota //nolint:unused - // RepoIndexerTypeStats repository stats indexer - 1 - RepoIndexerTypeStats - ) + const RepoIndexerTypeStats RepoIndexerType = 1 // RepoIndexerStatus see models/repo_indexer.go type RepoIndexerStatus struct { diff --git a/models/migrations/v1_13/v141.go b/models/migrations/v1_13/v141.go index ae211e0e44b7f..b54bc1727cb40 100644 --- a/models/migrations/v1_13/v141.go +++ b/models/migrations/v1_13/v141.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v142.go b/models/migrations/v1_13/v142.go index 7c7c01ad47d8a..d08a0ae0bf407 100644 --- a/models/migrations/v1_13/v142.go +++ b/models/migrations/v1_13/v142.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go index 885768dff37de..b9a856ed0faf1 100644 --- a/models/migrations/v1_13/v143.go +++ b/models/migrations/v1_13/v143.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_13/v144.go b/models/migrations/v1_13/v144.go index f5a0bc575100f..9352d78bc88dc 100644 --- a/models/migrations/v1_13/v144.go +++ b/models/migrations/v1_13/v144.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_13/v145.go b/models/migrations/v1_13/v145.go index bb1f40baa719b..86ebb4f9d95c8 100644 --- a/models/migrations/v1_13/v145.go +++ b/models/migrations/v1_13/v145.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v146.go b/models/migrations/v1_13/v146.go index 7d9a87870478c..355c772c268c8 100644 --- a/models/migrations/v1_13/v146.go +++ b/models/migrations/v1_13/v146.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_13/v147.go b/models/migrations/v1_13/v147.go index 510ef39b286b9..0059c062203f1 100644 --- a/models/migrations/v1_13/v147.go +++ b/models/migrations/v1_13/v147.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_13/v148.go b/models/migrations/v1_13/v148.go index 7bb8ab700b6a5..d276db3d61df3 100644 --- a/models/migrations/v1_13/v148.go +++ b/models/migrations/v1_13/v148.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v149.go b/models/migrations/v1_13/v149.go index 2a1db04cbb496..a96b8e5ac7da2 100644 --- a/models/migrations/v1_13/v149.go +++ b/models/migrations/v1_13/v149.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "fmt" diff --git a/models/migrations/v1_13/v150.go b/models/migrations/v1_13/v150.go index d5ba489566545..590ea72903068 100644 --- a/models/migrations/v1_13/v150.go +++ b/models/migrations/v1_13/v150.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_13/v151.go b/models/migrations/v1_13/v151.go index 1865d58f048ce..454929534fe2d 100644 --- a/models/migrations/v1_13/v151.go +++ b/models/migrations/v1_13/v151.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "context" diff --git a/models/migrations/v1_13/v152.go b/models/migrations/v1_13/v152.go index 502c82a40de59..648e26446fed3 100644 --- a/models/migrations/v1_13/v152.go +++ b/models/migrations/v1_13/v152.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import "xorm.io/xorm" diff --git a/models/migrations/v1_13/v153.go b/models/migrations/v1_13/v153.go index 0b2dd3eb62eac..e5462fc1624ce 100644 --- a/models/migrations/v1_13/v153.go +++ b/models/migrations/v1_13/v153.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_13/v154.go b/models/migrations/v1_13/v154.go index 60cc56713e5e4..5477d1b8891b9 100644 --- a/models/migrations/v1_13/v154.go +++ b/models/migrations/v1_13/v154.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_13 //nolint +package v1_13 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_14/main_test.go b/models/migrations/v1_14/main_test.go index 7a091b9b9acf6..978f88577c3b5 100644 --- a/models/migrations/v1_14/main_test.go +++ b/models/migrations/v1_14/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v155.go b/models/migrations/v1_14/v155.go index e814f59938d5b..505a9ae033479 100644 --- a/models/migrations/v1_14/v155.go +++ b/models/migrations/v1_14/v155.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v156.go b/models/migrations/v1_14/v156.go index 2cf4954a15f58..2fa5819610ee9 100644 --- a/models/migrations/v1_14/v156.go +++ b/models/migrations/v1_14/v156.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v157.go b/models/migrations/v1_14/v157.go index 7187278d29427..2c5625ebbd4e7 100644 --- a/models/migrations/v1_14/v157.go +++ b/models/migrations/v1_14/v157.go @@ -1,24 +1,13 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" ) func FixRepoTopics(x *xorm.Engine) error { - type Topic struct { //nolint:unused - ID int64 `xorm:"pk autoincr"` - Name string `xorm:"UNIQUE VARCHAR(25)"` - RepoCount int - } - - type RepoTopic struct { //nolint:unused - RepoID int64 `xorm:"pk"` - TopicID int64 `xorm:"pk"` - } - type Repository struct { ID int64 `xorm:"pk autoincr"` Topics []string `xorm:"TEXT JSON"` diff --git a/models/migrations/v1_14/v158.go b/models/migrations/v1_14/v158.go index a849ddf27e69d..3c57e8e3daa4b 100644 --- a/models/migrations/v1_14/v158.go +++ b/models/migrations/v1_14/v158.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "errors" diff --git a/models/migrations/v1_14/v159.go b/models/migrations/v1_14/v159.go index 149ae0f6a8e26..e6f6f0f061a25 100644 --- a/models/migrations/v1_14/v159.go +++ b/models/migrations/v1_14/v159.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_14/v160.go b/models/migrations/v1_14/v160.go index 4dea91b5148c1..73f3798954717 100644 --- a/models/migrations/v1_14/v160.go +++ b/models/migrations/v1_14/v160.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v161.go b/models/migrations/v1_14/v161.go index ac7e821a804b2..eb92dee77cb41 100644 --- a/models/migrations/v1_14/v161.go +++ b/models/migrations/v1_14/v161.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "context" diff --git a/models/migrations/v1_14/v162.go b/models/migrations/v1_14/v162.go index 2e4e0b8eb0547..a0ddd36d55f82 100644 --- a/models/migrations/v1_14/v162.go +++ b/models/migrations/v1_14/v162.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_14/v163.go b/models/migrations/v1_14/v163.go index 0cd8ba68c8ec1..84c35190b7b54 100644 --- a/models/migrations/v1_14/v163.go +++ b/models/migrations/v1_14/v163.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_14/v164.go b/models/migrations/v1_14/v164.go index 54f6951427ea2..d2fd9b8464a22 100644 --- a/models/migrations/v1_14/v164.go +++ b/models/migrations/v1_14/v164.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v165.go b/models/migrations/v1_14/v165.go index 926350cdf7803..6e1b34156b83e 100644 --- a/models/migrations/v1_14/v165.go +++ b/models/migrations/v1_14/v165.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "code.gitea.io/gitea/models/migrations/base" @@ -16,10 +16,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error { return nil } - type HookTask struct { //nolint:unused - Typ string `xorm:"VARCHAR(16) index"` - } - + // HookTask: Typ string `xorm:"VARCHAR(16) index"` if err := base.ModifyColumn(x, "hook_task", &schemas.Column{ Name: "typ", SQLType: schemas.SQLType{ @@ -42,10 +39,7 @@ func ConvertHookTaskTypeToVarcharAndTrim(x *xorm.Engine) error { return err } - type Webhook struct { //nolint:unused - Type string `xorm:"VARCHAR(16) index"` - } - + // Webhook: Type string `xorm:"VARCHAR(16) index"` if err := base.ModifyColumn(x, "webhook", &schemas.Column{ Name: "type", SQLType: schemas.SQLType{ diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index e5731582fdd59..4c106bd7daf0a 100644 --- a/models/migrations/v1_14/v166.go +++ b/models/migrations/v1_14/v166.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "crypto/sha256" diff --git a/models/migrations/v1_14/v167.go b/models/migrations/v1_14/v167.go index 9d416f6a32d47..d77bbc401e534 100644 --- a/models/migrations/v1_14/v167.go +++ b/models/migrations/v1_14/v167.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v168.go b/models/migrations/v1_14/v168.go index a30a8859f7fea..aa93eec19b0ae 100644 --- a/models/migrations/v1_14/v168.go +++ b/models/migrations/v1_14/v168.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import "xorm.io/xorm" diff --git a/models/migrations/v1_14/v169.go b/models/migrations/v1_14/v169.go index 5b81bb58b199c..4f9df0d96f295 100644 --- a/models/migrations/v1_14/v169.go +++ b/models/migrations/v1_14/v169.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v170.go b/models/migrations/v1_14/v170.go index 7b6498a3e9b4a..a2ff4623e1453 100644 --- a/models/migrations/v1_14/v170.go +++ b/models/migrations/v1_14/v170.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v171.go b/models/migrations/v1_14/v171.go index 51a35a02add16..7b200e960ad82 100644 --- a/models/migrations/v1_14/v171.go +++ b/models/migrations/v1_14/v171.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v172.go b/models/migrations/v1_14/v172.go index 0f9bef902a361..bbd61d87b287e 100644 --- a/models/migrations/v1_14/v172.go +++ b/models/migrations/v1_14/v172.go @@ -1,7 +1,7 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_14/v173.go b/models/migrations/v1_14/v173.go index 2d9eee9197ff4..7752fbe966484 100644 --- a/models/migrations/v1_14/v173.go +++ b/models/migrations/v1_14/v173.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v174.go b/models/migrations/v1_14/v174.go index c839e15db85d2..4049e43070d7d 100644 --- a/models/migrations/v1_14/v174.go +++ b/models/migrations/v1_14/v174.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v175.go b/models/migrations/v1_14/v175.go index 70d72b2600337..92ed1304734a2 100644 --- a/models/migrations/v1_14/v175.go +++ b/models/migrations/v1_14/v175.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v176.go b/models/migrations/v1_14/v176.go index 1ed49f75fac97..ef5dce9a02780 100644 --- a/models/migrations/v1_14/v176.go +++ b/models/migrations/v1_14/v176.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_14/v176_test.go b/models/migrations/v1_14/v176_test.go index ea3e750d7f953..5c1db4db71c17 100644 --- a/models/migrations/v1_14/v176_test.go +++ b/models/migrations/v1_14/v176_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_14/v177.go b/models/migrations/v1_14/v177.go index 6e1838f3696a5..96676bf8d9423 100644 --- a/models/migrations/v1_14/v177.go +++ b/models/migrations/v1_14/v177.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "fmt" diff --git a/models/migrations/v1_14/v177_test.go b/models/migrations/v1_14/v177_test.go index 5568a18fec0d4..263f69f338014 100644 --- a/models/migrations/v1_14/v177_test.go +++ b/models/migrations/v1_14/v177_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_14 //nolint +package v1_14 import ( "testing" diff --git a/models/migrations/v1_15/main_test.go b/models/migrations/v1_15/main_test.go index 366f19788ec7f..d01585e997801 100644 --- a/models/migrations/v1_15/main_test.go +++ b/models/migrations/v1_15/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v178.go b/models/migrations/v1_15/v178.go index 6d236eb049831..ca3a5c262e46b 100644 --- a/models/migrations/v1_15/v178.go +++ b/models/migrations/v1_15/v178.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v179.go b/models/migrations/v1_15/v179.go index f6b142eb42d46..d6fb86ffecc0a 100644 --- a/models/migrations/v1_15/v179.go +++ b/models/migrations/v1_15/v179.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go index c71e77186170c..dd132f83306c1 100644 --- a/models/migrations/v1_15/v180.go +++ b/models/migrations/v1_15/v180.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "code.gitea.io/gitea/modules/json" diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go index 2185ed02134aa..fb1d3d7a75f86 100644 --- a/models/migrations/v1_15/v181.go +++ b/models/migrations/v1_15/v181.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v181_test.go b/models/migrations/v1_15/v181_test.go index 7295aa4180c8f..73b5c1f3d6ce6 100644 --- a/models/migrations/v1_15/v181_test.go +++ b/models/migrations/v1_15/v181_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "strings" diff --git a/models/migrations/v1_15/v182.go b/models/migrations/v1_15/v182.go index 9ca500c0f9637..f53ff11df9cde 100644 --- a/models/migrations/v1_15/v182.go +++ b/models/migrations/v1_15/v182.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v182_test.go b/models/migrations/v1_15/v182_test.go index 75ef8e1cd83f3..5fc6a0c467e59 100644 --- a/models/migrations/v1_15/v182_test.go +++ b/models/migrations/v1_15/v182_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "testing" diff --git a/models/migrations/v1_15/v183.go b/models/migrations/v1_15/v183.go index effad1b467c39..5d0582f03d4dd 100644 --- a/models/migrations/v1_15/v183.go +++ b/models/migrations/v1_15/v183.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "fmt" diff --git a/models/migrations/v1_15/v184.go b/models/migrations/v1_15/v184.go index 4b3dd1467a839..2823bc1f7af61 100644 --- a/models/migrations/v1_15/v184.go +++ b/models/migrations/v1_15/v184.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "context" diff --git a/models/migrations/v1_15/v185.go b/models/migrations/v1_15/v185.go index e5878ec193879..60af59edca45d 100644 --- a/models/migrations/v1_15/v185.go +++ b/models/migrations/v1_15/v185.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_15/v186.go b/models/migrations/v1_15/v186.go index 01aab3add5c40..67dc97d13d7f4 100644 --- a/models/migrations/v1_15/v186.go +++ b/models/migrations/v1_15/v186.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_15/v187.go b/models/migrations/v1_15/v187.go index 21cd6772b7e92..5fd90c65fbfee 100644 --- a/models/migrations/v1_15/v187.go +++ b/models/migrations/v1_15/v187.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_15/v188.go b/models/migrations/v1_15/v188.go index 71e45cab0e317..4494e6ff0552b 100644 --- a/models/migrations/v1_15/v188.go +++ b/models/migrations/v1_15/v188.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_15 //nolint +package v1_15 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/main_test.go b/models/migrations/v1_16/main_test.go index 817a0c13a458a..7f93d6e9e5ef8 100644 --- a/models/migrations/v1_16/main_test.go +++ b/models/migrations/v1_16/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go index 56496450519fd..6bc99e58ab72f 100644 --- a/models/migrations/v1_16/v189.go +++ b/models/migrations/v1_16/v189.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/binary" diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index 2a73bfae0318f..fb56ac8e1160b 100644 --- a/models/migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v190.go b/models/migrations/v1_16/v190.go index 5953802849a7b..1eb6b6ddb4f07 100644 --- a/models/migrations/v1_16/v190.go +++ b/models/migrations/v1_16/v190.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v191.go b/models/migrations/v1_16/v191.go index c618783c08e86..957c82e484ca7 100644 --- a/models/migrations/v1_16/v191.go +++ b/models/migrations/v1_16/v191.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_16/v192.go b/models/migrations/v1_16/v192.go index 2d5d158a09e0d..9d03fbe3c8fd4 100644 --- a/models/migrations/v1_16/v192.go +++ b/models/migrations/v1_16/v192.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_16/v193.go b/models/migrations/v1_16/v193.go index 8d3ce7a5587c3..a5af2de380506 100644 --- a/models/migrations/v1_16/v193.go +++ b/models/migrations/v1_16/v193.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v193_test.go b/models/migrations/v1_16/v193_test.go index 7f43846bc3f20..2e827f0550b0c 100644 --- a/models/migrations/v1_16/v193_test.go +++ b/models/migrations/v1_16/v193_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v194.go b/models/migrations/v1_16/v194.go index 6aa13c50cf833..2e4ed8340e6dd 100644 --- a/models/migrations/v1_16/v194.go +++ b/models/migrations/v1_16/v194.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195.go b/models/migrations/v1_16/v195.go index 6d7e94141e446..4fd42b7bd2352 100644 --- a/models/migrations/v1_16/v195.go +++ b/models/migrations/v1_16/v195.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v195_test.go b/models/migrations/v1_16/v195_test.go index 742397bf32a49..946e06e399777 100644 --- a/models/migrations/v1_16/v195_test.go +++ b/models/migrations/v1_16/v195_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_16/v196.go b/models/migrations/v1_16/v196.go index 7cbafc61e56e0..6c9caa100f1c9 100644 --- a/models/migrations/v1_16/v196.go +++ b/models/migrations/v1_16/v196.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v197.go b/models/migrations/v1_16/v197.go index 97888b284797b..862bdfdcbdb5c 100644 --- a/models/migrations/v1_16/v197.go +++ b/models/migrations/v1_16/v197.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v198.go b/models/migrations/v1_16/v198.go index 115bb313a0643..f35ede138a0cd 100644 --- a/models/migrations/v1_16/v198.go +++ b/models/migrations/v1_16/v198.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v199.go b/models/migrations/v1_16/v199.go index 6adcf890afb67..4020352f2b50f 100644 --- a/models/migrations/v1_16/v199.go +++ b/models/migrations/v1_16/v199.go @@ -1,6 +1,6 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 // We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now. diff --git a/models/migrations/v1_16/v200.go b/models/migrations/v1_16/v200.go index c08c20e51de44..de57fad8fe78b 100644 --- a/models/migrations/v1_16/v200.go +++ b/models/migrations/v1_16/v200.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v201.go b/models/migrations/v1_16/v201.go index 35e0c9f2fbe34..2c43698b0c905 100644 --- a/models/migrations/v1_16/v201.go +++ b/models/migrations/v1_16/v201.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v202.go b/models/migrations/v1_16/v202.go index 6ba36152f1f2f..d8c8fdcadc307 100644 --- a/models/migrations/v1_16/v202.go +++ b/models/migrations/v1_16/v202.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v203.go b/models/migrations/v1_16/v203.go index e8e6b52453848..c3241cba57620 100644 --- a/models/migrations/v1_16/v203.go +++ b/models/migrations/v1_16/v203.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v204.go b/models/migrations/v1_16/v204.go index ece03e1305262..4d375307e7651 100644 --- a/models/migrations/v1_16/v204.go +++ b/models/migrations/v1_16/v204.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import "xorm.io/xorm" diff --git a/models/migrations/v1_16/v205.go b/models/migrations/v1_16/v205.go index d6c577083cdca..78241bad5b237 100644 --- a/models/migrations/v1_16/v205.go +++ b/models/migrations/v1_16/v205.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_16/v206.go b/models/migrations/v1_16/v206.go index 581a7d76e9e30..01a9c386eb291 100644 --- a/models/migrations/v1_16/v206.go +++ b/models/migrations/v1_16/v206.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "fmt" diff --git a/models/migrations/v1_16/v207.go b/models/migrations/v1_16/v207.go index 91208f066cabe..19126ead1f460 100644 --- a/models/migrations/v1_16/v207.go +++ b/models/migrations/v1_16/v207.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v208.go b/models/migrations/v1_16/v208.go index 1a11ef096ad9a..fb643324f485b 100644 --- a/models/migrations/v1_16/v208.go +++ b/models/migrations/v1_16/v208.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v209.go b/models/migrations/v1_16/v209.go index be3100e02a047..230838647bb35 100644 --- a/models/migrations/v1_16/v209.go +++ b/models/migrations/v1_16/v209.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index 51b7d81e998f1..0b94baf8e3df3 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "encoding/base32" diff --git a/models/migrations/v1_16/v210_test.go b/models/migrations/v1_16/v210_test.go index 7917301c980ab..3b4ac7aa4b140 100644 --- a/models/migrations/v1_16/v210_test.go +++ b/models/migrations/v1_16/v210_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_16 //nolint +package v1_16 import ( "testing" diff --git a/models/migrations/v1_17/main_test.go b/models/migrations/v1_17/main_test.go index 79cb3fa078863..571a4f55a347c 100644 --- a/models/migrations/v1_17/main_test.go +++ b/models/migrations/v1_17/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "testing" diff --git a/models/migrations/v1_17/v211.go b/models/migrations/v1_17/v211.go index 9b72c8610b6ea..517cf19388d9d 100644 --- a/models/migrations/v1_17/v211.go +++ b/models/migrations/v1_17/v211.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v212.go b/models/migrations/v1_17/v212.go index e3f94371212c0..788792211f556 100644 --- a/models/migrations/v1_17/v212.go +++ b/models/migrations/v1_17/v212.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_17/v213.go b/models/migrations/v1_17/v213.go index bb3f466e5283f..b2bbdf727953d 100644 --- a/models/migrations/v1_17/v213.go +++ b/models/migrations/v1_17/v213.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v214.go b/models/migrations/v1_17/v214.go index 2268164919d41..1925324f0f151 100644 --- a/models/migrations/v1_17/v214.go +++ b/models/migrations/v1_17/v214.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_17/v215.go b/models/migrations/v1_17/v215.go index b338f854178ba..748539225d3bc 100644 --- a/models/migrations/v1_17/v215.go +++ b/models/migrations/v1_17/v215.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "code.gitea.io/gitea/models/pull" diff --git a/models/migrations/v1_17/v216.go b/models/migrations/v1_17/v216.go index 268f472a4250f..37aeacb6fca80 100644 --- a/models/migrations/v1_17/v216.go +++ b/models/migrations/v1_17/v216.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 // This migration added non-ideal indices to the action table which on larger datasets slowed things down // it has been superseded by v218.go diff --git a/models/migrations/v1_17/v217.go b/models/migrations/v1_17/v217.go index 3f970b68a540d..04626bcbc59f5 100644 --- a/models/migrations/v1_17/v217.go +++ b/models/migrations/v1_17/v217.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_17/v218.go b/models/migrations/v1_17/v218.go index 4c05a9b5392b3..17d4cd89d4e97 100644 --- a/models/migrations/v1_17/v218.go +++ b/models/migrations/v1_17/v218.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_17/v219.go b/models/migrations/v1_17/v219.go index d266029fd9f28..6e335cb813a18 100644 --- a/models/migrations/v1_17/v219.go +++ b/models/migrations/v1_17/v219.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "time" diff --git a/models/migrations/v1_17/v220.go b/models/migrations/v1_17/v220.go index 904ddc5192935..4ac8c58e1ec96 100644 --- a/models/migrations/v1_17/v220.go +++ b/models/migrations/v1_17/v220.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( packages_model "code.gitea.io/gitea/models/packages" diff --git a/models/migrations/v1_17/v221.go b/models/migrations/v1_17/v221.go index 9e159388bdc7a..9e6a67eb1837d 100644 --- a/models/migrations/v1_17/v221.go +++ b/models/migrations/v1_17/v221.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v221_test.go b/models/migrations/v1_17/v221_test.go index 9ca54142e2d59..a2dc0fae55402 100644 --- a/models/migrations/v1_17/v221_test.go +++ b/models/migrations/v1_17/v221_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "encoding/base32" diff --git a/models/migrations/v1_17/v222.go b/models/migrations/v1_17/v222.go index 6c28f8102b0a7..a5ea537d8a415 100644 --- a/models/migrations/v1_17/v222.go +++ b/models/migrations/v1_17/v222.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_17/v223.go b/models/migrations/v1_17/v223.go index 018451ee4c3b1..b2bfb76551d5e 100644 --- a/models/migrations/v1_17/v223.go +++ b/models/migrations/v1_17/v223.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_17 //nolint +package v1_17 import ( "context" diff --git a/models/migrations/v1_18/main_test.go b/models/migrations/v1_18/main_test.go index f71a21d1fb24b..ebcfb45a941d5 100644 --- a/models/migrations/v1_18/main_test.go +++ b/models/migrations/v1_18/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v224.go b/models/migrations/v1_18/v224.go index f3d522b91a17a..6dc12020eaa62 100644 --- a/models/migrations/v1_18/v224.go +++ b/models/migrations/v1_18/v224.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v225.go b/models/migrations/v1_18/v225.go index b0ac3777fc248..bc6117e38f595 100644 --- a/models/migrations/v1_18/v225.go +++ b/models/migrations/v1_18/v225.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_18/v226.go b/models/migrations/v1_18/v226.go index f87e24b11de9f..8ed9761476dbb 100644 --- a/models/migrations/v1_18/v226.go +++ b/models/migrations/v1_18/v226.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/builder" diff --git a/models/migrations/v1_18/v227.go b/models/migrations/v1_18/v227.go index 5fe5dcd0c9563..3aca686d5972f 100644 --- a/models/migrations/v1_18/v227.go +++ b/models/migrations/v1_18/v227.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_18/v228.go b/models/migrations/v1_18/v228.go index 3e7a36de15e7e..b13f6461bd810 100644 --- a/models/migrations/v1_18/v228.go +++ b/models/migrations/v1_18/v228.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_18/v229.go b/models/migrations/v1_18/v229.go index 10d9f350979f6..bc15e01390862 100644 --- a/models/migrations/v1_18/v229.go +++ b/models/migrations/v1_18/v229.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "fmt" diff --git a/models/migrations/v1_18/v229_test.go b/models/migrations/v1_18/v229_test.go index d489328c00056..5722dd35574b7 100644 --- a/models/migrations/v1_18/v229_test.go +++ b/models/migrations/v1_18/v229_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_18/v230.go b/models/migrations/v1_18/v230.go index ea5b4d02e1f26..078fce7643d30 100644 --- a/models/migrations/v1_18/v230.go +++ b/models/migrations/v1_18/v230.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_18/v230_test.go b/models/migrations/v1_18/v230_test.go index 40db4c2ffe20b..25b2f6525da02 100644 --- a/models/migrations/v1_18/v230_test.go +++ b/models/migrations/v1_18/v230_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_18 //nolint +package v1_18 import ( "testing" diff --git a/models/migrations/v1_19/main_test.go b/models/migrations/v1_19/main_test.go index 59f42af111623..87e807be6e126 100644 --- a/models/migrations/v1_19/main_test.go +++ b/models/migrations/v1_19/main_test.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v231.go b/models/migrations/v1_19/v231.go index 79e46132f0a3c..8ef1e4e743805 100644 --- a/models/migrations/v1_19/v231.go +++ b/models/migrations/v1_19/v231.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v232.go b/models/migrations/v1_19/v232.go index 9caf587c1e9ca..493dbc6df8316 100644 --- a/models/migrations/v1_19/v232.go +++ b/models/migrations/v1_19/v232.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_19/v233.go b/models/migrations/v1_19/v233.go index ba4cd8e20b995..9eb6d40509912 100644 --- a/models/migrations/v1_19/v233.go +++ b/models/migrations/v1_19/v233.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "fmt" diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index 5d445d5132997..7436ff7483cd1 100644 --- a/models/migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "testing" diff --git a/models/migrations/v1_19/v234.go b/models/migrations/v1_19/v234.go index 728a580807b25..3475384d6f42f 100644 --- a/models/migrations/v1_19/v234.go +++ b/models/migrations/v1_19/v234.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_19/v235.go b/models/migrations/v1_19/v235.go index 3715de3920c89..297d90f65a2a2 100644 --- a/models/migrations/v1_19/v235.go +++ b/models/migrations/v1_19/v235.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v236.go b/models/migrations/v1_19/v236.go index f172a85b1fc93..0ed4d97a27207 100644 --- a/models/migrations/v1_19/v236.go +++ b/models/migrations/v1_19/v236.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_19/v237.go b/models/migrations/v1_19/v237.go index b23c765aa5aac..cf30226ccd853 100644 --- a/models/migrations/v1_19/v237.go +++ b/models/migrations/v1_19/v237.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v238.go b/models/migrations/v1_19/v238.go index 266e6cea58a8a..de681bfc7a4b8 100644 --- a/models/migrations/v1_19/v238.go +++ b/models/migrations/v1_19/v238.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_19/v239.go b/models/migrations/v1_19/v239.go index 10076f2401696..8f4a65be95d99 100644 --- a/models/migrations/v1_19/v239.go +++ b/models/migrations/v1_19/v239.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v240.go b/models/migrations/v1_19/v240.go index 4505f86299556..7fdbaeb9dc9b1 100644 --- a/models/migrations/v1_19/v240.go +++ b/models/migrations/v1_19/v240.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/models/db" diff --git a/models/migrations/v1_19/v241.go b/models/migrations/v1_19/v241.go index a617d6fd2f6f2..e35801a0572ea 100644 --- a/models/migrations/v1_19/v241.go +++ b/models/migrations/v1_19/v241.go @@ -1,7 +1,7 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_19/v242.go b/models/migrations/v1_19/v242.go index 4470835214f34..e9e759eaaa9f7 100644 --- a/models/migrations/v1_19/v242.go +++ b/models/migrations/v1_19/v242.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_19/v243.go b/models/migrations/v1_19/v243.go index 55bbfafb2fa4a..9c3f372594945 100644 --- a/models/migrations/v1_19/v243.go +++ b/models/migrations/v1_19/v243.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_19 //nolint +package v1_19 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/main_test.go b/models/migrations/v1_20/main_test.go index 92a1a9f622659..2fd63a7118efb 100644 --- a/models/migrations/v1_20/main_test.go +++ b/models/migrations/v1_20/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "testing" diff --git a/models/migrations/v1_20/v244.go b/models/migrations/v1_20/v244.go index 977566ad7dcd2..76cdccaca5b66 100644 --- a/models/migrations/v1_20/v244.go +++ b/models/migrations/v1_20/v244.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v245.go b/models/migrations/v1_20/v245.go index 5a195d2ccd745..4acb11416c3d4 100644 --- a/models/migrations/v1_20/v245.go +++ b/models/migrations/v1_20/v245.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "context" diff --git a/models/migrations/v1_20/v246.go b/models/migrations/v1_20/v246.go index e6340ef079d68..22bf7234043bc 100644 --- a/models/migrations/v1_20/v246.go +++ b/models/migrations/v1_20/v246.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v247.go b/models/migrations/v1_20/v247.go index 59fc5c46b5dbc..4f82937e185c0 100644 --- a/models/migrations/v1_20/v247.go +++ b/models/migrations/v1_20/v247.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_20/v248.go b/models/migrations/v1_20/v248.go index 40555210e7e0b..4f2091e4bcaa8 100644 --- a/models/migrations/v1_20/v248.go +++ b/models/migrations/v1_20/v248.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import "xorm.io/xorm" diff --git a/models/migrations/v1_20/v249.go b/models/migrations/v1_20/v249.go index 02951a74d6d1c..c6d3a177ca3da 100644 --- a/models/migrations/v1_20/v249.go +++ b/models/migrations/v1_20/v249.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_20/v250.go b/models/migrations/v1_20/v250.go index 86388ef0b8019..ec45e6e5c31ce 100644 --- a/models/migrations/v1_20/v250.go +++ b/models/migrations/v1_20/v250.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "strings" diff --git a/models/migrations/v1_20/v251.go b/models/migrations/v1_20/v251.go index 7743248a3f17b..a274c22a7311c 100644 --- a/models/migrations/v1_20/v251.go +++ b/models/migrations/v1_20/v251.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_20/v252.go b/models/migrations/v1_20/v252.go index ab61cd9b8b36e..d6aa6027534e2 100644 --- a/models/migrations/v1_20/v252.go +++ b/models/migrations/v1_20/v252.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_20/v253.go b/models/migrations/v1_20/v253.go index 96c494bd8d903..c96454dbf9201 100644 --- a/models/migrations/v1_20/v253.go +++ b/models/migrations/v1_20/v253.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/log" diff --git a/models/migrations/v1_20/v254.go b/models/migrations/v1_20/v254.go index 1e26979a5b2a2..9cdbfb3916459 100644 --- a/models/migrations/v1_20/v254.go +++ b/models/migrations/v1_20/v254.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v255.go b/models/migrations/v1_20/v255.go index 14b70f8f962f9..caf198700e09e 100644 --- a/models/migrations/v1_20/v255.go +++ b/models/migrations/v1_20/v255.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_20/v256.go b/models/migrations/v1_20/v256.go index 822153b93e568..7b84c1e1544c2 100644 --- a/models/migrations/v1_20/v256.go +++ b/models/migrations/v1_20/v256.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v257.go b/models/migrations/v1_20/v257.go index 6c6ca4c7486d0..9d5f7c07dfc8a 100644 --- a/models/migrations/v1_20/v257.go +++ b/models/migrations/v1_20/v257.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_20/v258.go b/models/migrations/v1_20/v258.go index 47174ce8051a9..1d3faffdaedde 100644 --- a/models/migrations/v1_20/v258.go +++ b/models/migrations/v1_20/v258.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_20/v259.go b/models/migrations/v1_20/v259.go index 0fdeb45957866..9e0dc9b61d9cb 100644 --- a/models/migrations/v1_20/v259.go +++ b/models/migrations/v1_20/v259.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "fmt" diff --git a/models/migrations/v1_20/v259_test.go b/models/migrations/v1_20/v259_test.go index a1aeb53d5dcfe..0bf63719e5e48 100644 --- a/models/migrations/v1_20/v259_test.go +++ b/models/migrations/v1_20/v259_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_20 //nolint +package v1_20 import ( "sort" diff --git a/models/migrations/v1_21/main_test.go b/models/migrations/v1_21/main_test.go index 9afdea16775ea..536a7ade0884b 100644 --- a/models/migrations/v1_21/main_test.go +++ b/models/migrations/v1_21/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "testing" diff --git a/models/migrations/v1_21/v260.go b/models/migrations/v1_21/v260.go index 6ca52c5998df7..8540c58ae864f 100644 --- a/models/migrations/v1_21/v260.go +++ b/models/migrations/v1_21/v260.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_21/v261.go b/models/migrations/v1_21/v261.go index 4ec1160d0b3eb..122b98eb93bf6 100644 --- a/models/migrations/v1_21/v261.go +++ b/models/migrations/v1_21/v261.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_21/v262.go b/models/migrations/v1_21/v262.go index 23e900572a223..6e88e29b9dbb5 100644 --- a/models/migrations/v1_21/v262.go +++ b/models/migrations/v1_21/v262.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v263.go b/models/migrations/v1_21/v263.go index 2c7cbadf0d89d..55c418bde0dc2 100644 --- a/models/migrations/v1_21/v263.go +++ b/models/migrations/v1_21/v263.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "fmt" diff --git a/models/migrations/v1_21/v264.go b/models/migrations/v1_21/v264.go index d737ef03b3b1b..7fc0ec602408d 100644 --- a/models/migrations/v1_21/v264.go +++ b/models/migrations/v1_21/v264.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "context" diff --git a/models/migrations/v1_21/v265.go b/models/migrations/v1_21/v265.go index 800eb95f72c08..b6892acc2770c 100644 --- a/models/migrations/v1_21/v265.go +++ b/models/migrations/v1_21/v265.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v266.go b/models/migrations/v1_21/v266.go index 79a5f5e14c575..440549e868b2f 100644 --- a/models/migrations/v1_21/v266.go +++ b/models/migrations/v1_21/v266.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v267.go b/models/migrations/v1_21/v267.go index bc0e954bdcc93..394139a17e200 100644 --- a/models/migrations/v1_21/v267.go +++ b/models/migrations/v1_21/v267.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_21/v268.go b/models/migrations/v1_21/v268.go index 332793ff073b8..b677d2383e9ad 100644 --- a/models/migrations/v1_21/v268.go +++ b/models/migrations/v1_21/v268.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v269.go b/models/migrations/v1_21/v269.go index 475ec023804e2..042040927d4f8 100644 --- a/models/migrations/v1_21/v269.go +++ b/models/migrations/v1_21/v269.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v270.go b/models/migrations/v1_21/v270.go index b9cc84d3ac41b..ab7c5660bad05 100644 --- a/models/migrations/v1_21/v270.go +++ b/models/migrations/v1_21/v270.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v271.go b/models/migrations/v1_21/v271.go index 098f6499d57e5..05e1af1351de0 100644 --- a/models/migrations/v1_21/v271.go +++ b/models/migrations/v1_21/v271.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_21/v272.go b/models/migrations/v1_21/v272.go index a729c49f1bc71..14c1e0c4b0fb1 100644 --- a/models/migrations/v1_21/v272.go +++ b/models/migrations/v1_21/v272.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go index 61c79f4a763d3..e614a56a7dc1d 100644 --- a/models/migrations/v1_21/v273.go +++ b/models/migrations/v1_21/v273.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_21/v274.go b/models/migrations/v1_21/v274.go index df5994f159ffb..d0b557a1519fc 100644 --- a/models/migrations/v1_21/v274.go +++ b/models/migrations/v1_21/v274.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 + import ( "time" diff --git a/models/migrations/v1_21/v275.go b/models/migrations/v1_21/v275.go index 78804a59d629b..2bfe5c72fa9e5 100644 --- a/models/migrations/v1_21/v275.go +++ b/models/migrations/v1_21/v275.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index 9d22c9052e18c..3ab7e22cd05d9 100644 --- a/models/migrations/v1_21/v276.go +++ b/models/migrations/v1_21/v276.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "context" diff --git a/models/migrations/v1_21/v277.go b/models/migrations/v1_21/v277.go index 12529160b754b..0c102edddecaf 100644 --- a/models/migrations/v1_21/v277.go +++ b/models/migrations/v1_21/v277.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v278.go b/models/migrations/v1_21/v278.go index d6a462d1e7e60..846f22867809d 100644 --- a/models/migrations/v1_21/v278.go +++ b/models/migrations/v1_21/v278.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_21/v279.go b/models/migrations/v1_21/v279.go index 2abd1bbe84bdc..beb39effe1ad0 100644 --- a/models/migrations/v1_21/v279.go +++ b/models/migrations/v1_21/v279.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_21 //nolint +package v1_21 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/main_test.go b/models/migrations/v1_22/main_test.go index efd8dbaa8c6b5..ac8facd6aa0ab 100644 --- a/models/migrations/v1_22/main_test.go +++ b/models/migrations/v1_22/main_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v280.go b/models/migrations/v1_22/v280.go index a8ee4a3bf7dac..2271cb6089602 100644 --- a/models/migrations/v1_22/v280.go +++ b/models/migrations/v1_22/v280.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v281.go b/models/migrations/v1_22/v281.go index fc1866aa8353e..129ec2cba09e1 100644 --- a/models/migrations/v1_22/v281.go +++ b/models/migrations/v1_22/v281.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_22/v282.go b/models/migrations/v1_22/v282.go index baad9e09168bd..eed64c30f79ce 100644 --- a/models/migrations/v1_22/v282.go +++ b/models/migrations/v1_22/v282.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v283.go b/models/migrations/v1_22/v283.go index 0a45c51245972..0eca031b65482 100644 --- a/models/migrations/v1_22/v283.go +++ b/models/migrations/v1_22/v283.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "fmt" diff --git a/models/migrations/v1_22/v283_test.go b/models/migrations/v1_22/v283_test.go index e89a7cbfc2c5d..743f860466fa8 100644 --- a/models/migrations/v1_22/v283_test.go +++ b/models/migrations/v1_22/v283_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v284.go b/models/migrations/v1_22/v284.go index 2b9507898021a..31b38f6aed863 100644 --- a/models/migrations/v1_22/v284.go +++ b/models/migrations/v1_22/v284.go @@ -1,7 +1,8 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 + import ( "xorm.io/xorm" ) diff --git a/models/migrations/v1_22/v285.go b/models/migrations/v1_22/v285.go index a55cc17c04f0a..fed89f670e087 100644 --- a/models/migrations/v1_22/v285.go +++ b/models/migrations/v1_22/v285.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "time" diff --git a/models/migrations/v1_22/v286.go b/models/migrations/v1_22/v286.go index 1fcde33202a14..f3ba50dbb63a5 100644 --- a/models/migrations/v1_22/v286.go +++ b/models/migrations/v1_22/v286.go @@ -1,6 +1,6 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "errors" diff --git a/models/migrations/v1_22/v286_test.go b/models/migrations/v1_22/v286_test.go index 4702e4c37c586..b4a50f6fcb495 100644 --- a/models/migrations/v1_22/v286_test.go +++ b/models/migrations/v1_22/v286_test.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v287.go b/models/migrations/v1_22/v287.go index c8b1593286945..5fd901f9deddd 100644 --- a/models/migrations/v1_22/v287.go +++ b/models/migrations/v1_22/v287.go @@ -1,7 +1,7 @@ // Copyright 2023 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v287_test.go b/models/migrations/v1_22/v287_test.go index 58c3152ac3dbe..2b42a33c389f3 100644 --- a/models/migrations/v1_22/v287_test.go +++ b/models/migrations/v1_22/v287_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "strconv" diff --git a/models/migrations/v1_22/v288.go b/models/migrations/v1_22/v288.go index 7c93bfcc6632e..26c850c218896 100644 --- a/models/migrations/v1_22/v288.go +++ b/models/migrations/v1_22/v288.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_22/v289.go b/models/migrations/v1_22/v289.go index b9941aadd90f1..78689a4ffaee0 100644 --- a/models/migrations/v1_22/v289.go +++ b/models/migrations/v1_22/v289.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v290.go b/models/migrations/v1_22/v290.go index 9c54d4e87c8cc..0f4d78410c14e 100644 --- a/models/migrations/v1_22/v290.go +++ b/models/migrations/v1_22/v290.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_22/v291.go b/models/migrations/v1_22/v291.go index 74726fae966c5..823a644a95537 100644 --- a/models/migrations/v1_22/v291.go +++ b/models/migrations/v1_22/v291.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v292.go b/models/migrations/v1_22/v292.go index beca556aee298..440f48ce8096a 100644 --- a/models/migrations/v1_22/v292.go +++ b/models/migrations/v1_22/v292.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 // NOTE: noop the original migration has bug which some projects will be skip, so // these projects will have no default board. diff --git a/models/migrations/v1_22/v293.go b/models/migrations/v1_22/v293.go index 53cc719294bdb..5299b8618f0dc 100644 --- a/models/migrations/v1_22/v293.go +++ b/models/migrations/v1_22/v293.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_22/v293_test.go b/models/migrations/v1_22/v293_test.go index cfe4345143e0a..2c8f7683a832d 100644 --- a/models/migrations/v1_22/v293_test.go +++ b/models/migrations/v1_22/v293_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v294.go b/models/migrations/v1_22/v294.go index 20e261fb1b8c5..8776e51a165e4 100644 --- a/models/migrations/v1_22/v294.go +++ b/models/migrations/v1_22/v294.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "fmt" diff --git a/models/migrations/v1_22/v294_test.go b/models/migrations/v1_22/v294_test.go index c3de332650526..1cf03d61201af 100644 --- a/models/migrations/v1_22/v294_test.go +++ b/models/migrations/v1_22/v294_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "testing" diff --git a/models/migrations/v1_22/v295.go b/models/migrations/v1_22/v295.go index 17bdadb4ad366..319b1a399b338 100644 --- a/models/migrations/v1_22/v295.go +++ b/models/migrations/v1_22/v295.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v296.go b/models/migrations/v1_22/v296.go index 1ecacab95f2f0..75350f9f654b0 100644 --- a/models/migrations/v1_22/v296.go +++ b/models/migrations/v1_22/v296.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_22/v297.go b/models/migrations/v1_22/v297.go index 7d4b5069258a6..9a4405f2666a7 100644 --- a/models/migrations/v1_22/v297.go +++ b/models/migrations/v1_22/v297.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import ( "code.gitea.io/gitea/models/perm" diff --git a/models/migrations/v1_22/v298.go b/models/migrations/v1_22/v298.go index b9f3b95ade808..7700173a004e5 100644 --- a/models/migrations/v1_22/v298.go +++ b/models/migrations/v1_22/v298.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_22 //nolint +package v1_22 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/main_test.go b/models/migrations/v1_23/main_test.go index b7948bd4dd248..f7b2caed83dff 100644 --- a/models/migrations/v1_23/main_test.go +++ b/models/migrations/v1_23/main_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_23/v299.go b/models/migrations/v1_23/v299.go index e5fde3749b6ce..11021d8855f00 100644 --- a/models/migrations/v1_23/v299.go +++ b/models/migrations/v1_23/v299.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v300.go b/models/migrations/v1_23/v300.go index 51de43da5e6da..13c6489c5e451 100644 --- a/models/migrations/v1_23/v300.go +++ b/models/migrations/v1_23/v300.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v301.go b/models/migrations/v1_23/v301.go index 99c8e3d8eac2f..ed8e9ef05996f 100644 --- a/models/migrations/v1_23/v301.go +++ b/models/migrations/v1_23/v301.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v302.go b/models/migrations/v1_23/v302.go index 5d2e9b143814a..e4a50b3ec88c5 100644 --- a/models/migrations/v1_23/v302.go +++ b/models/migrations/v1_23/v302.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_23/v302_test.go b/models/migrations/v1_23/v302_test.go index 29e85ae9d9ccd..b008b6fc03da2 100644 --- a/models/migrations/v1_23/v302_test.go +++ b/models/migrations/v1_23/v302_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_23/v303.go b/models/migrations/v1_23/v303.go index 1e3638893021b..dc541a9535ab6 100644 --- a/models/migrations/v1_23/v303.go +++ b/models/migrations/v1_23/v303.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_23/v304.go b/models/migrations/v1_23/v304.go index e108f477799f3..35d4d4881a920 100644 --- a/models/migrations/v1_23/v304.go +++ b/models/migrations/v1_23/v304.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v304_test.go b/models/migrations/v1_23/v304_test.go index 955219d3f97cd..c3dfa5e7e7ef2 100644 --- a/models/migrations/v1_23/v304_test.go +++ b/models/migrations/v1_23/v304_test.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "testing" diff --git a/models/migrations/v1_23/v305.go b/models/migrations/v1_23/v305.go index 4d881192b2770..3762279de1b03 100644 --- a/models/migrations/v1_23/v305.go +++ b/models/migrations/v1_23/v305.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_23/v306.go b/models/migrations/v1_23/v306.go index a1e698fe31f03..c5c89dbeb8f9f 100644 --- a/models/migrations/v1_23/v306.go +++ b/models/migrations/v1_23/v306.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import "xorm.io/xorm" diff --git a/models/migrations/v1_23/v307.go b/models/migrations/v1_23/v307.go index ef7f5f2c3f486..54a69d250b7ce 100644 --- a/models/migrations/v1_23/v307.go +++ b/models/migrations/v1_23/v307.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_23/v308.go b/models/migrations/v1_23/v308.go index 1e8a9b0af24f3..695fdfcc2de3f 100644 --- a/models/migrations/v1_23/v308.go +++ b/models/migrations/v1_23/v308.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_23/v309.go b/models/migrations/v1_23/v309.go index 5b39398443ff1..e629b718a80f3 100644 --- a/models/migrations/v1_23/v309.go +++ b/models/migrations/v1_23/v309.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_23/v310.go b/models/migrations/v1_23/v310.go index c856a708f9175..074b1c54d358c 100644 --- a/models/migrations/v1_23/v310.go +++ b/models/migrations/v1_23/v310.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_23/v311.go b/models/migrations/v1_23/v311.go index 21293d83be046..ef48085c79bdb 100644 --- a/models/migrations/v1_23/v311.go +++ b/models/migrations/v1_23/v311.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_23 //nolint +package v1_23 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v312.go b/models/migrations/v1_24/v312.go index 367a6c4947e4f..823b0eae40308 100644 --- a/models/migrations/v1_24/v312.go +++ b/models/migrations/v1_24/v312.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v313.go b/models/migrations/v1_24/v313.go index ee9d479340873..7e6cda6bfd323 100644 --- a/models/migrations/v1_24/v313.go +++ b/models/migrations/v1_24/v313.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_24/v314.go b/models/migrations/v1_24/v314.go index e537be13b5c35..51cb2e34aa96f 100644 --- a/models/migrations/v1_24/v314.go +++ b/models/migrations/v1_24/v314.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v315.go b/models/migrations/v1_24/v315.go index 22a72c31e92c9..52b9b44785627 100644 --- a/models/migrations/v1_24/v315.go +++ b/models/migrations/v1_24/v315.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v316.go b/models/migrations/v1_24/v316.go index e7f04333ccff0..14e888f9eeaa4 100644 --- a/models/migrations/v1_24/v316.go +++ b/models/migrations/v1_24/v316.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v317.go b/models/migrations/v1_24/v317.go index 3da5a4a0784f8..a13db2dd27e43 100644 --- a/models/migrations/v1_24/v317.go +++ b/models/migrations/v1_24/v317.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_24/v318.go b/models/migrations/v1_24/v318.go index 3e08c3d504947..9b4a54096097a 100644 --- a/models/migrations/v1_24/v318.go +++ b/models/migrations/v1_24/v318.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "code.gitea.io/gitea/models/perm" diff --git a/models/migrations/v1_24/v319.go b/models/migrations/v1_24/v319.go index 6571ddf75b031..648081f74e0c1 100644 --- a/models/migrations/v1_24/v319.go +++ b/models/migrations/v1_24/v319.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_24/v320.go b/models/migrations/v1_24/v320.go index 1d34444826f0f..ebef71939c264 100644 --- a/models/migrations/v1_24/v320.go +++ b/models/migrations/v1_24/v320.go @@ -1,7 +1,7 @@ // Copyright 2025 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_24 //nolint +package v1_24 import ( "code.gitea.io/gitea/modules/json" diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go index 74434a84a14f4..41f096694234c 100644 --- a/models/migrations/v1_6/v70.go +++ b/models/migrations/v1_6/v70.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v71.go b/models/migrations/v1_6/v71.go index 586187228b305..2b11f57c92f8f 100644 --- a/models/migrations/v1_6/v71.go +++ b/models/migrations/v1_6/v71.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_6/v72.go b/models/migrations/v1_6/v72.go index 04cef9a1707c7..9fad88a1b6123 100644 --- a/models/migrations/v1_6/v72.go +++ b/models/migrations/v1_6/v72.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_6 //nolint +package v1_6 import ( "fmt" diff --git a/models/migrations/v1_7/v73.go b/models/migrations/v1_7/v73.go index b5a748aae3a67..e0b7a28537530 100644 --- a/models/migrations/v1_7/v73.go +++ b/models/migrations/v1_7/v73.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_7/v74.go b/models/migrations/v1_7/v74.go index f0567e3c9b9c3..376be37a246e3 100644 --- a/models/migrations/v1_7/v74.go +++ b/models/migrations/v1_7/v74.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import "xorm.io/xorm" diff --git a/models/migrations/v1_7/v75.go b/models/migrations/v1_7/v75.go index fa7430970c91d..ef115754664d3 100644 --- a/models/migrations/v1_7/v75.go +++ b/models/migrations/v1_7/v75.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_7 //nolint +package v1_7 import ( "xorm.io/builder" diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go index d3fbd94deb104..81e93075497bd 100644 --- a/models/migrations/v1_8/v76.go +++ b/models/migrations/v1_8/v76.go @@ -1,7 +1,7 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_8/v77.go b/models/migrations/v1_8/v77.go index 8b199939245ff..4fe5ebe6350d6 100644 --- a/models/migrations/v1_8/v77.go +++ b/models/migrations/v1_8/v77.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_8/v78.go b/models/migrations/v1_8/v78.go index 8f041c14849d7..e67f46413125c 100644 --- a/models/migrations/v1_8/v78.go +++ b/models/migrations/v1_8/v78.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "code.gitea.io/gitea/models/migrations/base" diff --git a/models/migrations/v1_8/v79.go b/models/migrations/v1_8/v79.go index eb3a9ed0f4df3..3f50114d5a54d 100644 --- a/models/migrations/v1_8/v79.go +++ b/models/migrations/v1_8/v79.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "code.gitea.io/gitea/modules/setting" diff --git a/models/migrations/v1_8/v80.go b/models/migrations/v1_8/v80.go index cebbbead28b78..6f9df47a933e0 100644 --- a/models/migrations/v1_8/v80.go +++ b/models/migrations/v1_8/v80.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import "xorm.io/xorm" diff --git a/models/migrations/v1_8/v81.go b/models/migrations/v1_8/v81.go index a100dc1ef71f1..3c2acc64584af 100644 --- a/models/migrations/v1_8/v81.go +++ b/models/migrations/v1_8/v81.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_8 //nolint +package v1_8 import ( "fmt" diff --git a/models/migrations/v1_9/v82.go b/models/migrations/v1_9/v82.go index 26806dd64505d..c685d3b86b7cd 100644 --- a/models/migrations/v1_9/v82.go +++ b/models/migrations/v1_9/v82.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v83.go b/models/migrations/v1_9/v83.go index 10e6c45875785..a0cd57f7c5f68 100644 --- a/models/migrations/v1_9/v83.go +++ b/models/migrations/v1_9/v83.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "code.gitea.io/gitea/modules/timeutil" diff --git a/models/migrations/v1_9/v84.go b/models/migrations/v1_9/v84.go index c7155fe9cff8d..423915ae57c48 100644 --- a/models/migrations/v1_9/v84.go +++ b/models/migrations/v1_9/v84.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v85.go b/models/migrations/v1_9/v85.go index a23d7c5d6efe1..48e1cd5dc4e2f 100644 --- a/models/migrations/v1_9/v85.go +++ b/models/migrations/v1_9/v85.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "fmt" diff --git a/models/migrations/v1_9/v86.go b/models/migrations/v1_9/v86.go index cf2725d15854d..9464ff0cf6802 100644 --- a/models/migrations/v1_9/v86.go +++ b/models/migrations/v1_9/v86.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/migrations/v1_9/v87.go b/models/migrations/v1_9/v87.go index fa01b6e5e3694..81a4ebf80d034 100644 --- a/models/migrations/v1_9/v87.go +++ b/models/migrations/v1_9/v87.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package v1_9 //nolint +package v1_9 import ( "xorm.io/xorm" diff --git a/models/organization/org.go b/models/organization/org.go index dc889ea17fa8e..0f3aef146c220 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -602,8 +602,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder { "team_user.uid": userID, }) } - -// TeamsWithAccessToRepo returns all teams that have given access level to the repository. -func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) { - return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode) -} diff --git a/models/organization/team_repo.go b/models/organization/team_repo.go index 53edd203a8a7c..b3e266dbc7651 100644 --- a/models/organization/team_repo.go +++ b/models/organization/team_repo.go @@ -9,6 +9,8 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" + + "xorm.io/builder" ) // TeamRepo represents an team-repository relation. @@ -48,26 +50,27 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error { return err } -// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository. -func GetTeamsWithAccessToRepo(ctx context.Context, orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) { +// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit. +// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control. +// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details +func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) ([]*Team, error) { teams := make([]*Team, 0, 5) - return teams, db.GetEngine(ctx).Where("team.authorize >= ?", mode). - Join("INNER", "team_repo", "team_repo.team_id = team.id"). - And("team_repo.org_id = ?", orgID). - And("team_repo.repo_id = ?", repoID). - OrderBy("name"). - Find(&teams) -} -// GetTeamsWithAccessToRepoUnit returns all teams in an organization that have given access level to the repository special unit. -func GetTeamsWithAccessToRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) { - teams := make([]*Team, 0, 5) - return teams, db.GetEngine(ctx).Where("team_unit.access_mode >= ?", mode). + sub := builder.Select("team_id").From("team_unit"). + Where(builder.Expr("team_unit.team_id = team.id")). + And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))). + And(builder.Expr("team_unit.access_mode >= ?", mode)) + + err := db.GetEngine(ctx). Join("INNER", "team_repo", "team_repo.team_id = team.id"). - Join("INNER", "team_unit", "team_unit.team_id = team.id"). And("team_repo.org_id = ?", orgID). And("team_repo.repo_id = ?", repoID). - And("team_unit.type = ?", unitType). + And(builder.Or( + builder.Expr("team.authorize >= ?", mode), + builder.In("team.id", sub), + )). OrderBy("name"). Find(&teams) + + return teams, err } diff --git a/models/organization/team_repo_test.go b/models/organization/team_repo_test.go index c0d6750df90cb..73a06a93c2140 100644 --- a/models/organization/team_repo_test.go +++ b/models/organization/team_repo_test.go @@ -22,7 +22,7 @@ func TestGetTeamsWithAccessToRepoUnit(t *testing.T) { org41 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 41}) repo61 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 61}) - teams, err := organization.GetTeamsWithAccessToRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests) + teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests) assert.NoError(t, err) if assert.Len(t, teams, 2) { assert.EqualValues(t, 21, teams[0].ID) diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 45efb192c8b71..7de43ecd07c56 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -42,6 +42,7 @@ func (p *Permission) IsAdmin() bool { // HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository. // It doesn't count the "public(anonymous/everyone) access mode". +// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess` func (p *Permission) HasAnyUnitAccess() bool { for _, v := range p.unitsMode { if v >= perm_model.AccessModeRead { @@ -267,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use perm.units = repo.Units // anonymous user visit private repo. - // TODO: anonymous user visit public unit of private repo??? if user == nil && repo.IsPrivate { perm.AccessMode = perm_model.AccessModeNone return perm, nil @@ -286,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use } // Prevent strangers from checking out public repo of private organization/users - // Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself + // Allow user if they are a collaborator of a repo within a private user or a private organization but not a member of the organization itself + // TODO: rename it to "IsOwnerVisibleToDoer" if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator { perm.AccessMode = perm_model.AccessModeNone return perm, nil @@ -304,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use return perm, nil } - // plain user + // plain user TODO: this check should be replaced, only need to check collaborator access mode perm.AccessMode, err = accessLevel(ctx, user, repo) if err != nil { return perm, err @@ -314,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use return perm, nil } + // now: the owner is visible to doer, if the repo is public, then the min access mode is read + minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone) + perm.AccessMode = max(perm.AccessMode, minAccessMode) + + // get units mode from teams + teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID) + if err != nil { + return perm, err + } + if len(teams) == 0 { + return perm, nil + } + perm.unitsMode = make(map[unit.Type]perm_model.AccessMode) // Collaborators on organization @@ -323,12 +337,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use } } - // get units mode from teams - teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID) - if err != nil { - return perm, err - } - // if user in an owner team for _, team := range teams { if team.HasAdminAccess() { @@ -339,19 +347,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use } for _, u := range repo.Units { - var found bool for _, team := range teams { + unitAccessMode := minAccessMode if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist { - perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode) - found = true - } - } - - // for a public repo on an organization, a non-restricted user has read permission on non-team defined units. - if !found && !repo.IsPrivate && !user.IsRestricted { - if _, ok := perm.unitsMode[u.Type]; !ok { - perm.unitsMode[u.Type] = perm_model.AccessModeRead + unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode) } + perm.unitsMode[u.Type] = unitAccessMode } } @@ -408,13 +409,13 @@ func IsUserRepoAdmin(ctx context.Context, repo *repo_model.Repository, user *use // AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the // user does not have access. -func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint +func AccessLevel(ctx context.Context, user *user_model.User, repo *repo_model.Repository) (perm_model.AccessMode, error) { //nolint:revive // export stutter return AccessLevelUnit(ctx, user, repo, unit.TypeCode) } // AccessLevelUnit returns the Access a user has to a repository's. Will return NoneAccess if the // user does not have access. -func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint +func AccessLevelUnit(ctx context.Context, user *user_model.User, repo *repo_model.Repository, unitType unit.Type) (perm_model.AccessMode, error) { //nolint:revive // export stutter perm, err := GetUserRepoPermission(ctx, repo, user) if err != nil { return perm_model.AccessModeNone, err diff --git a/models/perm/access/repo_permission_test.go b/models/perm/access/repo_permission_test.go index 024f4400b3d66..c8675b1ded5bc 100644 --- a/models/perm/access/repo_permission_test.go +++ b/models/perm/access/repo_permission_test.go @@ -6,12 +6,16 @@ package access import ( "testing" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" perm_model "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestHasAnyUnitAccess(t *testing.T) { @@ -152,3 +156,45 @@ func TestUnitAccessMode(t *testing.T) { } assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map") } + +func TestGetUserRepoPermission(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + ctx := t.Context() + repo32 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) // org public repo + require.NoError(t, repo32.LoadOwner(ctx)) + require.True(t, repo32.Owner.IsOrganization()) + + require.NoError(t, db.TruncateBeans(ctx, &organization.Team{}, &organization.TeamUser{}, &organization.TeamRepo{}, &organization.TeamUnit{})) + org := repo32.Owner + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + team := &organization.Team{OrgID: org.ID, LowerName: "test_team"} + require.NoError(t, db.Insert(ctx, team)) + + t.Run("DoerInTeamWithNoRepo", func(t *testing.T) { + require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID})) + perm, err := GetUserRepoPermission(ctx, repo32, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode) + assert.Nil(t, perm.unitsMode) // doer in the team, but has no access to the repo + }) + + require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: repo32.ID})) + require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeNone})) + t.Run("DoerWithTeamUnitAccessNone", func(t *testing.T) { + perm, err := GetUserRepoPermission(ctx, repo32, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode) + assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeCode]) + assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues]) + }) + + require.NoError(t, db.TruncateBeans(ctx, &organization.TeamUnit{})) + require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeWrite})) + t.Run("DoerWithTeamUnitAccessWrite", func(t *testing.T) { + perm, err := GetUserRepoPermission(ctx, repo32, user) + require.NoError(t, err) + assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode) + assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode]) + assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues]) + }) +} diff --git a/models/project/project.go b/models/project/project.go index c35d17a2beb4e..ab2e1c66f1d0b 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -138,11 +138,11 @@ func (p *Project) LoadRepo(ctx context.Context) (err error) { return err } -func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint +func ProjectLinkForOrg(org *user_model.User, projectID int64) string { //nolint:revive // export stutter return fmt.Sprintf("%s/-/projects/%d", org.HomeLink(), projectID) } -func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint +func ProjectLinkForRepo(repo *repo_model.Repository, projectID int64) string { //nolint:revive // export stutter return fmt.Sprintf("%s/projects/%d", repo.Link(), projectID) } diff --git a/models/repo/language_stats.go b/models/repo/language_stats.go index 0bc0f1fb40203..7db8cd4dd2003 100644 --- a/models/repo/language_stats.go +++ b/models/repo/language_stats.go @@ -157,18 +157,17 @@ func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, for lang, size := range stats { if size > s { s = size - topLang = strings.ToLower(lang) + topLang = lang } } for lang, size := range stats { upd := false - llang := strings.ToLower(lang) for _, s := range oldstats { // Update already existing language - if strings.ToLower(s.Language) == llang { + if strings.EqualFold(s.Language, lang) { s.CommitID = commitID - s.IsPrimary = llang == topLang + s.IsPrimary = lang == topLang s.Size = size if _, err := sess.ID(s.ID).Cols("`commit_id`", "`size`", "`is_primary`").Update(s); err != nil { return err @@ -182,7 +181,7 @@ func UpdateLanguageStats(ctx context.Context, repo *Repository, commitID string, if err := db.Insert(ctx, &LanguageStat{ RepoID: repo.ID, CommitID: commitID, - IsPrimary: llang == topLang, + IsPrimary: lang == topLang, Language: lang, Size: size, }); err != nil { diff --git a/models/repo/transfer.go b/models/repo/transfer.go index b4a3592cbcf8f..3fb8cb69abdaa 100644 --- a/models/repo/transfer.go +++ b/models/repo/transfer.go @@ -61,7 +61,7 @@ func (err ErrRepoTransferInProgress) Unwrap() error { } // RepoTransfer is used to manage repository transfers -type RepoTransfer struct { //nolint +type RepoTransfer struct { //nolint:revive // export stutter ID int64 `xorm:"pk autoincr"` DoerID int64 Doer *user_model.User `xorm:"-"` diff --git a/models/repo/update.go b/models/repo/update.go index f82ff7c76c222..64065f11c46e2 100644 --- a/models/repo/update.go +++ b/models/repo/update.go @@ -40,21 +40,15 @@ func UpdateRepositoryUpdatedTime(ctx context.Context, repoID int64, updateTime t return err } -// UpdateRepositoryColsWithAutoTime updates repository's columns -func UpdateRepositoryColsWithAutoTime(ctx context.Context, repo *Repository, cols ...string) error { - if len(cols) == 0 { - return nil - } - _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).Update(repo) +// UpdateRepositoryColsWithAutoTime updates repository's columns and the timestamp fields automatically +func UpdateRepositoryColsWithAutoTime(ctx context.Context, repo *Repository, colName string, moreColNames ...string) error { + _, err := db.GetEngine(ctx).ID(repo.ID).Cols(append([]string{colName}, moreColNames...)...).Update(repo) return err } -// UpdateRepositoryColsNoAutoTime updates repository's columns and but applies time change automatically -func UpdateRepositoryColsNoAutoTime(ctx context.Context, repo *Repository, cols ...string) error { - if len(cols) == 0 { - return nil - } - _, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).NoAutoTime().Update(repo) +// UpdateRepositoryColsNoAutoTime updates repository's columns, doesn't change timestamp field automatically +func UpdateRepositoryColsNoAutoTime(ctx context.Context, repo *Repository, colName string, moreColNames ...string) error { + _, err := db.GetEngine(ctx).ID(repo.ID).Cols(append([]string{colName}, moreColNames...)...).NoAutoTime().Update(repo) return err } diff --git a/models/user/badge.go b/models/user/badge.go index 3ff3530a369a5..e475ceb74894d 100644 --- a/models/user/badge.go +++ b/models/user/badge.go @@ -19,7 +19,7 @@ type Badge struct { } // UserBadge represents a user badge -type UserBadge struct { //nolint:revive +type UserBadge struct { //nolint:revive // export stutter ID int64 `xorm:"pk autoincr"` BadgeID int64 UserID int64 `xorm:"INDEX"` diff --git a/modules/auth/httpauth/httpauth.go b/modules/auth/httpauth/httpauth.go new file mode 100644 index 0000000000000..7f1f1ee152cf7 --- /dev/null +++ b/modules/auth/httpauth/httpauth.go @@ -0,0 +1,47 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package httpauth + +import ( + "encoding/base64" + "strings" + + "code.gitea.io/gitea/modules/util" +) + +type BasicAuth struct { + Username, Password string +} + +type BearerToken struct { + Token string +} + +type ParsedAuthorizationHeader struct { + BasicAuth *BasicAuth + BearerToken *BearerToken +} + +func ParseAuthorizationHeader(header string) (ret ParsedAuthorizationHeader, _ bool) { + parts := strings.Fields(header) + if len(parts) != 2 { + return ret, false + } + if util.AsciiEqualFold(parts[0], "basic") { + s, err := base64.StdEncoding.DecodeString(parts[1]) + if err != nil { + return ret, false + } + u, p, ok := strings.Cut(string(s), ":") + if !ok { + return ret, false + } + ret.BasicAuth = &BasicAuth{Username: u, Password: p} + return ret, true + } else if util.AsciiEqualFold(parts[0], "token") || util.AsciiEqualFold(parts[0], "bearer") { + ret.BearerToken = &BearerToken{Token: parts[1]} + return ret, true + } + return ret, false +} diff --git a/modules/auth/httpauth/httpauth_test.go b/modules/auth/httpauth/httpauth_test.go new file mode 100644 index 0000000000000..087b86917f0af --- /dev/null +++ b/modules/auth/httpauth/httpauth_test.go @@ -0,0 +1,43 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package httpauth + +import ( + "encoding/base64" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseAuthorizationHeader(t *testing.T) { + type parsed = ParsedAuthorizationHeader + type basic = BasicAuth + type bearer = BearerToken + cases := []struct { + headerValue string + expected parsed + ok bool + }{ + {"", parsed{}, false}, + {"?", parsed{}, false}, + {"foo", parsed{}, false}, + {"any value", parsed{}, false}, + + {"Basic ?", parsed{}, false}, + {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo")), parsed{}, false}, + {"Basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true}, + {"basic " + base64.StdEncoding.EncodeToString([]byte("foo:bar")), parsed{BasicAuth: &basic{"foo", "bar"}}, true}, + + {"token value", parsed{BearerToken: &bearer{"value"}}, true}, + {"Token value", parsed{BearerToken: &bearer{"value"}}, true}, + {"bearer value", parsed{BearerToken: &bearer{"value"}}, true}, + {"Bearer value", parsed{BearerToken: &bearer{"value"}}, true}, + {"Bearer wrong value", parsed{}, false}, + } + for _, c := range cases { + ret, ok := ParseAuthorizationHeader(c.headerValue) + assert.Equal(t, c.ok, ok, "header %q", c.headerValue) + assert.Equal(t, c.expected, ret, "header %q", c.headerValue) + } +} diff --git a/modules/auth/password/hash/common.go b/modules/auth/password/hash/common.go index 487c0738f42f4..d5e2c34314ecd 100644 --- a/modules/auth/password/hash/common.go +++ b/modules/auth/password/hash/common.go @@ -18,7 +18,7 @@ func parseIntParam(value, param, algorithmName, config string, previousErr error return parsed, previousErr // <- Keep the previous error as this function should still return an error once everything has been checked if any call failed } -func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { //nolint:unparam +func parseUIntParam(value, param, algorithmName, config string, previousErr error) (uint64, error) { //nolint:unparam // algorithmName is always argon2 parsed, err := strconv.ParseUint(value, 10, 64) if err != nil { log.Error("invalid integer for %s representation in %s hash spec %s", param, algorithmName, config) diff --git a/modules/base/tool.go b/modules/base/tool.go index 02ca85569e1da..ed94575e741cf 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -8,13 +8,10 @@ import ( "crypto/sha1" "crypto/sha256" "crypto/subtle" - "encoding/base64" "encoding/hex" - "errors" "fmt" "hash" "strconv" - "strings" "time" "code.gitea.io/gitea/modules/setting" @@ -36,19 +33,6 @@ func ShortSha(sha1 string) string { return util.TruncateRunes(sha1, 10) } -// BasicAuthDecode decode basic auth string -func BasicAuthDecode(encoded string) (string, string, error) { - s, err := base64.StdEncoding.DecodeString(encoded) - if err != nil { - return "", "", err - } - - if username, password, ok := strings.Cut(string(s), ":"); ok { - return username, password, nil - } - return "", "", errors.New("invalid basic authentication") -} - // VerifyTimeLimitCode verify time limit code func VerifyTimeLimitCode(now time.Time, data string, minutes int, code string) bool { if len(code) <= 18 { diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go index 7cebedb073ceb..b7365e40c48a4 100644 --- a/modules/base/tool_test.go +++ b/modules/base/tool_test.go @@ -26,25 +26,6 @@ func TestShortSha(t *testing.T) { assert.Equal(t, "veryverylo", ShortSha("veryverylong")) } -func TestBasicAuthDecode(t *testing.T) { - _, _, err := BasicAuthDecode("?") - assert.Equal(t, "illegal base64 data at input byte 0", err.Error()) - - user, pass, err := BasicAuthDecode("Zm9vOmJhcg==") - assert.NoError(t, err) - assert.Equal(t, "foo", user) - assert.Equal(t, "bar", pass) - - _, _, err = BasicAuthDecode("aW52YWxpZA==") - assert.Error(t, err) - - _, _, err = BasicAuthDecode("invalid") - assert.Error(t, err) - - _, _, err = BasicAuthDecode("YWxpY2U=") // "alice", no colon - assert.Error(t, err) -} - func TestVerifyTimeLimitCode(t *testing.T) { defer test.MockVariableValue(&setting.InstallLock, true)() initGeneralSecret := func(secret string) { diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go index c5b52a2086ceb..7473c938af780 100644 --- a/modules/cache/cache_redis.go +++ b/modules/cache/cache_redis.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/nosql" - "gitea.com/go-chi/cache" //nolint:depguard + "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here "github.com/redis/go-redis/v9" ) diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go index 1eda2debc43aa..c8db686e576a0 100644 --- a/modules/cache/cache_twoqueue.go +++ b/modules/cache/cache_twoqueue.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/modules/json" - mc "gitea.com/go-chi/cache" //nolint:depguard + mc "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here lru "github.com/hashicorp/golang-lru/v2" ) diff --git a/modules/cache/string_cache.go b/modules/cache/string_cache.go index 4f659616f501e..3562b7a926cf0 100644 --- a/modules/cache/string_cache.go +++ b/modules/cache/string_cache.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" - chi_cache "gitea.com/go-chi/cache" //nolint:depguard + chi_cache "gitea.com/go-chi/cache" //nolint:depguard // we wrap this package here ) type GetJSONError struct { diff --git a/modules/commitstatus/commit_status.go b/modules/commitstatus/commit_status.go index 12004474ed093..a0ab4e71862db 100644 --- a/modules/commitstatus/commit_status.go +++ b/modules/commitstatus/commit_status.go @@ -5,7 +5,7 @@ package commitstatus // CommitStatusState holds the state of a CommitStatus // swagger:enum CommitStatusState -type CommitStatusState string //nolint +type CommitStatusState string //nolint:revive // export stutter const ( // CommitStatusPending is for when the CommitStatus is Pending @@ -56,7 +56,7 @@ func (css CommitStatusState) IsSkipped() bool { return css == CommitStatusSkipped } -type CommitStatusStates []CommitStatusState //nolint +type CommitStatusStates []CommitStatusState //nolint:revive // export stutter // According to https://docs.github.com/en/rest/commits/statuses?apiVersion=2022-11-28#get-the-combined-status-for-a-specific-reference // > Additionally, a combined state is returned. The state is one of: diff --git a/modules/fileicon/entry.go b/modules/fileicon/entry.go index e4ded363e58e2..0326c2bfa8ab9 100644 --- a/modules/fileicon/entry.go +++ b/modules/fileicon/entry.go @@ -6,17 +6,17 @@ package fileicon import "code.gitea.io/gitea/modules/git" type EntryInfo struct { - FullName string + BaseName string EntryMode git.EntryMode SymlinkToMode git.EntryMode IsOpen bool } -func EntryInfoFromGitTreeEntry(gitEntry *git.TreeEntry) *EntryInfo { - ret := &EntryInfo{FullName: gitEntry.Name(), EntryMode: gitEntry.Mode()} +func EntryInfoFromGitTreeEntry(commit *git.Commit, fullPath string, gitEntry *git.TreeEntry) *EntryInfo { + ret := &EntryInfo{BaseName: gitEntry.Name(), EntryMode: gitEntry.Mode()} if gitEntry.IsLink() { - if te, err := gitEntry.FollowLink(); err == nil && te.IsDir() { - ret.SymlinkToMode = te.Mode() + if res, err := git.EntryFollowLink(commit, fullPath, gitEntry); err == nil && res.TargetEntry.IsDir() { + ret.SymlinkToMode = res.TargetEntry.Mode() } } return ret diff --git a/modules/fileicon/material.go b/modules/fileicon/material.go index 449f527ee80bb..5361592d8a30d 100644 --- a/modules/fileicon/material.go +++ b/modules/fileicon/material.go @@ -5,7 +5,6 @@ package fileicon import ( "html/template" - "path" "strings" "sync" @@ -134,7 +133,7 @@ func (m *MaterialIconProvider) FindIconName(entry *EntryInfo) string { return "folder-git" } - fileNameLower := strings.ToLower(path.Base(entry.FullName)) + fileNameLower := strings.ToLower(entry.BaseName) if entry.EntryMode.IsDir() { if s, ok := m.rules.FolderNames[fileNameLower]; ok { return s diff --git a/modules/fileicon/material_test.go b/modules/fileicon/material_test.go index 68353d21899ff..d2a769eaac01a 100644 --- a/modules/fileicon/material_test.go +++ b/modules/fileicon/material_test.go @@ -20,8 +20,8 @@ func TestMain(m *testing.M) { func TestFindIconName(t *testing.T) { unittest.PrepareTestEnv(t) p := fileicon.DefaultMaterialIconProvider() - assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.php", EntryMode: git.EntryModeBlob})) - assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.PHP", EntryMode: git.EntryModeBlob})) - assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.js", EntryMode: git.EntryModeBlob})) - assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.vba", EntryMode: git.EntryModeBlob})) + assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.php", EntryMode: git.EntryModeBlob})) + assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.PHP", EntryMode: git.EntryModeBlob})) + assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.js", EntryMode: git.EntryModeBlob})) + assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{BaseName: "foo.vba", EntryMode: git.EntryModeBlob})) } diff --git a/modules/git/blob.go b/modules/git/blob.go index b7857dbbc6129..40d8f44e799d6 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "errors" "io" + "strings" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" @@ -21,17 +22,22 @@ func (b *Blob) Name() string { return b.name } -// GetBlobContent Gets the limited content of the blob as raw text -func (b *Blob) GetBlobContent(limit int64) (string, error) { +// GetBlobBytes Gets the limited content of the blob +func (b *Blob) GetBlobBytes(limit int64) ([]byte, error) { if limit <= 0 { - return "", nil + return nil, nil } dataRc, err := b.DataAsync() if err != nil { - return "", err + return nil, err } defer dataRc.Close() - buf, err := util.ReadWithLimit(dataRc, int(limit)) + return util.ReadWithLimit(dataRc, int(limit)) +} + +// GetBlobContent Gets the limited content of the blob as raw text +func (b *Blob) GetBlobContent(limit int64) (string, error) { + buf, err := b.GetBlobBytes(limit) return string(buf), err } @@ -63,42 +69,44 @@ func (b *Blob) GetBlobLineCount(w io.Writer) (int, error) { } } -// GetBlobContentBase64 Reads the content of the blob with a base64 encode and returns the encoded string -func (b *Blob) GetBlobContentBase64() (string, error) { +// GetBlobContentBase64 Reads the content of the blob with a base64 encoding and returns the encoded string +func (b *Blob) GetBlobContentBase64(originContent *strings.Builder) (string, error) { dataRc, err := b.DataAsync() if err != nil { return "", err } defer dataRc.Close() - pr, pw := io.Pipe() - encoder := base64.NewEncoder(base64.StdEncoding, pw) - - go func() { - _, err := io.Copy(encoder, dataRc) - _ = encoder.Close() - - if err != nil { - _ = pw.CloseWithError(err) - } else { - _ = pw.Close() + base64buf := &strings.Builder{} + encoder := base64.NewEncoder(base64.StdEncoding, base64buf) + buf := make([]byte, 32*1024) +loop: + for { + n, err := dataRc.Read(buf) + if n > 0 { + if originContent != nil { + _, _ = originContent.Write(buf[:n]) + } + if _, err := encoder.Write(buf[:n]); err != nil { + return "", err + } + } + switch { + case errors.Is(err, io.EOF): + break loop + case err != nil: + return "", err } - }() - - out, err := io.ReadAll(pr) - if err != nil { - return "", err } - return string(out), nil + _ = encoder.Close() + return base64buf.String(), nil } // GuessContentType guesses the content type of the blob. func (b *Blob) GuessContentType() (typesniffer.SniffedType, error) { - r, err := b.DataAsync() + buf, err := b.GetBlobBytes(typesniffer.SniffContentSize) if err != nil { return typesniffer.SniffedType{}, err } - defer r.Close() - - return typesniffer.DetectContentTypeFromReader(r) + return typesniffer.DetectContentType(buf), nil } diff --git a/modules/git/commit.go b/modules/git/commit.go index 1c1648eb8b85f..ed4876e7b3eff 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -20,7 +20,8 @@ import ( // Commit represents a git commit. type Commit struct { - Tree + Tree // FIXME: bad design, this field can be nil if the commit is from "last commit cache" + ID ObjectID // The ID of this commit object Author *Signature Committer *Signature diff --git a/modules/git/error.go b/modules/git/error.go index 6c86d1b04d62c..7d131345d0670 100644 --- a/modules/git/error.go +++ b/modules/git/error.go @@ -32,22 +32,6 @@ func (err ErrNotExist) Unwrap() error { return util.ErrNotExist } -// ErrSymlinkUnresolved entry.FollowLink error -type ErrSymlinkUnresolved struct { - Name string - Message string -} - -func (err ErrSymlinkUnresolved) Error() string { - return fmt.Sprintf("%s: %s", err.Name, err.Message) -} - -// IsErrSymlinkUnresolved if some error is ErrSymlinkUnresolved -func IsErrSymlinkUnresolved(err error) bool { - _, ok := err.(ErrSymlinkUnresolved) - return ok -} - // ErrBranchNotExist represents a "BranchNotExist" kind of error. type ErrBranchNotExist struct { Name string diff --git a/modules/git/tree_blob_nogogit.go b/modules/git/tree_blob_nogogit.go index b7bcf40edd2a9..b18d0fa05e6dd 100644 --- a/modules/git/tree_blob_nogogit.go +++ b/modules/git/tree_blob_nogogit.go @@ -11,7 +11,7 @@ import ( ) // GetTreeEntryByPath get the tree entries according the sub dir -func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { +func (t *Tree) GetTreeEntryByPath(relpath string) (_ *TreeEntry, err error) { if len(relpath) == 0 { return &TreeEntry{ ptree: t, @@ -21,27 +21,25 @@ func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { }, nil } - // FIXME: This should probably use git cat-file --batch to be a bit more efficient relpath = path.Clean(relpath) parts := strings.Split(relpath, "/") - var err error + tree := t - for i, name := range parts { - if i == len(parts)-1 { - entries, err := tree.ListEntries() - if err != nil { - return nil, err - } - for _, v := range entries { - if v.Name() == name { - return v, nil - } - } - } else { - tree, err = tree.SubTree(name) - if err != nil { - return nil, err - } + for _, name := range parts[:len(parts)-1] { + tree, err = tree.SubTree(name) + if err != nil { + return nil, err + } + } + + name := parts[len(parts)-1] + entries, err := tree.ListEntries() + if err != nil { + return nil, err + } + for _, v := range entries { + if v.Name() == name { + return v, nil } } return nil, ErrNotExist{"", relpath} diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index 57856d90eec36..5099d8ee79bc3 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -5,7 +5,7 @@ package git import ( - "io" + "path" "sort" "strings" @@ -24,77 +24,57 @@ func (te *TreeEntry) Type() string { } } -// FollowLink returns the entry pointed to by a symlink -func (te *TreeEntry) FollowLink() (*TreeEntry, error) { +type EntryFollowResult struct { + SymlinkContent string + TargetFullPath string + TargetEntry *TreeEntry +} + +func EntryFollowLink(commit *Commit, fullPath string, te *TreeEntry) (*EntryFollowResult, error) { if !te.IsLink() { - return nil, ErrSymlinkUnresolved{te.Name(), "not a symlink"} + return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q is not a symlink", fullPath) } - // read the link - r, err := te.Blob().DataAsync() - if err != nil { - return nil, err + // git's filename max length is 4096, hopefully a link won't be longer than multiple of that + const maxSymlinkSize = 20 * 4096 + if te.Blob().Size() > maxSymlinkSize { + return nil, util.ErrorWrap(util.ErrUnprocessableContent, "%q content exceeds symlink limit", fullPath) } - closed := false - defer func() { - if !closed { - _ = r.Close() - } - }() - buf := make([]byte, te.Size()) - _, err = io.ReadFull(r, buf) + + link, err := te.Blob().GetBlobContent(maxSymlinkSize) if err != nil { return nil, err } - _ = r.Close() - closed = true - - lnk := string(buf) - t := te.ptree - - // traverse up directories - for ; t != nil && strings.HasPrefix(lnk, "../"); lnk = lnk[3:] { - t = t.ptree + if strings.HasPrefix(link, "/") { + // It's said that absolute path will be stored as is in Git + return &EntryFollowResult{SymlinkContent: link}, util.ErrorWrap(util.ErrUnprocessableContent, "%q is an absolute symlink", fullPath) } - if t == nil { - return nil, ErrSymlinkUnresolved{te.Name(), "points outside of repo"} - } - - target, err := t.GetTreeEntryByPath(lnk) + targetFullPath := path.Join(path.Dir(fullPath), link) + targetEntry, err := commit.GetTreeEntryByPath(targetFullPath) if err != nil { - if IsErrNotExist(err) { - return nil, ErrSymlinkUnresolved{te.Name(), "broken link"} - } - return nil, err + return &EntryFollowResult{SymlinkContent: link}, err } - return target, nil + return &EntryFollowResult{SymlinkContent: link, TargetFullPath: targetFullPath, TargetEntry: targetEntry}, nil } -// FollowLinks returns the entry ultimately pointed to by a symlink -func (te *TreeEntry) FollowLinks(optLimit ...int) (*TreeEntry, error) { - if !te.IsLink() { - return nil, ErrSymlinkUnresolved{te.Name(), "not a symlink"} - } +func EntryFollowLinks(commit *Commit, firstFullPath string, firstTreeEntry *TreeEntry, optLimit ...int) (res *EntryFollowResult, err error) { limit := util.OptionalArg(optLimit, 10) - entry := te + treeEntry, fullPath := firstTreeEntry, firstFullPath for range limit { - if !entry.IsLink() { - break - } - next, err := entry.FollowLink() + res, err = EntryFollowLink(commit, fullPath, treeEntry) if err != nil { - return nil, err + return res, err } - if next.ID == entry.ID { - return nil, ErrSymlinkUnresolved{entry.Name(), "recursive link"} + treeEntry, fullPath = res.TargetEntry, res.TargetFullPath + if !treeEntry.IsLink() { + break } - entry = next } - if entry.IsLink() { - return nil, ErrSymlinkUnresolved{te.Name(), "too many levels of symbolic links"} + if treeEntry.IsLink() { + return res, util.ErrorWrap(util.ErrUnprocessableContent, "%q has too many links", firstFullPath) } - return entry, nil + return res, nil } // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree diff --git a/modules/git/tree_entry_common_test.go b/modules/git/tree_entry_common_test.go new file mode 100644 index 0000000000000..8b63bbb99314d --- /dev/null +++ b/modules/git/tree_entry_common_test.go @@ -0,0 +1,76 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "testing" + + "code.gitea.io/gitea/modules/util" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFollowLink(t *testing.T) { + r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare") + require.NoError(t, err) + defer r.Close() + + commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123") + require.NoError(t, err) + + // get the symlink + { + lnkFullPath := "foo/bar/link_to_hello" + lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello") + require.NoError(t, err) + assert.True(t, lnk.IsLink()) + + // should be able to dereference to target + res, err := EntryFollowLink(commit, lnkFullPath, lnk) + require.NoError(t, err) + assert.Equal(t, "hello", res.TargetEntry.Name()) + assert.Equal(t, "foo/nar/hello", res.TargetFullPath) + assert.False(t, res.TargetEntry.IsLink()) + assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", res.TargetEntry.ID.String()) + } + + { + // should error when called on a normal file + entry, err := commit.Tree.GetTreeEntryByPath("file1.txt") + require.NoError(t, err) + res, err := EntryFollowLink(commit, "file1.txt", entry) + assert.ErrorIs(t, err, util.ErrUnprocessableContent) + assert.Nil(t, res) + } + + { + // should error for broken links + entry, err := commit.Tree.GetTreeEntryByPath("foo/broken_link") + require.NoError(t, err) + assert.True(t, entry.IsLink()) + res, err := EntryFollowLink(commit, "foo/broken_link", entry) + assert.ErrorIs(t, err, util.ErrNotExist) + assert.Equal(t, "nar/broken_link", res.SymlinkContent) + } + + { + // should error for external links + entry, err := commit.Tree.GetTreeEntryByPath("foo/outside_repo") + require.NoError(t, err) + assert.True(t, entry.IsLink()) + res, err := EntryFollowLink(commit, "foo/outside_repo", entry) + assert.ErrorIs(t, err, util.ErrNotExist) + assert.Equal(t, "../../outside_repo", res.SymlinkContent) + } + + { + // testing fix for short link bug + entry, err := commit.Tree.GetTreeEntryByPath("foo/link_short") + require.NoError(t, err) + res, err := EntryFollowLink(commit, "foo/link_short", entry) + assert.ErrorIs(t, err, util.ErrNotExist) + assert.Equal(t, "a", res.SymlinkContent) + } +} diff --git a/modules/git/tree_entry_gogit.go b/modules/git/tree_entry_gogit.go index eb9b012681474..e6845f1c776fe 100644 --- a/modules/git/tree_entry_gogit.go +++ b/modules/git/tree_entry_gogit.go @@ -19,16 +19,12 @@ type TreeEntry struct { gogitTreeEntry *object.TreeEntry ptree *Tree - size int64 - sized bool - fullName string + size int64 + sized bool } // Name returns the name of the entry func (te *TreeEntry) Name() string { - if te.fullName != "" { - return te.fullName - } return te.gogitTreeEntry.Name } @@ -55,7 +51,7 @@ func (te *TreeEntry) Size() int64 { return te.size } -// IsSubModule if the entry is a sub module +// IsSubModule if the entry is a submodule func (te *TreeEntry) IsSubModule() bool { return te.gogitTreeEntry.Mode == filemode.Submodule } diff --git a/modules/git/tree_entry_mode.go b/modules/git/tree_entry_mode.go index d815a8bc2ed34..f36c07bc2a002 100644 --- a/modules/git/tree_entry_mode.go +++ b/modules/git/tree_entry_mode.go @@ -15,7 +15,7 @@ type EntryMode int // one of these. const ( // EntryModeNoEntry is possible if the file was added or removed in a commit. In the case of - // added the base commit will not have the file in its tree so a mode of 0o000000 is used. + // when adding the base commit doesn't have the file in its tree, a mode of 0o000000 is used. EntryModeNoEntry EntryMode = 0o000000 EntryModeBlob EntryMode = 0o100644 @@ -30,7 +30,7 @@ func (e EntryMode) String() string { return strconv.FormatInt(int64(e), 8) } -// IsSubModule if the entry is a sub module +// IsSubModule if the entry is a submodule func (e EntryMode) IsSubModule() bool { return e == EntryModeCommit } diff --git a/modules/git/tree_entry_nogogit.go b/modules/git/tree_entry_nogogit.go index 0c0e1835f172d..8fad96cdf8924 100644 --- a/modules/git/tree_entry_nogogit.go +++ b/modules/git/tree_entry_nogogit.go @@ -18,7 +18,7 @@ type TreeEntry struct { sized bool } -// Name returns the name of the entry +// Name returns the name of the entry (base name) func (te *TreeEntry) Name() string { return te.name } @@ -57,7 +57,7 @@ func (te *TreeEntry) Size() int64 { return te.size } -// IsSubModule if the entry is a sub module +// IsSubModule if the entry is a submodule func (te *TreeEntry) IsSubModule() bool { return te.entryMode.IsSubModule() } diff --git a/modules/git/tree_entry_test.go b/modules/git/tree_entry_test.go index 30eee13669e43..9ca82675e0797 100644 --- a/modules/git/tree_entry_test.go +++ b/modules/git/tree_entry_test.go @@ -53,50 +53,3 @@ func TestEntriesCustomSort(t *testing.T) { assert.Equal(t, "bcd", entries[6].Name()) assert.Equal(t, "abc", entries[7].Name()) } - -func TestFollowLink(t *testing.T) { - r, err := openRepositoryWithDefaultContext("tests/repos/repo1_bare") - assert.NoError(t, err) - defer r.Close() - - commit, err := r.GetCommit("37991dec2c8e592043f47155ce4808d4580f9123") - assert.NoError(t, err) - - // get the symlink - lnk, err := commit.Tree.GetTreeEntryByPath("foo/bar/link_to_hello") - assert.NoError(t, err) - assert.True(t, lnk.IsLink()) - - // should be able to dereference to target - target, err := lnk.FollowLink() - assert.NoError(t, err) - assert.Equal(t, "hello", target.Name()) - assert.False(t, target.IsLink()) - assert.Equal(t, "b14df6442ea5a1b382985a6549b85d435376c351", target.ID.String()) - - // should error when called on normal file - target, err = commit.Tree.GetTreeEntryByPath("file1.txt") - assert.NoError(t, err) - _, err = target.FollowLink() - assert.EqualError(t, err, "file1.txt: not a symlink") - - // should error for broken links - target, err = commit.Tree.GetTreeEntryByPath("foo/broken_link") - assert.NoError(t, err) - assert.True(t, target.IsLink()) - _, err = target.FollowLink() - assert.EqualError(t, err, "broken_link: broken link") - - // should error for external links - target, err = commit.Tree.GetTreeEntryByPath("foo/outside_repo") - assert.NoError(t, err) - assert.True(t, target.IsLink()) - _, err = target.FollowLink() - assert.EqualError(t, err, "outside_repo: points outside of repo") - - // testing fix for short link bug - target, err = commit.Tree.GetTreeEntryByPath("foo/link_short") - assert.NoError(t, err) - _, err = target.FollowLink() - assert.EqualError(t, err, "link_short: broken link") -} diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go index 421b0ecb0f0f9..272b018ffdd18 100644 --- a/modules/git/tree_gogit.go +++ b/modules/git/tree_gogit.go @@ -69,7 +69,7 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { seen := map[plumbing.Hash]bool{} walker := object.NewTreeWalker(t.gogitTree, true, seen) for { - fullName, entry, err := walker.Next() + _, entry, err := walker.Next() if err == io.EOF { break } @@ -84,7 +84,6 @@ func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) { ID: ParseGogitHash(entry.Hash), gogitTreeEntry: &entry, ptree: t, - fullName: fullName, } entries = append(entries, convertedEntry) } diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go index d776e0e9f9e20..457768d6ca064 100644 --- a/modules/graceful/manager_windows.go +++ b/modules/graceful/manager_windows.go @@ -41,8 +41,7 @@ func (g *Manager) start() { // Make SVC process run := svc.Run - //lint:ignore SA1019 We use IsAnInteractiveSession because IsWindowsService has a different permissions profile - isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck + isAnInteractiveSession, err := svc.IsAnInteractiveSession() //nolint:staticcheck // must use IsAnInteractiveSession because IsWindowsService has a different permissions profile if err != nil { log.Error("Unable to ascertain if running as an Windows Service: %v", err) return diff --git a/modules/htmlutil/html.go b/modules/htmlutil/html.go index 194135ba18ed8..efbc174b2ea02 100644 --- a/modules/htmlutil/html.go +++ b/modules/htmlutil/html.go @@ -42,6 +42,8 @@ func HTMLFormat(s template.HTML, rawArgs ...any) template.HTML { // for most basic types (including template.HTML which is safe), just do nothing and use it case string: args[i] = template.HTMLEscapeString(v) + case template.URL: + args[i] = template.HTMLEscapeString(string(v)) case fmt.Stringer: args[i] = template.HTMLEscapeString(v.String()) default: diff --git a/modules/htmlutil/html_test.go b/modules/htmlutil/html_test.go index 5ff05d75b36cc..22258ce59d63b 100644 --- a/modules/htmlutil/html_test.go +++ b/modules/htmlutil/html_test.go @@ -10,6 +10,15 @@ import ( "github.com/stretchr/testify/assert" ) +type testStringer struct{} + +func (t testStringer) String() string { + return "&StringMethod" +} + func TestHTMLFormat(t *testing.T) { assert.Equal(t, template.HTML("< < 1"), HTMLFormat("%s %s %d", "<", template.HTML("<"), 1)) + assert.Equal(t, template.HTML("%!s()"), HTMLFormat("%s", nil)) + assert.Equal(t, template.HTML("<>"), HTMLFormat("%s", template.URL("<>"))) + assert.Equal(t, template.HTML("&StringMethod &StringMethod"), HTMLFormat("%s %s", testStringer{}, &testStringer{})) } diff --git a/modules/json/json.go b/modules/json/json.go index acd41185731cf..444dc8526aeab 100644 --- a/modules/json/json.go +++ b/modules/json/json.go @@ -3,11 +3,10 @@ package json -// Allow "encoding/json" import. import ( "bytes" "encoding/binary" - "encoding/json" //nolint:depguard + "encoding/json" //nolint:depguard // this package wraps it "io" jsoniter "github.com/json-iterator/go" diff --git a/modules/lfs/pointer.go b/modules/lfs/pointer.go index ebde20f826834..9c95613057be6 100644 --- a/modules/lfs/pointer.go +++ b/modules/lfs/pointer.go @@ -15,15 +15,13 @@ import ( "strings" ) +// spec: https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md const ( - blobSizeCutoff = 1024 + MetaFileMaxSize = 1024 // spec says the maximum size of a pointer file must be smaller than 1024 - // MetaFileIdentifier is the string appearing at the first line of LFS pointer files. - // https://github.com/git-lfs/git-lfs/blob/master/docs/spec.md - MetaFileIdentifier = "version https://git-lfs.github.com/spec/v1" + MetaFileIdentifier = "version https://git-lfs.github.com/spec/v1" // the first line of a pointer file - // MetaFileOidPrefix appears in LFS pointer files on a line before the sha256 hash. - MetaFileOidPrefix = "oid sha256:" + MetaFileOidPrefix = "oid sha256:" // spec says the only supported hash is sha256 at the moment ) var ( @@ -39,7 +37,7 @@ var ( // ReadPointer tries to read LFS pointer data from the reader func ReadPointer(reader io.Reader) (Pointer, error) { - buf := make([]byte, blobSizeCutoff) + buf := make([]byte, MetaFileMaxSize) n, err := io.ReadFull(reader, buf) if err != nil && err != io.ErrUnexpectedEOF { return Pointer{}, err @@ -65,6 +63,7 @@ func ReadPointerFromBuffer(buf []byte) (Pointer, error) { return p, ErrInvalidStructure } + // spec says "key/value pairs MUST be sorted alphabetically in ascending order (version is exception and must be the first)" oid := strings.TrimPrefix(splitLines[1], MetaFileOidPrefix) if len(oid) != 64 || !oidPattern.MatchString(oid) { return p, ErrInvalidOIDFormat diff --git a/modules/lfs/pointer_scanner_gogit.go b/modules/lfs/pointer_scanner_gogit.go index f4302c23bcb59..e153b8e24e556 100644 --- a/modules/lfs/pointer_scanner_gogit.go +++ b/modules/lfs/pointer_scanner_gogit.go @@ -31,7 +31,7 @@ func SearchPointerBlobs(ctx context.Context, repo *git.Repository, pointerChan c default: } - if blob.Size > blobSizeCutoff { + if blob.Size > MetaFileMaxSize { return nil } diff --git a/modules/lfstransfer/backend/util.go b/modules/lfstransfer/backend/util.go index 98ce0b1e62ad8..afe02f799c0ad 100644 --- a/modules/lfstransfer/backend/util.go +++ b/modules/lfstransfer/backend/util.go @@ -132,6 +132,7 @@ func newInternalRequestLFS(ctx context.Context, internalURL, method string, head return nil } req := private.NewInternalRequest(ctx, internalURL, method) + req.SetReadWriteTimeout(0) for k, v := range headers { req.Header(k, v) } diff --git a/modules/log/logger.go b/modules/log/logger.go index 3fc524d55e875..8b89e0eb5a0c5 100644 --- a/modules/log/logger.go +++ b/modules/log/logger.go @@ -45,6 +45,6 @@ type Logger interface { LevelLogger } -type LogStringer interface { //nolint:revive +type LogStringer interface { //nolint:revive // export stutter LogString() string } diff --git a/modules/markup/common/footnote.go b/modules/markup/common/footnote.go index 26ab60bc1e39e..1ece436c662e1 100644 --- a/modules/markup/common/footnote.go +++ b/modules/markup/common/footnote.go @@ -197,7 +197,7 @@ func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parse return nil, parser.NoChildren } open := pos + 1 - closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint + closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint:staticcheck // deprecated function closes := pos + 1 + closure next := closes + 1 if closure > -1 { @@ -287,7 +287,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con return nil } open := pos - closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint + closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint:staticcheck // deprecated function if closure < 0 { return nil } diff --git a/modules/markup/console/console.go b/modules/markup/console/console.go index 06f3acfa68948..492579b0a5027 100644 --- a/modules/markup/console/console.go +++ b/modules/markup/console/console.go @@ -6,13 +6,14 @@ package console import ( "bytes" "io" - "path" + "unicode/utf8" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/modules/util" trend "github.com/buildkite/terminal-to-html/v3" - "github.com/go-enry/go-enry/v2" ) func init() { @@ -22,6 +23,8 @@ func init() { // Renderer implements markup.Renderer type Renderer struct{} +var _ markup.RendererContentDetector = (*Renderer)(nil) + // Name implements markup.Renderer func (Renderer) Name() string { return "console" @@ -40,15 +43,36 @@ func (Renderer) SanitizerRules() []setting.MarkupSanitizerRule { } // CanRender implements markup.RendererContentDetector -func (Renderer) CanRender(filename string, input io.Reader) bool { - buf, err := io.ReadAll(input) - if err != nil { +func (Renderer) CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool { + if !sniffedType.IsTextPlain() { return false } - if enry.GetLanguage(path.Base(filename), buf) != enry.OtherLanguage { + + s := util.UnsafeBytesToString(prefetchBuf) + rs := []rune(s) + cnt := 0 + firstErrPos := -1 + isCtrlSep := func(p int) bool { + return p < len(rs) && (rs[p] == ';' || rs[p] == 'm') + } + for i, c := range rs { + if c == 0 { + return false + } + if c == '\x1b' { + match := i+1 < len(rs) && rs[i+1] == '[' + if match && (isCtrlSep(i+2) || isCtrlSep(i+3) || isCtrlSep(i+4) || isCtrlSep(i+5)) { + cnt++ + } + } + if c == utf8.RuneError && firstErrPos == -1 { + firstErrPos = i + } + } + if firstErrPos != -1 && firstErrPos != len(rs)-1 { return false } - return bytes.ContainsRune(buf, '\x1b') + return cnt >= 2 // only render it as console output if there are at least two escape sequences } // Render renders terminal colors to HTML with all specific handling stuff. diff --git a/modules/markup/console/console_test.go b/modules/markup/console/console_test.go index 539f965ea17b8..d1192bebc2aad 100644 --- a/modules/markup/console/console_test.go +++ b/modules/markup/console/console_test.go @@ -8,23 +8,39 @@ import ( "testing" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/typesniffer" "github.com/stretchr/testify/assert" ) func TestRenderConsole(t *testing.T) { - var render Renderer - kases := map[string]string{ - "\x1b[37m\x1b[40mnpm\x1b[0m \x1b[0m\x1b[32minfo\x1b[0m \x1b[0m\x1b[35mit worked if it ends with\x1b[0m ok": "npm info it worked if it ends with ok", + cases := []struct { + input string + expected string + }{ + {"\x1b[37m\x1b[40mnpm\x1b[0m \x1b[0m\x1b[32minfo\x1b[0m \x1b[0m\x1b[35mit worked if it ends with\x1b[0m ok", `npm info it worked if it ends with ok`}, + {"\x1b[1;2m \x1b[123m 啊", ``}, + {"\x1b[1;2m \x1b[123m \xef", ``}, + {"\x1b[1;2m \x1b[123m \xef \xef", ``}, + {"\x1b[12", ``}, + {"\x1b[1", ``}, + {"\x1b[FOO\x1b[", ``}, + {"\x1b[mFOO\x1b[m", `FOO`}, } - for k, v := range kases { + var render Renderer + for i, c := range cases { var buf strings.Builder - canRender := render.CanRender("test", strings.NewReader(k)) - assert.True(t, canRender) + st := typesniffer.DetectContentType([]byte(c.input)) + canRender := render.CanRender("test", st, []byte(c.input)) + if c.expected == "" { + assert.False(t, canRender, "case %d: expected not to render", i) + continue + } - err := render.Render(markup.NewRenderContext(t.Context()), strings.NewReader(k), &buf) + assert.True(t, canRender) + err := render.Render(markup.NewRenderContext(t.Context()), strings.NewReader(c.input), &buf) assert.NoError(t, err) - assert.Equal(t, v, buf.String()) + assert.Equal(t, c.expected, buf.String()) } } diff --git a/modules/markup/markdown/transform_blockquote.go b/modules/markup/markdown/transform_blockquote.go index 3a8c6fa01869c..bf17f01681c33 100644 --- a/modules/markup/markdown/transform_blockquote.go +++ b/modules/markup/markdown/transform_blockquote.go @@ -46,7 +46,7 @@ func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.N if !ok { return "", nil } - val1 := string(node1.Text(reader.Source())) //nolint:staticcheck + val1 := string(node1.Text(reader.Source())) //nolint:staticcheck // Text is deprecated attentionType := strings.ToLower(val1) if g.attentionTypes.Contains(attentionType) { return attentionType, []ast.Node{node1} diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go index bccc43aad2510..c2e4295bc2b8c 100644 --- a/modules/markup/markdown/transform_codespan.go +++ b/modules/markup/markdown/transform_codespan.go @@ -68,7 +68,7 @@ func cssColorHandler(value string) bool { } func (g *ASTTransformer) transformCodeSpan(_ *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) { - colorContent := v.Text(reader.Source()) //nolint:staticcheck + colorContent := v.Text(reader.Source()) //nolint:staticcheck // Text is deprecated if cssColorHandler(string(colorContent)) { v.AppendChild(v, NewColorPreview(colorContent)) } diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go index cac3cd6617da5..a229a7b1a4d20 100644 --- a/modules/markup/markdown/transform_heading.go +++ b/modules/markup/markdown/transform_heading.go @@ -19,7 +19,7 @@ func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Headin v.SetAttribute(attr.Name, fmt.Appendf(nil, "%v", attr.Value)) } } - txt := v.Text(reader.Source()) //nolint:staticcheck + txt := v.Text(reader.Source()) //nolint:staticcheck // Text is deprecated header := Header{ Text: util.UnsafeBytesToString(txt), Level: v.Level, diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go index c589926b5e7ee..5a6504416ab35 100644 --- a/modules/markup/mdstripper/mdstripper.go +++ b/modules/markup/mdstripper/mdstripper.go @@ -46,7 +46,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error { coalesce := prevSibIsText r.processString( w, - v.Text(source), //nolint:staticcheck + v.Text(source), //nolint:staticcheck // Text is deprecated coalesce) if v.SoftLineBreak() { r.doubleSpace(w) @@ -91,8 +91,7 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) { } // Note: we're not attempting to match the URL scheme (http/https) - host := strings.ToLower(u.Host) - if host != "" && host != strings.ToLower(r.localhost.Host) { + if u.Host != "" && !strings.EqualFold(u.Host, r.localhost.Host) { // Process out of band r.links = append(r.links, linkStr) return diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go index 35f90eb46cbd9..b6e9c348b7319 100644 --- a/modules/markup/renderer.go +++ b/modules/markup/renderer.go @@ -4,12 +4,12 @@ package markup import ( - "bytes" "io" "path" "strings" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" ) // Renderer defines an interface for rendering markup file to HTML @@ -37,7 +37,7 @@ type ExternalRenderer interface { // RendererContentDetector detects if the content can be rendered // by specified renderer type RendererContentDetector interface { - CanRender(filename string, input io.Reader) bool + CanRender(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) bool } var ( @@ -60,13 +60,9 @@ func GetRendererByFileName(filename string) Renderer { } // DetectRendererType detects the markup type of the content -func DetectRendererType(filename string, input io.Reader) string { - buf, err := io.ReadAll(input) - if err != nil { - return "" - } +func DetectRendererType(filename string, sniffedType typesniffer.SniffedType, prefetchBuf []byte) string { for _, renderer := range renderers { - if detector, ok := renderer.(RendererContentDetector); ok && detector.CanRender(filename, bytes.NewReader(buf)) { + if detector, ok := renderer.(RendererContentDetector); ok && detector.CanRender(filename, sniffedType, prefetchBuf) { return renderer.Name() } } diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go index 21d3ad8470004..cf81a94cfce93 100644 --- a/modules/optional/serialization_test.go +++ b/modules/optional/serialization_test.go @@ -4,7 +4,7 @@ package optional_test import ( - std_json "encoding/json" //nolint:depguard + std_json "encoding/json" //nolint:depguard // for testing purpose "testing" "code.gitea.io/gitea/modules/json" diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go index afb464e462057..9b00472eb2785 100644 --- a/modules/packages/pub/metadata.go +++ b/modules/packages/pub/metadata.go @@ -88,7 +88,7 @@ func ParsePackage(r io.Reader) (*Package, error) { if err != nil { return nil, err } - } else if strings.ToLower(hd.Name) == "readme.md" { + } else if strings.EqualFold(hd.Name, "readme.md") { data, err := io.ReadAll(tr) if err != nil { return nil, err diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 2bf9930f19fd3..30aa0a6e85ec1 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -41,11 +41,12 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, if err != nil { return 0, fmt.Errorf("GetObjectFormat: %w", err) } - _, err = db.GetEngine(ctx).ID(repo.ID).Update(&repo_model.Repository{ObjectFormatName: objFmt.Name()}) - if err != nil { - return 0, fmt.Errorf("UpdateRepository: %w", err) + if objFmt.Name() != repo.ObjectFormatName { + repo.ObjectFormatName = objFmt.Name() + if err = repo_model.UpdateRepositoryColsWithAutoTime(ctx, repo, "object_format_name"); err != nil { + return 0, fmt.Errorf("UpdateRepositoryColsWithAutoTime: %w", err) + } } - repo.ObjectFormatName = objFmt.Name() // keep consistent with db allBranches := container.Set[string]{} { diff --git a/modules/setting/actions.go b/modules/setting/actions.go index 913872eaf2312..8bace1f75044d 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -62,11 +62,11 @@ func (c logCompression) IsValid() bool { } func (c logCompression) IsNone() bool { - return strings.ToLower(string(c)) == "none" + return string(c) == "none" } func (c logCompression) IsZstd() bool { - return c == "" || strings.ToLower(string(c)) == "zstd" + return c == "" || string(c) == "zstd" } func loadActionsFrom(rootCfg ConfigProvider) error { diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go index 5d94a9641f782..409588dc4418a 100644 --- a/modules/setting/config_env.go +++ b/modules/setting/config_env.go @@ -97,7 +97,7 @@ func decodeEnvSectionKey(encoded string) (ok bool, section, key string) { // decodeEnvironmentKey decode the environment key to section and key // The environment key is in the form of GITEA__SECTION__KEY or GITEA__SECTION__KEY__FILE -func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) { //nolint:unparam +func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, section, key string, useFileValue bool) { if !strings.HasPrefix(envKey, prefixGitea) { return false, "", "", false } diff --git a/modules/setting/config_env_test.go b/modules/setting/config_env_test.go index 217ea538603c4..7d270ac21adaf 100644 --- a/modules/setting/config_env_test.go +++ b/modules/setting/config_env_test.go @@ -73,6 +73,9 @@ func TestDecodeEnvironmentKey(t *testing.T) { assert.Equal(t, "sec", section) assert.Equal(t, "KEY", key) assert.True(t, file) + + ok, _, _, _ = decodeEnvironmentKey("PREFIX__", "", "PREFIX__SEC__KEY") + assert.True(t, ok) } func TestEnvironmentToConfig(t *testing.T) { diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index a0c53a10325f9..09eaaefdaff1c 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" - "gopkg.in/ini.v1" //nolint:depguard + "gopkg.in/ini.v1" //nolint:depguard // wrapper for this package ) type ConfigKey interface { diff --git a/modules/setting/security.go b/modules/setting/security.go index 3ae4c005c7b59..153b6bc944ff5 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -111,7 +111,7 @@ func loadSecurityFrom(rootCfg ConfigProvider) { if SecretKey == "" { // FIXME: https://github.com/go-gitea/gitea/issues/16832 // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value - SecretKey = "!#@FDEWREWR&*(" //nolint:gosec + SecretKey = "!#@FDEWREWR&*(" } CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible") diff --git a/modules/setting/storage.go b/modules/setting/storage.go index f43af1a8c05a5..ee246158d94ae 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -158,7 +158,7 @@ const ( targetSecIsSec // target section is from the name seciont [name] ) -func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { //nolint:unparam +func getStorageSectionByType(rootCfg ConfigProvider, typ string) (ConfigSection, targetSecType, error) { //nolint:unparam // FIXME: targetSecType is always 0, wrong design? targetSec, err := rootCfg.GetSection(storageSectionName + "." + typ) if err != nil { if !IsValidStorageType(StorageType(typ)) { @@ -283,7 +283,7 @@ func getStorageForLocal(targetSec, overrideSec ConfigSection, tp targetSecType, return &storage, nil } -func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl +func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates azure setup var storage Storage storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String()) if err := targetSec.MapTo(&storage.MinioConfig); err != nil { @@ -312,7 +312,7 @@ func getStorageForMinio(targetSec, overrideSec ConfigSection, tp targetSecType, return &storage, nil } -func getStorageForAzureBlob(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl +func getStorageForAzureBlob(targetSec, overrideSec ConfigSection, tp targetSecType, name string) (*Storage, error) { //nolint:dupl // duplicates minio setup var storage Storage storage.Type = StorageType(targetSec.Key("STORAGE_TYPE").String()) if err := targetSec.MapTo(&storage.AzureBlobConfig); err != nil { diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index f7c6d10ba0f83..c68b59a8978ee 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -8,8 +8,11 @@ import "time" // CreateUserOption create user options type CreateUserOption struct { - SourceID int64 `json:"source_id"` + SourceID int64 `json:"source_id"` + // identifier of the user, provided by the external authenticator (if configured) + // default: empty LoginName string `json:"login_name"` + // username of the user // required: true Username string `json:"username" binding:"Required;Username;MaxSize(40)"` FullName string `json:"full_name" binding:"MaxSize(100)"` @@ -32,6 +35,8 @@ type CreateUserOption struct { type EditUserOption struct { // required: true SourceID int64 `json:"source_id"` + // identifier of the user, provided by the external authenticator (if configured) + // default: empty // required: true LoginName string `json:"login_name" binding:"Required"` // swagger:strfmt email diff --git a/modules/structs/git_blob.go b/modules/structs/git_blob.go index 96770cc62e210..643b69ed3726a 100644 --- a/modules/structs/git_blob.go +++ b/modules/structs/git_blob.go @@ -10,4 +10,7 @@ type GitBlobResponse struct { URL string `json:"url"` SHA string `json:"sha"` Size int64 `json:"size"` + + LfsOid *string `json:"lfs_oid,omitempty"` + LfsSize *int64 `json:"lfs_size,omitempty"` } diff --git a/modules/structs/hook.go b/modules/structs/hook.go index cd0eef851a377..ac779a5740748 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -71,7 +71,8 @@ type PayloadUser struct { // Full name of the commit author Name string `json:"name"` // swagger:strfmt email - Email string `json:"email"` + Email string `json:"email"` + // username of the user UserName string `json:"username"` } @@ -286,6 +287,8 @@ const ( HookIssueReOpened HookIssueAction = "reopened" // HookIssueEdited edited HookIssueEdited HookIssueAction = "edited" + // HookIssueDeleted is an issue action for deleting an issue + HookIssueDeleted HookIssueAction = "deleted" // HookIssueAssigned assigned HookIssueAssigned HookIssueAction = "assigned" // HookIssueUnassigned unassigned diff --git a/modules/structs/issue_tracked_time.go b/modules/structs/issue_tracked_time.go index a3904af80eec5..befcfb323d046 100644 --- a/modules/structs/issue_tracked_time.go +++ b/modules/structs/issue_tracked_time.go @@ -14,7 +14,7 @@ type AddTimeOption struct { Time int64 `json:"time" binding:"Required"` // swagger:strfmt date-time Created time.Time `json:"created"` - // User who spent the time (optional) + // username of the user who spent the time working on the issue (optional) User string `json:"user_name"` } @@ -26,7 +26,8 @@ type TrackedTime struct { // Time in seconds Time int64 `json:"time"` // deprecated (only for backwards compatibility) - UserID int64 `json:"user_id"` + UserID int64 `json:"user_id"` + // username of the user UserName string `json:"user_name"` // deprecated (only for backwards compatibility) IssueID int64 `json:"issue_id"` diff --git a/modules/structs/org.go b/modules/structs/org.go index f93b3b6493db0..33b45c6344e95 100644 --- a/modules/structs/org.go +++ b/modules/structs/org.go @@ -15,6 +15,7 @@ type Organization struct { Location string `json:"location"` Visibility string `json:"visibility"` RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"` + // username of the organization // deprecated UserName string `json:"username"` } @@ -30,6 +31,7 @@ type OrganizationPermissions struct { // CreateOrgOption options for creating an organization type CreateOrgOption struct { + // username of the organization // required: true UserName string `json:"username" binding:"Required;Username;MaxSize(40)"` FullName string `json:"full_name" binding:"MaxSize(100)"` diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index a281620a3b86c..5a86db868b79d 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -22,6 +22,23 @@ type FileOptions struct { Signoff bool `json:"signoff"` } +type FileOptionsWithSHA struct { + FileOptions + // the blob ID (SHA) for the file that already exists, it is required for changing existing files + // required: true + SHA string `json:"sha" binding:"Required"` +} + +func (f *FileOptions) GetFileOptions() *FileOptions { + return f +} + +type FileOptionsInterface interface { + GetFileOptions() *FileOptions +} + +var _ FileOptionsInterface = (*FileOptions)(nil) + // CreateFileOptions options for creating files // Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) type CreateFileOptions struct { @@ -31,29 +48,16 @@ type CreateFileOptions struct { ContentBase64 string `json:"content"` } -// Branch returns branch name -func (o *CreateFileOptions) Branch() string { - return o.FileOptions.BranchName -} - // DeleteFileOptions options for deleting files (used for other File structs below) // Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) type DeleteFileOptions struct { - FileOptions - // sha is the SHA for the file that already exists - // required: true - SHA string `json:"sha" binding:"Required"` -} - -// Branch returns branch name -func (o *DeleteFileOptions) Branch() string { - return o.FileOptions.BranchName + FileOptionsWithSHA } // UpdateFileOptions options for updating files // Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) type UpdateFileOptions struct { - DeleteFileOptions + FileOptionsWithSHA // content must be base64 encoded // required: true ContentBase64 string `json:"content"` @@ -61,25 +65,21 @@ type UpdateFileOptions struct { FromPath string `json:"from_path" binding:"MaxSize(500)"` } -// Branch returns branch name -func (o *UpdateFileOptions) Branch() string { - return o.FileOptions.BranchName -} - -// FIXME: ChangeFileOperation.SHA is NOT required for update or delete if last commit is provided in the options. +// FIXME: there is no LastCommitID in FileOptions, actually it should be an alternative to the SHA in ChangeFileOperation // ChangeFileOperation for creating, updating or deleting a file type ChangeFileOperation struct { - // indicates what to do with the file + // indicates what to do with the file: "create" for creating a new file, "update" for updating an existing file, + // "upload" for creating or updating a file, "rename" for renaming a file, and "delete" for deleting an existing file. // required: true - // enum: create,update,delete + // enum: create,update,upload,rename,delete Operation string `json:"operation" binding:"Required"` // path to the existing or new file // required: true Path string `json:"path" binding:"Required;MaxSize(500)"` - // new or updated file content, must be base64 encoded + // new or updated file content, it must be base64 encoded ContentBase64 string `json:"content"` - // sha is the SHA for the file that already exists, required for update or delete + // the blob ID (SHA) for the file that already exists, required for changing existing files SHA string `json:"sha"` // old path of the file to move FromPath string `json:"from_path"` @@ -94,20 +94,10 @@ type ChangeFilesOptions struct { Files []*ChangeFileOperation `json:"files" binding:"Required"` } -// Branch returns branch name -func (o *ChangeFilesOptions) Branch() string { - return o.FileOptions.BranchName -} - -// FileOptionInterface provides a unified interface for the different file options -type FileOptionInterface interface { - Branch() string -} - // ApplyDiffPatchFileOptions options for applying a diff patch // Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used) type ApplyDiffPatchFileOptions struct { - DeleteFileOptions + FileOptions // required: true Content string `json:"content"` } @@ -119,16 +109,24 @@ type FileLinksResponse struct { HTMLURL *string `json:"html"` } +type ContentsExtResponse struct { + FileContents *ContentsResponse `json:"file_contents,omitempty"` + DirContents []*ContentsResponse `json:"dir_contents,omitempty"` +} + // ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content type ContentsResponse struct { - Name string `json:"name"` - Path string `json:"path"` - SHA string `json:"sha"` - LastCommitSHA string `json:"last_commit_sha"` + Name string `json:"name"` + Path string `json:"path"` + SHA string `json:"sha"` + + LastCommitSHA *string `json:"last_commit_sha,omitempty"` // swagger:strfmt date-time - LastCommitterDate time.Time `json:"last_committer_date"` + LastCommitterDate *time.Time `json:"last_committer_date,omitempty"` // swagger:strfmt date-time - LastAuthorDate time.Time `json:"last_author_date"` + LastAuthorDate *time.Time `json:"last_author_date,omitempty"` + LastCommitMessage *string `json:"last_commit_message,omitempty"` + // `type` will be `file`, `dir`, `symlink`, or `submodule` Type string `json:"type"` Size int64 `json:"size"` @@ -145,6 +143,9 @@ type ContentsResponse struct { // `submodule_git_url` is populated when `type` is `submodule`, otherwise null SubmoduleGitURL *string `json:"submodule_git_url"` Links *FileLinksResponse `json:"_links"` + + LfsOid *string `json:"lfs_oid,omitempty"` + LfsSize *int64 `json:"lfs_size,omitempty"` } // FileCommitResponse contains information generated from a Git commit for a repo's file. diff --git a/modules/structs/user.go b/modules/structs/user.go index 7338e45739734..89349cda2c757 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -15,9 +15,9 @@ import ( type User struct { // the user's id ID int64 `json:"id"` - // the user's username + // login of the user, same as `username` UserName string `json:"login"` - // the user's authentication sign-in name. + // identifier of the user, provided by the external authenticator (if configured) // default: empty LoginName string `json:"login_name"` // The ID of the user's Authentication Source diff --git a/modules/structs/user_email.go b/modules/structs/user_email.go index 9319667e8fca6..01895a0058451 100644 --- a/modules/structs/user_email.go +++ b/modules/structs/user_email.go @@ -11,6 +11,7 @@ type Email struct { Verified bool `json:"verified"` Primary bool `json:"primary"` UserID int64 `json:"user_id"` + // username of the user UserName string `json:"username"` } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index ff3f7cfda120e..e454bce4bd3c2 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -40,7 +40,6 @@ func NewFuncMap() template.FuncMap { "HTMLFormat": htmlFormat, "QueryEscape": queryEscape, "QueryBuild": QueryBuild, - "JSEscape": jsEscapeSafe, "SanitizeHTML": SanitizeHTML, "URLJoin": util.URLJoin, "DotEscape": dotEscape, @@ -181,10 +180,6 @@ func htmlFormat(s any, args ...any) template.HTML { panic(fmt.Sprintf("unexpected type %T", s)) } -func jsEscapeSafe(s string) template.HTML { - return template.HTML(template.JSEscapeString(s)) -} - func queryEscape(s string) template.URL { return template.URL(url.QueryEscape(s)) } diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go index 81f8235bd2b9e..7e3a952e7b728 100644 --- a/modules/templates/helper_test.go +++ b/modules/templates/helper_test.go @@ -57,10 +57,6 @@ func TestSubjectBodySeparator(t *testing.T) { "Insufficient\n--\nSeparators") } -func TestJSEscapeSafe(t *testing.T) { - assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, jsEscapeSafe(`&<>'"`)) -} - func TestSanitizeHTML(t *testing.T) { assert.Equal(t, template.HTML(`link xss
inline
`), SanitizeHTML(`link xss
inline
`)) } diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index f51936354e6a1..8073a6e5f5bd6 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -42,7 +42,7 @@ var ( var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors") -func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive +func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ctx context.Context) error { //nolint:revive // we don't use ctx, only pass it to the template executor name := string(tplName) if respWriter, ok := w.(http.ResponseWriter); ok { if respWriter.Header().Get("Content-Type") == "" { @@ -57,7 +57,7 @@ func (h *HTMLRender) HTML(w io.Writer, status int, tplName TplName, data any, ct return t.Execute(w, data) } -func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive +func (h *HTMLRender) TemplateLookup(name string, ctx context.Context) (TemplateExecutor, error) { //nolint:revive // we don't use ctx, only pass it to the template executor tmpls := h.templates.Load() if tmpls == nil { return nil, ErrTemplateNotInitialized diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index 0d84f8598b858..34e8b9ad70167 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -102,31 +102,28 @@ func escapeTemplate(t *template.Template) error { return nil } -//nolint:unused type htmlTemplate struct { - escapeErr error - text *texttemplate.Template + _/*escapeErr*/ error + text *texttemplate.Template } -//nolint:unused type textTemplateCommon struct { - tmpl map[string]*template.Template // Map from name to defined templates. - muTmpl sync.RWMutex // protects tmpl - option struct { + _/*tmpl*/ map[string]*template.Template + _/*muTmpl*/ sync.RWMutex + _/*option*/ struct { missingKey int } - muFuncs sync.RWMutex // protects parseFuncs and execFuncs - parseFuncs texttemplate.FuncMap - execFuncs map[string]reflect.Value + muFuncs sync.RWMutex + _/*parseFuncs*/ texttemplate.FuncMap + execFuncs map[string]reflect.Value } -//nolint:unused type textTemplate struct { - name string + _/*name*/ string *parse.Tree *textTemplateCommon - leftDelim string - rightDelim string + _/*leftDelim*/ string + _/*rightDelim*/ string } func ptr[T, P any](ptr *P) *T { diff --git a/modules/templates/util_json.go b/modules/templates/util_json.go index 71a4e23d364f3..29a04290fa02f 100644 --- a/modules/templates/util_json.go +++ b/modules/templates/util_json.go @@ -9,11 +9,11 @@ import ( "code.gitea.io/gitea/modules/json" ) -type JsonUtils struct{} //nolint:revive +type JsonUtils struct{} //nolint:revive // variable naming triggers on Json, wants JSON var jsonUtils = JsonUtils{} -func NewJsonUtils() *JsonUtils { //nolint:revive +func NewJsonUtils() *JsonUtils { //nolint:revive // variable naming triggers on Json, wants JSON return &jsonUtils } diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index 14655a53c3c5c..1056c4264334b 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -122,8 +122,23 @@ func (ut *RenderUtils) RenderIssueSimpleTitle(text string) template.HTML { return ret } -// RenderLabel renders a label +func (ut *RenderUtils) RenderLabelWithLink(label *issues_model.Label, link any) template.HTML { + var attrHref template.HTML + switch link.(type) { + case template.URL, string: + attrHref = htmlutil.HTMLFormat(`href="%s"`, link) + default: + panic(fmt.Sprintf("unexpected type %T for link", link)) + } + return ut.renderLabelWithTag(label, "a", attrHref) +} + func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML { + return ut.renderLabelWithTag(label, "span", "") +} + +// RenderLabel renders a label +func (ut *RenderUtils) renderLabelWithTag(label *issues_model.Label, tagName, tagAttrs template.HTML) template.HTML { locale := ut.ctx.Value(translation.ContextKey).(translation.Locale) var extraCSSClasses string textColor := util.ContrastColor(label.Color) @@ -137,8 +152,8 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML { if labelScope == "" { // Regular label - return htmlutil.HTMLFormat(`
%s
`, - extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name)) + return htmlutil.HTMLFormat(`<%s %s class="ui label %s" style="color: %s !important; background-color: %s !important;" data-tooltip-content title="%s">%s`, + tagName, tagAttrs, extraCSSClasses, textColor, label.Color, descriptionText, ut.RenderEmoji(label.Name), tagName) } // Scoped label @@ -152,7 +167,7 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML { // Ensure we add the same amount of contrast also near 0 and 1. darken := contrast + math.Max(luminance+contrast-1.0, 0.0) lighten := contrast + math.Max(contrast-luminance, 0.0) - // Compute factor to keep RGB values proportional. + // Compute the factor to keep RGB values proportional. darkenFactor := math.Max(luminance-darken, 0.0) / math.Max(luminance, 1.0/255.0) lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0) @@ -173,26 +188,29 @@ func (ut *RenderUtils) RenderLabel(label *issues_model.Label) template.HTML { if label.ExclusiveOrder > 0 { // |