Compare commits

..

1 Commits

Author SHA1 Message Date
wxiaoguang
433443ffa9 Dump should only copy regular files and symlink regular files (#20015) 2022-06-18 22:06:32 +08:00
191 changed files with 856 additions and 2860 deletions

View File

@@ -1,6 +1,3 @@
# config for changelog tool
# source: https://gitea.com/gitea/changelog
# The full repository name # The full repository name
repo: go-gitea/gitea repo: go-gitea/gitea
@@ -21,10 +18,6 @@ groups:
name: SECURITY name: SECURITY
labels: labels:
- kind/security - kind/security
-
name: FEDERATION
labels:
- theme/federation
- -
name: FEATURES name: FEATURES
labels: labels:

View File

@@ -4,353 +4,6 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/1.17.1) - 2022-08-17
* SECURITY
* Correctly escape within tribute.js (#20831) (#20832)
* ENHANCEMENTS
* Add support for NuGet API keys (#20721) (#20734)
* Display project in issue list (#20583)
* Add disable download source configuration (#20548) (#20579)
* Add username check to doctor (#20140) (#20671)
* Enable Wire 2 for Internal SSH Server (#20616) (#20617)
* BUGFIXES
* Use the total issue count for UI (#20785) (#20827)
* Add proxy host into allow list (#20798) (#20819)
* Add missing translation for queue flush workers (#20791) (#20792)
* Improve comment header for mobile (#20781) (#20789)
* Fix git.Init for doctor sub-command (#20782) (#20783)
* Check webhooks slice length before calling xorm (#20642) (#20768)
* Remove manual rollback for failed generated repositories (#20639) (#20762)
* Use correct field name in npm template (#20675) (#20760)
* Keep download count on Container tag overwrite (#20728) (#20735)
* Fix v220 migration to be compatible for MSSQL 2008 r2 (#20702) (#20707)
* Use request timeout for git service rpc (#20689) (#20693)
* Send correct NuGet status codes (#20647) (#20677)
* Use correct context to get package content (#20673) (#20676)
* Fix the JS error "EventSource is not defined" caused by some non-standard browsers (#20584) (#20663)
* Add default commit messages to PR for squash merge (#20618) (#20645)
* Fix package upload for files >32mb (#20622) (#20635)
* Fix the new-line copy-paste for rendered code (#20612)
* Clean up and fix clone button script (#20415 & #20600) (#20599)
* Fix default merge style (#20564) (#20565)
* Add repository condition for issue count (#20454) (#20496)
* Make branch icon stand out more (#20726) (#20774)
* Fix loading button with invalid form (#20754) (#20759)
* Fix SecToTime edge-cases (#20610) (#20611)
* Executable check always returns true for windows (#20637) (#20835)
* Check issue labels slice length before calling xorm Insert (#20655) (#20836)
* Fix owners cannot create organization repos bug (#20841) (#20854)
* Prevent 500 is head repo does not have PullRequest unit in IsUserAllowedToUpdate (#20839) (#20848)
## [1.17.0](https://github.com/go-gitea/gitea/releases/tag/v1.17.0) - 2022-07-30
* BREAKING
* Require go1.18 for Gitea 1.17 (#19918)
* Make AppDataPath absolute against the AppWorkPath if it is not (#19815)
* Nuke the incorrect permission report on /api/v1/notifications (#19761)
* Refactor git module, make Gitea use internal git config (#19732)
* Remove `RequireHighlightJS` field, update plantuml example. (#19615)
* Increase minimal required git version to 2.0 (#19577)
* Add a directory prefix `gitea-src-VERSION` to release-tar-file (#19396)
* Use "main" as default branch name (#19354)
* Make cron task no notice on success (#19221)
* Add pam account authorization check (#19040)
* Show messages for users if the ROOT_URL is wrong, show JavaScript errors (#18971)
* Refactor mirror code & fix StartToMirror (#18904)
* Remove deprecated SSH ciphers from default (#18697)
* Add the possibility to allow the user to have a favicon which differs from the main logo (#18542)
* Update reserved usernames list (#18438)
* Support custom ACME provider (#18340)
* Change initial TrustModel to committer (#18335)
* Update HTTP status codes (#18063)
* Upgrade Alpine from 3.13 to 3.15 (#18050)
* Restrict email address validation (#17688)
* Refactor Router Logger (#17308)
* SECURITY
* Use git.HOME_PATH for Git HOME directory (#20114) (#20293)
* Add write check for creating Commit Statuses (#20332) (#20333)
* Remove deprecated SSH ciphers from default (#18697)
* FEDERATION
* Return statistic information for nodeinfo (#19561)
* Add Webfinger endpoint (#19462)
* Store the foreign ID of issues during migration (#18446)
* FEATURES
* Automatically render wiki TOC (#19873)
* Adding button to link accounts from user settings (#19792)
* Allow set default merge style while creating repo (#19751)
* Auto merge pull requests when all checks succeeded (#9307 & #19648)
* Improve reviewing PR UX (#19612)
* Add support for rendering console output with colors (#19497)
* Add Helm Chart registry (#19406)
* Add Goroutine stack inspector to admin/monitor (#19207)
* RSS/Atom support for Orgs & Repos (#17714 & #19055)
* Add button for issue deletion (#19032)
* Allow to mark files in a PR as viewed (#19007)
* Add Index to comment for migrations and mirroring (#18806)
* Add health check endpoint (#18465)
* Add packagist webhook (#18224)
* Add "Allow edits from maintainer" feature (#18002)
* Add apply-patch, basic revert and cherry-pick functionality (#17902)
* Add Package Registry (#16510)
* Add LDAP group sync to Teams (#16299)
* Pause queues (#15928)
* Added auto-save whitespace behavior if it changed manually (#15566)
* Find files in repo (#15028)
* Provide configuration to allow camo-media proxying (#12802)
* API
* Add endpoint to serve blob or LFS file content (#19689)
* Add endpoint to check if team has repo access (#19540)
* More commit info (#19252)
* Allow to create file on empty repo (#19224)
* Allow removing issues (#18879)
* Add endpoint to query collaborators permission for a repository (#18761)
* Return primary language and repository language stats API URL (#18396)
* Implement http signatures support for the API (#17565)
* ENHANCEMENTS
* Make notification bell more prominent on mobile (#20108, #20236, #20251) (#20269)
* Adjust max-widths for the repository file table (#20243) (#20247)
* Display full name (#20171) (#20246)
* Add dbconsistency checks for Stopwatches (#20010)
* Add fetch.writeCommitGraph to gitconfig (#20006)
* Add fgprof pprof profiler (#20005)
* Move agit dependency (#19998)
* Empty log queue on flush and close (#19994)
* Remove tab/TabName usage where it's not needed (#19973)
* Improve file header on mobile (#19945)
* Move issues related files into models/issues (#19931)
* Add breaking email restrictions checker in doctor (#19903)
* Improve UX on modal for deleting an access token (#19894)
* Add alt text to logo (#19892)
* Move some code into models/git (#19879)
* Remove customized (unmaintained) dropdown, improve aria a11y for dropdown (#19861)
* Make user profile image show full image on mobile (#19840)
* Replace blue button and label classes with primary (#19763)
* Remove fomantic progress module (#19760)
* Allows repo search to match against "owner/repo" pattern strings (#19754)
* Move org functions (#19753)
* Move almost all functions' parameter db.Engine to context.Context (#19748)
* Show source/target branches on PR's list (#19747)
* Use http.StatusTemporaryRedirect(307) when serve avatar directly (#19739)
* Add doctor orphan check for orphaned pull requests without an existing base repo (#19731)
* Make Ctrl+Enter (quick submit) work for issue comment and wiki editor (#19729)
* Update go-chi/cache to utilize Ping() (#19719)
* Improve commit list/view on mobile (#19712)
* Move some repository related code into sub package (#19711)
* Use a better OlderThan for DeleteInactiveUsers (#19693)
* Introduce eslint-plugin-jquery (#19690)
* Tidy up `<head>` template (#19678)
* Calculate filename hash only once (#19654)
* Simplify `IsVendor` (#19626)
* Add "Reference" section to Issue view sidebar (#19609)
* Only set CanColorStdout / CanColorStderr to true if the stdout/stderr is a terminal (#19581)
* Use for a repo action one database transaction (#19576)
* Simplify loops to copy (#19569)
* Added X-Mailer header to outgoing emails (#19562)
* use middleware to open gitRepo (#19559)
* Mute link in diff header (#19556)
* Improve UI on mobile (#19546)
* Fix Pull Request comment filename word breaks (#19535)
* Permalink files In PR diff (#19534)
* PullService lock via pullID (#19520)
* Make repository file list useable on mobile (#19515)
* more context for models (#19511)
* Refactor readme file renderer (#19502)
* By default force vertical tabs on mobile (#19486)
* Github style following followers (#19482)
* Improve action table indices (#19472)
* Use horizontal tabs for repo header on mobile (#19468)
* pass gitRepo down since its used for main repo and wiki (#19461)
* Admin should not delete himself (#19423)
* Use queue instead of memory queue in webhook send service (#19390)
* Simplify the code to get issue count (#19380)
* Add commit status popup to issuelist (#19375)
* Add RSS Feed buttons to Repo, User and Org pages (#19370)
* Add logic to switch between source/rendered on Markdown (#19356)
* Move some helper files out of models (#19355)
* Move access and repo permission to models/perm/access (#19350)
* Disallow selecting the text of buttons (#19330)
* Allow custom redirect for landing page (#19324)
* Remove dependent on session auth for api/v1 routers (#19321)
* Never use /api/v1 from Gitea UI Pages (#19318)
* Remove legacy unmaintained packages, refactor to support change default locale (#19308)
* Move milestone to models/issues/ (#19278)
* Configure OpenSSH log level via Environment in Docker (#19274)
* Move reaction to models/issues/ (#19264)
* Make git.OpenRepository accept Context (#19260)
* Move some issue methods as functions (#19255)
* Show last cron messages on monitor page (#19223)
* New cron task: delete old system notices (#19219)
* Add Redis Sentinel Authentication Support (#19213)
* Add auto logging of goroutine pid label (#19212)
* Set OpenGraph title to DisplayName in profile pages (#19206)
* Add pprof labels in processes and for lifecycles (#19202)
* Let web and API routes have different auth methods group (#19168)
* Move init repository related functions to modules (#19159)
* Feeds: render markdown to html (#19058)
* Allow users to self-request a PR review (#19030)
* Allow render HTML with css/js external links (#19017)
* Fix script compatiable with OpenWrt (#19000)
* Support ignore all santize for external renderer (#18984)
* Add note to GPG key response if user has no keys (#18961)
* Improve Stopwatch behavior (#18930)
* Improve mirror iterator (#18928)
* Uncapitalize errors (#18915)
* Prevent Stats Indexer reporting error if repo dir missing (#18870)
* Refactor SecToTime() function (#18863)
* Replace deprecated String.prototype.substr() with String.prototype.slice() (#18796)
* Move deletebeans into models/db (#18781)
* Fix display time of milestones (#18753)
* Add config option to disable "Update branch by rebase" (#18745)
* Display template path of current page in dev mode (#18717)
* Add number in queue status to monitor page (#18712)
* Change git.cmd to RunWithContext (#18693)
* Refactor i18n, use Locale to provide i18n/translation related functions (#18648)
* Delete old git.NewCommand() and use it as git.NewCommandContext() (#18552)
* Move organization related structs into sub package (#18518)
* Warn at startup if the provided `SCRIPT_TYPE` is not on the PATH (#18467)
* Use `CryptoRandomBytes` instead of `CryptoRandomString` (#18439)
* Use explicit jQuery import, remove unused eslint globals (#18435)
* Allow to filter repositories by language in explore, user and organization repositories lists (#18430)
* Use base32 for 2FA scratch token (#18384)
* Unexport var git.GlobalCommandArgs (#18376)
* Don't underline commit status icon on hover (#18372)
* Always use git command but not os.Command (#18363)
* Switch to non-deprecation setting (#18358)
* Set the LastModified header for raw files (#18356)
* Refactor jwt.StandardClaims to RegisteredClaims (#18344)
* Enable deprecation error for v1.17.0 (#18341)
* Refactor httplib (#18338)
* Limit max-height of CodeMirror editors for issue comment and wiki (#18271)
* Validate migration files (#18203)
* Format with gofumpt (#18184)
* Allow custom default merge message with .gitea/default_merge_message/<merge_style>_TEMPLATE.md (#18177)
* Prettify number of issues (#17760)
* Add a "admin user generate-access-token" subcommand (#17722)
* Custom regexp external issues (#17624)
* Add smtp password to install page (#17564)
* Add config options to hide issue events (#17414)
* Prevent double click new issue/pull/comment button (#16157)
* Show issue assignee on project board (#15232)
* BUGFIXES
* WebAuthn CredentialID field needs to be increased in size (#20530) (#20555)
* Ensure that all unmerged files are merged when conflict checking (#20528) (#20536)
* Stop logging EOFs and exit(1)s in ssh handler (#20476) (#20529)
* Add labels to two buttons that were missing them (#20419) (#20524)
* Fix ROOT_URL detection for URLs without trailing slash (#20502) (#20503)
* Dismiss prior pull reviews if done via web in review dismiss (#20197) (#20407)
* Allow RSA 2047 bit keys (#20272) (#20396)
* Add missing return for when topic isn't found (#20351) (#20395)
* Fix commit status icon when in subdirectory (#20285) (#20385)
* Initialize cron last (#20373) (#20384)
* Set target on create release with existing tag (#20381) (#20382)
* Update xorm.io/xorm to fix a interpreting db column sizes issue on 32bit systems (#20371) (#20372)
* Make sure `repo_dir` is an empty directory or doesn't exist before 'dump-repo' (#20205) (#20370)
* Prevent context deadline error propagation in GetCommitsInfo (#20346) (#20361)
* Correctly handle draft releases without a tag (#20314) (#20335)
* Prevent "empty" scrollbars on Firefox (#20294) (#20308)
* Refactor SSH init code, fix directory creation for TrustedUserCAKeys file (#20299) (#20306)
* Bump goldmark to v1.4.13 (#20300) (#20301)
* Do not create empty ".ssh" directory when loading config (#20289) (#20298)
* Fix NPE when using non-numeric (#20277) (#20278)
* Store read access in access for team repositories (#20275) (#20276)
* EscapeFilter the group dn membership (#20200) (#20254)
* Only show Followers that current user can access (#20220) (#20252)
* Update Bluemonday to v1.0.19 (#20199) (#20209)
* Refix indices on actions table (#20158) (#20198)
* Check if project has the same repository id with issue when assign project to issue (#20133) (#20188)
* Fix remove file on initial comment (#20127) (#20128)
* Catch the error before the response is processed by goth (#20000) (#20102)
* Dashboard feed respect setting.UI.FeedPagingNum again (#20094) (#20099)
* Alter hook_task TEXT fields to LONGTEXT (#20038) (#20041)
* Respond with a 401 on git push when password isn't changed yet (#20026) (#20027)
* Return 404 when tag is broken (#20017) (#20024)
* Alter hook_task TEXT fields to LONGTEXT (#20038) (#20041)
* Respond with a 401 on git push when password isn't changed yet (#20026) (#20027)
* Return 404 when tag is broken (#20017) (#20024)
* Write Commit-Graphs in RepositoryDumper (#20004)
* Use DisplayName() instead of FullName in Oauth Provider (#19991)
* Don't buffer doctor logger (#19982)
* Always try to fetch repo for mirrors (#19975)
* Uppercase first languages letters (#19965)
* Fix cli command restore-repo: "units" should be parsed as StringSlice (#19953)
* Ensure minimum mirror interval is reported on settings page (#19895)
* Exclude Archived repos from Dashboard Milestones (#19882)
* gitconfig: set safe.directory = * (#19870)
* Prevent NPE on update mirror settings (#19864)
* Only return valid stopwatches to the EventSource (#19863)
* Prevent NPE whilst migrating if there is a team request review (#19855)
* Fix inconsistency in doctor output (#19836)
* Fix release tag for webhook (#19830)
* Add title attribute to dependencies in sidebar (#19807)
* Estimate Action Count in Statistics (#19775)
* Do not update user stars numbers unless fix is specified (#19750)
* Improved ref comment link when origin is body/title (#19741)
* Fix nodeinfo caching and prevent NPE if cache non-existent (#19721)
* Fix duplicate entry error when add team member (#19702)
* Fix sending empty notifications (#19589)
* Update image URL for Discord webhook (#19536)
* Don't let repo clone URL overflow (#19517)
* Allow commit status popup on /pulls page (#19507)
* Fix two UI bugs: JS error in imagediff.js, 500 error in diff/compare.tmpl (#19494)
* Fix logging of Transfer API (#19456)
* Fix panic in teams API when requesting members (#19360)
* Refactor CSRF protection modules, make sure CSRF tokens can be up-to-date. (#19337)
* An attempt to sync a non-mirror repo must give 400 (Bad Request) (#19300)
* Move checks for pulls before merge into own function (#19271)
* Fix `contrib/upgrade.sh` (#19222)
* Set the default branch for repositories generated from templates (#19136)
* Fix EasyMDE error when input Enter (#19004)
* Don't clean up hardcoded `tmp` (#18983)
* Delete related notifications on issue deletion too (#18953)
* Fix trace log to show value instead of pointers (#18926)
* Fix behavior or checkbox submission. (#18851)
* Add `ContextUser` (#18798)
* Fix some mirror bugs (#18649)
* Quote MAKE to prevent path expansion with space error (#18622)
* Preserve users if restoring a repository on the same Gitea instance (#18604)
* Fix non-ASCII search on database (#18437)
* Automatically pause queue if index service is unavailable (#15066)
* TESTING
* Allow postgres integration tests to run over unix pipe (#19875)
* Prevent intermittent NPE in queue tests (#19301)
* Add test for importing pull requests in gitea uploader for migrations (#18752)
* Remove redundant comparison in repo dump/restore (#18660)
* More repo dump/restore tests, including pull requests (#18621)
* Add test coverage for original author conversion during migrations (#18506)
* TRANSLATION
* Update issue_no_dependencies description (#19112)
* Refactor webhooks i18n (#18380)
* BUILD
* Use alpine 3.16 (#19797)
* Require node 14.0 (#19451)
* DOCS
* Update documents (git/fomantic/db, etc) (#19868)
* Update the ROOT documentation and error messages (#19832)
* Update document to use FHS `/usr/local/bin/gitea` instead of `/app/...` for Docker (#19794)
* Update documentation to disable duration settings with -1 instead of 0 (#19647)
* Add warning to set SENDMAIL_ARGS to -- (#19102)
* Update nginx reverse proxy docs (#18922)
* Add example to render html files (#18736)
* Make SSH passtrough documentation better (#18687)
* Changelog 1.16.0 & 1.15.11 (#18468 & #18455) (#18470)
* Update the SSH passthrough documentation (#18366)
* Add `contrib/upgrade.sh` (#18286)
* MISC
* Fix aria for logo (#19955)
* In code search, get code unit accessible repos in one (main) query (#19764)
* Add tooltip to pending PR comments (#19662)
* Improve sync performance for pull-mirrors (#19125)
* Improve dashboard's repo list performance (#18963)
* Avoid database lookups for `DescriptionHTML` (#18924)
* Remove CodeMirror dependencies (#18911)
* Disable unnecessary mirroring elements (#18527)
* Disable unnecessary OpenID/OAuth2 elements (#18491)
* Disable unnecessary GitHooks elements (#18485)
* Change some logging levels (#18421)
* Prevent showing webauthn error for every time visiting `/user/settings/security` (#18385)
* Use correct translation key for errors (#18342)
## [1.16.8](https://github.com/go-gitea/gitea/releases/tag/v1.16.8) - 2022-05-16 ## [1.16.8](https://github.com/go-gitea/gitea/releases/tag/v1.16.8) - 2022-05-16
* ENHANCEMENTS * ENHANCEMENTS

View File

@@ -7,17 +7,13 @@ package cmd
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"os"
"strings" "strings"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration" base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/migrations"
"github.com/urfave/cli" "github.com/urfave/cli"
@@ -87,11 +83,6 @@ func runDumpRepository(ctx *cli.Context) error {
return err return err
} }
// migrations.GiteaLocalUploader depends on git module
if err := git.InitSimple(context.Background()); err != nil {
return err
}
log.Info("AppPath: %s", setting.AppPath) log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath) log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath) log.Info("Custom path: %s", setting.CustomPath)
@@ -137,9 +128,7 @@ func runDumpRepository(ctx *cli.Context) error {
} else { } else {
units := strings.Split(ctx.String("units"), ",") units := strings.Split(ctx.String("units"), ",")
for _, unit := range units { for _, unit := range units {
switch strings.ToLower(strings.TrimSpace(unit)) { switch strings.ToLower(unit) {
case "":
continue
case "wiki": case "wiki":
opts.Wiki = true opts.Wiki = true
case "issues": case "issues":
@@ -156,29 +145,13 @@ func runDumpRepository(ctx *cli.Context) error {
opts.Comments = true opts.Comments = true
case "pull_requests": case "pull_requests":
opts.PullRequests = true opts.PullRequests = true
default:
return errors.New("invalid unit: " + unit)
} }
} }
} }
// the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files
repoDir := ctx.String("repo_dir")
if exists, err := util.IsExist(repoDir); err != nil {
return fmt.Errorf("unable to stat repo_dir %q: %v", repoDir, err)
} else if exists {
if isDir, _ := util.IsDir(repoDir); !isDir {
return fmt.Errorf("repo_dir %q already exists but it's not a directory", repoDir)
}
if dir, _ := os.ReadDir(repoDir); len(dir) > 0 {
return fmt.Errorf("repo_dir %q is not empty", repoDir)
}
}
if err := migrations.DumpRepository( if err := migrations.DumpRepository(
context.Background(), context.Background(),
repoDir, ctx.String("repo_dir"),
ctx.String("owner_name"), ctx.String("owner_name"),
opts, opts,
); err != nil { ); err != nil {

View File

@@ -1,23 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"testing"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
)
func init() {
setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest()
}
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: "..",
})
}

View File

@@ -12,11 +12,9 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/migrations" "code.gitea.io/gitea/models/migrations"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
@@ -27,13 +25,13 @@ import (
var CmdMigrateStorage = cli.Command{ var CmdMigrateStorage = cli.Command{
Name: "migrate-storage", Name: "migrate-storage",
Usage: "Migrate the storage", Usage: "Migrate the storage",
Description: "Copies stored files from storage configured in app.ini to parameter-configured storage", Description: "This is a command for migrating storage.",
Action: runMigrateStorage, Action: runMigrateStorage,
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "type, t", Name: "type, t",
Value: "", Value: "",
Usage: "Type of stored files to copy. Allowed types: 'attachments', 'lfs', 'avatars', 'repo-avatars', 'repo-archivers', 'packages'", Usage: "Kinds of files to migrate, currently only 'attachments' is supported",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "storage, s", Name: "storage, s",
@@ -82,53 +80,34 @@ var CmdMigrateStorage = cli.Command{
}, },
} }
func migrateAttachments(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateAttachments(dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(attach *repo_model.Attachment) error { return repo_model.IterateAttachment(func(attach *repo_model.Attachment) error {
_, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath()) _, err := storage.Copy(dstStorage, attach.RelativePath(), storage.Attachments, attach.RelativePath())
return err return err
}) })
} }
func migrateLFS(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateLFS(dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(mo *git_model.LFSMetaObject) error { return git_model.IterateLFS(func(mo *git_model.LFSMetaObject) error {
_, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath()) _, err := storage.Copy(dstStorage, mo.RelativePath(), storage.LFS, mo.RelativePath())
return err return err
}) })
} }
func migrateAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateAvatars(dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(user *user_model.User) error { return user_model.IterateUser(func(user *user_model.User) error {
_, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath()) _, err := storage.Copy(dstStorage, user.CustomAvatarRelativePath(), storage.Avatars, user.CustomAvatarRelativePath())
return err return err
}) })
} }
func migrateRepoAvatars(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateRepoAvatars(dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(repo *repo_model.Repository) error { return repo_model.IterateRepository(func(repo *repo_model.Repository) error {
_, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath()) _, err := storage.Copy(dstStorage, repo.CustomAvatarRelativePath(), storage.RepoAvatars, repo.CustomAvatarRelativePath())
return err return err
}) })
} }
func migrateRepoArchivers(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(archiver *repo_model.RepoArchiver) error {
p, err := archiver.RelativePath()
if err != nil {
return err
}
_, err = storage.Copy(dstStorage, p, storage.RepoArchives, p)
return err
})
}
func migratePackages(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.IterateObjects(ctx, func(pb *packages_model.PackageBlob) error {
p := packages_module.KeyToRelativePath(packages_module.BlobHash256Key(pb.HashSHA256))
_, err := storage.Copy(dstStorage, p, storage.Packages, p)
return err
})
}
func runMigrateStorage(ctx *cli.Context) error { func runMigrateStorage(ctx *cli.Context) error {
stdCtx, cancel := installSignals() stdCtx, cancel := installSignals()
defer cancel() defer cancel()
@@ -148,6 +127,8 @@ func runMigrateStorage(ctx *cli.Context) error {
return err return err
} }
goCtx := context.Background()
if err := storage.Init(); err != nil { if err := storage.Init(); err != nil {
return err return err
} }
@@ -164,13 +145,13 @@ func runMigrateStorage(ctx *cli.Context) error {
return nil return nil
} }
dstStorage, err = storage.NewLocalStorage( dstStorage, err = storage.NewLocalStorage(
stdCtx, goCtx,
storage.LocalStorageConfig{ storage.LocalStorageConfig{
Path: p, Path: p,
}) })
case string(storage.MinioStorageType): case string(storage.MinioStorageType):
dstStorage, err = storage.NewMinioStorage( dstStorage, err = storage.NewMinioStorage(
stdCtx, goCtx,
storage.MinioStorageConfig{ storage.MinioStorageConfig{
Endpoint: ctx.String("minio-endpoint"), Endpoint: ctx.String("minio-endpoint"),
AccessKeyID: ctx.String("minio-access-key-id"), AccessKeyID: ctx.String("minio-access-key-id"),
@@ -181,29 +162,35 @@ func runMigrateStorage(ctx *cli.Context) error {
UseSSL: ctx.Bool("minio-use-ssl"), UseSSL: ctx.Bool("minio-use-ssl"),
}) })
default: default:
return fmt.Errorf("unsupported storage type: %s", ctx.String("storage")) return fmt.Errorf("Unsupported storage type: %s", ctx.String("storage"))
} }
if err != nil { if err != nil {
return err return err
} }
migratedMethods := map[string]func(context.Context, storage.ObjectStorage) error{
"attachments": migrateAttachments,
"lfs": migrateLFS,
"avatars": migrateAvatars,
"repo-avatars": migrateRepoAvatars,
"repo-archivers": migrateRepoArchivers,
"packages": migratePackages,
}
tp := strings.ToLower(ctx.String("type")) tp := strings.ToLower(ctx.String("type"))
if m, ok := migratedMethods[tp]; ok { switch tp {
if err := m(stdCtx, dstStorage); err != nil { case "attachments":
if err := migrateAttachments(dstStorage); err != nil {
return err return err
} }
log.Info("%s files have successfully been copied to the new storage.", tp) case "lfs":
return nil if err := migrateLFS(dstStorage); err != nil {
return err
}
case "avatars":
if err := migrateAvatars(dstStorage); err != nil {
return err
}
case "repo-avatars":
if err := migrateRepoAvatars(dstStorage); err != nil {
return err
}
default:
return fmt.Errorf("Unsupported storage: %s", ctx.String("type"))
} }
return fmt.Errorf("unsupported storage: %s", ctx.String("type")) log.Warn("All files have been copied to the new placement but old files are still on the original placement.")
return nil
} }

View File

@@ -1,74 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package cmd
import (
"context"
"os"
"strings"
"testing"
"code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/storage"
packages_service "code.gitea.io/gitea/services/packages"
"github.com/stretchr/testify/assert"
)
func TestMigratePackages(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
creator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
content := "package main\n\nfunc main() {\nfmt.Println(\"hi\")\n}\n"
buf, err := packages_module.CreateHashedBufferFromReader(strings.NewReader(content), 1024)
assert.NoError(t, err)
defer buf.Close()
v, f, err := packages_service.CreatePackageAndAddFile(&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: creator,
PackageType: packages.TypeGeneric,
Name: "test",
Version: "1.0.0",
},
Creator: creator,
SemverCompatible: true,
VersionProperties: map[string]string{},
}, &packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
Filename: "a.go",
},
Data: buf,
IsLead: true,
})
assert.NoError(t, err)
assert.NotNil(t, v)
assert.NotNil(t, f)
ctx := context.Background()
p, err := os.MkdirTemp(os.TempDir(), "migrated_packages")
assert.NoError(t, err)
dstStorage, err := storage.NewLocalStorage(
ctx,
storage.LocalStorageConfig{
Path: p,
})
assert.NoError(t, err)
err = migratePackages(ctx, dstStorage)
assert.NoError(t, err)
entries, err := os.ReadDir(p)
assert.NoError(t, err)
assert.EqualValues(t, 2, len(entries))
assert.EqualValues(t, "01", entries[0].Name())
assert.EqualValues(t, "tmp", entries[1].Name())
}

View File

@@ -7,7 +7,6 @@ package cmd
import ( import (
"errors" "errors"
"net/http" "net/http"
"strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
@@ -38,10 +37,10 @@ var CmdRestoreRepository = cli.Command{
Value: "", Value: "",
Usage: "Restore destination repository name", Usage: "Restore destination repository name",
}, },
cli.StringFlag{ cli.StringSliceFlag{
Name: "units", Name: "units",
Value: "", Value: nil,
Usage: `Which items will be restored, one or more units should be separated as comma. Usage: `Which items will be restored, one or more units should be repeated with this flag.
wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`, wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`,
}, },
cli.BoolFlag{ cli.BoolFlag{
@@ -56,16 +55,13 @@ func runRestoreRepository(c *cli.Context) error {
defer cancel() defer cancel()
setting.LoadFromExisting() setting.LoadFromExisting()
var units []string
if s := c.String("units"); s != "" {
units = strings.Split(s, ",")
}
statusCode, errStr := private.RestoreRepo( statusCode, errStr := private.RestoreRepo(
ctx, ctx,
c.String("repo_dir"), c.String("repo_dir"),
c.String("owner_name"), c.String("owner_name"),
c.String("repo_name"), c.String("repo_name"),
units, c.StringSlice("units"),
c.Bool("validation"), c.Bool("validation"),
) )
if statusCode == http.StatusOK { if statusCode == http.StatusOK {

View File

@@ -617,10 +617,7 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; The path of git executable. If empty, Gitea searches through the PATH environment. ;; The path of git executable. If empty, Gitea searches through the PATH environment.
;PATH = PATH =
;;
;; The HOME directory for Git
;HOME_PATH = %(APP_DATA_PATH)/home
;; ;;
;; Disables highlight of added and removed changes ;; Disables highlight of added and removed changes
;DISABLE_DIFF_HIGHLIGHT = false ;DISABLE_DIFF_HIGHLIGHT = false
@@ -892,9 +889,6 @@ ROUTER = console
;; Allow deletion of unadopted repositories ;; Allow deletion of unadopted repositories
;ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES = false ;ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES = false
;; Don't allow download source archive files from UI
;DISABLE_DOWNLOAD_SOURCE_ARCHIVES = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[repository.editor] ;[repository.editor]
@@ -1248,7 +1242,7 @@ ROUTER = console
;; Define allowed algorithms and their minimum key length (use -1 to disable a type) ;; Define allowed algorithms and their minimum key length (use -1 to disable a type)
;ED25519 = 256 ;ED25519 = 256
;ECDSA = 256 ;ECDSA = 256
;RSA = 2047 ; we allow 2047 here because an otherwise valid 2048 bit RSA key can be reported as having 2047 bit length ;RSA = 2048
;DSA = -1 ; set to 1024 to switch on ;DSA = -1 ; set to 1024 to switch on
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -1693,7 +1687,7 @@ ROUTER = console
;ENABLED = true ;ENABLED = true
;; ;;
;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. ;; Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
;ALLOWED_TYPES = .csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip ;ALLOWED_TYPES = .docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip
;; ;;
;; Max size of each file. Defaults to 4MB ;; Max size of each file. Defaults to 4MB
;MAX_SIZE = 4 ;MAX_SIZE = 4
@@ -2253,10 +2247,10 @@ ROUTER = console
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Enable/Disable federation capabilities ;; Enable/Disable federation capabilities
;ENABLED = false ; ENABLED = true
;; ;;
;; Enable/Disable user statistics for nodeinfo if federation is enabled ;; Enable/Disable user statistics for nodeinfo if federation is enabled
;SHARE_USER_STATISTICS = true ; SHARE_USER_STATISTICS = true
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -78,7 +78,6 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
- `DEFAULT_BRANCH`: **main**: Default branch name of all repositories. - `DEFAULT_BRANCH`: **main**: Default branch name of all repositories.
- `ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to adopt unadopted repositories - `ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to adopt unadopted repositories
- `ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to delete unadopted repositories - `ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES`: **false**: Allow non-admin users to delete unadopted repositories
- `DISABLE_DOWNLOAD_SOURCE_ARCHIVES`: **false**: Don't allow download source archive files from UI
### Repository - Editor (`repository.editor`) ### Repository - Editor (`repository.editor`)
@@ -621,7 +620,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
- `ED25519`: **256** - `ED25519`: **256**
- `ECDSA`: **256** - `ECDSA`: **256**
- `RSA`: **2047**: We set 2047 here because an otherwise valid 2048 RSA key can be reported as 2047 length. - `RSA`: **2048**
- `DSA`: **-1**: DSA is now disabled by default. Set to **1024** to re-enable but ensure you may need to reconfigure your SSHD provider - `DSA`: **-1**: DSA is now disabled by default. Set to **1024** to re-enable but ensure you may need to reconfigure your SSHD provider
## Webhook (`webhook`) ## Webhook (`webhook`)
@@ -742,7 +741,7 @@ Default templates for project boards:
## Issue and pull request attachments (`attachment`) ## Issue and pull request attachments (`attachment`)
- `ENABLED`: **true**: Whether issue and pull request attachments are enabled. - `ENABLED`: **true**: Whether issue and pull request attachments are enabled.
- `ALLOWED_TYPES`: **.csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types. - `ALLOWED_TYPES`: **.docx,.gif,.gz,.jpeg,.jpg,mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip**: Comma-separated list of allowed file extensions (`.zip`), mime types (`text/plain`) or wildcard type (`image/*`, `audio/*`, `video/*`). Empty value or `*/*` allows all types.
- `MAX_SIZE`: **4**: Maximum size (MB). - `MAX_SIZE`: **4**: Maximum size (MB).
- `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once. - `MAX_FILES`: **5**: Maximum number of attachments that can be uploaded at once.
- `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` - `STORAGE_TYPE`: **local**: Storage type for attachments, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]`
@@ -948,8 +947,6 @@ Default templates for project boards:
## Git (`git`) ## Git (`git`)
- `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment. - `PATH`: **""**: The path of Git executable. If empty, Gitea searches through the PATH environment.
- `HOME_PATH`: **%(APP_DATA_PATH)/home**: The HOME directory for Git.
This directory will be used to contain the `.gitconfig` and possible `.gnupg` directories that Gitea's git calls will use. If you can confirm Gitea is the only application running in this environment, you can set it to the normal home directory for Gitea user.
- `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes. - `DISABLE_DIFF_HIGHLIGHT`: **false**: Disables highlight of added and removed changes.
- `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view. - `MAX_GIT_DIFF_LINES`: **1000**: Max number of lines allowed of a single file in diff view.
- `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view. - `MAX_GIT_DIFF_LINE_CHARACTERS`: **5000**: Max character count per line highlighted in diff view.
@@ -1091,7 +1088,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf
## Federation (`federation`) ## Federation (`federation`)
- `ENABLED`: **false**: Enable/Disable federation capabilities - `ENABLED`: **true**: Enable/Disable federation capabilities
- `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled - `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled
## Packages (`packages`) ## Packages (`packages`)

View File

@@ -97,11 +97,10 @@ repositories, `SIGNING_KEY=default` could be used to provide different
signing keys on a per-repository basis. However, this is clearly not an signing keys on a per-repository basis. However, this is clearly not an
ideal UI and therefore subject to change. ideal UI and therefore subject to change.
**Since 1.17**, Gitea runs git in its own home directory `[git].HOME_PATH` (default to `%(APP_DATA_PATH)/home`) **Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`.
and uses its own config `{[git].HOME_PATH}/.gitconfig`.
If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`) If you have your own customized git config for Gitea, you should set these configs in system git config (aka `/etc/gitconfig`)
or the Gitea internal git config `{[git].HOME_PATH}/.gitconfig`. or the Gitea internal git config `{[repository].ROOT}/.gitconfig`.
Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[git].HOME_PATH`. Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[repository].ROOT`.
### `INITIAL_COMMIT` ### `INITIAL_COMMIT`

View File

@@ -37,7 +37,7 @@ PUT https://gitea.example.com/api/packages/{owner}/generic/{package_name}/{packa
| ----------------- | ----------- | | ----------------- | ----------- |
| `owner` | The owner of the package. | | `owner` | The owner of the package. |
| `package_name` | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | | `package_name` | The package name. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). |
| `package_version` | The package version, a non-empty string. | | `package_version` | The package version as described in the [SemVer](https://semver.org/) spec. |
| `file_name` | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). | | `file_name` | The filename. It can contain only lowercase letters (`a-z`), uppercase letter (`A-Z`), numbers (`0-9`), dots (`.`), hyphens (`-`), or underscores (`_`). |
Example request using HTTP Basic authentication: Example request using HTTP Basic authentication:

View File

@@ -47,8 +47,6 @@ For example:
dotnet nuget add source --name gitea --username testuser --password password123 https://gitea.example.com/api/packages/testuser/nuget/index.json dotnet nuget add source --name gitea --username testuser --password password123 https://gitea.example.com/api/packages/testuser/nuget/index.json
``` ```
You can add the source without credentials and use the [`--api-key`](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-nuget-push) parameter when publishing packages. In this case you need to provide a [personal access token]({{< relref "doc/developers/api-usage.en-us.md#authentication" >}}).
## Publish a package ## Publish a package
Publish a package by running the following command: Publish a package by running the following command:

12
go.mod
View File

@@ -9,7 +9,7 @@ require (
gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 gitea.com/lunny/levelqueue v0.4.1
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.8.0 github.com/PuerkitoBio/goquery v1.8.0
@@ -64,7 +64,7 @@ require (
github.com/mattn/go-isatty v0.0.14 github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-sqlite3 v1.14.12 github.com/mattn/go-sqlite3 v1.14.12
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.19 github.com/microcosm-cc/bluemonday v1.0.18
github.com/minio/minio-go/v7 v7.0.26 github.com/minio/minio-go/v7 v7.0.26
github.com/msteinert/pam v1.0.0 github.com/msteinert/pam v1.0.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
@@ -85,15 +85,15 @@ require (
github.com/urfave/cli v1.22.9 github.com/urfave/cli v1.22.9
github.com/xanzy/go-gitlab v0.64.0 github.com/xanzy/go-gitlab v0.64.0
github.com/yohcop/openid-go v1.0.0 github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.4.13 github.com/yuin/goldmark v1.4.12
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
github.com/yuin/goldmark-meta v1.1.0 github.com/yuin/goldmark-meta v1.1.0
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3 go.jolheiser.com/pwn v0.0.3
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
golang.org/x/net v0.0.0-20220630215102-69896b714898 golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/tools v0.1.10 golang.org/x/tools v0.1.10
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -102,7 +102,7 @@ require (
mvdan.cc/xurls/v2 v2.4.0 mvdan.cc/xurls/v2 v2.4.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.11 xorm.io/builder v0.3.11
xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f xorm.io/xorm v1.3.1
) )
require ( require (

25
go.sum
View File

@@ -78,8 +78,8 @@ gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 h1:J/1i8u40TbcLP/w2w
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo= gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck= gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck=
gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 h1:Zc3RQWC2xOVglLciQH/ZIC5IqSk3Jn96LflGQLv18Rg= gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw=
gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE= gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
@@ -1146,8 +1146,8 @@ github.com/mholt/acmez v1.0.2 h1:C8wsEBIUVi6e0DYoxqCcFuXtwc4AWXL/jgcDjF7mjVo=
github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo=
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@@ -1540,8 +1540,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= github.com/yuin/goldmark v1.4.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU= github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
@@ -1783,13 +1783,14 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1936,8 +1937,8 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@@ -2413,5 +2414,5 @@ strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.11 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00= xorm.io/builder v0.3.11 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00=
xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f h1:3NvNsM4lnttTsHpk8ODHqrwN1MCEjsO3bD/rpd8A47k= xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM=
xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=

View File

@@ -11,20 +11,17 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestNodeinfo(t *testing.T) { func TestNodeinfo(t *testing.T) {
setting.Federation.Enabled = true
c = routers.NormalRoutes()
defer func() {
setting.Federation.Enabled = false
c = routers.NormalRoutes()
}()
onGiteaRun(t, func(*testing.T, *url.URL) { onGiteaRun(t, func(*testing.T, *url.URL) {
setting.Federation.Enabled = true
defer func() {
setting.Federation.Enabled = false
}()
req := NewRequestf(t, "GET", "/api/v1/nodeinfo") req := NewRequestf(t, "GET", "/api/v1/nodeinfo")
resp := MakeRequest(t, req, http.StatusOK) resp := MakeRequest(t, req, http.StatusOK)
var nodeinfo api.NodeInfo var nodeinfo api.NodeInfo

View File

@@ -26,7 +26,6 @@ import (
func TestPackageContainer(t *testing.T) { func TestPackageContainer(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
has := func(l packages_model.PackagePropertyList, name string) bool { has := func(l packages_model.PackagePropertyList, name string) bool {
@@ -37,15 +36,6 @@ func TestPackageContainer(t *testing.T) {
} }
return false return false
} }
getAllByName := func(l packages_model.PackagePropertyList, name string) []string {
values := make([]string, 0, len(l))
for _, pp := range l {
if pp.Name == name {
values = append(values, pp.Value)
}
}
return values
}
images := []string{"test", "te/st"} images := []string{"test", "te/st"}
tags := []string{"latest", "main"} tags := []string{"latest", "main"}
@@ -76,7 +66,7 @@ func TestPackageContainer(t *testing.T) {
Token string `json:"token"` Token string `json:"token"`
} }
authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token",service="container_registry",scope="*"`} authenticate := []string{`Bearer realm="` + setting.AppURL + `v2/token"`}
t.Run("Anonymous", func(t *testing.T) { t.Run("Anonymous", func(t *testing.T) {
defer PrintCurrentTest(t)() defer PrintCurrentTest(t)()
@@ -246,8 +236,7 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, tag, pd.Version.Version) assert.Equal(t, tag, pd.Version.Version)
assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged))
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata) metadata := pd.Metadata.(*container_module.Metadata)
@@ -275,23 +264,11 @@ func TestPackageContainer(t *testing.T) {
} }
} }
req = NewRequest(t, "GET", fmt.Sprintf("%s/manifests/%s", url, tag)) // Overwrite existing tag
addTokenAuthHeader(req, userToken)
MakeRequest(t, req, http.StatusOK)
pv, err = packages_model.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, image, tag)
assert.NoError(t, err)
assert.EqualValues(t, 1, pv.DownloadCount)
// Overwrite existing tag should keep the download count
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/manifests/%s", url, tag), strings.NewReader(manifestContent)) req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/manifests/%s", url, tag), strings.NewReader(manifestContent))
addTokenAuthHeader(req, userToken) addTokenAuthHeader(req, userToken)
req.Header.Set("Content-Type", oci.MediaTypeDockerManifest) req.Header.Set("Content-Type", oci.MediaTypeDockerManifest)
MakeRequest(t, req, http.StatusCreated) MakeRequest(t, req, http.StatusCreated)
pv, err = packages_model.GetVersionByNameAndVersion(db.DefaultContext, user.ID, packages_model.TypeContainer, image, tag)
assert.NoError(t, err)
assert.EqualValues(t, 1, pv.DownloadCount)
}) })
t.Run("HeadManifest", func(t *testing.T) { t.Run("HeadManifest", func(t *testing.T) {
@@ -353,8 +330,7 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, untaggedManifestDigest, pd.Version.Version) assert.Equal(t, untaggedManifestDigest, pd.Version.Version)
assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) assert.False(t, has(pd.Properties, container_module.PropertyManifestTagged))
assert.False(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
@@ -386,10 +362,18 @@ func TestPackageContainer(t *testing.T) {
assert.Nil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, image, pd.Package.Name) assert.Equal(t, image, pd.Package.Name)
assert.Equal(t, multiTag, pd.Version.Version) assert.Equal(t, multiTag, pd.Version.Version)
assert.ElementsMatch(t, []string{strings.ToLower(user.LowerName + "/" + image)}, getAllByName(pd.PackageProperties, container_module.PropertyRepository)) assert.True(t, has(pd.Properties, container_module.PropertyManifestTagged))
assert.True(t, has(pd.VersionProperties, container_module.PropertyManifestTagged))
assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.VersionProperties, container_module.PropertyManifestReference)) getAllByName := func(l packages_model.PackagePropertyList, name string) []string {
values := make([]string, 0, len(l))
for _, pp := range l {
if pp.Name == name {
values = append(values, pp.Value)
}
}
return values
}
assert.ElementsMatch(t, []string{manifestDigest, untaggedManifestDigest}, getAllByName(pd.Properties, container_module.PropertyManifestReference))
assert.IsType(t, &container_module.Metadata{}, pd.Metadata) assert.IsType(t, &container_module.Metadata{}, pd.Metadata)
metadata := pd.Metadata.(*container_module.Metadata) metadata := pd.Metadata.(*container_module.Metadata)
@@ -544,56 +528,4 @@ func TestPackageContainer(t *testing.T) {
}) })
}) })
} }
t.Run("OwnerNameChange", func(t *testing.T) {
defer PrintCurrentTest(t)()
checkCatalog := func(owner string) func(t *testing.T) {
return func(t *testing.T) {
defer PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%sv2/_catalog", setting.AppURL))
addTokenAuthHeader(req, userToken)
resp := MakeRequest(t, req, http.StatusOK)
type RepositoryList struct {
Repositories []string `json:"repositories"`
}
repoList := &RepositoryList{}
DecodeJSON(t, resp, &repoList)
assert.Len(t, repoList.Repositories, len(images))
names := make([]string, 0, len(images))
for _, image := range images {
names = append(names, strings.ToLower(owner+"/"+image))
}
assert.ElementsMatch(t, names, repoList.Repositories)
}
}
t.Run(fmt.Sprintf("Catalog[%s]", user.LowerName), checkCatalog(user.LowerName))
session := loginUser(t, user.Name)
newOwnerName := "newUsername"
req := NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": GetCSRF(t, session, "/user/settings"),
"name": newOwnerName,
"email": "user2@example.com",
"language": "en-US",
})
session.MakeRequest(t, req, http.StatusSeeOther)
t.Run(fmt.Sprintf("Catalog[%s]", newOwnerName), checkCatalog(newOwnerName))
req = NewRequestWithValues(t, "POST", "/user/settings", map[string]string{
"_csrf": GetCSRF(t, session, "/user/settings"),
"name": user.Name,
"email": "user2@example.com",
"language": "en-US",
})
session.MakeRequest(t, req, http.StatusSeeOther)
})
} }

View File

@@ -42,6 +42,7 @@ func TestPackageGeneric(t *testing.T) {
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, pd.SemVer)
assert.Nil(t, pd.Metadata) assert.Nil(t, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version) assert.Equal(t, packageVersion, pd.Version.Version)

View File

@@ -85,9 +85,9 @@ func TestPackageNpm(t *testing.T) {
assert.IsType(t, &npm.Metadata{}, pd.Metadata) assert.IsType(t, &npm.Metadata{}, pd.Metadata)
assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageName, pd.Package.Name)
assert.Equal(t, packageVersion, pd.Version.Version) assert.Equal(t, packageVersion, pd.Version.Version)
assert.Len(t, pd.VersionProperties, 1) assert.Len(t, pd.Properties, 1)
assert.Equal(t, npm.TagProperty, pd.VersionProperties[0].Name) assert.Equal(t, npm.TagProperty, pd.Properties[0].Name)
assert.Equal(t, packageTag, pd.VersionProperties[0].Value) assert.Equal(t, packageTag, pd.Properties[0].Value)
pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -24,16 +24,9 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func addNuGetAPIKeyHeader(request *http.Request, token string) *http.Request {
request.Header.Set("X-NuGet-ApiKey", token)
return request
}
func TestPackageNuGet(t *testing.T) { func TestPackageNuGet(t *testing.T) {
defer prepareTestEnv(t)() defer prepareTestEnv(t)()
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
token := getUserToken(t, user.Name)
packageName := "test.package" packageName := "test.package"
packageVersion := "1.0.3" packageVersion := "1.0.3"
@@ -67,10 +60,6 @@ func TestPackageNuGet(t *testing.T) {
req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
req = AddBasicAuthHeader(req, user.Name) req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
req = addNuGetAPIKeyHeader(req, token)
resp := MakeRequest(t, req, http.StatusOK) resp := MakeRequest(t, req, http.StatusOK)
var result nuget.ServiceIndexResponse var result nuget.ServiceIndexResponse
@@ -133,7 +122,7 @@ func TestPackageNuGet(t *testing.T) {
req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)) req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
req = AddBasicAuthHeader(req, user.Name) req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusConflict) MakeRequest(t, req, http.StatusBadRequest)
}) })
t.Run("SymbolPackage", func(t *testing.T) { t.Run("SymbolPackage", func(t *testing.T) {
@@ -219,7 +208,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage")) req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
req = AddBasicAuthHeader(req, user.Name) req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusConflict) MakeRequest(t, req, http.StatusBadRequest)
}) })
}) })
@@ -363,7 +352,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, packageVersion)) req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, packageVersion))
req = AddBasicAuthHeader(req, user.Name) req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusNoContent) MakeRequest(t, req, http.StatusOK)
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet) pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -105,11 +105,7 @@ func doAPICreateCommitStatus(ctx APITestContext, commitID string, status api.Com
} }
} }
func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) { func TestPullCreate_EmptyChangesWithCommits(t *testing.T) {
// Merge must continue if commits SHA are different, even if content is same
// Reason: gitflow and merging master back into develop, where is high possiblity, there are no changes
// but just commit saying "Merge branch". And this meta commit can be also tagged,
// so we need to have this meta commit also in develop branch.
onGiteaRun(t, func(t *testing.T, u *url.URL) { onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1") session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1") testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
@@ -130,28 +126,6 @@ func TestPullCreate_EmptyChangesWithDifferentCommits(t *testing.T) {
doc := NewHTMLParser(t, resp.Body) doc := NewHTMLParser(t, resp.Body)
text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) text := strings.TrimSpace(doc.doc.Find(".merge-section").Text())
assert.Contains(t, text, "This pull request can be merged automatically.") assert.Contains(t, text, "This branch is equal with the target branch.")
})
}
func TestPullCreate_EmptyChangesWithSameCommits(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1")
testRepoFork(t, session, "user2", "repo1", "user1", "repo1")
testCreateBranch(t, session, "user1", "repo1", "branch/master", "status1", http.StatusSeeOther)
url := path.Join("user1", "repo1", "compare", "master...status1")
req := NewRequestWithValues(t, "POST", url,
map[string]string{
"_csrf": GetCSRF(t, session, url),
"title": "pull request from status1",
},
)
session.MakeRequest(t, req, http.StatusSeeOther)
req = NewRequest(t, "GET", "/user1/repo1/pulls/1")
resp := session.MakeRequest(t, req, http.StatusOK)
doc := NewHTMLParser(t, resp.Body)
text := strings.TrimSpace(doc.doc.Find(".merge-section").Text())
assert.Contains(t, text, "This branch is already included in the target branch. There is nothing to merge.")
}) })
} }

View File

@@ -92,12 +92,12 @@ func init() {
// TableIndices implements xorm's TableIndices interface // TableIndices implements xorm's TableIndices interface
func (a *Action) TableIndices() []*schemas.Index { func (a *Action) TableIndices() []*schemas.Index {
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType) actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted") actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex} return []*schemas.Index{actUserIndex, repoIndex}
} }

View File

@@ -6,6 +6,7 @@ package auth
import ( import (
"context" "context"
"encoding/base32"
"fmt" "fmt"
"strings" "strings"
@@ -19,14 +20,14 @@ import (
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error. // ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
type ErrWebAuthnCredentialNotExist struct { type ErrWebAuthnCredentialNotExist struct {
ID int64 ID int64
CredentialID []byte CredentialID string
} }
func (err ErrWebAuthnCredentialNotExist) Error() string { func (err ErrWebAuthnCredentialNotExist) Error() string {
if len(err.CredentialID) == 0 { if err.CredentialID == "" {
return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID) return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID)
} }
return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID) return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %s]", err.CredentialID)
} }
// IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist. // IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist.
@@ -42,7 +43,7 @@ type WebAuthnCredential struct {
Name string Name string
LowerName string `xorm:"unique(s)"` LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"` UserID int64 `xorm:"INDEX unique(s)"`
CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` CredentialID string `xorm:"INDEX VARCHAR(410)"`
PublicKey []byte PublicKey []byte
AttestationType string AttestationType string
AAGUID []byte AAGUID []byte
@@ -93,8 +94,9 @@ type WebAuthnCredentialList []*WebAuthnCredential
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential { func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
creds := make([]webauthn.Credential, 0, len(list)) creds := make([]webauthn.Credential, 0, len(list))
for _, cred := range list { for _, cred := range list {
credID, _ := base32.HexEncoding.DecodeString(cred.CredentialID)
creds = append(creds, webauthn.Credential{ creds = append(creds, webauthn.Credential{
ID: cred.CredentialID, ID: credID,
PublicKey: cred.PublicKey, PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType, AttestationType: cred.AttestationType,
Authenticator: webauthn.Authenticator{ Authenticator: webauthn.Authenticator{
@@ -162,11 +164,11 @@ func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) {
} }
// GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID // GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID
func GetWebAuthnCredentialByCredID(userID int64, credID []byte) (*WebAuthnCredential, error) { func GetWebAuthnCredentialByCredID(userID int64, credID string) (*WebAuthnCredential, error) {
return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID) return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID)
} }
func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID []byte) (*WebAuthnCredential, error) { func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID string) (*WebAuthnCredential, error) {
cred := new(WebAuthnCredential) cred := new(WebAuthnCredential)
if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil { if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil {
return nil, err return nil, err
@@ -185,7 +187,7 @@ func createCredential(ctx context.Context, userID int64, name string, cred *weba
c := &WebAuthnCredential{ c := &WebAuthnCredential{
UserID: userID, UserID: userID,
Name: name, Name: name,
CredentialID: cred.ID, CredentialID: base32.HexEncoding.EncodeToString(cred.ID),
PublicKey: cred.PublicKey, PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType, AttestationType: cred.AttestationType,
AAGUID: cred.Authenticator.AAGUID, AAGUID: cred.Authenticator.AAGUID,

View File

@@ -5,6 +5,7 @@
package auth package auth
import ( import (
"encoding/base32"
"testing" "testing"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
@@ -60,7 +61,9 @@ func TestCreateCredential(t *testing.T) {
res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")}) res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "WebAuthn Created Credential", res.Name) assert.Equal(t, "WebAuthn Created Credential", res.Name)
assert.Equal(t, []byte("Test"), res.CredentialID) bs, err := base32.HexEncoding.DecodeString(res.CredentialID)
assert.NoError(t, err)
assert.Equal(t, []byte("Test"), bs)
unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1}) unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
} }

View File

@@ -1,34 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package db
import (
"context"
"code.gitea.io/gitea/modules/setting"
)
// IterateObjects iterate all the Bean object
func IterateObjects[Object any](ctx context.Context, f func(repo *Object) error) error {
var start int
batchSize := setting.Database.IterateBufferSize
sess := GetEngine(ctx)
for {
repos := make([]*Object, 0, batchSize)
if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
return err
}
if len(repos) == 0 {
return nil
}
start += len(repos)
for _, repo := range repos {
if err := f(repo); err != nil {
return err
}
}
}
}

View File

@@ -278,6 +278,29 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
return committer.Commit() return committer.Commit()
} }
// IterateLFS iterates lfs object
func IterateLFS(f func(mo *LFSMetaObject) error) error {
var start int
const batchSize = 100
e := db.GetEngine(db.DefaultContext)
for {
mos := make([]*LFSMetaObject, 0, batchSize)
if err := e.Limit(batchSize, start).Find(&mos); err != nil {
return err
}
if len(mos) == 0 {
return nil
}
start += len(mos)
for _, mo := range mos {
if err := f(mo); err != nil {
return err
}
}
}
}
// CopyLFS copies LFS data from one repo to another // CopyLFS copies LFS data from one repo to another
func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error { func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error {
var lfsObjects []*LFSMetaObject var lfsObjects []*LFSMetaObject

View File

@@ -9,7 +9,6 @@ import (
"fmt" "fmt"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
@@ -223,46 +222,6 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
return nil return nil
} }
func (issues IssueList) getProjectIDs() []int64 {
ids := make(map[int64]struct{}, len(issues))
for _, issue := range issues {
projectID := issue.ProjectID()
if _, ok := ids[projectID]; !ok {
ids[projectID] = struct{}{}
}
}
return container.KeysInt64(ids)
}
func (issues IssueList) loadProjects(ctx context.Context) error {
projectIDs := issues.getProjectIDs()
if len(projectIDs) == 0 {
return nil
}
projectMaps := make(map[int64]*project_model.Project, len(projectIDs))
left := len(projectIDs)
for left > 0 {
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
err := db.GetEngine(ctx).
In("id", projectIDs[:limit]).
Find(&projectMaps)
if err != nil {
return err
}
left -= limit
projectIDs = projectIDs[limit:]
}
for _, issue := range issues {
issue.Project = projectMaps[issue.ProjectID()]
}
return nil
}
func (issues IssueList) loadAssignees(ctx context.Context) error { func (issues IssueList) loadAssignees(ctx context.Context) error {
if len(issues) == 0 { if len(issues) == 0 {
return nil return nil
@@ -536,10 +495,6 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err) return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err)
} }
if err := issues.loadProjects(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadProjects: %v", err)
}
if err := issues.loadAssignees(ctx); err != nil { if err := issues.loadAssignees(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err) return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err)
} }

View File

@@ -124,17 +124,6 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64
func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error {
oldProjectID := issue.projectID(ctx) oldProjectID := issue.projectID(ctx)
// Only check if we add a new project and not remove it.
if newProjectID > 0 {
newProject, err := project_model.GetProjectByID(ctx, newProjectID)
if err != nil {
return err
}
if newProject.RepoID != issue.RepoID {
return fmt.Errorf("issue's repository is not the same as project's repository")
}
}
if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil { if _, err := db.GetEngine(ctx).Where("project_issue.issue_id=?", issue.ID).Delete(&project_model.ProjectIssue{}); err != nil {
return err return err
} }

View File

@@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -108,7 +107,6 @@ func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
counts, _ := CountIssuesByRepo(&IssuesOptions{ counts, _ := CountIssuesByRepo(&IssuesOptions{
RepoID: repoID, RepoID: repoID,
LabelIDs: []int64{labelID}, LabelIDs: []int64{labelID},
IsClosed: util.OptionalBoolFalse,
}) })
for _, count := range counts { for _, count := range counts {

View File

@@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -125,11 +124,6 @@ func NewMilestone(m *Milestone) (err error) {
return committer.Commit() return committer.Commit()
} }
// HasMilestoneByRepoID returns if the milestone exists in the repository.
func HasMilestoneByRepoID(ctx context.Context, repoID, id int64) (bool, error) {
return db.GetEngine(ctx).ID(id).Where("repo_id=?", repoID).Exist(new(Milestone))
}
// GetMilestoneByRepoID returns the milestone in a repository. // GetMilestoneByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) { func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) {
m := new(Milestone) m := new(Milestone)
@@ -362,11 +356,7 @@ func (opts GetMilestonesOption) toCond() builder.Cond {
} }
if len(opts.Name) != 0 { if len(opts.Name) != 0 {
if setting.Database.UseSQLite3 { cond = cond.And(builder.Like{"name", opts.Name})
cond = cond.And(builder.Like{"UPPER(name)", util.ToUpperASCII(opts.Name)})
} else {
cond = cond.And(builder.Like{"UPPER(name)", strings.ToUpper(opts.Name)})
}
} }
return cond return cond

View File

@@ -122,7 +122,6 @@ const (
PullRequestStatusManuallyMerged PullRequestStatusManuallyMerged
PullRequestStatusError PullRequestStatusError
PullRequestStatusEmpty PullRequestStatusEmpty
PullRequestStatusAncestor
) )
// PullRequestFlow the flow of pull request // PullRequestFlow the flow of pull request
@@ -424,11 +423,6 @@ func (pr *PullRequest) IsEmpty() bool {
return pr.Status == PullRequestStatusEmpty return pr.Status == PullRequestStatusEmpty
} }
// IsAncestor returns true if the Head Commit of this PR is an ancestor of the Base Commit
func (pr *PullRequest) IsAncestor() bool {
return pr.Status == PullRequestStatusAncestor
}
// SetMerged sets a pull request to merged and closes the corresponding issue // SetMerged sets a pull request to merged and closes the corresponding issue
func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
if pr.HasMerged { if pr.HasMerged {

View File

@@ -19,7 +19,6 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -475,35 +474,6 @@ func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, co
return review, comm, committer.Commit() return review, comm, committer.Commit()
} }
// GetReviewOptions represent filter options for GetReviews
type GetReviewOptions struct {
IssueID int64
ReviewerID int64
Dismissed util.OptionalBool
}
// GetReviews return reviews based on GetReviewOptions
func GetReviews(ctx context.Context, opts *GetReviewOptions) ([]*Review, error) {
if opts == nil {
return nil, fmt.Errorf("opts are nil")
}
sess := db.GetEngine(ctx)
if opts.IssueID != 0 {
sess = sess.Where("issue_id=?", opts.IssueID)
}
if opts.ReviewerID != 0 {
sess = sess.Where("reviewer_id=?", opts.ReviewerID)
}
if !opts.Dismissed.IsNone() {
sess = sess.Where("dismissed=?", opts.Dismissed.IsTrue())
}
reviews := make([]*Review, 0, 4)
return reviews, sess.Find(&reviews)
}
// GetReviewersByIssueID gets the latest review of each reviewer for a pull request // GetReviewersByIssueID gets the latest review of each reviewer for a pull request
func GetReviewersByIssueID(issueID int64) ([]*Review, error) { func GetReviewersByIssueID(issueID int64) ([]*Review, error) {
reviews := make([]*Review, 0, 10) reviews := make([]*Review, 0, 10)

View File

@@ -1,9 +0,0 @@
-
id: 1
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
-
id: 2
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR="
-
id: 4
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="

View File

@@ -1,31 +0,0 @@
-
id: 1
lower_name: "u2fkey-correctly-migrated"
name: "u2fkey-correctly-migrated"
user_id: 1
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'fido-u2f'
sign_count: 1
clone_warning: false
-
id: 2
lower_name: "non-u2f-key"
name: "non-u2f-key"
user_id: 1
credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR"
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'none'
sign_count: 1
clone_warning: false
-
id: 4
lower_name: "packed-key"
name: "packed-key"
user_id: 1
credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
attestation_type: 'fido-u2f'
sign_count: 1
clone_warning: false

View File

@@ -56,9 +56,6 @@ type Version struct {
Version int64 Version int64
} }
// Use noopMigration when there is a migration that has been no-oped
var noopMigration = func(_ *xorm.Engine) error { return nil }
// This is a sequence of migrations. Add new migrations to the bottom of the list. // This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, remove it from the top of the list and // If you want to "retire" a migration, remove it from the top of the list and
// update minDBVersion accordingly // update minDBVersion accordingly
@@ -354,7 +351,7 @@ var migrations = []Migration{
// v198 -> v199 // v198 -> v199
NewMigration("Add issue content history table", addTableIssueContentHistory), NewMigration("Add issue content history table", addTableIssueContentHistory),
// v199 -> v200 // v199 -> v200
NewMigration("No-op (remote version is using AppState now)", noopMigration), NewMigration("No-op (remote version is using AppState now)", addRemoteVersionTableNoop),
// v200 -> v201 // v200 -> v201
NewMigration("Add table app_state", addTableAppState), NewMigration("Add table app_state", addTableAppState),
// v201 -> v202 // v201 -> v202
@@ -391,21 +388,7 @@ var migrations = []Migration{
// v215 -> v216 // v215 -> v216
NewMigration("allow to view files in PRs", addReviewViewedFiles), NewMigration("allow to view files in PRs", addReviewViewedFiles),
// v216 -> v217 // v216 -> v217
NewMigration("No-op (Improve Action table indices v1)", noopMigration), NewMigration("Improve Action table indices", improveActionTableIndices),
// v217 -> v218
NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText),
// v218 -> v219
NewMigration("Improve Action table indices v2", improveActionTableIndices),
// v219 -> v220
NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror),
// v220 -> v221
NewMigration("Add container repository property", addContainerRepositoryProperty),
// v221 -> v222
NewMigration("Store WebAuthentication CredentialID as bytes and increase size to at least 1024", storeWebauthnCredentialIDAsBytes),
// v222 -> v223
NewMigration("Drop old CredentialID column", dropOldCredentialIDColumn),
// v223 -> v224
NewMigration("Rename CredentialIDBytes column to CredentialID", renameCredentialIDBytes),
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

View File

@@ -4,4 +4,11 @@
package migrations package migrations
// 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. import (
"xorm.io/xorm"
)
func addRemoteVersionTableNoop(x *xorm.Engine) error {
// 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.
return nil
}

View File

@@ -4,5 +4,64 @@
package migrations package migrations
// This migration added non-ideal indices to the action table which on larger datasets slowed things down import (
// it has been superceded by v218.go "code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type improveActionTableIndicesAction struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 // Receiver user id.
OpType int
ActUserID int64 // Action user id.
RepoID int64
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName sets the name of this table
func (a *improveActionTableIndicesAction) TableName() string {
return "action"
}
// TableIndices implements xorm's TableIndices interface
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
repoIndex := schemas.NewIndex("r_c_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex}
}
func improveActionTableIndices(x *xorm.Engine) error {
{
type Action struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"INDEX"` // Receiver user id.
OpType int
ActUserID int64 `xorm:"INDEX"` // Action user id.
RepoID int64 `xorm:"INDEX"`
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"INDEX NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"INDEX NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}
if err := x.Sync2(&Action{}); err != nil {
return err
}
if err := x.DropIndexes(&Action{}); err != nil {
return err
}
}
return x.Sync2(&improveActionTableIndicesAction{})
}

View File

@@ -1,26 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
)
func alterHookTaskTextFieldsToLongText(x *xorm.Engine) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if setting.Database.UseMySQL {
if _, err := sess.Exec("ALTER TABLE `hook_task` CHANGE `payload_content` `payload_content` LONGTEXT, CHANGE `request_content` `request_content` LONGTEXT, change `response_content` `response_content` LONGTEXT"); err != nil {
return err
}
}
return sess.Commit()
}

View File

@@ -1,46 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type improveActionTableIndicesAction struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 // Receiver user id.
OpType int
ActUserID int64 // Action user id.
RepoID int64
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName sets the name of this table
func (*improveActionTableIndicesAction) TableName() string {
return "action"
}
// TableIndices implements xorm's TableIndices interface
func (*improveActionTableIndicesAction) TableIndices() []*schemas.Index {
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
return []*schemas.Index{actUserIndex, repoIndex}
}
func improveActionTableIndices(x *xorm.Engine) error {
return x.Sync2(&improveActionTableIndicesAction{})
}

View File

@@ -1,31 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"time"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func addSyncOnCommitColForPushMirror(x *xorm.Engine) error {
type PushMirror struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX"`
Repo *repo.Repository `xorm:"-"`
RemoteName string
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`
Interval time.Duration
CreatedUnix timeutil.TimeStamp `xorm:"created"`
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
LastError string `xorm:"text"`
}
return x.Sync2(new(PushMirror))
}

View File

@@ -1,28 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
packages_model "code.gitea.io/gitea/models/packages"
container_module "code.gitea.io/gitea/modules/packages/container"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
func addContainerRepositoryProperty(x *xorm.Engine) (err error) {
switch x.Dialect().URI().DBType {
case schemas.SQLITE:
_, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
case schemas.MSSQL:
_, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name + '/' + p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
default:
_, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
}
return err
}

View File

@@ -1,75 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"encoding/base32"
"fmt"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func storeWebauthnCredentialIDAsBytes(x *xorm.Engine) error {
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
// Note the lack of INDEX here - these will be created once the column is renamed in v223.go
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync2(&webauthnCredential{}); err != nil {
return err
}
var start int
creds := make([]*webauthnCredential, 0, 50)
for {
err := x.Select("id, credential_id").OrderBy("id").Limit(50, start).Find(&creds)
if err != nil {
return err
}
err = func() error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("unable to allow start session. Error: %w", err)
}
for _, cred := range creds {
cred.CredentialIDBytes, err = base32.HexEncoding.DecodeString(cred.CredentialID)
if err != nil {
return fmt.Errorf("unable to parse credential id %s for credential[%d]: %w", cred.CredentialID, cred.ID, err)
}
count, err := sess.ID(cred.ID).Cols("credential_id_bytes").Update(cred)
if count != 1 || err != nil {
return fmt.Errorf("unable to update credential id bytes for credential[%d]: %d,%w", cred.ID, count, err)
}
}
return sess.Commit()
}()
if err != nil {
return err
}
if len(creds) < 50 {
break
}
start += 50
creds = creds[:0]
}
return nil
}

View File

@@ -1,65 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"encoding/base32"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_storeWebauthnCredentialIDAsBytes(t *testing.T) {
// Create webauthnCredential table
type WebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
}
type ExpectedWebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
CredentialID string // CredentialID is at most 1023 bytes as per spec released 20 July 2022
}
type ConvertedWebauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
}
// Prepare and load the testing database
x, deferable := prepareTestEnv(t, 0, new(WebauthnCredential), new(ExpectedWebauthnCredential))
defer deferable()
if x == nil || t.Failed() {
return
}
if err := storeWebauthnCredentialIDAsBytes(x); err != nil {
assert.NoError(t, err)
return
}
expected := []ExpectedWebauthnCredential{}
if err := x.Table("expected_webauthn_credential").Asc("id").Find(&expected); !assert.NoError(t, err) {
return
}
got := []ConvertedWebauthnCredential{}
if err := x.Table("webauthn_credential").Select("id, credential_id_bytes").Asc("id").Find(&got); !assert.NoError(t, err) {
return
}
for i, e := range expected {
credIDBytes, _ := base32.HexEncoding.DecodeString(e.CredentialID)
assert.Equal(t, credIDBytes, got[i].CredentialIDBytes)
}
}

View File

@@ -1,64 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"context"
"fmt"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func dropOldCredentialIDColumn(x *xorm.Engine) error {
// This migration maybe rerun so that we should check if it has been run
credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
if err != nil {
return err
}
if !credentialIDExist {
// Column is already non-extant
return nil
}
credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
if err != nil {
return err
}
if !credentialIDBytesExists {
// looks like 221 hasn't properly run
return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
}
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID string `xorm:"INDEX VARCHAR(410)"`
// Note the lack of the INDEX on CredentialIDBytes - we will add this in v223.go
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
if err := x.Sync2(&webauthnCredential{}); err != nil {
return err
}
// Drop the old credential ID
sess := x.NewSession()
defer sess.Close()
if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
return fmt.Errorf("unable to drop old credentialID column: %w", err)
}
return sess.Commit()
}

View File

@@ -1,103 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package migrations
import (
"context"
"fmt"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func renameCredentialIDBytes(x *xorm.Engine) error {
// This migration maybe rerun so that we should check if it has been run
credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
if err != nil {
return err
}
if credentialIDExist {
credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
if err != nil {
return err
}
if !credentialIDBytesExists {
return nil
}
}
err = func() error {
// webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
// Note the lack of INDEX here
CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := sess.Sync2(new(webauthnCredential)); err != nil {
return fmt.Errorf("error on Sync2: %v", err)
}
if credentialIDExist {
// if both errors and message exist, drop message at first
if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
return err
}
}
switch {
case setting.Database.UseMySQL:
if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil {
return err
}
case setting.Database.UseMSSQL:
if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil {
return err
}
default:
if _, err := sess.Exec("ALTER TABLE `webauthn_credential` RENAME COLUMN credential_id_bytes TO credential_id"); err != nil {
return err
}
}
return sess.Commit()
}()
if err != nil {
return err
}
// Create webauthnCredential table
type webauthnCredential struct {
ID int64 `xorm:"pk autoincr"`
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
PublicKey []byte
AttestationType string
AAGUID []byte
SignCount uint32 `xorm:"BIGINT"`
CloneWarning bool
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}
return x.Sync2(&webauthnCredential{})
}

View File

@@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
user_model "code.gitea.io/gitea/models/user"
container_module "code.gitea.io/gitea/modules/packages/container" container_module "code.gitea.io/gitea/modules/packages/container"
"xorm.io/builder" "xorm.io/builder"
@@ -211,7 +210,6 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
return pvs, count, err return pvs, count, err
} }
// SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) { func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
var cond builder.Cond = builder.Eq{ var cond builder.Cond = builder.Eq{
"package_version.is_internal": true, "package_version.is_internal": true,
@@ -227,37 +225,3 @@ func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([
Where(cond). Where(cond).
Find(&pfs) Find(&pfs)
} }
// GetRepositories gets a sorted list of all repositories
func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) {
var cond builder.Cond = builder.Eq{
"package.type": packages.TypeContainer,
"package_property.ref_type": packages.PropertyTypePackage,
"package_property.name": container_module.PropertyRepository,
}
cond = cond.And(builder.Exists(
builder.
Select("package_version.id").
Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))).
From("package_version"),
))
if last != "" {
cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)})
}
cond = cond.And(user_model.BuildCanSeeUserCondition(actor))
sess := db.GetEngine(ctx).
Table("package").
Select("package_property.value").
Join("INNER", "user", "`user`.id = package.owner_id").
Join("INNER", "package_property", "package_property.ref_id = package.id").
Where(cond).
Asc("package_property.value").
Limit(n)
repositories := make([]string, 0, n)
return repositories, sess.Find(&repositories)
}

View File

@@ -40,16 +40,15 @@ func (l PackagePropertyList) GetByName(name string) string {
// PackageDescriptor describes a package // PackageDescriptor describes a package
type PackageDescriptor struct { type PackageDescriptor struct {
Package *Package Package *Package
Owner *user_model.User Owner *user_model.User
Repository *repo_model.Repository Repository *repo_model.Repository
Version *PackageVersion Version *PackageVersion
SemVer *version.Version SemVer *version.Version
Creator *user_model.User Creator *user_model.User
PackageProperties PackagePropertyList Properties PackagePropertyList
VersionProperties PackagePropertyList Metadata interface{}
Metadata interface{} Files []*PackageFileDescriptor
Files []*PackageFileDescriptor
} }
// PackageFileDescriptor describes a package file // PackageFileDescriptor describes a package file
@@ -103,10 +102,6 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err return nil, err
} }
} }
pps, err := GetProperties(ctx, PropertyTypePackage, p.ID)
if err != nil {
return nil, err
}
pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID) pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -157,16 +152,15 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
} }
return &PackageDescriptor{ return &PackageDescriptor{
Package: p, Package: p,
Owner: o, Owner: o,
Repository: repository, Repository: repository,
Version: pv, Version: pv,
SemVer: semVer, SemVer: semVer,
Creator: creator, Creator: creator,
PackageProperties: PackagePropertyList(pps), Properties: PackagePropertyList(pvps),
VersionProperties: PackagePropertyList(pvps), Metadata: metadata,
Metadata: metadata, Files: pfds,
Files: pfds,
}, nil }, nil
} }

View File

@@ -131,12 +131,6 @@ func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) {
return p, nil return p, nil
} }
// DeletePackageByID deletes a package by id
func DeletePackageByID(ctx context.Context, packageID int64) error {
_, err := db.GetEngine(ctx).ID(packageID).Delete(&Package{})
return err
}
// SetRepositoryLink sets the linked repository // SetRepositoryLink sets the linked repository
func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error { func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
_, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID}) _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID})
@@ -198,20 +192,21 @@ func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([]
Find(&ps) Find(&ps)
} }
// FindUnreferencedPackages gets all packages without associated versions // DeletePackagesIfUnreferenced deletes a package if there are no associated versions
func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) { func DeletePackagesIfUnreferenced(ctx context.Context) error {
in := builder. in := builder.
Select("package.id"). Select("package.id").
From("package"). From("package").
LeftJoin("package_version", "package_version.package_id = package.id"). LeftJoin("package_version", "package_version.package_id = package.id").
Where(builder.Expr("package_version.id IS NULL")) Where(builder.Expr("package_version.id IS NULL"))
ps := make([]*Package, 0, 10) _, err := db.GetEngine(ctx).
return ps, db.GetEngine(ctx).
// double select workaround for MySQL // double select workaround for MySQL
// https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition // https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition
Where(builder.In("package.id", builder.Select("id").From(in, "temp"))). Where(builder.In("package.id", builder.Select("id").From(in, "temp"))).
Find(&ps) Delete(&Package{})
return err
} }
// HasOwnerPackages tests if a user/org has packages // HasOwnerPackages tests if a user/org has packages

View File

@@ -21,11 +21,9 @@ const (
PropertyTypeVersion PropertyType = iota // 0 PropertyTypeVersion PropertyType = iota // 0
// PropertyTypeFile means the reference is a package file // PropertyTypeFile means the reference is a package file
PropertyTypeFile // 1 PropertyTypeFile // 1
// PropertyTypePackage means the reference is a package
PropertyTypePackage // 2
) )
// PackageProperty represents a property of a package, version or file // PackageProperty represents a property of a package version or file
type PackageProperty struct { type PackageProperty struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
RefType PropertyType `xorm:"INDEX NOT NULL"` RefType PropertyType `xorm:"INDEX NOT NULL"`
@@ -70,9 +68,3 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{}) _, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
return err return err
} }
// DeletePropertyByName deletes properties by name
func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
_, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
return err
}

View File

@@ -86,13 +86,7 @@ func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mo
// FIXME: do cross-comparison so reduce deletions and additions to the minimum? // FIXME: do cross-comparison so reduce deletions and additions to the minimum?
func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) { func refreshAccesses(ctx context.Context, repo *repo_model.Repository, accessMap map[int64]*userAccess) (err error) {
minMode := perm.AccessModeRead minMode := perm.AccessModeRead
if err := repo.GetOwner(ctx); err != nil { if !repo.IsPrivate {
return fmt.Errorf("GetOwner: %v", err)
}
// If the repo isn't private and isn't owned by a organization,
// increase the minMode to Write.
if !repo.IsPrivate && !repo.Owner.IsOrganization() {
minMode = perm.AccessModeWrite minMode = perm.AccessModeWrite
} }

View File

@@ -226,6 +226,28 @@ func DeleteAttachmentsByRelease(releaseID int64) error {
return err return err
} }
// IterateAttachment iterates attachments; it should not be used when Gitea is servicing users.
func IterateAttachment(f func(attach *Attachment) error) error {
var start int
const batchSize = 100
for {
attachments := make([]*Attachment, 0, batchSize)
if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&attachments); err != nil {
return err
}
if len(attachments) == 0 {
return nil
}
start += len(attachments)
for _, attach := range attachments {
if err := f(attach); err != nil {
return err
}
}
}
}
// CountOrphanedAttachments returns the number of bad attachments // CountOrphanedAttachments returns the number of bad attachments
func CountOrphanedAttachments() (int64, error) { func CountOrphanedAttachments() (int64, error) {
return db.GetEngine(db.DefaultContext).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))"). return db.GetEngine(db.DefaultContext).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").

View File

@@ -153,9 +153,7 @@ func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
} }
for i := range repos { for i := range repos {
repos[i].Mirror = set[repos[i].ID] repos[i].Mirror = set[repos[i].ID]
if repos[i].Mirror != nil { repos[i].Mirror.Repo = repos[i]
repos[i].Mirror.Repo = repos[i]
}
} }
return nil return nil
} }

View File

@@ -14,12 +14,36 @@ import (
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
// IterateRepository iterate repositories
func IterateRepository(f func(repo *Repository) error) error {
var start int
batchSize := setting.Database.IterateBufferSize
sess := db.GetEngine(db.DefaultContext)
for {
repos := make([]*Repository, 0, batchSize)
if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
return err
}
if len(repos) == 0 {
return nil
}
start += len(repos)
for _, repo := range repos {
if err := f(repo); err != nil {
return err
}
}
}
}
// FindReposMapByIDs find repos as map // FindReposMapByIDs find repos as map
func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error { func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error {
return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res) return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res)

View File

@@ -107,8 +107,6 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages") setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages")
setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home")
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err) fatalTestError("storage.Init: %v\n", err)
} }

View File

@@ -9,6 +9,7 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@@ -57,7 +58,31 @@ func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session {
cond = cond.And(builder.In("visibility", opts.Visible)) cond = cond.And(builder.In("visibility", opts.Visible))
} }
cond = cond.And(BuildCanSeeUserCondition(opts.Actor)) if opts.Actor != nil {
var exprCond builder.Cond = builder.Expr("org_user.org_id = `user`.id")
// If Admin - they see all users!
if !opts.Actor.IsAdmin {
// Force visibility for privacy
var accessCond builder.Cond
if !opts.Actor.IsRestricted {
accessCond = builder.Or(
builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID}, builder.Eq{"visibility": structs.VisibleTypePrivate}))),
builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
} else {
// restricted users only see orgs they are a member of
accessCond = builder.In("id", builder.Select("org_id").From("org_user").LeftJoin("`user`", exprCond).Where(builder.And(builder.Eq{"uid": opts.Actor.ID})))
}
// Don't forget about self
accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID})
cond = cond.And(accessCond)
}
} else {
// Force visibility for privacy
// Not logged in - only public users
cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
}
if opts.UID > 0 { if opts.UID > 0 {
cond = cond.And(builder.Eq{"id": opts.UID}) cond = cond.And(builder.Eq{"id": opts.UID})
@@ -124,25 +149,24 @@ func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
return users, count, sessQuery.Find(&users) return users, count, sessQuery.Find(&users)
} }
// BuildCanSeeUserCondition creates a condition which can be used to restrict results to users/orgs the actor can see // IterateUser iterate users
func BuildCanSeeUserCondition(actor *User) builder.Cond { func IterateUser(f func(user *User) error) error {
if actor != nil { var start int
// If Admin - they see all users! batchSize := setting.Database.IterateBufferSize
if !actor.IsAdmin { for {
// Users can see an organization they are a member of users := make([]*User, 0, batchSize)
cond := builder.In("`user`.id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": actor.ID})) if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&users); err != nil {
if !actor.IsRestricted { return err
// Not-Restricted users can see public and limited users/organizations
cond = cond.Or(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
}
// Don't forget about self
return cond.Or(builder.Eq{"`user`.id": actor.ID})
} }
if len(users) == 0 {
return nil
}
start += len(users)
return nil for _, user := range users {
if err := f(user); err != nil {
return err
}
}
} }
// Force visibility for privacy
// Not logged in - only public users
return builder.In("`user`.visibility", structs.VisibleTypePublic)
} }

View File

@@ -316,45 +316,37 @@ func (u *User) GenerateEmailActivateCode(email string) string {
} }
// GetUserFollowers returns range of user's followers. // GetUserFollowers returns range of user's followers.
func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) {
sess := db.GetEngine(ctx). sess := db.GetEngine(db.DefaultContext).
Select("`user`.*").
Join("LEFT", "follow", "`user`.id=follow.user_id").
Where("follow.follow_id=?", u.ID). Where("follow.follow_id=?", u.ID).
And(isUserVisibleToViewerCond(viewer)) Join("LEFT", "follow", "`user`.id=follow.user_id")
if listOptions.Page != 0 { if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions) sess = db.SetSessionPagination(sess, &listOptions)
users := make([]*User, 0, listOptions.PageSize) users := make([]*User, 0, listOptions.PageSize)
count, err := sess.FindAndCount(&users) return users, sess.Find(&users)
return users, count, err
} }
users := make([]*User, 0, 8) users := make([]*User, 0, 8)
count, err := sess.FindAndCount(&users) return users, sess.Find(&users)
return users, count, err
} }
// GetUserFollowing returns range of user's following. // GetUserFollowing returns range of user's following.
func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) {
sess := db.GetEngine(db.DefaultContext). sess := db.GetEngine(db.DefaultContext).
Select("`user`.*").
Join("LEFT", "follow", "`user`.id=follow.follow_id").
Where("follow.user_id=?", u.ID). Where("follow.user_id=?", u.ID).
And(isUserVisibleToViewerCond(viewer)) Join("LEFT", "follow", "`user`.id=follow.follow_id")
if listOptions.Page != 0 { if listOptions.Page != 0 {
sess = db.SetSessionPagination(sess, &listOptions) sess = db.SetSessionPagination(sess, &listOptions)
users := make([]*User, 0, listOptions.PageSize) users := make([]*User, 0, listOptions.PageSize)
count, err := sess.FindAndCount(&users) return users, sess.Find(&users)
return users, count, err
} }
users := make([]*User, 0, 8) users := make([]*User, 0, 8)
count, err := sess.FindAndCount(&users) return users, sess.Find(&users)
return users, count, err
} }
// NewGitSig generates and returns the signature of given user. // NewGitSig generates and returns the signature of given user.
@@ -493,9 +485,6 @@ func (u *User) GitName() string {
// ShortName ellipses username to length // ShortName ellipses username to length
func (u *User) ShortName(length int) string { func (u *User) ShortName(length int) string {
if setting.UI.DefaultShowFullName && len(u.FullName) > 0 {
return base.EllipsisString(u.FullName, length)
}
return base.EllipsisString(u.Name, length) return base.EllipsisString(u.Name, length)
} }
@@ -1230,39 +1219,6 @@ func GetAdminUser() (*User, error) {
return &admin, nil return &admin, nil
} }
func isUserVisibleToViewerCond(viewer *User) builder.Cond {
if viewer != nil && viewer.IsAdmin {
return builder.NewCond()
}
if viewer == nil || viewer.IsRestricted {
return builder.Eq{
"`user`.visibility": structs.VisibleTypePublic,
}
}
return builder.Neq{
"`user`.visibility": structs.VisibleTypePrivate,
}.Or(
builder.In("`user`.id",
builder.
Select("`follow`.user_id").
From("follow").
Where(builder.Eq{"`follow`.follow_id": viewer.ID})),
builder.In("`user`.id",
builder.
Select("`team_user`.uid").
From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.id = `t2`.id").
Where(builder.Eq{"`t2`.uid": viewer.ID})),
builder.In("`user`.id",
builder.
Select("`team_user`.uid").
From("team_user").
Join("INNER", "`team_user` AS t2", "`team_user`.org_id = `t2`.org_id").
Where(builder.Eq{"`t2`.uid": viewer.ID})))
}
// IsUserVisibleToViewer check if viewer is able to see user profile // IsUserVisibleToViewer check if viewer is able to see user profile
func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool { func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
if viewer != nil && viewer.IsAdmin { if viewer != nil && viewer.IsAdmin {

View File

@@ -105,7 +105,7 @@ type HookTask struct {
HookID int64 HookID int64
UUID string UUID string
api.Payloader `xorm:"-"` api.Payloader `xorm:"-"`
PayloadContent string `xorm:"LONGTEXT"` PayloadContent string `xorm:"TEXT"`
EventType HookEventType EventType HookEventType
IsDelivered bool IsDelivered bool
Delivered int64 Delivered int64
@@ -113,9 +113,9 @@ type HookTask struct {
// History info. // History info.
IsSucceed bool IsSucceed bool
RequestContent string `xorm:"LONGTEXT"` RequestContent string `xorm:"TEXT"`
RequestInfo *HookRequest `xorm:"-"` RequestInfo *HookRequest `xorm:"-"`
ResponseContent string `xorm:"LONGTEXT"` ResponseContent string `xorm:"TEXT"`
ResponseInfo *HookResponse `xorm:"-"` ResponseInfo *HookResponse `xorm:"-"`
} }

View File

@@ -399,10 +399,6 @@ func CreateWebhook(ctx context.Context, w *Webhook) error {
// CreateWebhooks creates multiple web hooks // CreateWebhooks creates multiple web hooks
func CreateWebhooks(ctx context.Context, ws []*Webhook) error { func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
// xorm returns err "no element on slice when insert" for empty slices.
if len(ws) == 0 {
return nil
}
for i := 0; i < len(ws); i++ { for i := 0; i < len(ws); i++ {
ws[i].Type = strings.TrimSpace(ws[i].Type) ws[i].Type = strings.TrimSpace(ws[i].Type)
} }

View File

@@ -16,7 +16,6 @@ import (
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
@@ -269,7 +268,6 @@ func APIContexter() func(http.Handler) http.Handler {
} }
} }
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["Context"] = &ctx ctx.Data["Context"] = &ctx

View File

@@ -7,7 +7,6 @@ package context
import ( import (
"net/http" "net/http"
"strings"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -42,10 +41,6 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
if ctx.Doer.MustChangePassword { if ctx.Doer.MustChangePassword {
if ctx.Req.URL.Path != "/user/settings/change_password" { if ctx.Req.URL.Path != "/user/settings/change_password" {
if strings.HasPrefix(ctx.Req.UserAgent(), "git") {
ctx.Error(http.StatusUnauthorized, ctx.Tr("auth.must_change_password"))
return
}
ctx.Data["Title"] = ctx.Tr("auth.must_change_password") ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
if ctx.Req.URL.Path != "/user/events" { if ctx.Req.URL.Path != "/user/events" {

View File

@@ -28,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
mc "code.gitea.io/gitea/modules/cache" mc "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -224,7 +223,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
ctx.Data["TemplateLoadTimes"] = func() string { ctx.Data["TemplateLoadTimes"] = func() string {
return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms" return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
} }
if err := ctx.Render.HTML(ctx.Resp, status, string(name), templates.BaseVars().Merge(ctx.Data)); err != nil { if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil {
if status == http.StatusInternalServerError && name == base.TplName("status/500") { if status == http.StatusInternalServerError && name == base.TplName("status/500") {
ctx.PlainText(http.StatusInternalServerError, "Unable to find status/500 template") ctx.PlainText(http.StatusInternalServerError, "Unable to find status/500 template")
return return
@@ -768,7 +767,6 @@ func Contexter() func(next http.Handler) http.Handler {
} }
} }
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["CsrfToken"] = ctx.csrf.GetToken() ctx.Data["CsrfToken"] = ctx.csrf.GetToken()

View File

@@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
) )
@@ -53,30 +52,14 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
} }
if ctx.Package.Owner.IsOrganization() { if ctx.Package.Owner.IsOrganization() {
org := organization.OrgFromUser(ctx.Package.Owner)
// 1. Get user max authorize level for the org (may be none, if user is not member of the org) // 1. Get user max authorize level for the org (may be none, if user is not member of the org)
if ctx.Doer != nil { if ctx.Doer != nil {
var err error var err error
ctx.Package.AccessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID) ctx.Package.AccessMode, err = organization.OrgFromUser(ctx.Package.Owner).GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
if err != nil { if err != nil {
errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err) errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err)
return return
} }
// If access mode is less than write check every team for more permissions
if ctx.Package.AccessMode < perm.AccessModeWrite {
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
if err != nil {
errCb(http.StatusInternalServerError, "GetUserOrgTeams", err)
return
}
for _, t := range teams {
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
if ctx.Package.AccessMode < perm {
ctx.Package.AccessMode = perm
}
}
}
} }
// 2. If authorize level is none, check if org is visible to user // 2. If authorize level is none, check if org is visible to user
if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) { if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {

View File

@@ -942,10 +942,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
if err != nil { if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound("GetTagCommit", err)
return
}
ctx.ServerError("GetTagCommit", err) ctx.ServerError("GetTagCommit", err)
return return
} }

View File

@@ -58,29 +58,6 @@ func checkUserEmail(ctx context.Context, logger log.Logger, _ bool) error {
return nil return nil
} }
// From time to time Gitea makes changes to the reserved usernames and which symbols
// are allowed for various reasons. This check helps with detecting users that, according
// to our reserved names, don't have a valid username.
func checkUserName(ctx context.Context, logger log.Logger, _ bool) error {
var invalidUserCount int64
if err := iterateUserAccounts(ctx, func(u *user.User) error {
if err := user.IsUsableUsername(u.Name); err != nil {
invalidUserCount++
logger.Warn("User[id=%d] does not have a valid username: %v", u.ID, err)
}
return nil
}); err != nil {
return fmt.Errorf("iterateUserAccounts: %v", err)
}
if invalidUserCount == 0 {
logger.Info("All users have a valid username.")
} else {
logger.Warn("%d user(s) have a non-valid username.", invalidUserCount)
}
return nil
}
func init() { func init() {
Register(&Check{ Register(&Check{
Title: "Check if users has an valid email address", Title: "Check if users has an valid email address",
@@ -89,11 +66,4 @@ func init() {
Run: checkUserEmail, Run: checkUserEmail,
Priority: 9, Priority: 9,
}) })
Register(&Check{
Title: "Check if users have a valid username",
Name: "check-user-names",
IsDefault: false,
Run: checkUserName,
Priority: 9,
})
} }

View File

@@ -201,10 +201,10 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"), "oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
// find stopwatches without existing user // find stopwatches without existing user
genericOrphanCheck("Orphaned Stopwatches without existing User", genericOrphanCheck("Orphaned Stopwatches without existing User",
"stopwatch", "user", "stopwatch.user_id=`user`.id"), "stopwatch", "user", "stopwatch.user_id=user.id"),
// find stopwatches without existing issue // find stopwatches without existing issue
genericOrphanCheck("Orphaned Stopwatches without existing Issue", genericOrphanCheck("Orphaned Stopwatches without existing Issue",
"stopwatch", "issue", "stopwatch.issue_id=`issue`.id"), "stopwatch", "issue", "stopwatch.issue_id=issue.id"),
) )
for _, c := range consistencyChecks { for _, c := range consistencyChecks {

View File

@@ -11,7 +11,6 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )
@@ -50,11 +49,7 @@ func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
setting.NewXORMLogService(disableConsole) setting.NewXORMLogService(disableConsole)
if err := db.InitEngine(ctx); err != nil { if err := db.InitEngine(ctx); err != nil {
return fmt.Errorf("db.InitEngine: %w", err) return fmt.Errorf("models.SetEngine: %v", err)
}
// some doctor sub-commands need to use git command
if err := git.InitOnceWithSync(ctx); err != nil {
return fmt.Errorf("git.InitOnceWithSync: %w", err)
} }
return nil return nil
} }

View File

@@ -95,47 +95,33 @@ func (c *Command) AddArguments(args ...string) *Command {
return c return c
} }
// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored. // RunOpts represents parameters to run the command
type RunOpts struct { type RunOpts struct {
Env []string Env []string
Timeout time.Duration Timeout time.Duration
UseContextTimeout bool Dir string
Dir string Stdout, Stderr io.Writer
Stdout, Stderr io.Writer Stdin io.Reader
Stdin io.Reader PipelineFunc func(context.Context, context.CancelFunc) error
PipelineFunc func(context.Context, context.CancelFunc) error
}
func commonBaseEnvs() []string {
// at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
envs := []string{
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
}
// some environment variables should be passed to git command
passThroughEnvKeys := []string{
"GNUPGHOME", // git may call gnupg to do commit signing
}
for _, key := range passThroughEnvKeys {
if val, ok := os.LookupEnv(key); ok {
envs = append(envs, key+"="+val)
}
}
return envs
} }
// CommonGitCmdEnvs returns the common environment variables for a "git" command. // CommonGitCmdEnvs returns the common environment variables for a "git" command.
func CommonGitCmdEnvs() []string { func CommonGitCmdEnvs() []string {
return append(commonBaseEnvs(), []string{ // at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it
"LC_ALL=" + DefaultLocale, return []string{
"GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3 fmt.Sprintf("LC_ALL=%s", DefaultLocale),
}...) "GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
}
} }
// CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command // CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command
func CommonCmdServEnvs() []string { func CommonCmdServEnvs() []string {
return commonBaseEnvs() return []string{
"GIT_NO_REPLACE_OBJECTS=1", // ignore replace references (https://git-scm.com/docs/git-replace)
"HOME=" + HomeDir(), // make Gitea use internal git config only, to prevent conflicts with user's git config
}
} }
// Run runs the command with the RunOpts // Run runs the command with the RunOpts
@@ -172,15 +158,7 @@ func (c *Command) Run(opts *RunOpts) error {
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), opts.Dir) desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), opts.Dir)
} }
var ctx context.Context ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
var cancel context.CancelFunc
var finished context.CancelFunc
if opts.UseContextTimeout {
ctx, cancel, finished = process.GetManager().AddContext(c.parentContext, desc)
} else {
ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
}
defer finished() defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...) cmd := exec.CommandContext(ctx, c.name, c.args...)

View File

@@ -11,8 +11,6 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"regexp"
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
@@ -127,8 +125,8 @@ func VersionInfo() string {
} }
func checkInit() error { func checkInit() error {
if setting.Git.HomePath == "" { if setting.RepoRootPath == "" {
return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules") return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
} }
if DefaultContext != nil { if DefaultContext != nil {
log.Warn("git module has been initialized already, duplicate init should be fixed") log.Warn("git module has been initialized already, duplicate init should be fixed")
@@ -138,14 +136,14 @@ func checkInit() error {
// HomeDir is the home dir for git to store the global config file used by Gitea internally // HomeDir is the home dir for git to store the global config file used by Gitea internally
func HomeDir() string { func HomeDir() string {
if setting.Git.HomePath == "" { if setting.RepoRootPath == "" {
// strict check, make sure the git module is initialized correctly. // strict check, make sure the git module is initialized correctly.
// attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users. // attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons. // for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules") log.Fatal("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly")
return "" return ""
} }
return setting.Git.HomePath return setting.RepoRootPath
} }
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments. // InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
@@ -176,15 +174,11 @@ func InitOnceWithSync(ctx context.Context) (err error) {
} }
initOnce.Do(func() { initOnce.Do(func() {
if err = InitSimple(ctx); err != nil { err = InitSimple(ctx)
if err != nil {
return return
} }
// when git works with gnupg (commit signing), there should be a stable home for gnupg commands
if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
_ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
}
// Since git wire protocol has been released from git v2.18 // Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
@@ -211,7 +205,7 @@ func InitOnceWithSync(ctx context.Context) (err error) {
// syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem) // syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem)
func syncGitConfig() (err error) { func syncGitConfig() (err error) {
if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil {
return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), err) return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err)
} }
// Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults" // Git requires setting user.name and user.email in order to commit changes - old comment: "if they're not set just add some defaults"
@@ -343,7 +337,7 @@ func configSetNonExist(key, value string) error {
} }
func configAddNonExist(key, value string) error { func configAddNonExist(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil) _, _, err := NewCommand(DefaultContext, "config", "--fixed-value", "--get", key, value).RunStdString(nil)
if err == nil { if err == nil {
// already exist // already exist
return nil return nil
@@ -363,7 +357,7 @@ func configUnsetAll(key, value string) error {
_, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) _, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil)
if err == nil { if err == nil {
// exist, need to remove // exist, need to remove
_, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil) _, _, err = NewCommand(DefaultContext, "config", "--global", "--fixed-value", "--unset-all", key, value).RunStdString(nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) return fmt.Errorf("failed to unset git global config %s, err: %w", key, err)
} }

View File

@@ -21,12 +21,12 @@ import (
func testRun(m *testing.M) error { func testRun(m *testing.M) error {
_ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`) _ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`)
gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home") repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos")
if err != nil { if err != nil {
return fmt.Errorf("unable to create temp dir: %w", err) return fmt.Errorf("unable to create temp dir: %w", err)
} }
defer util.RemoveAll(gitHomePath) defer util.RemoveAll(repoRootPath)
setting.Git.HomePath = gitHomePath setting.RepoRootPath = repoRootPath
if err = InitOnceWithSync(context.Background()); err != nil { if err = InitOnceWithSync(context.Background()); err != nil {
return fmt.Errorf("failed to call Init: %w", err) return fmt.Errorf("failed to call Init: %w", err)
@@ -78,10 +78,4 @@ func TestGitConfig(t *testing.T) {
assert.NoError(t, configUnsetAll("test.key-b", "val-2b")) assert.NoError(t, configUnsetAll("test.key-b", "val-2b"))
assert.False(t, gitConfigContains("key-b = val-2b")) assert.False(t, gitConfigContains("key-b = val-2b"))
assert.NoError(t, configSet("test.key-x", "*"))
assert.True(t, gitConfigContains("key-x = *"))
assert.NoError(t, configSetNonExist("test.key-x", "*"))
assert.NoError(t, configUnsetAll("test.key-x", "*"))
assert.False(t, gitConfigContains("key-x = *"))
} }

View File

@@ -8,7 +8,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"context" "context"
"errors"
"io" "io"
"path" "path"
"sort" "sort"
@@ -63,10 +62,9 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p
}) })
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
return } else {
_ = stdoutWriter.Close()
} }
_ = stdoutWriter.Close()
}() }()
// For simplicities sake we'll us a buffered reader to read from the cat-file --batch // For simplicities sake we'll us a buffered reader to read from the cat-file --batch
@@ -356,7 +354,7 @@ heaploop:
} }
current, err := g.Next(treepath, path2idx, changed, maxpathlen) current, err := g.Next(treepath, path2idx, changed, maxpathlen)
if err != nil { if err != nil {
if errors.Is(err, context.DeadlineExceeded) { if err == context.DeadlineExceeded {
break heaploop break heaploop
} }
g.Close() g.Close()

View File

@@ -26,7 +26,7 @@ import (
) )
// don't index files larger than this many bytes for performance purposes // don't index files larger than this many bytes for performance purposes
const sizeLimit = 1024 * 1024 const sizeLimit = 1000000
var ( var (
// For custom user mapping // For custom user mapping
@@ -58,7 +58,7 @@ func NewContext() {
func Code(fileName, language, code string) string { func Code(fileName, language, code string) string {
NewContext() NewContext()
// diff view newline will be passed as empty, change to literal '\n' so it can be copied // diff view newline will be passed as empty, change to literal \n so it can be copied
// preserve literal newline in blame view // preserve literal newline in blame view
if code == "" || code == "\n" { if code == "" || code == "\n" {
return "\n" return "\n"
@@ -104,11 +104,6 @@ func Code(fileName, language, code string) string {
return CodeFromLexer(lexer, code) return CodeFromLexer(lexer, code)
} }
type nopPreWrapper struct{}
func (nopPreWrapper) Start(code bool, styleAttr string) string { return "" }
func (nopPreWrapper) End(code bool) string { return "" }
// CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes // CodeFromLexer returns a HTML version of code string with chroma syntax highlighting classes
func CodeFromLexer(lexer chroma.Lexer, code string) string { func CodeFromLexer(lexer chroma.Lexer, code string) string {
formatter := html.New(html.WithClasses(true), formatter := html.New(html.WithClasses(true),
@@ -131,9 +126,9 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string {
return code return code
} }
_ = htmlw.Flush() htmlw.Flush()
// Chroma will add newlines for certain lexers in order to highlight them properly // Chroma will add newlines for certain lexers in order to highlight them properly
// Once highlighted, strip them here, so they don't cause copy/paste trouble in HTML output // Once highlighted, strip them here so they don't cause copy/paste trouble in HTML output
return strings.TrimSuffix(htmlbuf.String(), "\n") return strings.TrimSuffix(htmlbuf.String(), "\n")
} }
@@ -146,7 +141,7 @@ func File(numLines int, fileName, language string, code []byte) []string {
} }
formatter := html.New(html.WithClasses(true), formatter := html.New(html.WithClasses(true),
html.WithLineNumbers(false), html.WithLineNumbers(false),
html.WithPreWrapper(nopPreWrapper{}), html.PreventSurroundingPre(true),
) )
if formatter == nil { if formatter == nil {
@@ -194,19 +189,27 @@ func File(numLines int, fileName, language string, code []byte) []string {
return plainText(string(code), numLines) return plainText(string(code), numLines)
} }
_ = htmlw.Flush() htmlw.Flush()
finalNewLine := false finalNewLine := false
if len(code) > 0 { if len(code) > 0 {
finalNewLine = code[len(code)-1] == '\n' finalNewLine = code[len(code)-1] == '\n'
} }
m := strings.SplitN(htmlbuf.String(), `</span></span><span class="line"><span class="cl">`, numLines) m := make([]string, 0, numLines)
if len(m) > 0 { for _, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) {
m[0] = m[0][len(`<span class="line"><span class="cl">`):] content := string(v)
last := m[len(m)-1] // need to keep lines that are only \n so copy/paste works properly in browser
m[len(m)-1] = last[:len(last)-len(`</span></span>`)] if content == "" {
content = "\n"
} else if content == `</span><span class="w">` {
content += "\n</span>"
} else if content == `</span></span><span class="line"><span class="cl">` {
content += "\n"
}
content = strings.TrimSuffix(content, `<span class="w">`)
content = strings.TrimPrefix(content, `</span>`)
m = append(m, content)
} }
if finalNewLine { if finalNewLine {
m = append(m, "<span class=\"w\">\n</span>") m = append(m, "<span class=\"w\">\n</span>")
} }
@@ -216,14 +219,14 @@ func File(numLines int, fileName, language string, code []byte) []string {
// return unhiglighted map // return unhiglighted map
func plainText(code string, numLines int) []string { func plainText(code string, numLines int) []string {
m := strings.SplitN(code, "\n", numLines) m := make([]string, 0, numLines)
for _, v := range strings.SplitN(string(code), "\n", numLines) {
for i, content := range m { content := string(v)
// need to keep lines that are only \n so copy/paste works properly in browser // need to keep lines that are only \n so copy/paste works properly in browser
if content == "" { if content == "" {
content = "\n" content = "\n"
} }
m[i] = gohtml.EscapeString(content) m = append(m, gohtml.EscapeString(content))
} }
return m return m
} }

View File

@@ -43,29 +43,18 @@ func TestFile(t *testing.T) {
- go test -v -race -coverprofile=coverage.txt -covermode=atomic - go test -v -race -coverprofile=coverage.txt -covermode=atomic
`), `),
want: util.Dedent(` want: util.Dedent(`
<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span><span class="w"> <span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>
<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span><span class="w"> </span></span><span class="line"><span class="cl">
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>
<span class="w"> </span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>
<span class="w"></span><span class="nt">steps</span><span class="p">:</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>
<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span></span></span>
<span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span>
<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span><span class="w">
</span>
<span class="w"> </span><span class="nt">commands</span><span class="p">:</span><span class="w">
</span>
<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span><span class="w">
</span>
<span class="w"> </span>- <span class="l">go build -v</span><span class="w">
</span>
<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>
`), `),
}, },
{ {
@@ -87,30 +76,19 @@ func TestFile(t *testing.T) {
- go test -v -race -coverprofile=coverage.txt -covermode=atomic - go test -v -race -coverprofile=coverage.txt -covermode=atomic
`)+"\n", "name: default", "name: default ", 1), `)+"\n", "name: default", "name: default ", 1),
want: util.Dedent(` want: util.Dedent(`
<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span><span class="w"> <span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>
<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span><span class="w"> </span></span><span class="line"><span class="cl">
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>
<span class="w"> </span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>
<span class="w"></span><span class="nt">steps</span><span class="p">:</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>
<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>
<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span><span class="w"> </span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>
</span> </span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>
<span class="w"> </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span></span>
</span>
<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span><span class="w">
</span>
<span class="w"> </span><span class="nt">commands</span><span class="p">:</span><span class="w">
</span>
<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span><span class="w">
</span>
<span class="w"> </span>- <span class="l">go build -v</span><span class="w">
</span>
<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
</span>
<span class="w"> <span class="w">
</span> </span>
`), `),

View File

@@ -78,11 +78,6 @@ func (hl *HostMatchList) AppendBuiltin(builtin string) {
hl.builtins = append(hl.builtins, builtin) hl.builtins = append(hl.builtins, builtin)
} }
// AppendPattern appends more pattern to match
func (hl *HostMatchList) AppendPattern(pattern string) {
hl.patterns = append(hl.patterns, pattern)
}
// IsEmpty checks if the checklist is empty // IsEmpty checks if the checklist is empty
func (hl *HostMatchList) IsEmpty() bool { func (hl *HostMatchList) IsEmpty() bool {
return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0) return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0)

View File

@@ -17,23 +17,16 @@ import (
) )
// AddCacheControlToHeader adds suitable cache-control headers to response // AddCacheControlToHeader adds suitable cache-control headers to response
func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) { func AddCacheControlToHeader(h http.Header, d time.Duration) {
directives := make([]string, 0, 2+len(additionalDirectives))
if setting.IsProd { if setting.IsProd {
if maxAge == 0 { h.Set("Cache-Control", "private, max-age="+strconv.Itoa(int(d.Seconds())))
directives = append(directives, "no-store")
} else {
directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds())))
}
} else { } else {
directives = append(directives, "no-store") h.Set("Cache-Control", "no-store")
// to remind users they are using non-prod setting. // to remind users they are using non-prod setting.
// some users may be confused by "Cache-Control: no-store" in their setup if they did wrong to `RUN_MODE` in `app.ini`.
h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode) h.Add("X-Gitea-Debug", "RUN_MODE="+setting.RunMode)
h.Add("X-Gitea-Debug", "CacheControl=no-store")
} }
h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", "))
} }
// generateETag generates an ETag based on size, filename and file modification time // generateETag generates an ETag based on size, filename and file modification time

View File

@@ -284,7 +284,7 @@ func (b *ElasticSearchIndexer) Index(ctx context.Context, repo *repo_model.Repos
reqs := make([]elastic.BulkableRequest, 0) reqs := make([]elastic.BulkableRequest, 0)
if len(changes.Updates) > 0 { if len(changes.Updates) > 0 {
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil { if err := git.EnsureValidGitRepository(git.DefaultContext, repo.RepoPath()); err != nil {
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
return err return err
} }

View File

@@ -841,10 +841,9 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
// Repos with external issue trackers might still need to reference local PRs // Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is // We need to concern with the first one that shows up in the text, whichever it is
if hasExtTrackFormat && !isNumericStyle && refNumeric != nil { if hasExtTrackFormat && !isNumericStyle {
// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that // If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
// Allow a free-pass when non-numeric pattern wasn't found. if foundNumeric && refNumeric.RefLocation.Start < ref.RefLocation.Start {
if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) {
found = foundNumeric found = foundNumeric
ref = refNumeric ref = refNumeric
} }

View File

@@ -19,52 +19,52 @@ func (n NullDownloader) SetContext(_ context.Context) {}
// GetRepoInfo returns a repository information // GetRepoInfo returns a repository information
func (n NullDownloader) GetRepoInfo() (*Repository, error) { func (n NullDownloader) GetRepoInfo() (*Repository, error) {
return nil, ErrNotSupported{Entity: "RepoInfo"} return nil, &ErrNotSupported{Entity: "RepoInfo"}
} }
// GetTopics return repository topics // GetTopics return repository topics
func (n NullDownloader) GetTopics() ([]string, error) { func (n NullDownloader) GetTopics() ([]string, error) {
return nil, ErrNotSupported{Entity: "Topics"} return nil, &ErrNotSupported{Entity: "Topics"}
} }
// GetMilestones returns milestones // GetMilestones returns milestones
func (n NullDownloader) GetMilestones() ([]*Milestone, error) { func (n NullDownloader) GetMilestones() ([]*Milestone, error) {
return nil, ErrNotSupported{Entity: "Milestones"} return nil, &ErrNotSupported{Entity: "Milestones"}
} }
// GetReleases returns releases // GetReleases returns releases
func (n NullDownloader) GetReleases() ([]*Release, error) { func (n NullDownloader) GetReleases() ([]*Release, error) {
return nil, ErrNotSupported{Entity: "Releases"} return nil, &ErrNotSupported{Entity: "Releases"}
} }
// GetLabels returns labels // GetLabels returns labels
func (n NullDownloader) GetLabels() ([]*Label, error) { func (n NullDownloader) GetLabels() ([]*Label, error) {
return nil, ErrNotSupported{Entity: "Labels"} return nil, &ErrNotSupported{Entity: "Labels"}
} }
// GetIssues returns issues according start and limit // GetIssues returns issues according start and limit
func (n NullDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) { func (n NullDownloader) GetIssues(page, perPage int) ([]*Issue, bool, error) {
return nil, false, ErrNotSupported{Entity: "Issues"} return nil, false, &ErrNotSupported{Entity: "Issues"}
} }
// GetComments returns comments of an issue or PR // GetComments returns comments of an issue or PR
func (n NullDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) { func (n NullDownloader) GetComments(commentable Commentable) ([]*Comment, bool, error) {
return nil, false, ErrNotSupported{Entity: "Comments"} return nil, false, &ErrNotSupported{Entity: "Comments"}
} }
// GetAllComments returns paginated comments // GetAllComments returns paginated comments
func (n NullDownloader) GetAllComments(page, perPage int) ([]*Comment, bool, error) { func (n NullDownloader) GetAllComments(page, perPage int) ([]*Comment, bool, error) {
return nil, false, ErrNotSupported{Entity: "AllComments"} return nil, false, &ErrNotSupported{Entity: "AllComments"}
} }
// GetPullRequests returns pull requests according page and perPage // GetPullRequests returns pull requests according page and perPage
func (n NullDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) { func (n NullDownloader) GetPullRequests(page, perPage int) ([]*PullRequest, bool, error) {
return nil, false, ErrNotSupported{Entity: "PullRequests"} return nil, false, &ErrNotSupported{Entity: "PullRequests"}
} }
// GetReviews returns pull requests review // GetReviews returns pull requests review
func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) { func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) {
return nil, ErrNotSupported{Entity: "Reviews"} return nil, &ErrNotSupported{Entity: "Reviews"}
} }
// FormatCloneURL add authentication into remote URLs // FormatCloneURL add authentication into remote URLs

View File

@@ -8,9 +8,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
goversion "github.com/hashicorp/go-version"
) )
const ( const (
@@ -55,9 +56,7 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
if !namePattern.MatchString(name) { if !namePattern.MatchString(name) {
return nil, ErrValidation return nil, ErrValidation
} }
if _, err := goversion.NewSemver(version); err != nil {
v := strings.TrimSpace(version)
if v == "" {
return nil, ErrValidation return nil, ErrValidation
} }
if user != "" && !namePattern.MatchString(user) { if user != "" && !namePattern.MatchString(user) {
@@ -70,7 +69,7 @@ func NewRecipeReference(name, version, user, channel, revision string) (*RecipeR
return nil, ErrValidation return nil, ErrValidation
} }
return &RecipeReference{name, v, user, channel, revision}, nil return &RecipeReference{name, version, user, channel, revision}, nil
} }
func (r *RecipeReference) RevisionOrDefault() string { func (r *RecipeReference) RevisionOrDefault() string {

View File

@@ -34,7 +34,6 @@ func TestNewRecipeReference(t *testing.T) {
{"name", "1.0", "_", "_", "", true}, {"name", "1.0", "_", "_", "", true},
{"name", "1.0", "_", "_", "0", true}, {"name", "1.0", "_", "_", "0", true},
{"name", "1.0", "", "", "0", true}, {"name", "1.0", "", "", "0", true},
{"name", "1.0.0q", "", "", "0", true},
{"name", "1.0", "", "", "000000000000000000000000000000000000000000000000000000000000", false}, {"name", "1.0", "", "", "000000000000000000000000000000000000000000000000000000000000", false},
} }

View File

@@ -16,7 +16,6 @@ import (
) )
const ( const (
PropertyRepository = "container.repository"
PropertyDigest = "container.digest" PropertyDigest = "container.digest"
PropertyMediaType = "container.mediatype" PropertyMediaType = "container.mediatype"
PropertyManifestTagged = "container.manifest.tagged" PropertyManifestTagged = "container.manifest.tagged"

View File

@@ -27,21 +27,21 @@ func NewContentStore() *ContentStore {
// Get gets a package blob // Get gets a package blob
func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) { func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
return s.store.Open(KeyToRelativePath(key)) return s.store.Open(keyToRelativePath(key))
} }
// Save stores a package blob // Save stores a package blob
func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error { func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error {
_, err := s.store.Save(KeyToRelativePath(key), r, size) _, err := s.store.Save(keyToRelativePath(key), r, size)
return err return err
} }
// Delete deletes a package blob // Delete deletes a package blob
func (s *ContentStore) Delete(key BlobHash256Key) error { func (s *ContentStore) Delete(key BlobHash256Key) error {
return s.store.Delete(KeyToRelativePath(key)) return s.store.Delete(keyToRelativePath(key))
} }
// KeyToRelativePath converts the sha256 key aabb000000... to aa/bb/aabb000000... // keyToRelativePath converts the sha256 key aabb000000... to aa/bb/aabb000000...
func KeyToRelativePath(key BlobHash256Key) string { func keyToRelativePath(key BlobHash256Key) string {
return path.Join(string(key)[0:2], string(key)[2:4], string(key)) return path.Join(string(key)[0:2], string(key)[2:4], string(key))
} }

View File

@@ -1,47 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package packages
import (
"fmt"
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestHashedBuffer(t *testing.T) {
cases := []struct {
MaxMemorySize int
Data string
HashMD5 string
HashSHA1 string
HashSHA256 string
HashSHA512 string
}{
{5, "test", "098f6bcd4621d373cade4e832627b4f6", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"},
{5, "testtest", "05a671c66aefea124cc08b76ea6d30bb", "51abb9636078defbf888d8457a7c76f85c8f114c", "37268335dd6931045bdcdf92623ff819a64244b53d0e746d438797349d4da578", "125d6d03b32c84d492747f79cf0bf6e179d287f341384eb5d6d3197525ad6be8e6df0116032935698f99a09e265073d1d6c32c274591bf1d0a20ad67cba921bc"},
}
for _, c := range cases {
buf, err := CreateHashedBufferFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
assert.NoError(t, err)
assert.EqualValues(t, len(c.Data), buf.Size())
data, err := io.ReadAll(buf)
assert.NoError(t, err)
assert.Equal(t, c.Data, string(data))
hashMD5, hashSHA1, hashSHA256, hashSHA512 := buf.Sums()
assert.Equal(t, c.HashMD5, fmt.Sprintf("%x", hashMD5))
assert.Equal(t, c.HashSHA1, fmt.Sprintf("%x", hashSHA1))
assert.Equal(t, c.HashSHA256, fmt.Sprintf("%x", hashSHA256))
assert.Equal(t, c.HashSHA512, fmt.Sprintf("%x", hashSHA512))
assert.NoError(t, buf.Close())
}
}

View File

@@ -80,6 +80,7 @@ type gemspec struct {
VersionRequirements requirement `yaml:"version_requirements"` VersionRequirements requirement `yaml:"version_requirements"`
} `yaml:"dependencies"` } `yaml:"dependencies"`
Description string `yaml:"description"` Description string `yaml:"description"`
Email string `yaml:"email"`
Executables []string `yaml:"executables"` Executables []string `yaml:"executables"`
Extensions []interface{} `yaml:"extensions"` Extensions []interface{} `yaml:"extensions"`
ExtraRdocFiles []string `yaml:"extra_rdoc_files"` ExtraRdocFiles []string `yaml:"extra_rdoc_files"`

View File

@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -154,10 +153,6 @@ func createDelegateHooks(repoPath string) (err error) {
} }
func checkExecutable(filename string) bool { func checkExecutable(filename string) bool {
// windows has no concept of a executable bit
if runtime.GOOS == "windows" {
return true
}
fileInfo, err := os.Stat(filename) fileInfo, err := os.Stat(filename)
if err != nil { if err != nil {
return false return false

View File

@@ -27,7 +27,7 @@ func newAttachmentService() {
Attachment.Storage = getStorage("attachments", storageType, sec) Attachment.Storage = getStorage("attachments", storageType, sec)
Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".csv,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.xls,.xlsx,.zip") Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip")
Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) Attachment.MaxSize = sec.Key("MAX_SIZE").MustInt64(4)
Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5) Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5)
Attachment.Enabled = sec.Key("ENABLED").MustBool(true) Attachment.Enabled = sec.Key("ENABLED").MustBool(true)

View File

@@ -12,7 +12,7 @@ var (
Enabled bool Enabled bool
ShareUserStatistics bool ShareUserStatistics bool
}{ }{
Enabled: false, Enabled: true,
ShareUserStatistics: true, ShareUserStatistics: true,
} }
) )

View File

@@ -5,7 +5,6 @@
package setting package setting
import ( import (
"path/filepath"
"time" "time"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -14,7 +13,6 @@ import (
// Git settings // Git settings
var Git = struct { var Git = struct {
Path string Path string
HomePath string
DisableDiffHighlight bool DisableDiffHighlight bool
MaxGitDiffLines int MaxGitDiffLines int
MaxGitDiffLineCharacters int MaxGitDiffLineCharacters int
@@ -69,16 +67,7 @@ var Git = struct {
} }
func newGit() { func newGit() {
sec := Cfg.Section("git") if err := Cfg.Section("git").MapTo(&Git); err != nil {
if err := sec.MapTo(&Git); err != nil {
log.Fatal("Failed to map Git settings: %v", err) log.Fatal("Failed to map Git settings: %v", err)
} }
Git.HomePath = sec.Key("HOME_PATH").MustString("home")
if !filepath.IsAbs(Git.HomePath) {
Git.HomePath = filepath.Join(AppDataPath, Git.HomePath)
} else {
Git.HomePath = filepath.Clean(Git.HomePath)
}
} }

View File

@@ -48,7 +48,6 @@ var (
DefaultBranch string DefaultBranch string
AllowAdoptionOfUnadoptedRepositories bool AllowAdoptionOfUnadoptedRepositories bool
AllowDeleteOfUnadoptedRepositories bool AllowDeleteOfUnadoptedRepositories bool
DisableDownloadSourceArchives bool
// Repository editor settings // Repository editor settings
Editor struct { Editor struct {

View File

@@ -170,7 +170,7 @@ var (
ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"},
KeygenPath: "ssh-keygen", KeygenPath: "ssh-keygen",
MinimumKeySizeCheck: true, MinimumKeySizeCheck: true,
MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048},
ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"},
AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}",
PerWriteTimeout: PerWriteTimeout, PerWriteTimeout: PerWriteTimeout,
@@ -843,9 +843,8 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.StartBuiltinServer = false SSH.StartBuiltinServer = false
} }
SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem")) trustedUserCaKeys := sec.Key("SSH_TRUSTED_USER_CA_KEYS").Strings(",")
for _, caKey := range trustedUserCaKeys {
for _, caKey := range SSH.TrustedUserCAKeys {
pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey))
if err != nil { if err != nil {
log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err)
@@ -853,7 +852,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey)
} }
if len(SSH.TrustedUserCAKeys) > 0 { if len(trustedUserCaKeys) > 0 {
// Set the default as email,username otherwise we can leave it empty // Set the default as email,username otherwise we can leave it empty
sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email")
} else { } else {
@@ -862,6 +861,22 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
if !SSH.Disabled && !SSH.StartBuiltinServer {
if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
} else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
}
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
fname := sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
if err := os.WriteFile(fname,
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
log.Fatal("Failed to create '%s': %v", fname, err)
}
}
}
SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck) SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool(SSH.MinimumKeySizeCheck)
minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys() minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys()
for _, key := range minimumKeySizes { for _, key := range minimumKeySizes {

View File

@@ -1,55 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package ssh
import (
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
func Init() error {
if setting.SSH.Disabled {
return nil
}
if setting.SSH.StartBuiltinServer {
Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
log.Info("SSH server started on %s. Cipher list (%v), key exchange algorithms (%v), MACs (%v)",
net.JoinHostPort(setting.SSH.ListenHost, strconv.Itoa(setting.SSH.ListenPort)),
setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs,
)
return nil
}
builtinUnused()
// FIXME: why 0o644 for a directory .....
if err := os.MkdirAll(setting.SSH.KeyTestPath, 0o644); err != nil {
return fmt.Errorf("failed to create directory %q for ssh key test: %w", setting.SSH.KeyTestPath, err)
}
if len(setting.SSH.TrustedUserCAKeys) > 0 && setting.SSH.AuthorizedPrincipalsEnabled {
caKeysFileName := setting.SSH.TrustedUserCAKeysFile
caKeysFileDir := filepath.Dir(caKeysFileName)
err := os.MkdirAll(caKeysFileDir, 0o700) // SSH.RootPath by default (That is `~/.ssh` in most cases)
if err != nil {
return fmt.Errorf("failed to create directory %q for ssh trusted ca keys: %w", caKeysFileDir, err)
}
if err := os.WriteFile(caKeysFileName, []byte(strings.Join(setting.SSH.TrustedUserCAKeys, "\n")), 0o600); err != nil {
return fmt.Errorf("failed to write ssh trusted ca keys to %q: %w", caKeysFileName, err)
}
}
return nil
}

View File

@@ -11,7 +11,6 @@ import (
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -75,21 +74,11 @@ func sessionHandler(session ssh.Session) {
ctx, cancel := context.WithCancel(session.Context()) ctx, cancel := context.WithCancel(session.Context())
defer cancel() defer cancel()
gitProtocol := ""
for _, env := range session.Environ() {
if strings.HasPrefix(env, "GIT_PROTOCOL=") {
// The value would be version=2, so using normal split doesn't work here.
gitProtocol = strings.SplitN(env, "=", 2)[1]
break
}
}
cmd := exec.CommandContext(ctx, setting.AppPath, args...) cmd := exec.CommandContext(ctx, setting.AppPath, args...)
cmd.Env = append( cmd.Env = append(
os.Environ(), os.Environ(),
"SSH_ORIGINAL_COMMAND="+command, "SSH_ORIGINAL_COMMAND="+command,
"SKIP_MINWINSVC=1", "SKIP_MINWINSVC=1",
"GIT_PROTOCOL="+gitProtocol,
) )
stdout, err := cmd.StdoutPipe() stdout, err := cmd.StdoutPipe()
@@ -153,14 +142,10 @@ func sessionHandler(session ssh.Session) {
// Wait for the command to exit and log any errors we get // Wait for the command to exit and log any errors we get
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
// Cannot use errors.Is here because ExitError doesn't implement Is log.Error("SSH: Wait: %v", err)
// Thus errors.Is will do equality test NOT type comparison
if _, ok := err.(*exec.ExitError); !ok {
log.Error("SSH: Wait: %v", err)
}
} }
if err := session.Exit(getExitStatusFromError(err)); err != nil && !errors.Is(err, io.EOF) { if err := session.Exit(getExitStatusFromError(err)); err != nil {
log.Error("Session failed to exit. %s", err) log.Error("Session failed to exit. %s", err)
} }
} }

View File

@@ -29,7 +29,7 @@ func listen(server *ssh.Server) {
log.Info("SSH Listener: %s Closed", server.Addr) log.Info("SSH Listener: %s Closed", server.Addr)
} }
// builtinUnused informs our cleanup routine that we will not be using a ssh port // Unused informs our cleanup routine that we will not be using a ssh port
func builtinUnused() { func Unused() {
graceful.GetManager().InformCleanup() graceful.GetManager().InformCleanup()
} }

View File

@@ -97,7 +97,6 @@ type SubmitPullReviewOptions struct {
// DismissPullReviewOptions are options to dismiss a pull review // DismissPullReviewOptions are options to dismiss a pull review
type DismissPullReviewOptions struct { type DismissPullReviewOptions struct {
Message string `json:"message"` Message string `json:"message"`
Priors bool `json:"priors"`
} }
// PullReviewRequestOptions are options to add or remove pull review requests // PullReviewRequestOptions are options to add or remove pull review requests

View File

@@ -35,11 +35,10 @@ func BaseVars() Vars {
"IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore, "IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore,
"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations, "IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
"ShowRegistrationButton": setting.Service.ShowRegistrationButton, "ShowRegistrationButton": setting.Service.ShowRegistrationButton,
"ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage, "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
"ShowFooterBranding": setting.ShowFooterBranding, "ShowFooterBranding": setting.ShowFooterBranding,
"ShowFooterVersion": setting.ShowFooterVersion, "ShowFooterVersion": setting.ShowFooterVersion,
"DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives,
"EnableSwagger": setting.API.EnableSwagger, "EnableSwagger": setting.API.EnableSwagger,
"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn, "EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,

View File

@@ -70,16 +70,6 @@ func (ct SniffedType) IsRepresentableAsText() bool {
return ct.IsText() || ct.IsSvgImage() return ct.IsText() || ct.IsSvgImage()
} }
// IsBrowsableType returns whether a non-text type can be displayed in a browser
func (ct SniffedType) IsBrowsableBinaryType() bool {
return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio()
}
// GetMimeType returns the mime type
func (ct SniffedType) GetMimeType() string {
return strings.SplitN(ct.contentType, ";", 2)[0]
}
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty. // DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
func DetectContentType(data []byte) SniffedType { func DetectContentType(data []byte) SniffedType {
if len(data) == 0 { if len(data) == 0 {

View File

@@ -103,45 +103,35 @@ func (b *FileBackedBuffer) Size() int64 {
return b.size return b.size
} }
func (b *FileBackedBuffer) switchToReader() error { func (b *FileBackedBuffer) switchToReader() {
if b.reader != nil { if b.reader != nil {
return nil return
} }
if b.file != nil { if b.file != nil {
if _, err := b.file.Seek(0, io.SeekStart); err != nil {
return err
}
b.reader = b.file b.reader = b.file
} else { } else {
b.reader = bytes.NewReader(b.buffer.Bytes()) b.reader = bytes.NewReader(b.buffer.Bytes())
} }
return nil
} }
// Read implements io.Reader // Read implements io.Reader
func (b *FileBackedBuffer) Read(p []byte) (int, error) { func (b *FileBackedBuffer) Read(p []byte) (int, error) {
if err := b.switchToReader(); err != nil { b.switchToReader()
return 0, err
}
return b.reader.Read(p) return b.reader.Read(p)
} }
// ReadAt implements io.ReaderAt // ReadAt implements io.ReaderAt
func (b *FileBackedBuffer) ReadAt(p []byte, off int64) (int, error) { func (b *FileBackedBuffer) ReadAt(p []byte, off int64) (int, error) {
if err := b.switchToReader(); err != nil { b.switchToReader()
return 0, err
}
return b.reader.ReadAt(p, off) return b.reader.ReadAt(p, off)
} }
// Seek implements io.Seeker // Seek implements io.Seeker
func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) { func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) {
if err := b.switchToReader(); err != nil { b.switchToReader()
return 0, err
}
return b.reader.Seek(offset, whence) return b.reader.Seek(offset, whence)
} }

View File

@@ -1,36 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package filebuffer
import (
"io"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFileBackedBuffer(t *testing.T) {
cases := []struct {
MaxMemorySize int
Data string
}{
{5, "test"},
{5, "testtest"},
}
for _, c := range cases {
buf, err := CreateFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
assert.NoError(t, err)
assert.EqualValues(t, len(c.Data), buf.Size())
data, err := io.ReadAll(buf)
assert.NoError(t, err)
assert.Equal(t, c.Data, string(data))
assert.NoError(t, buf.Close())
}
}

View File

@@ -16,28 +16,12 @@ import (
// 1563418 -> 2 weeks 4 days // 1563418 -> 2 weeks 4 days
// 3937125s -> 1 month 2 weeks // 3937125s -> 1 month 2 weeks
// 45677465s -> 1 year 6 months // 45677465s -> 1 year 6 months
//
// Magic numbers:
// 3600 = 60 * 60 (amount of seconds in a hour)
// 86400 = 60 * 60 * 24 (amount of seconds in a day)
func SecToTime(duration int64) string { func SecToTime(duration int64) string {
formattedTime := "" formattedTime := ""
years := duration / (3600 * 24 * 7 * 4 * 12)
// The following four variables are calculated by taking months := (duration / (3600 * 24 * 30)) % 12
// into account the previously calculated variables, this avoids weeks := (duration / (3600 * 24 * 7)) % 4
// pitfalls when using remainders. As that could lead to incorrect days := (duration / (3600 * 24)) % 7
// results when the calculated number equals the quotient number.
remainingDays := duration / (60 * 60 * 24)
years := remainingDays / 365
remainingDays -= years * 365
months := remainingDays * 12 / 365
remainingDays -= months * 365 / 12
weeks := remainingDays / 7
remainingDays -= weeks * 7
days := remainingDays
// The following three variables are calculated without depending
// on the previous calculated variables.
hours := (duration / 3600) % 24 hours := (duration / 3600) % 24
minutes := (duration / 60) % 60 minutes := (duration / 60) % 60
seconds := duration % 60 seconds := duration % 60

View File

@@ -11,21 +11,10 @@ import (
) )
func TestSecToTime(t *testing.T) { func TestSecToTime(t *testing.T) {
second := int64(1) assert.Equal(t, SecToTime(66), "1 minute 6 seconds")
minute := 60 * second assert.Equal(t, SecToTime(52410), "14 hours 33 minutes")
hour := 60 * minute assert.Equal(t, SecToTime(563418), "6 days 12 hours")
day := 24 * hour assert.Equal(t, SecToTime(1563418), "2 weeks 4 days")
year := 365 * day assert.Equal(t, SecToTime(3937125), "1 month 2 weeks")
assert.Equal(t, SecToTime(45677465), "1 year 5 months")
assert.Equal(t, "1 minute 6 seconds", SecToTime(minute+6*second))
assert.Equal(t, "1 hour", SecToTime(hour))
assert.Equal(t, "1 hour", SecToTime(hour+second))
assert.Equal(t, "14 hours 33 minutes", SecToTime(14*hour+33*minute+30*second))
assert.Equal(t, "6 days 12 hours", SecToTime(6*day+12*hour+30*minute+18*second))
assert.Equal(t, "2 weeks 4 days", SecToTime((2*7+4)*day+2*hour+16*minute+58*second))
assert.Equal(t, "4 weeks", SecToTime(4*7*day))
assert.Equal(t, "4 weeks 1 day", SecToTime((4*7+1)*day))
assert.Equal(t, "1 month 2 weeks", SecToTime((6*7+3)*day+13*hour+38*minute+45*second))
assert.Equal(t, "11 months", SecToTime(year-25*day))
assert.Equal(t, "1 year 5 months", SecToTime(year+163*day+10*hour+11*minute+5*second))
} }

View File

@@ -1531,8 +1531,7 @@ pulls.remove_prefix = Remove <strong>%s</strong> prefix
pulls.data_broken = This pull request is broken due to missing fork information. pulls.data_broken = This pull request is broken due to missing fork information.
pulls.files_conflicted = This pull request has changes conflicting with the target branch. pulls.files_conflicted = This pull request has changes conflicting with the target branch.
pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments."
pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge." pulls.is_empty = "This branch is equal with the target branch."
pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit."
pulls.required_status_check_failed = Some required checks were not successful. pulls.required_status_check_failed = Some required checks were not successful.
pulls.required_status_check_missing = Some required checks are missing. pulls.required_status_check_missing = Some required checks are missing.
pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. pulls.required_status_check_administrator = As an administrator, you may still merge this pull request.
@@ -2886,7 +2885,6 @@ monitor.queue.nopool.title = No Worker Pool
monitor.queue.nopool.desc = This queue wraps other queues and does not itself have a worker pool. monitor.queue.nopool.desc = This queue wraps other queues and does not itself have a worker pool.
monitor.queue.wrapped.desc = A wrapped queue wraps a slow starting queue, buffering queued requests in a channel. It does not have a worker pool itself. monitor.queue.wrapped.desc = A wrapped queue wraps a slow starting queue, buffering queued requests in a channel. It does not have a worker pool itself.
monitor.queue.persistable-channel.desc = A persistable-channel wraps two queues, a channel queue that has its own worker pool and a level queue for persisted requests from previous shutdowns. It does not have a worker pool itself. monitor.queue.persistable-channel.desc = A persistable-channel wraps two queues, a channel queue that has its own worker pool and a level queue for persisted requests from previous shutdowns. It does not have a worker pool itself.
monitor.queue.flush = Flush worker
monitor.queue.pool.timeout = Timeout monitor.queue.pool.timeout = Timeout
monitor.queue.pool.addworkers.title = Add Workers monitor.queue.pool.addworkers.title = Add Workers
monitor.queue.pool.addworkers.submit = Add Workers monitor.queue.pool.addworkers.submit = Add Workers
@@ -3039,7 +3037,6 @@ title = Packages
desc = Manage repository packages. desc = Manage repository packages.
empty = There are no packages yet. empty = There are no packages yet.
empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">the documentation</a>. empty.documentation = For more information on the package registry, see <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/packages/overview">the documentation</a>.
empty.repo = Did you upload a package, but it's not shown here? Go to <a href="%[1]s">package settings</a> and link it to this repo.
filter.type = Type filter.type = Type
filter.type.all = All filter.type.all = All
filter.no_result = Your filter produced no results. filter.no_result = Your filter produced no results.

View File

@@ -1282,7 +1282,7 @@ issues.reopened_at=`переоткрыл(а) эту проблему <a id="%[1]
issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.commit_ref_at=`упомянул эту задачу в коммите <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">ссылка на эту проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_issue_from=`<a href="%[3]s">ссылка на эту проблему %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_pull_from=`<a href="%[3]s">ссылается на этот запрос на слияние %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_pull_from=`<a href="%[3]s">ссылается на этот запрос на слияние %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closing_from=`<a href="%[3]s">ссылается на запрос на слияние %[4]s, который закроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closing_from=`<a href="%[3]s">ссылается на запрос на слияние %[4], который закроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopening_from=`<a href="%[3]s">ссылается на запрос на слияние %[4]s, который вновь откроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopening_from=`<a href="%[3]s">ссылается на запрос на слияние %[4]s, который вновь откроет эту задачу</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_closed_from=`<a href="%[3]s">закрыл этот запрос %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_closed_from=`<a href="%[3]s">закрыл этот запрос %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_reopened_from=`<a href="%[3]s">переоткрыл эту задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>` issues.ref_reopened_from=`<a href="%[3]s">переоткрыл эту задачу %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`

Some files were not shown because too many files have changed in this diff Show More