diff --git a/.changelog.yml b/.changelog.yml index 657dfa1c0ee52..5c0b2a0cb06a4 100644 --- a/.changelog.yml +++ b/.changelog.yml @@ -1,3 +1,6 @@ +# config for changelog tool +# source: https://gitea.com/gitea/changelog + # The full repository name repo: go-gitea/gitea @@ -18,6 +21,10 @@ groups: name: SECURITY labels: - kind/security + - + name: FEDERATION + labels: + - theme/federation - name: FEATURES labels: diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ca8bef008bf6..ae2159d1ce3a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,318 @@ 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 been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.17.0-rc2](https://github.com/go-gitea/gitea/releases/tag/v1.17.0-rc2) - 2022-07-13 + +* SECURITY + * Use git.HOME_PATH for Git HOME directory (#20114) (#20293) + * Add write check for creating Commit Statuses (#20332) (#20333) +* 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) +* BUGFIXES + * 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) + +## [1.17.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.17.0-rc1) - 2022-06-18 + +* 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 + * 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 + * 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 `` 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) + * Allow package dump skipping (#19506) + * 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) + * Repository level enable package or disable (#19323) + * 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/_TEMPLATE.md (#18177) + * Prettify number of issues (#17760) + * Add a "admin user generate-access-token" subcommand (#17722) + * Move project files into models/project sub package (#17704) + * 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 + * 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) + * Enable packages by default again (#19746) + * 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 * ENHANCEMENTS diff --git a/cmd/dump.go b/cmd/dump.go index ea41c0c029d9b..d807cb0587c13 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -22,7 +22,7 @@ import ( "code.gitea.io/gitea/modules/util" "gitea.com/go-chi/session" - archiver "github.com/mholt/archiver/v3" + "github.com/mholt/archiver/v3" "github.com/urfave/cli" ) @@ -439,8 +439,23 @@ func addRecursiveExclude(w archiver.Writer, insidePath, absPath string, excludeA } } } else { - if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil { - return err + // only copy regular files and symlink regular files, skip non-regular files like socket/pipe/... + shouldAdd := file.Mode().IsRegular() + if !shouldAdd && file.Mode()&os.ModeSymlink == os.ModeSymlink { + target, err := filepath.EvalSymlinks(currentAbsPath) + if err != nil { + return err + } + targetStat, err := os.Stat(target) + if err != nil { + return err + } + shouldAdd = targetStat.Mode().IsRegular() + } + if shouldAdd { + if err = addFile(w, currentInsidePath, currentAbsPath, verbose); err != nil { + return err + } } } } diff --git a/cmd/dump_repo.go b/cmd/dump_repo.go index e980af30110aa..72456c61d3398 100644 --- a/cmd/dump_repo.go +++ b/cmd/dump_repo.go @@ -7,13 +7,17 @@ package cmd import ( "context" "errors" + "fmt" + "os" "strings" "code.gitea.io/gitea/modules/convert" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/migrations" "github.com/urfave/cli" @@ -83,6 +87,11 @@ func runDumpRepository(ctx *cli.Context) error { 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("AppWorkPath: %s", setting.AppWorkPath) log.Info("Custom path: %s", setting.CustomPath) @@ -128,7 +137,9 @@ func runDumpRepository(ctx *cli.Context) error { } else { units := strings.Split(ctx.String("units"), ",") for _, unit := range units { - switch strings.ToLower(unit) { + switch strings.ToLower(strings.TrimSpace(unit)) { + case "": + continue case "wiki": opts.Wiki = true case "issues": @@ -145,13 +156,29 @@ func runDumpRepository(ctx *cli.Context) error { opts.Comments = true case "pull_requests": 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( context.Background(), - ctx.String("repo_dir"), + repoDir, ctx.String("owner_name"), opts, ); err != nil { diff --git a/cmd/restore_repo.go b/cmd/restore_repo.go index c3081279dfc25..2256cc61abfca 100644 --- a/cmd/restore_repo.go +++ b/cmd/restore_repo.go @@ -7,6 +7,7 @@ package cmd import ( "errors" "net/http" + "strings" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/private" @@ -37,10 +38,10 @@ var CmdRestoreRepository = cli.Command{ Value: "", Usage: "Restore destination repository name", }, - cli.StringSliceFlag{ + cli.StringFlag{ Name: "units", - Value: nil, - Usage: `Which items will be restored, one or more units should be repeated with this flag. + Value: "", + Usage: `Which items will be restored, one or more units should be separated as comma. wiki, issues, labels, releases, release_assets, milestones, pull_requests, comments are allowed. Empty means all units.`, }, cli.BoolFlag{ @@ -55,13 +56,16 @@ func runRestoreRepository(c *cli.Context) error { defer cancel() setting.LoadFromExisting() - + var units []string + if s := c.String("units"); s != "" { + units = strings.Split(s, ",") + } statusCode, errStr := private.RestoreRepo( ctx, c.String("repo_dir"), c.String("owner_name"), c.String("repo_name"), - c.StringSlice("units"), + units, c.Bool("validation"), ) if statusCode == http.StatusOK { diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 065c57ef51d5c..ecd7febebad21 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -617,7 +617,10 @@ ROUTER = console ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; 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 ;DISABLE_DIFF_HIGHLIGHT = false @@ -1242,7 +1245,7 @@ PATH = ;; Define allowed algorithms and their minimum key length (use -1 to disable a type) ;ED25519 = 256 ;ECDSA = 256 -;RSA = 2048 +;RSA = 2047 ; we allow 2047 here because an otherwise valid 2048 bit RSA key can be reported as having 2047 bit length ;DSA = -1 ; set to 1024 to switch on ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1687,7 +1690,7 @@ PATH = ;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. -;ALLOWED_TYPES = .docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip +;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 ;; ;; Max size of each file. Defaults to 4MB ;MAX_SIZE = 4 @@ -2247,10 +2250,10 @@ PATH = ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Enable/Disable federation capabilities -; ENABLED = true +;ENABLED = false ;; ;; Enable/Disable user statistics for nodeinfo if federation is enabled -; SHARE_USER_STATISTICS = true +;SHARE_USER_STATISTICS = true ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 4f041d417edf8..ca756c2e6af31 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -620,7 +620,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type - `ED25519`: **256** - `ECDSA`: **256** -- `RSA`: **2048** +- `RSA`: **2047**: We set 2047 here because an otherwise valid 2048 RSA key can be reported as 2047 length. - `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`) @@ -741,7 +741,7 @@ Default templates for project boards: ## Issue and pull request attachments (`attachment`) - `ENABLED`: **true**: Whether issue and pull request attachments are enabled. -- `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. +- `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. - `MAX_SIZE`: **4**: Maximum size (MB). - `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]` @@ -947,6 +947,8 @@ Default templates for project boards: ## Git (`git`) - `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. - `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. @@ -1088,7 +1090,7 @@ Task queue configuration has been moved to `queue.task`. However, the below conf ## Federation (`federation`) -- `ENABLED`: **true**: Enable/Disable federation capabilities +- `ENABLED`: **false**: Enable/Disable federation capabilities - `SHARE_USER_STATISTICS`: **true**: Enable/Disable user statistics for nodeinfo if federation is enabled ## Packages (`packages`) diff --git a/docs/content/doc/advanced/signing.en-us.md b/docs/content/doc/advanced/signing.en-us.md index 8ae2f94e9ece9..9ef94deb75ce4 100644 --- a/docs/content/doc/advanced/signing.en-us.md +++ b/docs/content/doc/advanced/signing.en-us.md @@ -97,10 +97,11 @@ repositories, `SIGNING_KEY=default` could be used to provide different signing keys on a per-repository basis. However, this is clearly not an ideal UI and therefore subject to change. -**Since 1.17**, Gitea runs git in its own home directory `[repository].ROOT` and uses its own config `{[repository].ROOT}/.gitconfig`. +**Since 1.17**, Gitea runs git in its own home directory `[git].HOME_PATH` (default to `%(APP_DATA_PATH)/home`) +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`) -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 `[repository].ROOT`. +or the Gitea internal git config `{[git].HOME_PATH}/.gitconfig`. +Related home files for git command (like `.gnupg`) should also be put in Gitea's git home directory `[git].HOME_PATH`. ### `INITIAL_COMMIT` diff --git a/go.mod b/go.mod index e06ccfe13d84f..fe331b69ee7fb 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/mattn/go-isatty v0.0.14 github.com/mattn/go-sqlite3 v1.14.12 github.com/mholt/archiver/v3 v3.5.1 - github.com/microcosm-cc/bluemonday v1.0.18 + github.com/microcosm-cc/bluemonday v1.0.19 github.com/minio/minio-go/v7 v7.0.26 github.com/msteinert/pam v1.0.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 @@ -85,15 +85,15 @@ require ( github.com/urfave/cli v1.22.9 github.com/xanzy/go-gitlab v0.64.0 github.com/yohcop/openid-go v1.0.0 - github.com/yuin/goldmark v1.4.12 + github.com/yuin/goldmark v1.4.13 github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 github.com/yuin/goldmark-meta v1.1.0 go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/pwn v0.0.3 golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 + golang.org/x/net v0.0.0-20220630215102-69896b714898 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/text v0.3.7 golang.org/x/tools v0.1.10 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df @@ -102,7 +102,7 @@ require ( mvdan.cc/xurls/v2 v2.4.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.11 - xorm.io/xorm v1.3.1 + xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f ) require ( diff --git a/go.sum b/go.sum index 9c99adfbe14cc..b767a205c7ac1 100644 --- a/go.sum +++ b/go.sum @@ -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/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= -github.com/microcosm-cc/bluemonday v1.0.18 h1:6HcxvXDAi3ARt3slx6nTesbvorIc3QeTzBNRvWktHBo= -github.com/microcosm-cc/bluemonday v1.0.18/go.mod h1:Z0r70sCuXHig8YpBzCc5eGHAap2K7e/u082ZUpDRRqM= +github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= +github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= 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.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.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.12 h1:6hffw6vALvEDqJ19dOJvJKOoAOKe4NDaTqvd2sktGN0= -github.com/yuin/goldmark v1.4.12/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/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/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU= github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= @@ -1783,14 +1783,13 @@ 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-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-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-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-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-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw= +golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1937,8 +1936,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-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-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= @@ -2414,5 +2413,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 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00= xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM= -xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= +xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f h1:3NvNsM4lnttTsHpk8ODHqrwN1MCEjsO3bD/rpd8A47k= +xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= diff --git a/integrations/api_nodeinfo_test.go b/integrations/api_nodeinfo_test.go index c2fcd2fea5619..cf9ff4da1b532 100644 --- a/integrations/api_nodeinfo_test.go +++ b/integrations/api_nodeinfo_test.go @@ -11,17 +11,20 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/routers" "github.com/stretchr/testify/assert" ) func TestNodeinfo(t *testing.T) { - onGiteaRun(t, func(*testing.T, *url.URL) { - setting.Federation.Enabled = true - defer func() { - setting.Federation.Enabled = false - }() + setting.Federation.Enabled = true + c = routers.NormalRoutes() + defer func() { + setting.Federation.Enabled = false + c = routers.NormalRoutes() + }() + onGiteaRun(t, func(*testing.T, *url.URL) { req := NewRequestf(t, "GET", "/api/v1/nodeinfo") resp := MakeRequest(t, req, http.StatusOK) var nodeinfo api.NodeInfo diff --git a/integrations/pull_status_test.go b/integrations/pull_status_test.go index a5247f56ec5f3..33a27cd81275a 100644 --- a/integrations/pull_status_test.go +++ b/integrations/pull_status_test.go @@ -105,7 +105,11 @@ func doAPICreateCommitStatus(ctx APITestContext, commitID string, status api.Com } } -func TestPullCreate_EmptyChangesWithCommits(t *testing.T) { +func TestPullCreate_EmptyChangesWithDifferentCommits(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) { session := loginUser(t, "user1") testRepoFork(t, session, "user2", "repo1", "user1", "repo1") @@ -126,6 +130,28 @@ func TestPullCreate_EmptyChangesWithCommits(t *testing.T) { doc := NewHTMLParser(t, resp.Body) text := strings.TrimSpace(doc.doc.Find(".merge-section").Text()) - assert.Contains(t, text, "This branch is equal with the target branch.") + assert.Contains(t, text, "This pull request can be merged automatically.") + }) +} + +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.") }) } diff --git a/models/action.go b/models/action.go index 78cc93e1a2144..7dbdaa8530cee 100644 --- a/models/action.go +++ b/models/action.go @@ -92,12 +92,12 @@ func init() { // TableIndices implements xorm's TableIndices interface 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.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} } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index 5e0a337f7d63b..c054fa3a4343a 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -124,6 +124,17 @@ func ChangeProjectAssign(issue *Issue, doer *user_model.User, newProjectID int64 func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.User, newProjectID int64) error { 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 { return err } diff --git a/models/issues/label.go b/models/issues/label.go index 9ad488252ab7f..667a608687436 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -107,6 +108,7 @@ func (label *Label) CalOpenOrgIssues(repoID, labelID int64) { counts, _ := CountIssuesByRepo(&IssuesOptions{ RepoID: repoID, LabelIDs: []int64{labelID}, + IsClosed: util.OptionalBoolFalse, }) for _, count := range counts { diff --git a/models/issues/milestone.go b/models/issues/milestone.go index 6c1095910877f..c49799f391dc3 100644 --- a/models/issues/milestone.go +++ b/models/issues/milestone.go @@ -124,6 +124,11 @@ func NewMilestone(m *Milestone) (err error) { 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. func GetMilestoneByRepoID(ctx context.Context, repoID, id int64) (*Milestone, error) { m := new(Milestone) @@ -356,7 +361,7 @@ func (opts GetMilestonesOption) toCond() builder.Cond { } if len(opts.Name) != 0 { - cond = cond.And(builder.Like{"name", opts.Name}) + cond = cond.And(builder.Like{"UPPER(name)", strings.ToUpper(opts.Name)}) } return cond diff --git a/models/issues/pull.go b/models/issues/pull.go index f2ca19b03e9a5..0524acb21118f 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -122,6 +122,7 @@ const ( PullRequestStatusManuallyMerged PullRequestStatusError PullRequestStatusEmpty + PullRequestStatusAncestor ) // PullRequestFlow the flow of pull request @@ -423,6 +424,11 @@ func (pr *PullRequest) IsEmpty() bool { 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 func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) { if pr.HasMerged { diff --git a/models/issues/review.go b/models/issues/review.go index ee65bec3f8825..c43e688ad1321 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -474,6 +475,35 @@ func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, co 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 func GetReviewersByIssueID(issueID int64) ([]*Review, error) { reviews := make([]*Review, 0, 10) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index c843e33eb7805..0d35ac78d3ece 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -56,6 +56,9 @@ type Version struct { 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. // If you want to "retire" a migration, remove it from the top of the list and // update minDBVersion accordingly @@ -351,7 +354,7 @@ var migrations = []Migration{ // v198 -> v199 NewMigration("Add issue content history table", addTableIssueContentHistory), // v199 -> v200 - NewMigration("No-op (remote version is using AppState now)", addRemoteVersionTableNoop), + NewMigration("No-op (remote version is using AppState now)", noopMigration), // v200 -> v201 NewMigration("Add table app_state", addTableAppState), // v201 -> v202 @@ -388,7 +391,11 @@ var migrations = []Migration{ // v215 -> v216 NewMigration("allow to view files in PRs", addReviewViewedFiles), // v216 -> v217 - NewMigration("Improve Action table indices", improveActionTableIndices), + NewMigration("No-op (Improve Action table indices v1)", noopMigration), + // v217 -> v218 + NewMigration("Alter hook_task table TEXT fields to LONGTEXT", alterHookTaskTextFieldsToLongText), + // v218 -> v219 + NewMigration("Improve Action table indices v2", improveActionTableIndices), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v199.go b/models/migrations/v199.go index 4351ba4fa80bd..29f9d49dbeaee 100644 --- a/models/migrations/v199.go +++ b/models/migrations/v199.go @@ -4,11 +4,4 @@ package migrations -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 -} +// We used to use a table `remote_version` to store information for updater, now we use `AppState`, so this migration task is a no-op now. diff --git a/models/migrations/v216.go b/models/migrations/v216.go index b011c11d95e32..ab44808402e9b 100644 --- a/models/migrations/v216.go +++ b/models/migrations/v216.go @@ -4,64 +4,5 @@ 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 (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{}) -} +// This migration added non-ideal indices to the action table which on larger datasets slowed things down +// it has been superceded by v218.go diff --git a/models/migrations/v217.go b/models/migrations/v217.go new file mode 100644 index 0000000000000..280dba17a917e --- /dev/null +++ b/models/migrations/v217.go @@ -0,0 +1,26 @@ +// 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() +} diff --git a/models/migrations/v218.go b/models/migrations/v218.go new file mode 100644 index 0000000000000..dee8e5517e54a --- /dev/null +++ b/models/migrations/v218.go @@ -0,0 +1,46 @@ +// 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{}) +} diff --git a/models/perm/access/access.go b/models/perm/access/access.go index 7647519025977..0e5e4ff2bb5d2 100644 --- a/models/perm/access/access.go +++ b/models/perm/access/access.go @@ -86,7 +86,13 @@ func updateUserAccess(accessMap map[int64]*userAccess, user *user_model.User, mo // 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) { minMode := perm.AccessModeRead - if !repo.IsPrivate { + if err := repo.GetOwner(ctx); err != nil { + 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 } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index baea46dbce68f..7f9af553747cb 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -107,6 +107,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) { setting.Packages.Storage.Path = filepath.Join(setting.AppDataPath, "packages") + setting.Git.HomePath = filepath.Join(setting.AppDataPath, "home") + if err = storage.Init(); err != nil { fatalTestError("storage.Init: %v\n", err) } diff --git a/models/user/user.go b/models/user/user.go index f7d457b91b5a5..490e7223ce638 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -316,37 +316,45 @@ func (u *User) GenerateEmailActivateCode(email string) string { } // GetUserFollowers returns range of user's followers. -func GetUserFollowers(u *User, listOptions db.ListOptions) ([]*User, error) { - sess := db.GetEngine(db.DefaultContext). +func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { + sess := db.GetEngine(ctx). + Select("`user`.*"). + Join("LEFT", "follow", "`user`.id=follow.user_id"). Where("follow.follow_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.user_id") + And(isUserVisibleToViewerCond(viewer)) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } users := make([]*User, 0, 8) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } // GetUserFollowing returns range of user's following. -func GetUserFollowing(u *User, listOptions db.ListOptions) ([]*User, error) { +func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { sess := db.GetEngine(db.DefaultContext). + Select("`user`.*"). + Join("LEFT", "follow", "`user`.id=follow.follow_id"). Where("follow.user_id=?", u.ID). - Join("LEFT", "follow", "`user`.id=follow.follow_id") + And(isUserVisibleToViewerCond(viewer)) if listOptions.Page != 0 { sess = db.SetSessionPagination(sess, &listOptions) users := make([]*User, 0, listOptions.PageSize) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } users := make([]*User, 0, 8) - return users, sess.Find(&users) + count, err := sess.FindAndCount(&users) + return users, count, err } // NewGitSig generates and returns the signature of given user. @@ -485,6 +493,9 @@ func (u *User) GitName() string { // ShortName ellipses username to length 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) } @@ -1219,6 +1230,39 @@ func GetAdminUser() (*User, error) { 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 func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool { if viewer != nil && viewer.IsAdmin { diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go index c71b18f66253b..aff94fb38c3f4 100644 --- a/models/webhook/hooktask.go +++ b/models/webhook/hooktask.go @@ -105,7 +105,7 @@ type HookTask struct { HookID int64 UUID string api.Payloader `xorm:"-"` - PayloadContent string `xorm:"TEXT"` + PayloadContent string `xorm:"LONGTEXT"` EventType HookEventType IsDelivered bool Delivered int64 @@ -113,9 +113,9 @@ type HookTask struct { // History info. IsSucceed bool - RequestContent string `xorm:"TEXT"` + RequestContent string `xorm:"LONGTEXT"` RequestInfo *HookRequest `xorm:"-"` - ResponseContent string `xorm:"TEXT"` + ResponseContent string `xorm:"LONGTEXT"` ResponseInfo *HookResponse `xorm:"-"` } diff --git a/modules/context/api.go b/modules/context/api.go index 33534dbf6bf49..6a99de284a1b3 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -16,6 +16,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web/middleware" @@ -268,6 +269,7 @@ 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.Data["Context"] = &ctx diff --git a/modules/context/auth.go b/modules/context/auth.go index 09c22954550c7..e6d882eb5b34e 100644 --- a/modules/context/auth.go +++ b/modules/context/auth.go @@ -7,6 +7,7 @@ package context import ( "net/http" + "strings" "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/modules/log" @@ -41,6 +42,10 @@ func Toggle(options *ToggleOptions) func(ctx *Context) { if ctx.Doer.MustChangePassword { 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["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password" if ctx.Req.URL.Path != "/user/events" { diff --git a/modules/context/context.go b/modules/context/context.go index dcc43973ca244..a199721197a87 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -28,6 +28,7 @@ import ( "code.gitea.io/gitea/modules/base" mc "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -767,6 +768,7 @@ 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.Data["CsrfToken"] = ctx.csrf.GetToken() diff --git a/modules/context/repo.go b/modules/context/repo.go index 8e75ad07d58f2..217cbd3dfca33 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -942,6 +942,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName) if err != nil { + if git.IsErrNotExist(err) { + ctx.NotFound("GetTagCommit", err) + return + } ctx.ServerError("GetTagCommit", err) return } diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go index 349a2121cf572..f8b62e898a4bd 100644 --- a/modules/doctor/dbconsistency.go +++ b/modules/doctor/dbconsistency.go @@ -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"), // find 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 genericOrphanCheck("Orphaned Stopwatches without existing Issue", - "stopwatch", "issue", "stopwatch.issue_id=issue.id"), + "stopwatch", "issue", "stopwatch.issue_id=`issue`.id"), ) for _, c := range consistencyChecks { diff --git a/modules/git/command.go b/modules/git/command.go index d71497f1d791e..a1bacbb707a25 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -105,23 +105,36 @@ type RunOpts struct { PipelineFunc func(context.Context, context.CancelFunc) error } -// CommonGitCmdEnvs returns the common environment variables for a "git" command. -func CommonGitCmdEnvs() []string { +func commonBaseEnvs() []string { // at the moment, do not set "GIT_CONFIG_NOSYSTEM", users may have put some configs like "receive.certNonceSeed" in it - return []string{ - 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) + 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. +func CommonGitCmdEnvs() []string { + return append(commonBaseEnvs(), []string{ + "LC_ALL=" + DefaultLocale, + "GIT_TERMINAL_PROMPT=0", // avoid prompting for credentials interactively, supported since git v2.3 + }...) } // CommonCmdServEnvs is like CommonGitCmdEnvs but it only returns minimal required environment variables for the "gitea serv" command func CommonCmdServEnvs() []string { - 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 - } + return commonBaseEnvs() } // Run runs the command with the RunOpts diff --git a/modules/git/git.go b/modules/git/git.go index 3a3663995b3f0..b8317396c0150 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -11,6 +11,8 @@ import ( "fmt" "os" "os/exec" + "path/filepath" + "regexp" "runtime" "strings" "sync" @@ -125,8 +127,8 @@ func VersionInfo() string { } func checkInit() error { - if setting.RepoRootPath == "" { - return errors.New("can not init Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") + if setting.Git.HomePath == "" { + return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules") } if DefaultContext != nil { log.Warn("git module has been initialized already, duplicate init should be fixed") @@ -136,14 +138,14 @@ func checkInit() error { // HomeDir is the home dir for git to store the global config file used by Gitea internally func HomeDir() string { - if setting.RepoRootPath == "" { + if setting.Git.HomePath == "" { // 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. // 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("can not get Git's HomeDir (RepoRootPath is empty), the setting and git modules are not initialized correctly") + log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules") return "" } - return setting.RepoRootPath + return setting.Git.HomePath } // InitSimple initializes git module with a very simple step, no config changes, no global command arguments. @@ -174,11 +176,15 @@ func InitOnceWithSync(ctx context.Context) (err error) { } initOnce.Do(func() { - err = InitSimple(ctx) - if err != nil { + if err = InitSimple(ctx); err != nil { 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 if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") @@ -205,7 +211,7 @@ func InitOnceWithSync(ctx context.Context) (err error) { // syncGitConfig only modifies gitconfig, won't change global variables (otherwise there will be data-race problem) func syncGitConfig() (err error) { if err = os.MkdirAll(HomeDir(), os.ModePerm); err != nil { - return fmt.Errorf("unable to create directory %s, err: %w", setting.RepoRootPath, err) + return fmt.Errorf("unable to prepare git home directory %s, err: %w", HomeDir(), 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" @@ -337,7 +343,7 @@ func configSetNonExist(key, value string) error { } func configAddNonExist(key, value string) error { - _, _, err := NewCommand(DefaultContext, "config", "--fixed-value", "--get", key, value).RunStdString(nil) + _, _, err := NewCommand(DefaultContext, "config", "--get", key, regexp.QuoteMeta(value)).RunStdString(nil) if err == nil { // already exist return nil @@ -357,7 +363,7 @@ func configUnsetAll(key, value string) error { _, _, err := NewCommand(DefaultContext, "config", "--get", key).RunStdString(nil) if err == nil { // exist, need to remove - _, _, err = NewCommand(DefaultContext, "config", "--global", "--fixed-value", "--unset-all", key, value).RunStdString(nil) + _, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all", key, regexp.QuoteMeta(value)).RunStdString(nil) if err != nil { return fmt.Errorf("failed to unset git global config %s, err: %w", key, err) } diff --git a/modules/git/git_test.go b/modules/git/git_test.go index 061c876cde0a1..c5a63de0644c0 100644 --- a/modules/git/git_test.go +++ b/modules/git/git_test.go @@ -21,12 +21,12 @@ import ( func testRun(m *testing.M) error { _ = log.NewLogger(1000, "console", "console", `{"level":"trace","stacktracelevel":"NONE","stderr":true}`) - repoRootPath, err := os.MkdirTemp(os.TempDir(), "repos") + gitHomePath, err := os.MkdirTemp(os.TempDir(), "git-home") if err != nil { return fmt.Errorf("unable to create temp dir: %w", err) } - defer util.RemoveAll(repoRootPath) - setting.RepoRootPath = repoRootPath + defer util.RemoveAll(gitHomePath) + setting.Git.HomePath = gitHomePath if err = InitOnceWithSync(context.Background()); err != nil { return fmt.Errorf("failed to call Init: %w", err) @@ -78,4 +78,10 @@ func TestGitConfig(t *testing.T) { assert.NoError(t, configUnsetAll("test.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 = *")) } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index ffd0a0991bf43..e1e117ff4b842 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -8,6 +8,7 @@ import ( "bufio" "bytes" "context" + "errors" "io" "path" "sort" @@ -62,9 +63,10 @@ func LogNameStatusRepo(ctx context.Context, repository, head, treepath string, p }) if err != nil { _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) - } else { - _ = stdoutWriter.Close() + return } + + _ = stdoutWriter.Close() }() // For simplicities sake we'll us a buffered reader to read from the cat-file --batch @@ -354,7 +356,7 @@ heaploop: } current, err := g.Next(treepath, path2idx, changed, maxpathlen) if err != nil { - if err == context.DeadlineExceeded { + if errors.Is(err, context.DeadlineExceeded) { break heaploop } g.Close() diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go index 5797e981cf80f..750233d4a71c2 100644 --- a/modules/httpcache/httpcache.go +++ b/modules/httpcache/httpcache.go @@ -17,16 +17,23 @@ import ( ) // AddCacheControlToHeader adds suitable cache-control headers to response -func AddCacheControlToHeader(h http.Header, d time.Duration) { +func AddCacheControlToHeader(h http.Header, maxAge time.Duration, additionalDirectives ...string) { + directives := make([]string, 0, 2+len(additionalDirectives)) + if setting.IsProd { - h.Set("Cache-Control", "private, max-age="+strconv.Itoa(int(d.Seconds()))) + if maxAge == 0 { + directives = append(directives, "no-store") + } else { + directives = append(directives, "private", "max-age="+strconv.Itoa(int(maxAge.Seconds()))) + } } else { - h.Set("Cache-Control", "no-store") + directives = append(directives, "no-store") + // 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", "CacheControl=no-store") } + + h.Set("Cache-Control", strings.Join(append(directives, additionalDirectives...), ", ")) } // generateETag generates an ETag based on size, filename and file modification time diff --git a/modules/indexer/code/elastic_search.go b/modules/indexer/code/elastic_search.go index 7263f27657c8e..1f6c7d1840cbd 100644 --- a/modules/indexer/code/elastic_search.go +++ b/modules/indexer/code/elastic_search.go @@ -284,7 +284,7 @@ func (b *ElasticSearchIndexer) Index(ctx context.Context, repo *repo_model.Repos reqs := make([]elastic.BulkableRequest, 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! - if err := git.EnsureValidGitRepository(git.DefaultContext, repo.RepoPath()); err != nil { + if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil { log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) return err } diff --git a/modules/markup/html.go b/modules/markup/html.go index 69d9ba3ef2a46..6071180501c42 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -841,9 +841,10 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { // 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 - if hasExtTrackFormat && !isNumericStyle { + if hasExtTrackFormat && !isNumericStyle && refNumeric != nil { // If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that - if foundNumeric && refNumeric.RefLocation.Start < ref.RefLocation.Start { + // Allow a free-pass when non-numeric pattern wasn't found. + if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) { found = foundNumeric ref = refNumeric } diff --git a/modules/migration/null_downloader.go b/modules/migration/null_downloader.go index 32da720f1601f..ad925c32ce3cf 100644 --- a/modules/migration/null_downloader.go +++ b/modules/migration/null_downloader.go @@ -19,52 +19,52 @@ func (n NullDownloader) SetContext(_ context.Context) {} // GetRepoInfo returns a repository information func (n NullDownloader) GetRepoInfo() (*Repository, error) { - return nil, &ErrNotSupported{Entity: "RepoInfo"} + return nil, ErrNotSupported{Entity: "RepoInfo"} } // GetTopics return repository topics func (n NullDownloader) GetTopics() ([]string, error) { - return nil, &ErrNotSupported{Entity: "Topics"} + return nil, ErrNotSupported{Entity: "Topics"} } // GetMilestones returns milestones func (n NullDownloader) GetMilestones() ([]*Milestone, error) { - return nil, &ErrNotSupported{Entity: "Milestones"} + return nil, ErrNotSupported{Entity: "Milestones"} } // GetReleases returns releases func (n NullDownloader) GetReleases() ([]*Release, error) { - return nil, &ErrNotSupported{Entity: "Releases"} + return nil, ErrNotSupported{Entity: "Releases"} } // GetLabels returns labels func (n NullDownloader) GetLabels() ([]*Label, error) { - return nil, &ErrNotSupported{Entity: "Labels"} + return nil, ErrNotSupported{Entity: "Labels"} } // GetIssues returns issues according start and limit 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 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 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 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 func (n NullDownloader) GetReviews(reviewable Reviewable) ([]*Review, error) { - return nil, &ErrNotSupported{Entity: "Reviews"} + return nil, ErrNotSupported{Entity: "Reviews"} } // FormatCloneURL add authentication into remote URLs diff --git a/modules/setting/attachment.go b/modules/setting/attachment.go index c3c2c3fafdb7e..474b73293cceb 100644 --- a/modules/setting/attachment.go +++ b/modules/setting/attachment.go @@ -27,7 +27,7 @@ func newAttachmentService() { Attachment.Storage = getStorage("attachments", storageType, sec) - Attachment.AllowedTypes = sec.Key("ALLOWED_TYPES").MustString(".docx,.gif,.gz,.jpeg,.jpg,.mp4,.log,.pdf,.png,.pptx,.txt,.xlsx,.zip") + 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.MaxSize = sec.Key("MAX_SIZE").MustInt64(4) Attachment.MaxFiles = sec.Key("MAX_FILES").MustInt(5) Attachment.Enabled = sec.Key("ENABLED").MustBool(true) diff --git a/modules/setting/federation.go b/modules/setting/federation.go index fd39e5c7c2aff..258a666aee81d 100644 --- a/modules/setting/federation.go +++ b/modules/setting/federation.go @@ -12,7 +12,7 @@ var ( Enabled bool ShareUserStatistics bool }{ - Enabled: true, + Enabled: false, ShareUserStatistics: true, } ) diff --git a/modules/setting/git.go b/modules/setting/git.go index 9b2698f01e780..266bbc3c5aa3a 100644 --- a/modules/setting/git.go +++ b/modules/setting/git.go @@ -5,6 +5,7 @@ package setting import ( + "path/filepath" "time" "code.gitea.io/gitea/modules/log" @@ -13,6 +14,7 @@ import ( // Git settings var Git = struct { Path string + HomePath string DisableDiffHighlight bool MaxGitDiffLines int MaxGitDiffLineCharacters int @@ -67,7 +69,16 @@ var Git = struct { } func newGit() { - if err := Cfg.Section("git").MapTo(&Git); err != nil { + sec := Cfg.Section("git") + + if err := sec.MapTo(&Git); err != nil { 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) + } } diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 88f306b3fa7e3..f5e624946733a 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -170,7 +170,7 @@ var ( ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, KeygenPath: "ssh-keygen", MinimumKeySizeCheck: true, - MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2048}, + MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", PerWriteTimeout: PerWriteTimeout, @@ -843,8 +843,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) { SSH.StartBuiltinServer = false } - trustedUserCaKeys := sec.Key("SSH_TRUSTED_USER_CA_KEYS").Strings(",") - for _, caKey := range trustedUserCaKeys { + SSH.TrustedUserCAKeysFile = sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem")) + + for _, caKey := range SSH.TrustedUserCAKeys { pubKey, _, _, _, err := gossh.ParseAuthorizedKey([]byte(caKey)) if err != nil { log.Fatal("Failed to parse TrustedUserCaKeys: %s %v", caKey, err) @@ -852,7 +853,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) { SSH.TrustedUserCAKeysParsed = append(SSH.TrustedUserCAKeysParsed, pubKey) } - if len(trustedUserCaKeys) > 0 { + if len(SSH.TrustedUserCAKeys) > 0 { // Set the default as email,username otherwise we can leave it empty sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").MustString("username,email") } else { @@ -861,22 +862,6 @@ func loadFromConf(allowEmpty bool, extraConfig string) { 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) minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys() for _, key := range minimumKeySizes { diff --git a/modules/ssh/init.go b/modules/ssh/init.go new file mode 100644 index 0000000000000..f6332bb18b724 --- /dev/null +++ b/modules/ssh/init.go @@ -0,0 +1,55 @@ +// 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 +} diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go index 98fe17b3bc611..9b91baf09e9bb 100644 --- a/modules/ssh/ssh_graceful.go +++ b/modules/ssh/ssh_graceful.go @@ -29,7 +29,7 @@ func listen(server *ssh.Server) { log.Info("SSH Listener: %s Closed", server.Addr) } -// Unused informs our cleanup routine that we will not be using a ssh port -func Unused() { +// builtinUnused informs our cleanup routine that we will not be using a ssh port +func builtinUnused() { graceful.GetManager().InformCleanup() } diff --git a/modules/structs/pull_review.go b/modules/structs/pull_review.go index 6544604acbaed..7c9360a0c2200 100644 --- a/modules/structs/pull_review.go +++ b/modules/structs/pull_review.go @@ -97,6 +97,7 @@ type SubmitPullReviewOptions struct { // DismissPullReviewOptions are options to dismiss a pull review type DismissPullReviewOptions struct { Message string `json:"message"` + Priors bool `json:"priors"` } // PullReviewRequestOptions are options to add or remove pull review requests diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 347022fbdb39b..3c03dc9c44d04 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1531,7 +1531,8 @@ pulls.remove_prefix = Remove %s prefix 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.is_checking = "Merge conflict checking is in progress. Try again in few moments." -pulls.is_empty = "This branch is equal with the target branch." +pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge." +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_missing = Some required checks are missing. pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 03f7a57d5c861..ff29d7fb50bf9 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1010,7 +1010,7 @@ func Routes() *web.Route { }, mustAllowPulls, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo()) m.Group("/statuses", func() { m.Combo("/{sha}").Get(repo.GetCommitStatuses). - Post(reqToken(), bind(api.CreateStatusOption{}), repo.NewCommitStatus) + Post(reqToken(), reqRepoWriter(unit.TypeCode), bind(api.CreateStatusOption{}), repo.NewCommitStatus) }, reqRepoReader(unit.TypeCode)) m.Group("/commits", func() { m.Get("", context.ReferencesGitRepo(), repo.GetAllCommits) diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index 57548f2102911..f36d0586ab6b9 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -823,7 +823,7 @@ func DismissPullReview(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" opts := web.GetForm(ctx).(*api.DismissPullReviewOptions) - dismissReview(ctx, opts.Message, true) + dismissReview(ctx, opts.Message, true, opts.Priors) } // UnDismissPullReview cancel to dismiss a review for a pull request @@ -863,10 +863,10 @@ func UnDismissPullReview(ctx *context.APIContext) { // "$ref": "#/responses/forbidden" // "422": // "$ref": "#/responses/validationError" - dismissReview(ctx, "", false) + dismissReview(ctx, "", false, false) } -func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { +func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors bool) { if !ctx.Repo.IsAdmin() { ctx.Error(http.StatusForbidden, "", "Must be repo admin") return @@ -886,7 +886,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss bool) { return } - _, err := pull_service.DismissReview(ctx, review.ID, msg, ctx.Doer, isDismiss) + _, err := pull_service.DismissReview(ctx, review.ID, ctx.Repo.Repository.ID, msg, ctx.Doer, isDismiss, dismissPriors) if err != nil { ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err) return diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 8dfe7e06d26f8..80009f78e99c0 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -224,6 +224,7 @@ func CreateRelease(ctx *context.APIContext) { rel.IsTag = false rel.Repo = ctx.Repo.Repository rel.Publisher = ctx.Doer + rel.Target = form.Target if err = release_service.UpdateRelease(ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 1cc2c50dc20d8..64dc763dc3811 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -240,6 +240,7 @@ func DeleteTopic(ctx *context.APIContext) { if topic == nil { ctx.NotFound() + return } ctx.Status(http.StatusNoContent) diff --git a/routers/api/v1/user/follower.go b/routers/api/v1/user/follower.go index 3c81b27f8dad4..22f8f40e1c810 100644 --- a/routers/api/v1/user/follower.go +++ b/routers/api/v1/user/follower.go @@ -24,13 +24,13 @@ func responseAPIUsers(ctx *context.APIContext, users []*user_model.User) { } func listUserFollowers(ctx *context.APIContext, u *user_model.User) { - users, err := user_model.GetUserFollowers(u, utils.GetListOptions(ctx)) + users, count, err := user_model.GetUserFollowers(ctx, u, ctx.Doer, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) return } - ctx.SetTotalCountHeader(int64(u.NumFollowers)) + ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } @@ -86,13 +86,13 @@ func ListFollowers(ctx *context.APIContext) { } func listUserFollowing(ctx *context.APIContext, u *user_model.User) { - users, err := user_model.GetUserFollowing(u, utils.GetListOptions(ctx)) + users, count, err := user_model.GetUserFollowing(ctx, u, ctx.Doer, utils.GetListOptions(ctx)) if err != nil { ctx.Error(http.StatusInternalServerError, "GetUserFollowing", err) return } - ctx.SetTotalCountHeader(int64(u.NumFollowing)) + ctx.SetTotalCountHeader(count) responseAPIUsers(ctx, users) } diff --git a/routers/init.go b/routers/init.go index 2898c446072f1..e640ca48453bc 100644 --- a/routers/init.go +++ b/routers/init.go @@ -6,10 +6,8 @@ package routers import ( "context" - "net" "reflect" "runtime" - "strconv" "code.gitea.io/gitea/models" asymkey_model "code.gitea.io/gitea/models/asymkey" @@ -143,7 +141,6 @@ func GlobalInitInstalled(ctx context.Context) { mustInit(repo_service.Init) // Booting long running goroutines. - cron.NewContext(ctx) issue_indexer.InitIssueIndexer(false) code_indexer.Init() mustInit(stats_indexer.Init) @@ -158,16 +155,13 @@ func GlobalInitInstalled(ctx context.Context) { mustInitCtx(ctx, syncAppPathForGit) - if setting.SSH.StartBuiltinServer { - ssh.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) - } else { - ssh.Unused() - } + mustInit(ssh.Init) + auth.Init() svg.Init() + + // Finally start up the cron + cron.NewContext(ctx) } // NormalRoutes represents non install routes diff --git a/routers/install/routes.go b/routers/install/routes.go index e77081afe0a8b..d4a0465818dbe 100644 --- a/routers/install/routes.go +++ b/routers/install/routes.go @@ -9,6 +9,7 @@ import ( "net/http" "path" + "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/setting" @@ -62,6 +63,7 @@ func installRecovery() func(next http.Handler) http.Handler { "SignedUserName": "", } + httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform") w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) if !setting.IsProd { diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index d868b05a44a25..56f8294b1a9d9 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -37,6 +37,7 @@ import ( "gitea.com/go-chi/binding" "github.com/golang-jwt/jwt/v4" "github.com/markbates/goth" + "github.com/markbates/goth/gothic" ) const ( @@ -1098,24 +1099,31 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model func oAuth2UserLoginCallback(authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { oauth2Source := authSource.Cfg.(*oauth2.Source) + // Make sure that the response is not an error response. + errorName := request.FormValue("error") + + if len(errorName) > 0 { + errorDescription := request.FormValue("error_description") + + // Delete the goth session + err := gothic.Logout(response, request) + if err != nil { + return nil, goth.User{}, err + } + + return nil, goth.User{}, errCallback{ + Code: errorName, + Description: errorDescription, + } + } + + // Proceed to authenticate through goth. gothUser, err := oauth2Source.Callback(request, response) if err != nil { if err.Error() == "securecookie: the value is too long" || strings.Contains(err.Error(), "Data too long") { log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) err = fmt.Errorf("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) } - // goth does not provide the original error message - // https://github.com/markbates/goth/issues/348 - if strings.Contains(err.Error(), "server response missing access_token") || strings.Contains(err.Error(), "could not find a matching session for this request") { - errorCode := request.FormValue("error") - errorDescription := request.FormValue("error_description") - if errorCode != "" || errorDescription != "" { - return nil, goth.User{}, errCallback{ - Code: errorCode, - Description: errorDescription, - } - } - } return nil, goth.User{}, err } diff --git a/routers/web/base.go b/routers/web/base.go index 5f817c77ce78b..f843c5798444a 100644 --- a/routers/web/base.go +++ b/routers/web/base.go @@ -158,6 +158,7 @@ func Recovery() func(next http.Handler) http.Handler { store["SignedUserName"] = "" } + httpcache.AddCacheControlToHeader(w.Header(), 0, "no-transform") w.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) if !setting.IsProd { diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 11d2daeeff0da..e6f9529e31e81 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -803,7 +803,8 @@ func NewIssue(ctx *context.Context) { body := ctx.FormString("body") ctx.Data["BodyQuery"] = body - ctx.Data["IsProjectsEnabled"] = ctx.Repo.CanRead(unit.TypeProjects) + isProjectsEnabled := ctx.Repo.CanRead(unit.TypeProjects) + ctx.Data["IsProjectsEnabled"] = isProjectsEnabled ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled upload.AddUploadContext(ctx, "comment") @@ -819,7 +820,7 @@ func NewIssue(ctx *context.Context) { } projectID := ctx.FormInt64("project") - if projectID > 0 { + if projectID > 0 && isProjectsEnabled { project, err := project_model.GetProjectByID(ctx, projectID) if err != nil { log.Error("GetProjectByID: %d: %v", projectID, err) @@ -878,6 +879,11 @@ func DeleteIssue(ctx *context.Context) { return } + if issue.IsPull { + ctx.Redirect(fmt.Sprintf("%s/pulls", ctx.Repo.Repository.HTMLURL()), http.StatusSeeOther) + return + } + ctx.Redirect(fmt.Sprintf("%s/issues", ctx.Repo.Repository.HTMLURL()), http.StatusSeeOther) } @@ -1038,6 +1044,11 @@ func NewIssuePost(ctx *context.Context) { } if projectID > 0 { + if !ctx.Repo.CanRead(unit.TypeProjects) { + // User must also be able to see the project. + ctx.Error(http.StatusBadRequest, "user hasn't permissions to read projects") + return + } if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { ctx.ServerError("ChangeProjectAssign", err) return @@ -1778,6 +1789,10 @@ func getActionIssues(ctx *context.Context) []*issues_model.Issue { issueUnitEnabled := ctx.Repo.CanRead(unit.TypeIssues) prUnitEnabled := ctx.Repo.CanRead(unit.TypePullRequests) for _, issue := range issues { + if issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("some issue's RepoID is incorrect", errors.New("some issue's RepoID is incorrect")) + return nil + } if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) return nil diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 51c891dbf0b34..71f264830d460 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "net/http" "net/url" @@ -633,10 +634,17 @@ func MoveIssues(ctx *context.Context) { } if len(movedIssues) != len(form.Issues) { - ctx.ServerError("IssuesNotFound", err) + ctx.ServerError("some issues do not exist", errors.New("some issues do not exist")) return } + for _, issue := range movedIssues { + if issue.RepoID != project.RepoID { + ctx.ServerError("Some issue's repoID is not equal to project's repoID", errors.New("Some issue's repoID is not equal to project's repoID")) + return + } + } + if err = project_model.MoveIssuesOnProjectBoard(board, sortedIssueIDs); err != nil { ctx.ServerError("MoveIssuesOnProjectBoard", err) return diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index cc7ae9bbfa3df..bc64f35472ea4 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -5,6 +5,7 @@ package repo import ( + "errors" "fmt" "net/http" @@ -118,6 +119,11 @@ func UpdateResolveConversation(ctx *context.Context) { return } + if comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.NotFound("comment's repoID is incorrect", errors.New("comment's repoID is incorrect")) + return + } + var permResult bool if permResult, err = issues_model.CanMarkConversation(comment.Issue, ctx.Doer); err != nil { ctx.ServerError("CanMarkConversation", err) @@ -236,7 +242,7 @@ func SubmitReview(ctx *context.Context) { // DismissReview dismissing stale review by repo admin func DismissReview(ctx *context.Context) { form := web.GetForm(ctx).(*forms.DismissReviewForm) - comm, err := pull_service.DismissReview(ctx, form.ReviewID, form.Message, ctx.Doer, true) + comm, err := pull_service.DismissReview(ctx, form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.Doer, true, true) if err != nil { ctx.ServerError("pull_service.DismissReview", err) return diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 666294631ced5..df953fd0b95f7 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -98,7 +98,14 @@ func releasesOrTags(ctx *context.Context, isTagList bool) { listOptions.PageSize = setting.API.MaxResponseItems } - tags, err := ctx.Repo.GitRepo.GetTags(listOptions.GetStartEnd()) + // TODO(20073) tags are used for compare feature witch needs all tags + // filtering is doen at the client side atm + tagListStart, tagListEnd := 0, 0 + if isTagList { + tagListStart, tagListEnd = listOptions.GetStartEnd() + } + + tags, err := ctx.Repo.GitRepo.GetTags(tagListStart, tagListEnd) if err != nil { ctx.ServerError("GetTags", err) return diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index fae62c102077c..57e44630f6890 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -474,7 +474,7 @@ func SettingsPost(ctx *context.Context) { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects) } - if form.EnablePackages && !unit_model.TypeProjects.UnitGlobalDisabled() { + if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() { units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: unit_model.TypePackages, diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index c327f959f6556..169c6fa82d1c2 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -854,15 +854,15 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri } ctx.Data["LatestCommitVerification"] = verification ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(latestCommit) - } - statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), db.ListOptions{}) - if err != nil { - log.Error("GetLatestCommitStatus: %v", err) - } + statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptions{}) + if err != nil { + log.Error("GetLatestCommitStatus: %v", err) + } - ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses) - ctx.Data["LatestCommitStatuses"] = statuses + ctx.Data["LatestCommitStatus"] = git_model.CalcCommitStatus(statuses) + ctx.Data["LatestCommitStatuses"] = statuses + } branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() treeLink := branchLink diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 7fe80a2a4ba4f..698117e9571fb 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -141,6 +141,7 @@ func Dashboard(ctx *context.Context) { OnlyPerformedBy: false, IncludeDeleted: false, Date: ctx.FormString("date"), + ListOptions: db.ListOptions{PageSize: setting.UI.FeedPagingNum}, }) if err != nil { ctx.ServerError("GetFeeds", err) diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index dd5804cd42d5c..6f23d239e26a5 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -157,7 +157,7 @@ func Profile(ctx *context.Context) { switch tab { case "followers": - items, err := user_model.GetUserFollowers(ctx.ContextUser, db.ListOptions{ + items, count, err := user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -167,9 +167,9 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctx.ContextUser.NumFollowers + total = int(count) case "following": - items, err := user_model.GetUserFollowing(ctx.ContextUser, db.ListOptions{ + items, count, err := user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{ PageSize: setting.UI.User.RepoPagingNum, Page: page, }) @@ -179,7 +179,7 @@ func Profile(ctx *context.Context) { } ctx.Data["Cards"] = items - total = ctx.ContextUser.NumFollowing + total = int(count) case "activity": ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{ RequestedUser: ctx.ContextUser, @@ -188,6 +188,7 @@ func Profile(ctx *context.Context) { OnlyPerformedBy: true, IncludeDeleted: false, Date: ctx.FormString("date"), + ListOptions: db.ListOptions{PageSize: setting.UI.FeedPagingNum}, }) if err != nil { ctx.ServerError("GetFeeds", err) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 3e96cc7c85257..0228b5521bcee 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -34,6 +34,7 @@ func Account(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true ctx.Data["Email"] = ctx.Doer.Email + ctx.Data["EnableNotifyMail"] = setting.Service.EnableNotifyMail loadAccountData(ctx) diff --git a/routers/web/web.go b/routers/web/web.go index 374bafbc8dbcc..d594caf643401 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -721,7 +721,7 @@ func RegisterRoutes(m *web.Route) { }, reqPackageAccess(perm.AccessModeWrite)) }) }) - }, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) + }, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) } }, context_service.UserAssignmentWeb()) @@ -898,7 +898,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) - m.Post("/projects", reqRepoIssuesOrPullsWriter, repo.UpdateIssueProject) + m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(forms.DismissReviewForm{}), repo.DismissReview) diff --git a/services/auth/source/ldap/source_search.go b/services/auth/source/ldap/source_search.go index d01fd14c8b97f..1c0eb783d9997 100644 --- a/services/auth/source/ldap/source_search.go +++ b/services/auth/source/ldap/source_search.go @@ -199,7 +199,7 @@ func checkRestricted(l *ldap.Conn, ls *Source, userDN string) bool { // List all group memberships of a user func (ls *Source) listLdapGroupMemberships(l *ldap.Conn, uid string) []string { var ldapGroups []string - groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, uid) + groupFilter := fmt.Sprintf("(%s=%s)", ls.GroupMemberUID, ldap.EscapeFilter(uid)) result, err := l.Search(ldap.NewSearchRequest( ls.GroupDN, ldap.ScopeWholeSubtree, diff --git a/services/issue/issue.go b/services/issue/issue.go index ded281e209e2b..467bc14b84594 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -149,7 +149,7 @@ func DeleteIssue(doer *user_model.User, gitRepo *git.Repository, issue *issues_m // delete pull request related git data if issue.IsPull { - if err := gitRepo.RemoveReference(fmt.Sprintf("%s%d", git.PullPrefix, issue.PullRequest.Index)); err != nil { + if err := gitRepo.RemoveReference(fmt.Sprintf("%s%d/head", git.PullPrefix, issue.PullRequest.Index)); err != nil { return err } } diff --git a/services/issue/milestone.go b/services/issue/milestone.go index af337c3f142f5..d7c5fa45516f3 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -15,6 +15,17 @@ import ( ) func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) error { + // Only check if milestone exists if we don't remove it. + if issue.MilestoneID > 0 { + has, err := issues_model.HasMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) + if err != nil { + return fmt.Errorf("HasMilestoneByRepoID: %v", err) + } + if !has { + return fmt.Errorf("HasMilestoneByRepoID: issue doesn't exist") + } + } + if err := issues_model.UpdateIssueCols(ctx, issue, "milestone_id"); err != nil { return err } diff --git a/services/migrations/dump.go b/services/migrations/dump.go index 21d03b333f8cb..a9ec459519e56 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -590,7 +590,7 @@ func updateOptionsUnits(opts *base.MigrateOptions, units []string) error { opts.ReleaseAssets = true } else { for _, unit := range units { - switch strings.ToLower(unit) { + switch strings.ToLower(strings.TrimSpace(unit)) { case "": continue case "wiki": diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index e71b2ca17af35..c7a6f9b02f2c3 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -7,7 +7,6 @@ package migrations import ( "context" - "errors" "fmt" "io" "os" @@ -268,7 +267,7 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error { // calc NumCommits if possible if rel.TagName != "" { commit, err := g.gitRepo.GetTagCommit(rel.TagName) - if !errors.Is(err, git.ErrNotExist{}) { + if !git.IsErrNotExist(err) { if err != nil { return fmt.Errorf("GetTagCommit[%v]: %v", rel.TagName, err) } diff --git a/services/pull/check.go b/services/pull/check.go index 6621a281fa543..288f4dc0b73b7 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -89,7 +89,7 @@ func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *acce return ErrIsWorkInProgress } - if !pr.CanAutoMerge() { + if !pr.CanAutoMerge() && !pr.IsEmpty() { return ErrNotMergableState } diff --git a/services/pull/patch.go b/services/pull/patch.go index c7a69501c32f0..bb09acc89f63a 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -87,6 +87,14 @@ func TestPatch(pr *issues_model.PullRequest) error { } } pr.MergeBase = strings.TrimSpace(pr.MergeBase) + if pr.HeadCommitID, err = gitRepo.GetRefCommitID(git.BranchPrefix + "tracking"); err != nil { + return fmt.Errorf("GetBranchCommitID: can't find commit ID for head: %w", err) + } + + if pr.HeadCommitID == pr.MergeBase { + pr.Status = issues_model.PullRequestStatusAncestor + return nil + } // 2. Check for conflicts if conflicts, err := checkConflicts(ctx, pr, gitRepo, tmpBasePath); err != nil || conflicts || pr.Status == issues_model.PullRequestStatusEmpty { diff --git a/services/pull/review.go b/services/pull/review.go index 9cb58fa3a159d..bfc7b7cdd66d4 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) // CreateCodeComment creates a comment on the code line @@ -271,7 +272,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos } // DismissReview dismissing stale review by repo admin -func DismissReview(ctx context.Context, reviewID int64, message string, doer *user_model.User, isDismiss bool) (comment *issues_model.Comment, err error) { +func DismissReview(ctx context.Context, reviewID, repoID int64, message string, doer *user_model.User, isDismiss, dismissPriors bool) (comment *issues_model.Comment, err error) { review, err := issues_model.GetReviewByID(ctx, reviewID) if err != nil { return @@ -281,18 +282,40 @@ func DismissReview(ctx context.Context, reviewID int64, message string, doer *us return nil, fmt.Errorf("not need to dismiss this review because it's type is not Approve or change request") } + // load data for notify + if err = review.LoadAttributes(ctx); err != nil { + return nil, err + } + + // Check if the review's repoID is the one we're currently expecting. + if review.Issue.RepoID != repoID { + return nil, fmt.Errorf("reviews's repository is not the same as the one we expect") + } + if err = issues_model.DismissReview(review, isDismiss); err != nil { return } + if dismissPriors { + reviews, err := issues_model.GetReviews(ctx, &issues_model.GetReviewOptions{ + IssueID: review.IssueID, + ReviewerID: review.ReviewerID, + Dismissed: util.OptionalBoolFalse, + }) + if err != nil { + return nil, err + } + for _, oldReview := range reviews { + if err = issues_model.DismissReview(oldReview, true); err != nil { + return nil, err + } + } + } + if !isDismiss { return nil, nil } - // load data for notify - if err = review.LoadAttributes(ctx); err != nil { - return - } if err = review.Issue.LoadPullRequest(); err != nil { return } diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index fab1d2d0b11e4..e367575bca7c1 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -1,8 +1,22 @@ - + {{svg "octicon-bell"}} - {{.i18n.Tr "notifications"}} - {{$notificationUnreadCount := 0}} - {{if .NotificationUnreadCount}}{{$notificationUnreadCount = call .NotificationUnreadCount}}{{end}} {{$notificationUnreadCount}} diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 5a23bfa33b164..23aa97217d5d0 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -195,12 +195,12 @@ {{svg "octicon-sync"}} {{$.i18n.Tr "repo.pulls.is_checking"}} - {{else if .Issue.PullRequest.IsEmpty}} + {{else if .Issue.PullRequest.IsAncestor}}
{{svg "octicon-alert" 16}} - {{$.i18n.Tr "repo.pulls.is_empty"}} + {{$.i18n.Tr "repo.pulls.is_ancestor"}}
- {{else if .Issue.PullRequest.CanAutoMerge}} + {{else if or .Issue.PullRequest.CanAutoMerge .Issue.PullRequest.IsEmpty}} {{if .IsBlockedByApprovals}}
{{svg "octicon-x"}} @@ -282,7 +282,6 @@
{{end}} {{end}} - {{if and (gt .Issue.PullRequest.CommitsBehind 0) (not .Issue.IsClosed) (not .Issue.PullRequest.IsChecking) (not .IsPullFilesConflicted) (not .IsPullRequestBroken) (not $canAutoMerge)}}
@@ -321,6 +320,14 @@
{{end}} + {{if .Issue.PullRequest.IsEmpty}} +
+ +
+ {{svg "octicon-alert" 16}} + {{$.i18n.Tr "repo.pulls.is_empty"}} +
+ {{end}} {{if .AllowMerge}} {{/* user is allowed to merge */}} {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} @@ -348,6 +355,7 @@ 'canMergeNow': {{$canMergeNow}}, 'allOverridableChecksOk': {{not $notAllOverridableChecksOk}}, + 'emptyCommit': {{.Issue.PullRequest.IsEmpty}}, 'pullHeadCommitID': {{.PullHeadCommitID}}, 'isPullBranchDeletable': {{.IsPullBranchDeletable}}, 'defaultDeleteBranchAfterMerge': {{$prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge}}, diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 4da8b12af4bb1..63ef828606e9c 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -14803,6 +14803,10 @@ "message": { "type": "string", "x-go-name": "Message" + }, + "priors": { + "type": "boolean", + "x-go-name": "Priors" } }, "x-go-package": "code.gitea.io/gitea/modules/structs" diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index bd7d54b6705d9..9024d9ede68e3 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -61,11 +61,11 @@
diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 3662ec0fa7f02..1b3fd136de139 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -43,6 +43,7 @@
+ {{end}} {{range .Emails}}
{{if not .IsPrimary}} diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index cf13b2a559d30..213c9e41df5db 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -20,6 +20,11 @@ export function showGlobalErrorMessage(msg) { * @param {ErrorEvent} e */ function processWindowErrorEvent(e) { + if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) { + // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240 + // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0. + return; // ignore such nonsense error event + } showGlobalErrorMessage(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); } diff --git a/web_src/js/components/PullRequestMergeForm.vue b/web_src/js/components/PullRequestMergeForm.vue index 75fbceb8007a7..08b1f9cb86e90 100644 --- a/web_src/js/components/PullRequestMergeForm.vue +++ b/web_src/js/components/PullRequestMergeForm.vue @@ -48,7 +48,7 @@
-
+