Compare commits

..

93 Commits

Author SHA1 Message Date
Lunny Xiao
09b76295f1 Add changelog for v1.16.8 (#19724)
* Add changelog for v1.16.8

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-05-16 19:27:23 +02:00
Gusted
38acce2f3f Fix issue overview for teams (#19652) (#19653)
- Backport #19652
  - Don't use hacky solution to limit to the correct RepoID's, instead use current code to handle these limits. The existing code is more correct than the hacky solution.
  - Resolves #19636
2022-05-16 17:48:16 +08:00
6543
8f44d00f22 Delete user related oauth stuff on user deletion too (#19677) (#19680)
Backport (#19677)

* delete user related oauth stuff on user deletion too

* extend doctor check-db-consistency

* make it build for v1.16.x
2022-05-12 13:32:48 +02:00
Lunny Xiao
4386eb751f Fix oauth setting list bug (#19681) 2022-05-11 21:05:58 +08:00
Lunny Xiao
d6aab069ed Fix new release from tags list UI (#19670) (#19673) 2022-05-10 22:55:28 +02:00
singuliere
f4fb8dbc87 [doctor] Add check/fix for bogus action rows (#19656) (#19669)
Co-authored-by: Loïc Dachary <loic@dachary.org>

Conflicts:
	models/consistency_test.go
	 trivial context conflict.
2022-05-10 16:05:34 +03:00
Lunny Xiao
c7c18e0eb2 Revert "Add finalizers to ensure that repos are closed and blobreaders are closed (#19495) (#19496)" (#19659)
This reverts commit 88da50674f.

because it caused a memleak
2022-05-09 13:03:44 +02:00
singuliere
0a2d618d85 GetFeeds must always discard actions with dangling repo_id (#19598) (#19629)
Co-authored-by: Loïc Dachary <loic@dachary.org>
(cherry picked from commit b536b65189)

Conflicts:
        models/action_test.go
	  The GetFeeds function does not have a Context argument in 1.16.
	models/action.go
	  The SQL statement is essentially the same in 1.16 but
	  structured differently. The Join() was copied and the
   	  created_unix field prefixed with `action`.
	models/action_list.go
	  in 1.16 the loadRepoOwner method did not exist and
	  it was done in the RetrieveFeeds method of web/feed/profile.go.
          The safeguard to skip when act.Repo == nil was moved there.
2022-05-08 15:05:40 +02:00
Jimmy Praet
c8a83ace59 Only show accessible teams in dashboard dropdown list (#19642) (#19645)
Fixes #19637
2022-05-07 17:50:34 +02:00
techknowlogick
59d132f0b3 Set safe dir for git operations in .drone.yml CI (#19641) (#19643)
Our drone by necessity runs on git repositories not owned by the drone process. Unfortunately this means that git operations and thence CI builds will fail without the `safe.directory` option being set. 

See: https://drone.gitea.io/go-gitea/gitea/54632/2/8
2022-05-06 14:09:54 -04:00
zeripath
18dd49a4ab Prevent NPE when checking repo units if the user is nil (#19625) (#19630)
Backport #19625

CheckRepoUnitUser should tolerate nil users.

Fix #19613

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-05-06 18:01:08 +08:00
wxiaoguang
46637b1164 Call MultipartForm.RemoveAll when request finishes (#19606) (#19607) 2022-05-05 16:13:59 +02:00
Eekle
7b18c67ac9 Make .cs highlighting legible on dark themes (#19604) (#19605) 2022-05-04 18:45:00 +08:00
99rgosse
6eb3c05cb7 Avoid MoreThanOne Error (#19557) (#19591)
Backport #19557
2022-05-03 20:36:58 +08:00
Gusted
82f24bedc2 Fix sending empty notifications (#19589) (#19590)
- Backport #19589
  - Don't send empty notifications on read notifications API.
2022-05-02 21:28:12 -05:00
zeripath
88da50674f Add finalizers to ensure that repos are closed and blobreaders are closed (#19495) (#19496)
It may be prudent to add runtime finalizers to the git.Repository and
git.blobReader objects to absolutely ensure that these are both properly
cancelled, cleaned and closed out.

This commit is a backport of an extract from #19448

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-05-02 20:44:45 +08:00
wxiaoguang
35a7db49b4 ignore DNS error when doing migration allow/block check (#19567)
Co-authored-by: Lauris BH <lauris@nix.lv>
2022-05-02 08:11:45 +03:00
6543
f4729e2418 Add Changelog v1.16.7 (#19575)
Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: Gusted <williamzijl7@hotmail.com>
2022-05-02 05:41:09 +02:00
6543
f7330fd027 Dont overwrite err with nil (part #19572) (#19574)
* Dont overwrite err with nil (part #19572)


Co-authored-by: Gusted <williamzijl7@hotmail.com>
2022-05-02 01:54:20 +02:00
6543
755d8e21ad Migration: only write commit-graph if wiki clone was successfull (#19563) (#19568) 2022-05-01 00:22:42 +02:00
Jimmy Praet
7c0bf06d96 Respect DefaultUserIsRestricted system default when creating new user (#19310 ) (#19560) 2022-04-30 15:00:14 +02:00
Gusted
0d196e29e8 Don't error when branch's commit doesn't exist (#19547) (#19548)
- Backport #19547
  - If one of the branches no longer exists, don't throw an error, it's possible that the branch was destroyed during the process. Simply skip it and disregard it.
  - Resolves #19541
2022-04-29 12:25:19 +02:00
wxiaoguang
b86606fa38 Support hostname:port to pass host matcher's check (#19543) (#19544)
Backport #19543 
hostmatcher: split the hostname from the hostname:port string, use the correct hostname to do the match.
2022-04-29 01:41:58 +08:00
zeripath
74602bb487 Prevent intermittent race in attribute reader close (#19537) (#19539)
Backport #19537

There is a potential rare race possible whereby the c.running channel could
be closed twice. Looking at the code I do not see a need for this c.running
channel and therefore I think we can remove this. (I think the c.running
might have been some attempt to prevent a hang but the use of os.Pipes should
prevent that.)

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-04-28 17:00:01 +02:00
Gusted
1465e0cbb2 Fix 64-bit atomic operations on 32-bit machines (#19531) (#19532)
- Backport #19531
  - Doing 64-bit atomic operations on 32-bit machines is a bit tricky by golang, as they can only be done under certain set of conditions(https://pkg.go.dev/sync/atomic#pkg-note-BUG).
  - This PR fixes such case whereby the conditions weren't met, it moves the int64 to the first field of the struct, which will 64-bit operations happening on this property on 32-bit machines.
  - Resolves #19518
2022-04-27 10:32:28 -05:00
Lunny Xiao
928b603d19 Fix migrate release from github (#19510) (#19523)
* Fix migrate release from github

* Fix bug
2022-04-27 14:46:00 +02:00
Lunny Xiao
8ff542c1a2 When view _Siderbar or _Footer, just display once (#19501) (#19522)
Co-authored-by: zeripath <art27@cantab.net>
2022-04-27 14:04:53 +02:00
zeripath
39a0db6ecf Prevent dangling archiver goroutine (#19516) (#19526)
Backport #19516

Within doArchive there is a service goroutine that performs the
archiving function.  This goroutine reports its error using a `chan
error` called `done`. Prior to this PR this channel had 0 capacity
meaning that the goroutine would block until the `done` channel was
cleared - however there are a couple of ways in which this channel might
not be read.

The simplest solution is to add a single space of capacity to the
goroutine which will mean that the goroutine will always complete and
even if the `done` channel is not read it will be simply garbage
collected away.

(The PR also contains two other places when setting up the indexers
which do not leak but where the blocking of the sending goroutine is
also unnecessary and so we should just add a small amount of capacity
and let the sending goroutine complete as soon as it can.)

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: 6543 <6543@obermui.de>

Co-authored-by: 6543 <6543@obermui.de>
2022-04-27 16:05:52 +08:00
techknowlogick
9cc93c05cd Unset git author/committer variables when running integration tests (#19512) (#19519)
TestAPIGitTag (and likely others) will fail if the running environment contains
GIT_AUTHOR_NAME and other env variables like it.

This PR simply unsets these when running the integration tests.

Fix #14247

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2022-04-26 19:23:54 -04:00
Lunny Xiao
b31418edd9 Fix blame page select range error and some typos (#19503)
Partially back port from #19500 and fix two typos.
2022-04-26 20:19:52 +01:00
6543
242f7f1a52 Add notags to fetch (#19487) (#19490)
* Add notags to fetch (#19487)

* gofumpt
2022-04-25 20:26:17 +02:00
6543
8d7f1e430a User specific repoID or xorm builder conditions for issue search (#19475) (#19476) 2022-04-25 15:28:47 +02:00
Pilou
a6b32adc45 [doctor] authorized-keys: fix displayed check name (backport #19464) (#19484)
The registered check name is authorized-keys, not authorized_keys.
2022-04-25 13:45:18 +02:00
Gusted
1f0dca4614 Mark TemplateLoading error as "UnprocessableEntity" (#19445) (#19446)
* Mark TemplateLoading error as "UnprocessableEntity" (#19445)

- Backport #19445
  - Don't return Internal Server error if the user provide incorrect label template, instead return UnprocessableEntity.
  - Resolves #19399

- dep: upgrade: github.com/gogs/chardet
2022-04-22 21:07:57 +02:00
6543
1d665da32f Prevent dangling cat-file calls (goroutine alternative) (#19454) (#19466)
If an `os/exec.Command` is passed non `*os.File` as an input/output, go
will create `os.Pipe`s and wait for their closure in `cmd.Wait()`.  If
the code following this is responsible for closing `io.Pipe`s or other
handlers then on process death from context cancellation the `Wait` can
hang.

There are two possible solutions:

1. use `os.Pipe` as the input/output as `cmd.Wait` does not wait for these.
2. create a goroutine waiting on the context cancellation that will close the inputs.

This PR provides the second option - which is a simpler change that can
be more easily backported.

Closes #19448

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2022-04-22 16:58:50 +01:00
Gusted
09adc26eb6 Set correct PR status on 3way on conflict checking (#19457) (#19458)
- Backport #19457
  - When 3-way merge is enabled for conflict checking, it has a new interesting behavior that it doesn't return any error when it found a conflict, so we change the condition to not check for the error, but instead check if conflictedfiles is populated, this fixes a issue whereby PR status wasn't correctly on conflicted PR's.
  - Refactor the mergeable property(which was incorrectly set and lead me this bug) to be more maintainable.
  - Add a dedicated test for conflicting checking, so it should prevent future issues with this.
  - Ref: Fix the latest error for https://gitea.com/gitea/go-sdk/pulls/579

Co-authored-by: zeripath <art27@cantab.net>
2022-04-22 09:11:42 +08:00
6543
297346a762 RepoAssignment ensure to close before overwrite (#19449) (#19460)
* check if GitRepo already open and close if

* Only run RepoAssignment once
2022-04-21 18:55:44 +02:00
6543
acd648061d Add Changelog v1.16.6 (#19339)
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
2022-04-21 01:33:50 +02:00
6543
c5fe0a096d When dumping trim the standard suffices instead of a random suffix (#19440) (#19447)
* When dumping trim the standard suffices instead of a random suffix

Instead of using the `path.Ext()` to trim the last "extension" suffix, just iterate
through the supported suffices and trim those.

Fix #19424

Signed-off-by: Andrew Thornton <art27@cantab.net>

* fix enum with to have correct supported types only

Co-authored-by: 6543 <6543@obermui.de>

Co-authored-by: zeripath <art27@cantab.net>
2022-04-20 23:26:03 +01:00
Gusted
0c7bf6801f Fix DELETE request for non-existent public key (#19443) (#19444)
- Backport #19443
  - Add a return for the first "block" of errors, which fixes the double error messages.
  - Add a return for `externallyManaged`.
  - Resolves #19398

Co-authored-by: 6543 <6543@obermui.de>
2022-04-20 23:24:56 +01:00
Gusted
5863f7e048 Don't panic on ErrEmailInvalid (#19441) (#19442)
- Backport #19441
  - Don't panic on `ErrEmailInvalid`, this was caused due that we were trying to force `ErrEmailCharIsNotSupported` interface, which panics.
  - Resolves #19397

Co-authored-by: 6543 <6543@obermui.de>
2022-04-20 23:24:07 +01:00
6543
a785c46ca8 Add uploadpack.allowAnySHA1InWant to allow --filter=blob:none with older git clients (#19430) (#19438)
Older git clients need uploadpack.allowAnySHA1InWant if partial cloning is allowed.

Fix #19118

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2022-04-20 20:54:36 +02:00
6543
6bddfd3086 Warn on SSH connection for incorrect configuration (#19317) (#19437)
Backport #19317

- Warn on SSH connection for incorrect configuration
- When `setting.RepoRootPath` cannot be found(most likely due to
incorrect configuration) show "Gitea: Incorrect configuration" on the
client-side to help easier with debugging the problem.

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-04-20 19:18:23 +02:00
6543
dd8a726b25 API: Search Issues, dont show 500 if filter result in empty list (#19244) (#19436)
Backport #19244

* remove error who is none

* use setupSessionNoLimit instead of setupSessionWithLimit when no pagination

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-04-20 18:30:42 +02:00
zeripath
08eecba32b When updating mirror repo intervals by API reschedule next update too (#19429) (#19433)
Backport #19429

When a mirror repo interval is updated by the UI it is rescheduled with that interval
however the API does not do this. The API also lacks the enable_prune option.

This PR adds this functionality in to the API Edit Repo endpoint.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-04-20 16:04:26 +02:00
wxiaoguang
9c2212df15 Fix nil error when some pages are rendered outside request context (#19428) 2022-04-19 19:30:16 -04:00
Lunny Xiao
9b4746967c Only request write when necessary (#18657) (#19422)
* Only request write when necessary

- Only request write for `INTERNAL_TOKEN_URI` when no token was found.
- Resolves #18655

* Fix perm

* Update setting.go

* Update setting.go

* Update setting.go

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>

Co-authored-by: Gusted <williamzijl7@hotmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
2022-04-19 12:10:24 -04:00
Gusted
00da1facc4 Fix double blob-hunk on diff page (#19404) (#19405)
- Don't show the blob-hunk twice on diff page
- Backport #19404
2022-04-15 11:27:04 +08:00
techknowlogick
b461993775 go get -u crypto (#19388) 2022-04-12 22:45:58 -04:00
Vasiliy Bukharev
b885e57762 Update locale_ru-RU.ini (#19383) (#19387)
Signed-off-by: bvp <bvp-yar@ya.ru>
2022-04-13 08:15:03 +08:00
Gusted
081449d7a5 Don't allow merging PR's which are being conflict checked (#19357) (#19358)
* Don't allow merging PR's which are being conflict checked (#19357)

- Backport of #19357
  - When a PR is still being conflict checked, don't allow the PR to be merged(the merge button could already be visible before e.g. a new commit was pushed to the PR).
  - Resolves #19352

* Update error message
2022-04-13 00:38:41 +08:00
Gusted
ee3a21a537 Fix middleware function's placements for /user/... (#19377) (#19378)
- Backport #19377
  - Add reqSignIn to `/user/task/{task}` as it specific to a logged in user currently not-logged in user could cause a NPE.
  - Remove `/user/active` reqSignIn middleware, because when you want to active a account you're not "signed in" so it doesn't make sense to add that middleware.
2022-04-12 11:06:07 +08:00
silverwind
61c7732e12 Disable service worker by default (#18914) (#19342)
The service worker causes a lot of issues with JS errors after instance
upgrades while not bringing any real performance gain over regular HTTP
caching.

Disable it by default for this reason. Maybe later we can remove it
completely, as I simply see no benefit in having it.
2022-04-07 20:08:24 +02:00
wxiaoguang
57c2ca7f26 Fix invalid CSRF token bug, make sure CSRF tokens can be up-to-date (#19338)
There was a bug that the CSRF token wouldn't in 24h. This fix just does what the CSRF function comment says: If this request is a GET request, it will generate a new token. Then the CSRF token can be kept up-to-date.
2022-04-06 23:47:58 +08:00
Lunny Xiao
0704009dd7 Revert the minimal golang version requirement from 1.17 to 1.16 and add a warning in Makefile (#19319)
* Revert the minimal golang version requirement from 1.17 to 1.16 and add a warning in Makefile

* Apply suggestions from code review

Co-authored-by: John Olheiser <john.olheiser@gmail.com>

* 1.16

* Update modules/util/net.go

Co-authored-by: Gusted <williamzijl7@hotmail.com>

* correct bool conditional

yay tests for catching this :)

* Update hostmatcher.go

Co-authored-by: John Olheiser <john.olheiser@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Gusted <williamzijl7@hotmail.com>
2022-04-05 13:32:24 -04:00
zeripath
14a6aafb50 Restore user autoregistration with email addresses (#19261) (#19312)
Backport #19261

Unfortunately #18789 disabled autoregistration using email addresses as they would
be shortcut to email address does not exist.

This PR attempts to restore autoregistration by allowing an unknown email address
to percolate through to the autoregistration path of UserSignin.

Fix #19256

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-04-02 20:36:47 -04:00
Lunny Xiao
471a1e8111 Performance improvement for add team user when org has more than 1000 repositories (#19227) (#19289) 2022-04-01 11:36:12 +03:00
6543
123c254b84 Move checks for pulls before merge into own function (#19271) (#19277)
Backport #19271

Fix:
* The API does ignore issue dependencies where Web does not
* The API checks if "IsSignedIfRequired" where Web does not - UI probably do but nothing will some to craft custom requests
* Default merge message is crafted a bit different between API and Web if not set on specific cases ...
2022-03-31 16:57:13 +02:00
zeripath
db43f63c53 Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235) (#19236)
* Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235)

Strangely #19038 appears to relate to an issue whereby a tag appears to
be listed in `git show-ref --tags` but then does not appear when `git
show-ref --tags -- short_name` is called.

As a solution though I propose to stop the second call as it is
unnecessary and only likely to cause problems.

I've also noticed that the tags calls are wildly inefficient and aren't using the common cat-files - so these have been added.

I've also noticed that the git commit-graph is not being written on mirroring - so I've also added writing this to the migration which should improve mirror rendering somewhat.

Fix #19038

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: 6543 <6543@obermui.de>

* fix rebase relict

Co-authored-by: 6543 <6543@obermui.de>
2022-03-29 23:19:57 +03:00
John Olheiser
3ecd520f8e Granular webhook events in editHook (#19251) (#19257)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2022-03-29 18:26:51 +02:00
zeripath
e9935d358c Only send webhook events to active system webhooks and only deliver to active hooks (#19234) (#19248)
Backport #19234

There is a bug in the system webhooks whereby the active state is not checked when
webhooks are prepared and there is a bug that deactivating webhooks do not prevent
queued deliveries.

* Only add SystemWebhooks to the prepareWebhooks list if they are active
* At the time of delivery if the underlying webhook is not active mark it
as "delivered" but with a failed delivery so it does not get delivered.

Fix #19220

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-29 14:12:56 +02:00
wxiaoguang
8d653b148b Check go and nodejs version by go.mod and package.json (#19197) (#19254)
* Check go and nodejs version by go.mod and package.json 
* Update Go official site URL 

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: gesangtome <gesangtome@foxmail.com>
2022-03-29 15:32:38 +08:00
wxiaoguang
b702f2dac3 Fix clone url JS error for the empty repo page (#19209)
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: zeripath <art27@cantab.net>
2022-03-29 11:04:29 +08:00
6543
d59b8541f2 Use goproxy.io instead of goproxy.cn (#19242) (#19246)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-03-29 02:22:55 +01:00
zeripath
efd34d0d7d Prevent intermittent failures in RepoIndexerTest (#19225 #19229) (#19228)
Backport #19225
Backport #19229

The RepoIndexerTest is failing with considerable frequency due to a race inherrent in
its design. This PR adjust this test to avoid the reliance on waiting for the populate
repo indexer to run and forcibly adds the repo to the queue. It then flushes the queue.

It may be worth separating out the tests somewhat by testing the Index function
directly away from the queue however, this forceful method should solve the current
problem.

Fix #19162

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-28 01:01:53 +02:00
zeripath
2ec2935f78 Touch mirrors on even on fail to update (#19217) (#19233)
Backport #19217

If a mirror fails to be synchronised it should be pushed to the bottom of the queue
of the awaiting mirrors to be synchronised. At present if there LIMIT number of
broken mirrors they can effectively prevent all other mirrors from being synchronized
as their last_updated time will remain earlier than other mirrors.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-27 23:08:28 +02:00
Lunny Xiao
540541caa2 Hide sensitive content on admin panel progress monitor (#19218 & #19226) (#19231)
* Hide sensitive content on admin panel progress monitor (#19218)

Sanitize urls within git process descriptions.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Andrew Thornton <art27@cantab.net>

* Do not include global arguments in process manager (#19226)

Backport #19226

The git command by default adds a number of global arguments. These are not
helpful to be displayed in the process manager and so should be skipped for
default process descriptions.

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Andrew Thornton <art27@cantab.net>
2022-03-27 18:21:59 +01:00
Robert Kaussow
a13d64bf98 Bump goldmark to v1.4.11 (#19201) (#19203)
* Bump goldmark to v1.4.11

* fix go.sum

Signed-off-by: Andrew Thornton <art27@cantab.net>

* add testcase

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Andrew Thornton <art27@cantab.net>
2022-03-24 11:47:40 -04:00
zeripath
bab7d885aa Changelog for 1.16.5 (#19189)
* Changelog for 1.16.5

 ## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/1.16.5) - 2022-03-23

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: 6543 <6543@obermui.de>
2022-03-24 01:13:52 +01:00
6543
42229dc0b8 Fix showing issues in your repositories (#18916) (#19191)
- Make a restriction on which issues can be shown based on if you the user or team has write permission to the repository.
- Fixes a issue whereby you wouldn't see any associated issues with a specific team on a organization if you wasn't a member(fixed by zeroing the User{ID} in the options).
- Resolves #18913

Co-authored-by: Gusted <williamzijl7@hotmail.com>
2022-03-24 00:36:38 +01:00
zeripath
e3d8e92bdc Prevent redirect to Host (2) (#19175) (#19186)
Backport #19175

Unhelpfully Locations starting with `/\` will be converted by the
browser to `//` because ... well I do not fully understand. Certainly
the RFCs and MDN do not indicate that this would be expected. Providing
"compatibility" with the (mis)behaviour of a certain proprietary OS is
my suspicion. However, we clearly have to protect against this.

Therefore we should reject redirection locations that match the regular
expression: `^/[\\\\/]+`

Reference #9678

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-23 20:01:23 +00:00
zeripath
6fc73a8433 Fix compare link in active feeds for new branch (#19149) (#19185)
Backport #19149

When a new branch is pushed the old SHA is always listed as the empty sha and thus the compare link that is created does not work correctly.

Therefore when creating the compare link for new branches:

1. Attempt to get the parent of the first commit and use that as the basis
for the compare link.
2. If this is not possible make a comparison to the default branch
3. Finally if that is not possible simply do not show a compare link.

However, there are multiple broken compare links remaining therefore, in order for these to not break we will simply make the compare link redirect to the default branch.

Fix #19144

Signed-off-by: a1012112796 <1012112796@qq.com>
Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: a1012112796 <1012112796@qq.com>
2022-03-23 19:04:50 +00:00
zeripath
b1a0a78a51 Redirect .wiki/* ui link to /wiki (#18831) (#19184)
Backport #18831

Redirect .wiki/* ui link to /wiki

fix #18590

Signed-off-by: a1012112796 <1012112796@qq.com>
Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: a1012112796 <1012112796@qq.com>
2022-03-23 16:46:08 +00:00
Andrew Thornton
9c7d8b3096 Prevent start panic due to missing DotEscape function
Unfortunately #19169 causing a panic at startup in prod mode. This was hidden by dev
mode because the templates are compiled dynamically there. The issue is that DotEscape
is not in the original FuncMap at the time of compilation which causes a panic.

Ref #19169

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-23 16:09:57 +00:00
zeripath
93feb1a666 Fix the bug: deploy key with write access can not push (#19010) (#19182)
Backport #19010

Use DeployKeyID to replace the IsDeployKey, then CanWriteCode uses the DeployKeyID to check the write permission.

Fix #19009

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-03-23 13:44:41 +00:00
zeripath
bb0e2121a3 Try to prevent autolinking of displaynames by email readers (#19169) (#19183)
Backport #19169

Unfortunately many email readers will (helpfully) detect url or url-like names and
automatically create links to them, even in HTML emails. This is not ideal when
usernames can have dots in them.

This PR tries to prevent this behaviour by sticking ZWJ characters between dots and
also set the meta tag to prevent format detection.

Not every email template has been changed in this way - just the activation emails but
it may be that we should be setting the above meta tag in all of our emails too.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-23 15:18:11 +02:00
zeripath
d21b7fd3af Clean paths when looking in Storage (#19124) (#19179)
Backport #19124

* Clean paths when looking in Storage

Ensure paths are clean for minio aswell as local storage.

Use url.Path not RequestURI/EscapedPath in storageHandler.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Apply suggestions from code review

Co-authored-by: Lauris BH <lauris@nix.lv>
2022-03-23 09:23:00 +00:00
Norwin
743553f3e9 Cleanup protected branches when deleting users & teams (#19158) (#19174)
* Clean up protected_branches when deleting user

fixes #19094

* Clean up protected_branches when deleting teams

* fix issue

Co-authored-by: Lauris BH <lauris@nix.lv>

Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-03-23 13:56:53 +08:00
zeripath
a3ccbb5b7f Ensure that setting.LocalURL always has a trailing slash (#19171) (#19177)
Backport #19171

Fix #19166

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-03-23 12:56:52 +08:00
zeripath
4b7cb813e6 Use the new/choose link for New Issue on project page (#19172) (#19176)
Backport #19172

Extend issues/new/choose to pass the project id and change New Issue
link on project page to use new/choose

Fix #19170

Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-03-23 09:41:12 +08:00
zeripath
23b8214549 Use IterateBufferSize whilst querying repositories during adoption check (#19140) (#19160)
Backport #19140

The adoption page checks directories to see if they are repositories by querying the
db on a per user basis. This can lead to problems if a user has a large number of
repositories or putative repositories.

This PR changes the buffering to check the db in IterataeBufferSize batches instead.

Fix #19137

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-22 09:11:22 +08:00
zeripath
08feb6b664 Ensure isSSH is set whenever DISABLE_HTTP_GIT is set (#19028) (#19146)
Backport #19028

When DISABLE_HTTP_GIT is set we should always show the SSH button

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-03-21 08:59:55 +08:00
Abheek Dhawan
1aa5dc75df Use custom favicon when viewing static files if it exists (#19130) (#19152)
Redirect `/favicon.ico` to `/assets/img/favicon.png`.

Fix #19109

Co-authored-by: zeripath <art27@cantab.net>
2022-03-20 20:28:35 -04:00
Gusted
ee234aff61 Fix NPE /repos/issues/search when not signed in (#19154) (#19155)
- Backport #19154

  - Don't panic when on `/repos/issues/search?{created,assigned,mentioned,review_requested}=true` when client didn't pass any authentication.
  - Resolves #19115
2022-03-20 22:42:31 +01:00
zeripath
a3f3e310fb Fix wrong scopes caused by empty scope input (#19029) (#19145)
Backport #19029
Fix #18972 Gitea prepends requested openid scope with + after updating authentication source
2022-03-21 03:13:18 +08:00
zeripath
ea56bdca5f Fix the editor height in review box (#19003) (#19147)
Backport #19003

Fix the height problem in  https://github.com/go-gitea/gitea/pull/18862#issuecomment-1059329539

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-03-20 10:29:49 +08:00
zeripath
45c836badc Do not send notification emails to inactive users (#19131) (#19139)
Backport #19131
Backport #19142

Emails should not be sent to inactive users except for Activate and ResetPassword
messages.

Fix #18950

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-19 18:33:32 +00:00
techknowlogick
f9ea4ab69a Bump to build with go1.18 (#19120 et al) (#19127)
Backport #19120 
Backport #19099 
Backport #18874 
Backport #18420
Backport #19128
Backport #18270 

Bump to build with go1.18

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Signed-off-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Jelle Hulter <jellehulter@gmail.com>
2022-03-19 18:46:47 +01:00
zeripath
e6d46eeb55 Make migrations SKIP_TLS_VERIFY apply to git too (#19132) (#19141)
Backport #19132

Make SKIP_TLS_VERIFY apply to git data migrations too through adding the `-c http.sslVerify=false` option to the git clone command.

Fix #18998

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-19 16:20:26 +00:00
zeripath
5bb0c92b6c Do not send activation email if manual confirm is set (#19119) (#19122)
Backport #19119

If the mailer is configured then even if Manual confirm is set an activation email
is still being sent because `handleUserCreated` is not checking for this case.

Fix #17263

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-03-19 14:20:42 +00:00
Gusted
c1e6be47d7 Update golang.org/x/crypto (#19097) (#19098)
Backport #19097

* Update golang.org/x/crypto (#19097)

- Backport #19097

* Fix deprecation notice

* Backport workaround removal
2022-03-19 12:16:15 +00:00
Lunny Xiao
79a5e68816 Handle email address not exist (#19089) (#19121)
Backport #19089

* Handle email address not exist. (#19089)

* Fix lint about strings.Title

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2022-03-19 11:35:23 +00:00
Gusted
9bcbbd419f Update json-iterator (#18644) (#19100)
- Backport #18644
2022-03-15 17:29:06 -04:00
172 changed files with 3161 additions and 1397 deletions

View File

@@ -25,7 +25,7 @@ steps:
- make deps-frontend - make deps-frontend
- name: deps-backend - name: deps-backend
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- make deps-backend - make deps-backend
@@ -45,32 +45,41 @@ steps:
commands: commands:
- make lint-backend - make lint-backend
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
depends_on: [deps-backend] depends_on: [deps-backend]
volumes:
- name: deps
path: /go
- name: lint-backend-windows - name: lint-backend-windows
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
commands: commands:
- make golangci-lint vet - make golangci-lint-windows vet
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows GOOS: windows
GOARCH: amd64 GOARCH: amd64
depends_on: [deps-backend] depends_on: [deps-backend]
volumes:
- name: deps
path: /go
- name: lint-backend-gogit - name: lint-backend-gogit
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
commands: commands:
- make lint-backend - make lint-backend
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org GOSUMDB: sum.golang.org
TAGS: bindata gogit sqlite sqlite_unlock_notify TAGS: bindata gogit sqlite sqlite_unlock_notify
depends_on: [deps-backend] depends_on: [deps-backend]
volumes:
- name: deps
path: /go
- name: checks-frontend - name: checks-frontend
image: node:16 image: node:16
@@ -79,7 +88,7 @@ steps:
depends_on: [deps-frontend] depends_on: [deps-frontend]
- name: checks-backend - name: checks-backend
image: golang:1.17 image: golang:1.18
commands: commands:
- make checks-backend - make checks-backend
depends_on: [deps-backend] depends_on: [deps-backend]
@@ -104,7 +113,7 @@ steps:
pull: always pull: always
environment: environment:
GO111MODULE: on GO111MODULE: on
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
commands: commands:
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag - go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
depends_on: [deps-backend, checks-backend] depends_on: [deps-backend, checks-backend]
@@ -113,10 +122,10 @@ steps:
path: /go path: /go
- name: build-backend-arm64 - name: build-backend-arm64
image: golang:1.17 image: golang:1.18
environment: environment:
GO111MODULE: on GO111MODULE: on
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
GOOS: linux GOOS: linux
GOARCH: arm64 GOARCH: arm64
TAGS: bindata gogit TAGS: bindata gogit
@@ -129,10 +138,10 @@ steps:
path: /go path: /go
- name: build-backend-windows - name: build-backend-windows
image: golang:1.17 image: golang:1.18
environment: environment:
GO111MODULE: on GO111MODULE: on
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
GOOS: windows GOOS: windows
GOARCH: amd64 GOARCH: amd64
TAGS: bindata gogit TAGS: bindata gogit
@@ -144,10 +153,10 @@ steps:
path: /go path: /go
- name: build-backend-386 - name: build-backend-386
image: golang:1.17 image: golang:1.18
environment: environment:
GO111MODULE: on GO111MODULE: on
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
GOOS: linux GOOS: linux
GOARCH: 386 GOARCH: 386
commands: commands:
@@ -226,6 +235,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
when: when:
event: event:
@@ -233,7 +243,7 @@ steps:
- pull_request - pull_request
- name: deps-backend - name: deps-backend
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- make deps-backend - make deps-backend
@@ -260,7 +270,7 @@ steps:
- ./build/test-env-check.sh - ./build/test-env-check.sh
- make backend - make backend
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org GOSUMDB: sum.golang.org
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
depends_on: [deps-backend, prepare-test-env] depends_on: [deps-backend, prepare-test-env]
@@ -274,7 +284,7 @@ steps:
commands: commands:
- make unit-test-coverage test-check - make unit-test-coverage test-check
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
RACE_ENABLED: true RACE_ENABLED: true
GITHUB_READ_TOKEN: GITHUB_READ_TOKEN:
@@ -290,7 +300,7 @@ steps:
commands: commands:
- make unit-test-coverage test-check - make unit-test-coverage test-check
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata gogit sqlite sqlite_unlock_notify TAGS: bindata gogit sqlite sqlite_unlock_notify
RACE_ENABLED: true RACE_ENABLED: true
GITHUB_READ_TOKEN: GITHUB_READ_TOKEN:
@@ -306,7 +316,7 @@ steps:
commands: commands:
- make test-mysql-migration integration-test-coverage - make test-mysql-migration integration-test-coverage
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata TAGS: bindata
RACE_ENABLED: true RACE_ENABLED: true
TEST_LDAP: 1 TEST_LDAP: 1
@@ -323,7 +333,7 @@ steps:
commands: commands:
- timeout -s ABRT 40m make test-mysql8-migration test-mysql8 - timeout -s ABRT 40m make test-mysql8-migration test-mysql8
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata TAGS: bindata
RACE_ENABLED: true RACE_ENABLED: true
TEST_LDAP: 1 TEST_LDAP: 1
@@ -339,7 +349,7 @@ steps:
commands: commands:
- make test-mssql-migration test-mssql - make test-mssql-migration test-mssql
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata TAGS: bindata
RACE_ENABLED: true RACE_ENABLED: true
TEST_LDAP: 1 TEST_LDAP: 1
@@ -350,11 +360,11 @@ steps:
path: /go path: /go
- name: generate-coverage - name: generate-coverage
image: golang:1.17 image: golang:1.18
commands: commands:
- make coverage - make coverage
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata TAGS: bindata
depends_on: [unit-test, test-mysql] depends_on: [unit-test, test-mysql]
when: when:
@@ -418,6 +428,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
when: when:
event: event:
@@ -425,7 +436,7 @@ steps:
- pull_request - pull_request
- name: deps-backend - name: deps-backend
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- make deps-backend - make deps-backend
@@ -446,7 +457,7 @@ steps:
- ./build/test-env-check.sh - ./build/test-env-check.sh
- make backend - make backend
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
GOSUMDB: sum.golang.org GOSUMDB: sum.golang.org
TAGS: bindata gogit sqlite sqlite_unlock_notify TAGS: bindata gogit sqlite sqlite_unlock_notify
depends_on: [deps-backend, prepare-test-env] depends_on: [deps-backend, prepare-test-env]
@@ -460,7 +471,7 @@ steps:
commands: commands:
- timeout -s ABRT 40m make test-sqlite-migration test-sqlite - timeout -s ABRT 40m make test-sqlite-migration test-sqlite
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata gogit sqlite sqlite_unlock_notify TAGS: bindata gogit sqlite sqlite_unlock_notify
RACE_ENABLED: true RACE_ENABLED: true
TEST_TAGS: gogit sqlite sqlite_unlock_notify TEST_TAGS: gogit sqlite sqlite_unlock_notify
@@ -476,7 +487,7 @@ steps:
commands: commands:
- timeout -s ABRT 40m make test-pgsql-migration test-pgsql - timeout -s ABRT 40m make test-pgsql-migration test-pgsql
environment: environment:
GOPROXY: https://goproxy.cn GOPROXY: https://goproxy.io
TAGS: bindata gogit TAGS: bindata gogit
RACE_ENABLED: true RACE_ENABLED: true
TEST_TAGS: gogit TEST_TAGS: gogit
@@ -567,7 +578,7 @@ trigger:
steps: steps:
- name: download - name: download
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- timeout -s ABRT 40m make generate-license generate-gitignore - timeout -s ABRT 40m make generate-license generate-gitignore
@@ -619,6 +630,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: deps-frontend - name: deps-frontend
@@ -628,7 +640,7 @@ steps:
- make deps-frontend - make deps-frontend
- name: deps-backend - name: deps-backend
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- make deps-backend - make deps-backend
@@ -637,14 +649,14 @@ steps:
path: /go path: /go
- name: static - name: static
image: techknowlogick/xgo:go-1.17.x image: techknowlogick/xgo:go-1.18.x
pull: always pull: always
commands: commands:
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- make release - make release
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
volumes: volumes:
- name: deps - name: deps
@@ -737,6 +749,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: deps-frontend - name: deps-frontend
@@ -746,7 +759,7 @@ steps:
- make deps-frontend - make deps-frontend
- name: deps-backend - name: deps-backend
image: golang:1.17 image: golang:1.18
pull: always pull: always
commands: commands:
- make deps-backend - make deps-backend
@@ -755,14 +768,14 @@ steps:
path: /go path: /go
- name: static - name: static
image: techknowlogick/xgo:go-1.17.x image: techknowlogick/xgo:go-1.18.x
pull: always pull: always
commands: commands:
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs - curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
- export PATH=$PATH:$GOPATH/bin - export PATH=$PATH:$GOPATH/bin
- make release - make release
environment: environment:
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
depends_on: [fetch-tags] depends_on: [fetch-tags]
volumes: volumes:
@@ -882,6 +895,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -892,7 +906,7 @@ steps:
auto_tag_suffix: linux-amd64 auto_tag_suffix: linux-amd64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -910,7 +924,7 @@ steps:
auto_tag_suffix: linux-amd64-rootless auto_tag_suffix: linux-amd64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -945,6 +959,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -955,7 +970,7 @@ steps:
tags: dev-linux-amd64 tags: dev-linux-amd64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -973,7 +988,7 @@ steps:
tags: dev-linux-amd64-rootless tags: dev-linux-amd64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1007,6 +1022,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -1017,7 +1033,7 @@ steps:
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64 tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1035,7 +1051,7 @@ steps:
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1070,7 +1086,7 @@ steps:
repo: gitea/gitea repo: gitea/gitea
tags: linux-arm64 tags: linux-arm64
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
environment: environment:
PLUGIN_MIRROR: PLUGIN_MIRROR:
from_secret: plugin_mirror from_secret: plugin_mirror
@@ -1103,6 +1119,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -1113,7 +1130,7 @@ steps:
auto_tag_suffix: linux-arm64 auto_tag_suffix: linux-arm64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1131,7 +1148,7 @@ steps:
auto_tag_suffix: linux-arm64-rootless auto_tag_suffix: linux-arm64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1166,6 +1183,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -1176,7 +1194,7 @@ steps:
tags: dev-linux-arm64 tags: dev-linux-arm64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1194,7 +1212,7 @@ steps:
tags: dev-linux-arm64-rootless tags: dev-linux-arm64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1228,6 +1246,7 @@ steps:
image: docker:git image: docker:git
pull: always pull: always
commands: commands:
- git config --global --add safe.directory /drone/src
- git fetch --tags --force - git fetch --tags --force
- name: publish - name: publish
@@ -1238,7 +1257,7 @@ steps:
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64 tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:
@@ -1256,7 +1275,7 @@ steps:
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless
repo: gitea/gitea repo: gitea/gitea
build_args: build_args:
- GOPROXY=https://goproxy.cn - GOPROXY=https://goproxy.io
password: password:
from_secret: docker_password from_secret: docker_password
username: username:

View File

@@ -13,7 +13,7 @@ linters:
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time. #- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt - gofmt
- misspell - misspell
- gocritic #- gocritic # TODO: disabled until fixed with go 1.18
- bidichk - bidichk
- ineffassign - ineffassign
- revive - revive
@@ -22,7 +22,7 @@ linters:
fast: false fast: false
run: run:
timeout: 3m timeout: 10m
skip-dirs: skip-dirs:
- node_modules - node_modules
- public - public
@@ -61,6 +61,9 @@ linters-settings:
- name: errorf - name: errorf
- name: duplicated-imports - name: duplicated-imports
- name: modifies-value-receiver - name: modifies-value-receiver
gofumpt:
extra-rules: true
lang-version: 1.18
issues: issues:
exclude-rules: exclude-rules:
@@ -148,3 +151,11 @@ issues:
- path: models/user/openid.go - path: models/user/openid.go
linters: linters:
- golint - golint
- linters: staticcheck
text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
- linters: staticcheck
text: "util.FindClosure is deprecated: This function can not handle newlines. Many elements can be existed over multiple lines(e.g. link labels). Use text.Reader.FindClosure."
- linters: staticcheck
text: "gossh.SigAlgoRSASHA2256 is deprecated: use KeyAlgoRSASHA256."
- linters: staticcheck
text: "gossh.SigAlgoRSASHA2512 is deprecated: use KeyAlgoRSASHA512."

View File

@@ -4,6 +4,112 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.16.8](https://github.com/go-gitea/gitea/releases/tag/v1.16.8) - 2022-05-16
* ENHANCEMENTS
* Add doctor check/fix for bogus action rows (#19656) (#19669)
* Make .cs highlighting legible on dark themes. (#19604) (#19605)
* BUGFIXES
* Fix oauth setting list bug (#19681)
* Delete user related oauth stuff on user deletion too (#19677) (#19680)
* Fix new release from tags list UI (#19670) (#19673)
* Prevent NPE when checking repo units if the user is nil (#19625) (#19630)
* GetFeeds must always discard actions with dangling repo_id (#19598) (#19629)
* Call MultipartForm.RemoveAll when request finishes (#19606) (#19607)
* Avoid MoreThanOne error when creating a branch whose name conflicts with other ref names (#19557) (#19591)
* Fix sending empty notifications (#19589) (#19590)
* Ignore DNS error when doing migration allow/block check (#19566) (#19567)
* Fix issue overview for teams (#19652) (#19653)
## [1.16.7](https://github.com/go-gitea/gitea/releases/tag/v1.16.7) - 2022-05-02
* SECURITY
* Escape git fetch remote (#19487) (#19490)
* BUGFIXES
* Don't overwrite err with nil (#19572) (#19574)
* On Migrations, only write commit-graph if wiki clone was successful (#19563) (#19568)
* Respect DefaultUserIsRestricted system default when creating new user (#19310) (#19560)
* Don't error when branch's commit doesn't exist (#19547) (#19548)
* Support `hostname:port` to pass host matcher's check (#19543) (#19544)
* Prevent intermittent race in attribute reader close (#19537) (#19539)
* Fix 64-bit atomic operations on 32-bit machines (#19531) (#19532)
* Prevent dangling archiver goroutine (#19516) (#19526)
* Fix migrate release from github (#19510) (#19523)
* When view _Siderbar or _Footer, just display once (#19501) (#19522)
* Fix blame page select range error and some typos (#19503)
* Fix name of doctor fix "authorized-keys" in hints (#19464) (#19484)
* User specific repoID or xorm builder conditions for issue search (#19475) (#19476)
* Prevent dangling cat-file calls (goroutine alternative) (#19454) (#19466)
* RepoAssignment ensure to close before overwrite (#19449) (#19460)
* Set correct PR status on 3way on conflict checking (#19457) (#19458)
* Mark TemplateLoading error as "UnprocessableEntity" (#19445) (#19446)
## [1.16.6](https://github.com/go-gitea/gitea/releases/tag/v1.16.6) - 2022-04-20
* ENHANCEMENTS
* Only request write when necessary (#18657) (#19422)
* Disable service worker by default (#18914) (#19342)
* BUGFIXES
* When dumping trim the standard suffices instead of a random suffix (#19440) (#19447)
* Fix DELETE request for non-existent public key (#19443) (#19444)
* Don't panic on ErrEmailInvalid (#19441) (#19442)
* Add uploadpack.allowAnySHA1InWant to allow --filter=blob:none with older git clients (#19430) (#19438)
* Warn on SSH connection for incorrect configuration (#19317) (#19437)
* Search Issues via API, dont show 500 if filter result in empty list (#19244) (#19436)
* When updating mirror repo intervals by API reschedule next update too (#19429) (#19433)
* Fix nil error when some pages are rendered outside request context (#19427) (#19428)
* Fix double blob-hunk on diff page (#19404) (#19405)
* Don't allow merging PR's which are being conflict checked (#19357) (#19358)
* Fix middleware function's placements (#19377) (#19378)
* Fix invalid CSRF token bug, make sure CSRF tokens can be up-to-date (#19338)
* Restore user autoregistration with email addresses (#19261) (#19312)
* Move checks for pulls before merge into own function (#19271) (#19277)
* Granular webhook events in editHook (#19251) (#19257)
* Only send webhook events to active system webhooks and only deliver to active hooks (#19234) (#19248)
* Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235) (#19236)
* Touch mirrors on even on fail to update (#19217) (#19233)
* Hide sensitive content on admin panel progress monitor (#19218 & #19226) (#19231)
* Fix clone url JS error for the empty repo page (#19209)
* Bump goldmark to v1.4.11 (#19201) (#19203)
* TESTING
* Prevent intermittent failures in RepoIndexerTest (#19225 #19229) (#19228)
* BUILD
* Revert the minimal golang version requirement from 1.17 to 1.16 and add a warning in Makefile (#19319)
* MISC
* Performance improvement for add team user when org has more than 1000 repositories (#19227) (#19289)
* Check go and nodejs version by go.mod and package.json (#19197) (#19254)
## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/v1.16.5) - 2022-03-23
* BREAKING
* Bump to build with go1.18 (#19120 et al) (#19127)
* SECURITY
* Prevent redirect to Host (2) (#19175) (#19186)
* Try to prevent autolinking of displaynames by email readers (#19169) (#19183)
* Clean paths when looking in Storage (#19124) (#19179)
* Do not send notification emails to inactive users (#19131) (#19139)
* Do not send activation email if manual confirm is set (#19119) (#19122)
* ENHANCEMENTS
* Use the new/choose link for New Issue on project page (#19172) (#19176)
* BUGFIXES
* Fix showing issues in your repositories (#18916) (#19191)
* Fix compare link in active feeds for new branch (#19149) (#19185)
* Redirect .wiki/* ui link to /wiki (#18831) (#19184)
* Ensure deploy keys with write access can push (#19010) (#19182)
* Ensure that setting.LocalURL always has a trailing slash (#19171) (#19177)
* Cleanup protected branches when deleting users & teams (#19158) (#19174)
* Use IterateBufferSize whilst querying repositories during adoption check (#19140) (#19160)
* Fix NPE /repos/issues/search when not signed in (#19154) (#19155)
* Use custom favicon when viewing static files if it exists (#19130) (#19152)
* Fix the editor height in review box (#19003) (#19147)
* Ensure isSSH is set whenever DISABLE_HTTP_GIT is set (#19028) (#19146)
* Fix wrong scopes caused by empty scope input (#19029) (#19145)
* Make migrations SKIP_TLS_VERIFY apply to git too (#19132) (#19141)
* Handle email address not exist (#19089) (#19121)
* MISC
* Update json-iterator to allow compilation with go1.18 (#18644) (#19100)
* Update golang.org/x/crypto (#19097) (#19098)
## [1.16.4](https://github.com/go-gitea/gitea/releases/tag/v1.16.4) - 2022-03-14 ## [1.16.4](https://github.com/go-gitea/gitea/releases/tag/v1.16.4) - 2022-03-14
* SECURITY * SECURITY

View File

@@ -1,7 +1,5 @@
#Build stage
################################### FROM golang:1.18-alpine3.15 AS build-env
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct} ENV GOPROXY ${GOPROXY:-direct}

View File

@@ -1,7 +1,5 @@
#Build stage
################################### FROM golang:1.18-alpine3.15 AS build-env
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY ${GOPROXY:-direct} ENV GOPROXY ${GOPROXY:-direct}

109
Makefile
View File

@@ -24,10 +24,17 @@ SHASUM ?= shasum -a 256
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" ) HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
COMMA := , COMMA := ,
XGO_VERSION := go-1.17.x XGO_VERSION := go-1.18.x
MIN_GO_VERSION := 001016000
MIN_NODE_VERSION := 012017000 AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
MIN_GOLANGCI_LINT_VERSION := 001043000 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.0
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.44.2
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
DOCKER_IMAGE ?= gitea/gitea DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest DOCKER_TAG ?= latest
@@ -125,8 +132,6 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
GO_SOURCES += $(BINDATA_DEST) GO_SOURCES += $(BINDATA_DEST)
endif endif
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
SWAGGER := $(GO) run github.com/go-swagger/go-swagger/cmd/swagger
SWAGGER_SPEC := templates/swagger/v1_json.tmpl SWAGGER_SPEC := templates/swagger/v1_json.tmpl
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
@@ -196,10 +201,15 @@ help:
.PHONY: go-check .PHONY: go-check
go-check: go-check:
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');)) $(eval MIN_GO_VERSION_STR := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
$(eval GO_VERSION_STR := $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+'))
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(GO_VERSION_STR)' | tr '.' ' ')))
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \ @if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \ echo "Gitea requires Go $(MIN_GO_VERSION_STR) or greater to build, but $(GO_VERSION) was found. You can get an updated version at https://go.dev/dl/"; \
exit 1; \ exit 1; \
else \
echo "WARNING: Please ensure Go $(GO_VERSION_STR) is still maintained to avoid possible security problems. You can check it at https://go.dev/dl/"; \
fi fi
.PHONY: git-check .PHONY: git-check
@@ -211,11 +221,12 @@ git-check:
.PHONY: node-check .PHONY: node-check
node-check: node-check:
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');)) $(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
$(eval MIN_NODE_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_NODE_VERSION) | grep -o ...)))
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1)) $(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \ @if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
echo "Gitea requires Node.js $(MIN_NODE_VER_FMT) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \ echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
exit 1; \ exit 1; \
fi fi
@@ -234,8 +245,8 @@ clean:
.PHONY: fmt .PHONY: fmt
fmt: fmt:
@echo "Running gitea-fmt(with gofmt)..." @echo "Running gitea-fmt (with gofumpt)..."
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}' @MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
.PHONY: vet .PHONY: vet
vet: vet:
@@ -254,7 +265,7 @@ endif
.PHONY: generate-swagger .PHONY: generate-swagger
generate-swagger: generate-swagger:
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)' $(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)' $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)' $(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
@@ -270,21 +281,18 @@ swagger-check: generate-swagger
.PHONY: swagger-validate .PHONY: swagger-validate
swagger-validate: swagger-validate:
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)' $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
$(SWAGGER) validate './$(SWAGGER_SPEC)' $(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)' $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
.PHONY: errcheck .PHONY: errcheck
errcheck: errcheck:
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
fi
@echo "Running errcheck..." @echo "Running errcheck..."
@errcheck $(GO_PACKAGES) $(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
.PHONY: fmt-check .PHONY: fmt-check
fmt-check: fmt-check:
# get all go files and run gitea-fmt (with gofmt) on them # get all go files and run gitea-fmt (with gofmt) on them
@diff=$$($(GO) run build/code-batch-process.go gitea-fmt -s -d '{file-list}'); \ @diff=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{file-list}'); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \ echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \ echo "$${diff}"; \
@@ -323,10 +331,7 @@ watch-frontend: node-check node_modules
.PHONY: watch-backend .PHONY: watch-backend
watch-backend: go-check watch-backend: go-check
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ $(GO) run $(AIR_PACKAGE) -c .air.toml
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
fi
air -c .air.toml
.PHONY: test .PHONY: test
test: test-frontend test-backend test: test-frontend test-backend
@@ -599,12 +604,9 @@ $(DIST_DIRS):
.PHONY: release-windows .PHONY: release-windows
release-windows: | $(DIST_DIRS) release-windows: | $(DIST_DIRS)
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
$(GO) install src.techknowlogick.com/xgo@latest; \
fi
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq (,$(findstring gogit,$(TAGS))) ifeq (,$(findstring gogit,$(TAGS)))
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
endif endif
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
@@ -612,20 +614,14 @@ endif
.PHONY: release-linux .PHONY: release-linux
release-linux: | $(DIST_DIRS) release-linux: | $(DIST_DIRS)
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
$(GO) install src.techknowlogick.com/xgo@latest; \
fi
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
endif endif
.PHONY: release-darwin .PHONY: release-darwin
release-darwin: | $(DIST_DIRS) release-darwin: | $(DIST_DIRS)
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
$(GO) install src.techknowlogick.com/xgo@latest; \
fi
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
ifeq ($(CI),drone) ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries cp /build/* $(DIST)/binaries
endif endif
@@ -640,10 +636,7 @@ release-check: | $(DIST_DIRS)
.PHONY: release-compress .PHONY: release-compress
release-compress: | $(DIST_DIRS) release-compress: | $(DIST_DIRS)
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
$(GO) install github.com/ulikunitz/xz/cmd/gxz@v0.5.10; \
fi
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
.PHONY: release-sources .PHONY: release-sources
release-sources: | $(DIST_DIRS) release-sources: | $(DIST_DIRS)
@@ -673,6 +666,15 @@ deps-frontend: node_modules
.PHONY: deps-backend .PHONY: deps-backend
deps-backend: deps-backend:
$(GO) mod download $(GO) mod download
$(GO) install $(AIR_PACKAGE)
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
$(GO) install $(ERRCHECK_PACKAGE)
$(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(GXZ_PAGAGE)
$(GO) install $(MISSPELL_PACKAGE)
$(GO) install $(SWAGGER_PACKAGE)
$(GO) install $(XGO_PACKAGE)
node_modules: package-lock.json node_modules: package-lock.json
npm install --no-save npm install --no-save
@@ -766,22 +768,19 @@ pr\#%: clean-all
$(GO) run contrib/pr/checkout.go $* $(GO) run contrib/pr/checkout.go $*
.PHONY: golangci-lint .PHONY: golangci-lint
golangci-lint: golangci-lint-check golangci-lint:
golangci-lint run --timeout 10m $(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: golangci-lint-check # workaround step for the lint-backend-windows CI task because 'go run' can not
golangci-lint-check: # have distinct GOOS/GOARCH for its build and run steps
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');)) .PHONY: golangci-lint-windows
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...))) golangci-lint-windows:
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \ @GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ golangci-lint run
export BINARY="golangci-lint"; \
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \ .PHONY: editorconfig-checker
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \ editorconfig-checker:
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \ $(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
export BINARY="golangci-lint"; \
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
fi
.PHONY: docker .PHONY: docker
docker: docker:

View File

@@ -73,7 +73,7 @@ or if SQLite support is required:
The `build` target is split into two sub-targets: The `build` target is split into two sub-targets:
- `make backend` which requires [Go 1.16](https://golang.org/dl/) or greater. - `make backend` which requires [Go 1.17](https://go.dev/dl/) or greater.
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies. - `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.

View File

@@ -40,7 +40,7 @@ func passThroughCmd(cmd string, args []string) error {
} }
c := exec.Cmd{ c := exec.Cmd{
Path: foundCmd, Path: foundCmd,
Args: args, Args: append([]string{cmd}, args...),
Stdin: os.Stdin, Stdin: os.Stdin,
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
@@ -270,9 +270,10 @@ func main() {
if containsString(subArgs, "-w") { if containsString(subArgs, "-w") {
cmdErrors = append(cmdErrors, giteaFormatGoImports(files)) cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
} }
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs)) cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-l"), containsString(subArgs, "-w")))
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", "1.17"}, substArgs...)))
case "misspell": case "misspell":
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs)) cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
default: default:
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs) log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
} }

View File

@@ -25,6 +25,7 @@ import (
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/util"
auth_service "code.gitea.io/gitea/services/auth" auth_service "code.gitea.io/gitea/services/auth"
"code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/oauth2"
"code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/auth/source/smtp"
@@ -113,6 +114,10 @@ var (
Name: "access-token", Name: "access-token",
Usage: "Generate access token for the user", Usage: "Generate access token for the user",
}, },
cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},
}, },
} }
@@ -537,17 +542,26 @@ func runCreateUser(c *cli.Context) error {
changePassword = c.Bool("must-change-password") changePassword = c.Bool("must-change-password")
} }
restricted := util.OptionalBoolNone
if c.IsSet("restricted") {
restricted = util.OptionalBoolOf(c.Bool("restricted"))
}
u := &user_model.User{ u := &user_model.User{
Name: username, Name: username,
Email: c.String("email"), Email: c.String("email"),
Passwd: password, Passwd: password,
IsActive: true,
IsAdmin: c.Bool("admin"), IsAdmin: c.Bool("admin"),
MustChangePassword: changePassword, MustChangePassword: changePassword,
Theme: setting.UI.DefaultTheme,
} }
if err := user_model.CreateUser(u); err != nil { overwriteDefault := &user_model.CreateUserOverwriteOptions{
IsActive: util.OptionalBoolTrue,
IsRestricted: restricted,
}
if err := user_model.CreateUser(u, overwriteDefault); err != nil {
return fmt.Errorf("CreateUser: %v", err) return fmt.Errorf("CreateUser: %v", err)
} }

View File

@@ -86,7 +86,7 @@ func (o outputType) String() string {
} }
var outputTypeEnum = &outputType{ var outputTypeEnum = &outputType{
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"}, Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
Default: "zip", Default: "zip",
} }
@@ -160,7 +160,12 @@ func runDump(ctx *cli.Context) error {
fatal("Deleting default logger failed. Can not write to stdout: %v", err) fatal("Deleting default logger failed. Can not write to stdout: %v", err)
} }
} else { } else {
fileName = strings.TrimSuffix(fileName, path.Ext(fileName)) for _, suffix := range outputTypeEnum.Enum {
if strings.HasSuffix(fileName, "."+suffix) {
fileName = strings.TrimSuffix(fileName, "."+suffix)
break
}
}
fileName += "." + outType fileName += "." + outType
} }
setting.LoadFromExisting() setting.LoadFromExisting()

View File

@@ -185,7 +185,7 @@ Gitea or set your environment appropriately.`, "")
reponame := os.Getenv(models.EnvRepoName) reponame := os.Getenv(models.EnvRepoName)
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64) userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64) prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey)) deployKeyID, _ := strconv.ParseInt(os.Getenv(models.EnvDeployKeyID), 10, 64)
hookOptions := private.HookOptions{ hookOptions := private.HookOptions{
UserID: userID, UserID: userID,
@@ -194,7 +194,7 @@ Gitea or set your environment appropriately.`, "")
GitQuarantinePath: os.Getenv(private.GitQuarantinePath), GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
GitPushOptions: pushOptions(), GitPushOptions: pushOptions(),
PullRequestID: prID, PullRequestID: prID,
IsDeployKey: isDeployKey, DeployKeyID: deployKeyID,
} }
scanner := bufio.NewScanner(os.Stdin) scanner := bufio.NewScanner(os.Stdin)

View File

@@ -243,7 +243,7 @@ func runServ(c *cli.Context) error {
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10)) os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10)) os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0)) os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey)) os.Setenv(models.EnvDeployKeyID, fmt.Sprintf("%d", results.DeployKeyID))
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID)) os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
os.Setenv(models.EnvAppURL, setting.AppURL) os.Setenv(models.EnvAppURL, setting.AppURL)
@@ -297,6 +297,15 @@ func runServ(c *cli.Context) error {
gitcmd = exec.CommandContext(ctx, verb, repoPath) gitcmd = exec.CommandContext(ctx, verb, repoPath)
} }
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
if _, err := os.Stat(setting.RepoRootPath); err != nil {
if os.IsNotExist(err) {
return fail("Incorrect configuration.",
"Directory `[repository]` `ROOT` was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository]` `ROOT` an absolute value.")
}
}
gitcmd.Dir = setting.RepoRootPath gitcmd.Dir = setting.RepoRootPath
gitcmd.Stdout = os.Stdout gitcmd.Stdout = os.Stdout
gitcmd.Stdin = os.Stdin gitcmd.Stdin = os.Stdin

View File

@@ -1075,7 +1075,7 @@ PATH =
;SEARCH_REPO_DESCRIPTION = true ;SEARCH_REPO_DESCRIPTION = true
;; ;;
;; Whether to enable a Service Worker to cache frontend assets ;; Whether to enable a Service Worker to cache frontend assets
;USE_SERVICE_WORKER = true ;USE_SERVICE_WORKER = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@@ -18,9 +18,9 @@ params:
description: Git with a cup of tea description: Git with a cup of tea
author: The Gitea Authors author: The Gitea Authors
website: https://docs.gitea.io website: https://docs.gitea.io
version: 1.16.0 version: 1.16.4
minGoVersion: 1.16 minGoVersion: 1.16
goVersion: 1.17 goVersion: 1.18
minNodeVersion: 12.17 minNodeVersion: 12.17
outputs: outputs:

View File

@@ -189,7 +189,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
add it to this config. add it to this config.
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used. - `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page. - `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
- `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets. - `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
### UI - Admin (`ui.admin`) ### UI - Admin (`ui.admin`)

View File

@@ -185,8 +185,6 @@ Before committing, make sure the linters pass:
make lint-frontend make lint-frontend
``` ```
Note: When working on frontend code, set `USE_SERVICE_WORKER` to `false` in `app.ini` to prevent undesirable caching of frontend assets.
### Building and adding SVGs ### Building and adding SVGs
SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/img/svg`. Custom icons can be added in the `web_src/svg` directory. SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/img/svg`. Custom icons can be added in the `web_src/svg` directory.

View File

@@ -43,7 +43,7 @@ Vous devriez avoir une instance fonctionnelle de Gitea. Pour accèder à l'inter
## Named Volumes ## Named Volumes
Ce guide aboutira à une installation avec les données Gita et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité. Ce guide aboutira à une installation avec les données Gitea et PostgreSQL stockées dans des volumes nommés. Cela permet une sauvegarde, une restauration et des mises à niveau en toute simplicité.
### The Database ### The Database

152
go.mod
View File

@@ -3,7 +3,6 @@ module code.gitea.io/gitea
go 1.16 go 1.16
require ( require (
cloud.google.com/go v0.78.0 // indirect
code.gitea.io/gitea-vet v0.2.1 code.gitea.io/gitea-vet v0.2.1
code.gitea.io/sdk/gitea v0.15.1 code.gitea.io/sdk/gitea v0.15.1
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
@@ -12,22 +11,13 @@ require (
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
gitea.com/lunny/levelqueue v0.4.1 gitea.com/lunny/levelqueue v0.4.1
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect github.com/PuerkitoBio/goquery v1.8.0
github.com/PuerkitoBio/goquery v1.7.0 github.com/alecthomas/chroma v0.10.0
github.com/alecthomas/chroma v0.9.4 github.com/blevesearch/bleve/v2 v2.3.1
github.com/andybalholm/brotli v1.0.3 // indirect github.com/caddyserver/certmagic v0.15.4
github.com/andybalholm/cascadia v1.2.0 // indirect
github.com/blevesearch/bleve/v2 v2.3.0
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/caddyserver/certmagic v0.15.2
github.com/chi-middleware/proxy v1.1.1 github.com/chi-middleware/proxy v1.1.1
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect github.com/denisenkom/go-mssqldb v0.12.0
github.com/couchbase/gomemcached v0.1.2 // indirect
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
github.com/denisenkom/go-mssqldb v0.10.0
github.com/djherbis/buffer v1.2.0 github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1 github.com/djherbis/nio/v3 v3.0.1
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951 github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
@@ -36,8 +26,7 @@ require (
github.com/emirpasic/gods v1.12.0 github.com/emirpasic/gods v1.12.0
github.com/ethantkoenig/rupture v1.0.0 github.com/ethantkoenig/rupture v1.0.0
github.com/gliderlabs/ssh v0.3.3 github.com/gliderlabs/ssh v0.3.3
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/chi/v5 v5.0.4
github.com/go-chi/cors v1.2.0 github.com/go-chi/cors v1.2.0
github.com/go-enry/go-enry/v2 v2.7.1 github.com/go-enry/go-enry/v2 v2.7.1
github.com/go-git/go-billy/v5 v5.3.1 github.com/go-git/go-billy/v5 v5.3.1
@@ -48,97 +37,124 @@ require (
github.com/go-swagger/go-swagger v0.27.0 github.com/go-swagger/go-swagger v0.27.0
github.com/go-testfixtures/testfixtures/v3 v3.6.1 github.com/go-testfixtures/testfixtures/v3 v3.6.1
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v4 v4.2.0 github.com/golang-jwt/jwt/v4 v4.3.0
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-github/v39 v39.2.0 github.com/google/go-github/v39 v39.2.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/gorilla/feeds v1.1.1 github.com/gorilla/feeds v1.1.1
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/sessions v1.2.1 github.com/gorilla/sessions v1.2.1
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-version v1.4.0
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/hashicorp/go-version v1.3.1
github.com/hashicorp/golang-lru v0.5.4 github.com/hashicorp/golang-lru v0.5.4
github.com/huandu/xstrings v1.3.2 github.com/huandu/xstrings v1.3.2
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7 github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
github.com/json-iterator/go v1.1.11 github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.13.1 github.com/klauspost/compress v1.13.6
github.com/klauspost/cpuid/v2 v2.0.9 github.com/klauspost/cpuid/v2 v2.0.9
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/lib/pq v1.10.2 github.com/lib/pq v1.10.2
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
github.com/markbates/goth v1.68.0 github.com/markbates/goth v1.69.0
github.com/mattn/go-isatty v0.0.13 github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mattn/go-sqlite3 v1.14.12
github.com/mattn/go-sqlite3 v1.14.8 github.com/mholt/archiver/v3 v3.5.1
github.com/mholt/archiver/v3 v3.5.0 github.com/microcosm-cc/bluemonday v1.0.18
github.com/microcosm-cc/bluemonday v1.0.16 github.com/minio/minio-go/v7 v7.0.23
github.com/minio/md5-simd v1.1.2 // indirect github.com/msteinert/pam v1.0.0
github.com/minio/minio-go/v7 v7.0.12
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/msteinert/pam v0.0.0-20201130170657-e61372126161
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/niklasfasching/go-org v1.6.1 github.com/niklasfasching/go-org v1.6.2
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/oliamb/cutter v0.2.2 github.com/oliamb/cutter v0.2.2
github.com/olivere/elastic/v7 v7.0.25 github.com/olivere/elastic/v7 v7.0.31
github.com/pelletier/go-toml v1.9.0 // indirect
github.com/pierrec/lz4/v4 v4.1.8 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.3.0 github.com/pquerna/otp v1.3.0
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.12.1
github.com/quasoft/websspi v1.0.0 github.com/quasoft/websspi v1.1.2
github.com/rs/xid v1.3.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sergi/go-diff v1.2.0 github.com/sergi/go-diff v1.2.0
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0 github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/unknwon/com v1.0.1 github.com/unknwon/com v1.0.1
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44 github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae
github.com/unrolled/render v1.4.0 github.com/unrolled/render v1.4.0
github.com/urfave/cli v1.22.5 github.com/urfave/cli v1.22.5
github.com/xanzy/go-gitlab v0.50.1 github.com/xanzy/go-gitlab v0.58.0
github.com/yohcop/openid-go v1.0.0 github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.4.0 github.com/yuin/goldmark v1.4.11
github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01 github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
github.com/yuin/goldmark-meta v1.0.0 github.com/yuin/goldmark-meta v1.1.0
go.etcd.io/bbolt v1.3.6 // indirect
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3 go.jolheiser.com/pwn v0.0.3
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.19.0 // indirect go.uber.org/zap v1.21.0 // indirect
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect golang.org/x/tools v0.1.9
golang.org/x/tools v0.1.0
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.62.0 gopkg.in/ini.v1 v1.66.2
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
mvdan.cc/xurls/v2 v2.2.0 mvdan.cc/xurls/v2 v2.2.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.9 xorm.io/builder v0.3.10
xorm.io/xorm v1.2.5 xorm.io/xorm v1.2.5
) )
require (
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/bits-and-blooms/bitset v1.2.1 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/cloudflare/cfssl v1.6.1 // indirect
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
github.com/couchbase/gomemcached v0.1.2 // indirect
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/runtime v0.21.1 // indirect
github.com/go-openapi/strfmt v0.21.1 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
github.com/kevinburke/ssh_config v1.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mholt/acmez v1.0.2 // indirect
github.com/miekg/dns v1.1.46 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/rogpeppe/go-internal v1.8.1 // indirect
github.com/rs/xid v1.3.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/spf13/afero v1.8.0 // indirect
github.com/spf13/cobra v1.3.0 // indirect
github.com/spf13/viper v1.10.1 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.mongodb.org/mongo-driver v1.8.2 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce

864
go.sum

File diff suppressed because it is too large Load Diff

View File

@@ -47,7 +47,7 @@ func TestAPIPrivateServ(t *testing.T) {
results, err := private.ServCommand(ctx, 1, "user2", "repo1", perm.AccessModeWrite, "git-upload-pack", "") results, err := private.ServCommand(ctx, 1, "user2", "repo1", perm.AccessModeWrite, "git-upload-pack", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, results.IsWiki) assert.False(t, results.IsWiki)
assert.False(t, results.IsDeployKey) assert.Zero(t, results.DeployKeyID)
assert.Equal(t, int64(1), results.KeyID) assert.Equal(t, int64(1), results.KeyID)
assert.Equal(t, "user2@localhost", results.KeyName) assert.Equal(t, "user2@localhost", results.KeyName)
assert.Equal(t, "user2", results.UserName) assert.Equal(t, "user2", results.UserName)
@@ -70,7 +70,7 @@ func TestAPIPrivateServ(t *testing.T) {
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", perm.AccessModeRead, "git-upload-pack", "") results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", perm.AccessModeRead, "git-upload-pack", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, results.IsWiki) assert.False(t, results.IsWiki)
assert.False(t, results.IsDeployKey) assert.Zero(t, results.DeployKeyID)
assert.Equal(t, int64(1), results.KeyID) assert.Equal(t, int64(1), results.KeyID)
assert.Equal(t, "user2@localhost", results.KeyName) assert.Equal(t, "user2@localhost", results.KeyName)
assert.Equal(t, "user2", results.UserName) assert.Equal(t, "user2", results.UserName)
@@ -92,7 +92,7 @@ func TestAPIPrivateServ(t *testing.T) {
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", perm.AccessModeRead, "git-upload-pack", "") results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", perm.AccessModeRead, "git-upload-pack", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, results.IsWiki) assert.False(t, results.IsWiki)
assert.True(t, results.IsDeployKey) assert.NotZero(t, results.DeployKeyID)
assert.Equal(t, deployKey.KeyID, results.KeyID) assert.Equal(t, deployKey.KeyID, results.KeyID)
assert.Equal(t, "test-deploy", results.KeyName) assert.Equal(t, "test-deploy", results.KeyName)
assert.Equal(t, "user15", results.UserName) assert.Equal(t, "user15", results.UserName)
@@ -129,7 +129,7 @@ func TestAPIPrivateServ(t *testing.T) {
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeRead, "git-upload-pack", "") results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeRead, "git-upload-pack", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, results.IsWiki) assert.False(t, results.IsWiki)
assert.True(t, results.IsDeployKey) assert.NotZero(t, results.DeployKeyID)
assert.Equal(t, deployKey.KeyID, results.KeyID) assert.Equal(t, deployKey.KeyID, results.KeyID)
assert.Equal(t, "test-deploy", results.KeyName) assert.Equal(t, "test-deploy", results.KeyName)
assert.Equal(t, "user15", results.UserName) assert.Equal(t, "user15", results.UserName)
@@ -142,7 +142,7 @@ func TestAPIPrivateServ(t *testing.T) {
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeWrite, "git-upload-pack", "") results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeWrite, "git-upload-pack", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, results.IsWiki) assert.False(t, results.IsWiki)
assert.True(t, results.IsDeployKey) assert.NotZero(t, results.DeployKeyID)
assert.Equal(t, deployKey.KeyID, results.KeyID) assert.Equal(t, deployKey.KeyID, results.KeyID)
assert.Equal(t, "test-deploy", results.KeyName) assert.Equal(t, "test-deploy", results.KeyName)
assert.Equal(t, "user15", results.UserName) assert.Equal(t, "user15", results.UserName)

View File

@@ -69,6 +69,12 @@ func TestAPIAddEmail(t *testing.T) {
Primary: false, Primary: false,
}, },
}, emails) }, emails)
opts = api.CreateEmailOption{
Emails: []string{"notAEmail"},
}
req = NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts)
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
} }
func TestAPIDeleteEmail(t *testing.T) { func TestAPIDeleteEmail(t *testing.T) {

View File

@@ -112,6 +112,13 @@ func TestMain(m *testing.M) {
} }
} }
os.Unsetenv("GIT_AUTHOR_NAME")
os.Unsetenv("GIT_AUTHOR_EMAIL")
os.Unsetenv("GIT_AUTHOR_DATE")
os.Unsetenv("GIT_COMMITTER_NAME")
os.Unsetenv("GIT_COMMITTER_EMAIL")
os.Unsetenv("GIT_COMMITTER_DATE")
err := unittest.InitFixtures( err := unittest.InitFixtures(
unittest.FixturesOptions{ unittest.FixturesOptions{
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),

View File

@@ -25,6 +25,8 @@ import (
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/services/pull" "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
files_service "code.gitea.io/gitea/services/repository/files"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/unknwon/i18n" "github.com/unknwon/i18n"
@@ -65,7 +67,7 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str
func TestPullMerge(t *testing.T) { func TestPullMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
assert.NoError(t, err) assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks) hookTasksLenBefore := len(hookTasks)
@@ -87,7 +89,7 @@ func TestPullMerge(t *testing.T) {
func TestPullRebase(t *testing.T) { func TestPullRebase(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
assert.NoError(t, err) assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks) hookTasksLenBefore := len(hookTasks)
@@ -109,7 +111,7 @@ func TestPullRebase(t *testing.T) {
func TestPullRebaseMerge(t *testing.T) { func TestPullRebaseMerge(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
assert.NoError(t, err) assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks) hookTasksLenBefore := len(hookTasks)
@@ -131,7 +133,7 @@ func TestPullRebaseMerge(t *testing.T) {
func TestPullSquash(t *testing.T) { func TestPullSquash(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
hookTasks, err := webhook.HookTasks(1, 1) //Retrieve previous hook number hookTasks, err := webhook.HookTasks(1, 1) // Retrieve previous hook number
assert.NoError(t, err) assert.NoError(t, err)
hookTasksLenBefore := len(hookTasks) hookTasksLenBefore := len(hookTasks)
@@ -335,3 +337,74 @@ func TestCantMergeUnrelated(t *testing.T) {
gitRepo.Close() gitRepo.Close()
}) })
} }
func TestConflictChecking(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
// Create new clean repo to test conflict checking.
baseRepo, err := repo_service.CreateRepository(user, user, models.CreateRepoOptions{
Name: "conflict-checking",
Description: "Tempo repo",
AutoInit: true,
Readme: "Default",
DefaultBranch: "main",
})
assert.NoError(t, err)
assert.NotEmpty(t, baseRepo)
// create a commit on new branch.
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
TreePath: "important_file",
Message: "Add a important file",
Content: "Just a non-important file",
IsNewFile: true,
OldBranch: "main",
NewBranch: "important-secrets",
})
assert.NoError(t, err)
// create a commit on main branch.
_, err = files_service.CreateOrUpdateRepoFile(baseRepo, user, &files_service.UpdateRepoFileOptions{
TreePath: "important_file",
Message: "Add a important file",
Content: "Not the same content :P",
IsNewFile: true,
OldBranch: "main",
NewBranch: "main",
})
assert.NoError(t, err)
// create Pull to merge the important-secrets branch into main branch.
pullIssue := &models.Issue{
RepoID: baseRepo.ID,
Title: "PR with conflict!",
PosterID: user.ID,
Poster: user,
IsPull: true,
}
pullRequest := &models.PullRequest{
HeadRepoID: baseRepo.ID,
BaseRepoID: baseRepo.ID,
HeadBranch: "important-secrets",
BaseBranch: "main",
HeadRepo: baseRepo,
BaseRepo: baseRepo,
Type: models.PullRequestGitea,
}
err = pull.NewPullRequest(baseRepo, pullIssue, nil, nil, pullRequest, nil)
assert.NoError(t, err)
issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue)
conflictingPR, err := models.GetPullRequestByIssueID(issue.ID)
assert.NoError(t, err)
// Ensure conflictedFiles is populated.
assert.Equal(t, 1, len(conflictingPR.ConflictedFiles))
// Check if status is correct.
assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status)
// Ensure that mergeable returns false
assert.False(t, conflictingPR.Mergeable())
})
}

View File

@@ -337,7 +337,7 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
actions := make([]*Action, 0, setting.UI.FeedPagingNum) actions := make([]*Action, 0, setting.UI.FeedPagingNum)
if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("created_unix").Where(cond).Find(&actions); err != nil { if err := db.GetEngine(db.DefaultContext).Limit(setting.UI.FeedPagingNum).Desc("`action`.created_unix").Where(cond).Join("INNER", "repository", "`repository`.id = `action`.repo_id").Find(&actions); err != nil {
return nil, fmt.Errorf("Find: %v", err) return nil, fmt.Errorf("Find: %v", err)
} }
@@ -401,7 +401,7 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID}) cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
} }
if !opts.IncludePrivate { if !opts.IncludePrivate {
cond = cond.And(builder.Eq{"is_private": false}) cond = cond.And(builder.Eq{"`action`.is_private": false})
} }
if !opts.IncludeDeleted { if !opts.IncludeDeleted {
cond = cond.And(builder.Eq{"is_deleted": false}) cond = cond.And(builder.Eq{"is_deleted": false})
@@ -414,8 +414,8 @@ func activityQueryCondition(opts GetFeedsOptions) (builder.Cond, error) {
} else { } else {
dateHigh := dateLow.Add(86399000000000) // 23h59m59s dateHigh := dateLow.Add(86399000000000) // 23h59m59s
cond = cond.And(builder.Gte{"created_unix": dateLow.Unix()}) cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
cond = cond.And(builder.Lte{"created_unix": dateHigh.Unix()}) cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
} }
} }

View File

@@ -129,3 +129,20 @@ func TestNotifyWatchers(t *testing.T) {
OpType: action.OpType, OpType: action.OpType,
}) })
} }
func TestGetFeedsCorrupted(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
unittest.AssertExistsAndLoadBean(t, &Action{
ID: 8,
RepoID: 1700,
})
actions, err := GetFeeds(GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.Len(t, actions, 0)
}

View File

@@ -58,7 +58,7 @@ func (key *DeployKey) GetContent() error {
return nil return nil
} }
// IsReadOnly checks if the key can only be used for read operations // IsReadOnly checks if the key can only be used for read operations, used by template
func (key *DeployKey) IsReadOnly() bool { func (key *DeployKey) IsReadOnly() bool {
return key.Mode == perm.AccessModeRead return key.Mode == perm.AccessModeRead
} }
@@ -203,12 +203,6 @@ func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
return err return err
} }
// UpdateDeployKey updates deploy key information.
func UpdateDeployKey(key *DeployKey) error {
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).AllCols().Update(key)
return err
}
// ListDeployKeysOptions are options for ListDeployKeys // ListDeployKeysOptions are options for ListDeployKeys
type ListDeployKeysOptions struct { type ListDeployKeysOptions struct {
db.ListOptions db.ListOptions

View File

@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -247,3 +248,23 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
return res.RowsAffected() return res.RowsAffected()
} }
// CountActionCreatedUnixString count actions where created_unix is an empty string
func CountActionCreatedUnixString() (int64, error) {
if setting.Database.UseSQLite3 {
return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
}
return 0, nil
}
// FixActionCreatedUnixString set created_unix to zero if it is an empty string
func FixActionCreatedUnixString() (int64, error) {
if setting.Database.UseSQLite3 {
res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
if err != nil {
return 0, err
}
return res.RowsAffected()
}
return 0, nil
}

View File

@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -33,3 +34,46 @@ func TestDeleteOrphanedObjects(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, countBefore, countAfter) assert.EqualValues(t, countBefore, countAfter)
} }
func TestConsistencyUpdateAction(t *testing.T) {
if !setting.Database.UseSQLite3 {
t.Skip("Test is only for SQLite database.")
}
assert.NoError(t, unittest.PrepareTestDatabase())
id := 8
unittest.AssertExistsAndLoadBean(t, &Action{
ID: int64(id),
})
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
assert.NoError(t, err)
actions := make([]*Action, 0, 1)
//
// XORM returns an error when created_unix is a string
//
err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "type string to a int64: invalid syntax")
}
//
// Get rid of incorrectly set created_unix
//
count, err := CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
count, err = CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
count, err = FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
//
// XORM must be happy now
//
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
unittest.CheckConsistencyFor(t, &Action{})
}

View File

@@ -332,7 +332,6 @@ type ErrInvalidCloneAddr struct {
IsProtocolInvalid bool IsProtocolInvalid bool
IsPermissionDenied bool IsPermissionDenied bool
LocalPath bool LocalPath bool
NotResolvedIP bool
} }
// IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr. // IsErrInvalidCloneAddr checks if an error is a ErrInvalidCloneAddr.
@@ -342,9 +341,6 @@ func IsErrInvalidCloneAddr(err error) bool {
} }
func (err *ErrInvalidCloneAddr) Error() string { func (err *ErrInvalidCloneAddr) Error() string {
if err.NotResolvedIP {
return fmt.Sprintf("migration/cloning from '%s' is not allowed: unknown hostname", err.Host)
}
if err.IsInvalidPath { if err.IsInvalidPath {
return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host) return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided path is invalid", err.Host)
} }

View File

@@ -56,3 +56,11 @@
repo_id: 8 repo_id: 8
is_private: false is_private: false
created_unix: 1603011540 # grouped with id:7 created_unix: 1603011540 # grouped with id:7
- id: 8
user_id: 1
op_type: 12 # close issue
act_user_id: 1
repo_id: 1700 # dangling intentional
is_private: false
created_unix: 1603011541

View File

@@ -23,8 +23,8 @@ const (
EnvPusherName = "GITEA_PUSHER_NAME" EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL" EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID" EnvPusherID = "GITEA_PUSHER_ID"
EnvKeyID = "GITEA_KEY_ID" EnvKeyID = "GITEA_KEY_ID" // public key ID
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY" EnvDeployKeyID = "GITEA_DEPLOY_KEY_ID"
EnvPRID = "GITEA_PR_ID" EnvPRID = "GITEA_PR_ID"
EnvIsInternal = "GITEA_INTERNAL_PUSH" EnvIsInternal = "GITEA_INTERNAL_PUSH"
EnvAppURL = "GITEA_ROOT_URL" EnvAppURL = "GITEA_ROOT_URL"

View File

@@ -1165,7 +1165,8 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
// IssuesOptions represents options of an issue. // IssuesOptions represents options of an issue.
type IssuesOptions struct { type IssuesOptions struct {
db.ListOptions db.ListOptions
RepoIDs []int64 // include all repos if empty RepoID int64 // overwrites RepoCond if not 0
RepoCond builder.Cond
AssigneeID int64 AssigneeID int64
PosterID int64 PosterID int64
MentionedID int64 MentionedID int64
@@ -1238,7 +1239,7 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64
} }
} }
func (opts *IssuesOptions) setupSession(sess *xorm.Session) { func (opts *IssuesOptions) setupSessionWithLimit(sess *xorm.Session) {
if opts.Page >= 0 && opts.PageSize > 0 { if opts.Page >= 0 && opts.PageSize > 0 {
var start int var start int
if opts.Page == 0 { if opts.Page == 0 {
@@ -1248,20 +1249,23 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
} }
sess.Limit(opts.PageSize, start) sess.Limit(opts.PageSize, start)
} }
opts.setupSessionNoLimit(sess)
}
func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
if len(opts.IssueIDs) > 0 { if len(opts.IssueIDs) > 0 {
sess.In("issue.id", opts.IssueIDs) sess.In("issue.id", opts.IssueIDs)
} }
if len(opts.RepoIDs) > 0 { if opts.RepoID != 0 {
applyReposCondition(sess, opts.RepoIDs) opts.RepoCond = builder.Eq{"issue.repo_id": opts.RepoID}
}
if opts.RepoCond != nil {
sess.And(opts.RepoCond)
} }
switch opts.IsClosed { if !opts.IsClosed.IsNone() {
case util.OptionalBoolTrue: sess.And("issue.is_closed=?", opts.IsClosed.IsTrue())
sess.And("issue.is_closed=?", true)
case util.OptionalBoolFalse:
sess.And("issue.is_closed=?", false)
} }
if opts.AssigneeID > 0 { if opts.AssigneeID > 0 {
@@ -1342,9 +1346,7 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
} }
if opts.User != nil { if opts.User != nil {
sess.And( sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()))
issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.IsTrue()),
)
} }
} }
@@ -1380,10 +1382,6 @@ func issuePullAccessibleRepoCond(repoIDstr string, userID int64, org *Organizati
return cond return cond
} }
func applyReposCondition(sess *xorm.Session, repoIDs []int64) *xorm.Session {
return sess.In("issue.repo_id", repoIDs)
}
func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session { func applyAssigneeCondition(sess *xorm.Session, assigneeID int64) *xorm.Session {
return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id"). return sess.Join("INNER", "issue_assignees", "issue.id = issue_assignees.issue_id").
And("issue_assignees.assignee_id = ?", assigneeID) And("issue_assignees.assignee_id = ?", assigneeID)
@@ -1413,8 +1411,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
e := db.GetEngine(db.DefaultContext) e := db.GetEngine(db.DefaultContext)
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id") sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionNoLimit(sess)
opts.setupSession(sess)
countsSlice := make([]*struct { countsSlice := make([]*struct {
RepoID int64 RepoID int64
@@ -1424,7 +1421,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
Select("issue.repo_id AS repo_id, COUNT(*) AS count"). Select("issue.repo_id AS repo_id, COUNT(*) AS count").
Table("issue"). Table("issue").
Find(&countsSlice); err != nil { Find(&countsSlice); err != nil {
return nil, err return nil, fmt.Errorf("unable to CountIssuesByRepo: %w", err)
} }
countMap := make(map[int64]int64, len(countsSlice)) countMap := make(map[int64]int64, len(countsSlice))
@@ -1440,15 +1437,14 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
e := db.GetEngine(db.DefaultContext) e := db.GetEngine(db.DefaultContext)
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id") sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSessionNoLimit(sess)
opts.setupSession(sess)
accessCond := accessibleRepositoryCondition(user) accessCond := accessibleRepositoryCondition(user)
if err := sess.Where(accessCond). if err := sess.Where(accessCond).
Distinct("issue.repo_id"). Distinct("issue.repo_id").
Table("issue"). Table("issue").
Find(&repoIDs); err != nil { Find(&repoIDs); err != nil {
return nil, err return nil, fmt.Errorf("unable to GetRepoIDsForIssuesOptions: %w", err)
} }
return repoIDs, nil return repoIDs, nil
@@ -1459,17 +1455,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
e := db.GetEngine(db.DefaultContext) e := db.GetEngine(db.DefaultContext)
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id") sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSession(sess) opts.setupSessionWithLimit(sess)
sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID) sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID)
issues := make([]*Issue, 0, opts.ListOptions.PageSize) issues := make([]*Issue, 0, opts.ListOptions.PageSize)
if err := sess.Find(&issues); err != nil { if err := sess.Find(&issues); err != nil {
return nil, fmt.Errorf("Find: %v", err) return nil, fmt.Errorf("unable to query Issues: %w", err)
} }
sess.Close()
if err := IssueList(issues).LoadAttributes(); err != nil { if err := IssueList(issues).LoadAttributes(); err != nil {
return nil, fmt.Errorf("LoadAttributes: %v", err) return nil, fmt.Errorf("unable to LoadAttributes for Issues: %w", err)
} }
return issues, nil return issues, nil
@@ -1480,18 +1475,18 @@ func CountIssues(opts *IssuesOptions) (int64, error) {
e := db.GetEngine(db.DefaultContext) e := db.GetEngine(db.DefaultContext)
countsSlice := make([]*struct { countsSlice := make([]*struct {
RepoID int64 Count int64
Count int64
}, 0, 1) }, 0, 1)
sess := e.Select("COUNT(issue.id) AS count").Table("issue") sess := e.Select("COUNT(issue.id) AS count").Table("issue")
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id") sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
opts.setupSession(sess) opts.setupSessionNoLimit(sess)
if err := sess.Find(&countsSlice); err != nil { if err := sess.Find(&countsSlice); err != nil {
return 0, fmt.Errorf("Find: %v", err) return 0, fmt.Errorf("unable to CountIssues: %w", err)
} }
if len(countsSlice) < 1 { if len(countsSlice) != 1 {
return 0, fmt.Errorf("there is less than one result sql record") return 0, fmt.Errorf("unable to get one row result when CountIssues, row count=%d", len(countsSlice))
} }
return countsSlice[0].Count, nil return countsSlice[0].Count, nil
} }
@@ -1551,6 +1546,7 @@ const (
FilterModeCreate FilterModeCreate
FilterModeMention FilterModeMention
FilterModeReviewRequested FilterModeReviewRequested
FilterModeYourRepositories
) )
func parseCountResult(results []map[string][]byte) int64 { func parseCountResult(results []map[string][]byte) int64 {
@@ -1695,6 +1691,7 @@ type UserIssueStatsOptions struct {
IssueIDs []int64 IssueIDs []int64
IsArchived util.OptionalBool IsArchived util.OptionalBool
LabelIDs []int64 LabelIDs []int64
RepoCond builder.Cond
Org *Organization Org *Organization
Team *Team Team *Team
} }
@@ -1712,6 +1709,9 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
if len(opts.IssueIDs) > 0 { if len(opts.IssueIDs) > 0 {
cond = cond.And(builder.In("issue.id", opts.IssueIDs)) cond = cond.And(builder.In("issue.id", opts.IssueIDs))
} }
if opts.RepoCond != nil {
cond = cond.And(opts.RepoCond)
}
if opts.UserID > 0 { if opts.UserID > 0 {
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull)) cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
@@ -1733,7 +1733,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
} }
switch opts.FilterMode { switch opts.FilterMode {
case FilterModeAll: case FilterModeAll, FilterModeYourRepositories:
stats.OpenCount, err = sess(cond). stats.OpenCount, err = sess(cond).
And("issue.is_closed = ?", false). And("issue.is_closed = ?", false).
Count(new(Issue)) Count(new(Issue))

View File

@@ -101,12 +101,9 @@ func (label *Label) CalOpenIssues() {
// CalOpenOrgIssues calculates the open issues of a label for a specific repo // CalOpenOrgIssues calculates the open issues of a label for a specific repo
func (label *Label) CalOpenOrgIssues(repoID, labelID int64) { func (label *Label) CalOpenOrgIssues(repoID, labelID int64) {
repoIDs := []int64{repoID}
labelIDs := []int64{labelID}
counts, _ := CountIssuesByRepo(&IssuesOptions{ counts, _ := CountIssuesByRepo(&IssuesOptions{
RepoIDs: repoIDs, RepoID: repoID,
LabelIDs: labelIDs, LabelIDs: []int64{labelID},
}) })
for _, count := range counts { for _, count := range counts {

View File

@@ -17,6 +17,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"xorm.io/builder"
) )
func TestIssue_ReplaceLabels(t *testing.T) { func TestIssue_ReplaceLabels(t *testing.T) {
@@ -153,7 +154,7 @@ func TestIssues(t *testing.T) {
}, },
{ {
IssuesOptions{ IssuesOptions{
RepoIDs: []int64{1, 3}, RepoCond: builder.In("repo_id", 1, 3),
SortType: "oldest", SortType: "oldest",
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
Page: 1, Page: 1,
@@ -340,7 +341,7 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
}, },
{ {
IssuesOptions{ IssuesOptions{
RepoIDs: []int64{1, 2}, RepoCond: builder.In("repo_id", 1, 2),
}, },
[]int64{1, 2}, []int64{1, 2},
}, },

View File

@@ -453,7 +453,7 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
// Downgrading Gitea's database version not supported // Downgrading Gitea's database version not supported
if int(v-minDBVersion) > len(migrations) { if int(v-minDBVersion) > len(migrations) {
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gita, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations)) msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)." msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
if !setting.IsProd { if !setting.IsProd {
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations)) msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))

View File

@@ -19,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -776,8 +777,45 @@ func DeleteTeam(t *Team) error {
return err return err
} }
if err := t.removeAllRepositories(ctx); err != nil { // update branch protections
return err {
protections := make([]*ProtectedBranch, 0, 10)
err := sess.In("repo_id",
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
Find(&protections)
if err != nil {
return fmt.Errorf("findProtectedBranches: %v", err)
}
for _, p := range protections {
var matched1, matched2, matched3 bool
if len(p.WhitelistTeamIDs) != 0 {
p.WhitelistTeamIDs, matched1 = util.RemoveIDFromList(
p.WhitelistTeamIDs, t.ID)
}
if len(p.ApprovalsWhitelistTeamIDs) != 0 {
p.ApprovalsWhitelistTeamIDs, matched2 = util.RemoveIDFromList(
p.ApprovalsWhitelistTeamIDs, t.ID)
}
if len(p.MergeWhitelistTeamIDs) != 0 {
p.MergeWhitelistTeamIDs, matched3 = util.RemoveIDFromList(
p.MergeWhitelistTeamIDs, t.ID)
}
if matched1 || matched2 || matched3 {
if _, err = sess.ID(p.ID).Cols(
"whitelist_team_i_ds",
"merge_whitelist_team_i_ds",
"approvals_whitelist_team_i_ds",
).Update(p); err != nil {
return fmt.Errorf("updateProtectedBranches: %v", err)
}
}
}
}
if !t.IncludesAllRepositories {
if err := t.removeAllRepositories(ctx); err != nil {
return err
}
} }
// Delete team-user. // Delete team-user.
@@ -902,11 +940,6 @@ func AddTeamMember(team *Team, userID int64) error {
return err return err
} }
// Get team and its repositories.
if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
return err
}
ctx, committer, err := db.TxContext() ctx, committer, err := db.TxContext()
if err != nil { if err != nil {
return err return err
@@ -928,17 +961,51 @@ func AddTeamMember(team *Team, userID int64) error {
team.NumMembers++ team.NumMembers++
// Give access to team repositories. // Give access to team repositories.
for _, repo := range team.Repos { // update exist access if mode become bigger
if err := recalculateUserAccess(ctx, repo, userID); err != nil { subQuery := builder.Select("repo_id").From("team_repo").
return err Where(builder.Eq{"team_id": team.ID})
}
if setting.Service.AutoWatchNewRepos { if _, err := sess.Where("user_id=?", userID).
if err = repo_model.WatchRepoCtx(ctx, userID, repo.ID, true); err != nil { In("repo_id", subQuery).
return err And("mode < ?", team.AccessMode).
SetExpr("mode", team.AccessMode).
Update(new(Access)); err != nil {
return fmt.Errorf("update user accesses: %v", err)
}
// for not exist access
var repoIDs []int64
accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": userID})
if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
return fmt.Errorf("select id accesses: %v", err)
}
accesses := make([]*Access, 0, 100)
for i, repoID := range repoIDs {
accesses = append(accesses, &Access{RepoID: repoID, UserID: userID, Mode: team.AccessMode})
if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
if err = db.Insert(ctx, accesses); err != nil {
return fmt.Errorf("insert new user accesses: %v", err)
} }
accesses = accesses[:0]
} }
} }
// watch could be failed, so run it in a goroutine
if setting.Service.AutoWatchNewRepos {
// Get team and its repositories.
if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
log.Error("getRepositories failed: %v", err)
}
go func(repos []*repo_model.Repository) {
for _, repo := range repos {
if err = repo_model.WatchRepoCtx(db.DefaultContext, userID, repo.ID, true); err != nil {
log.Error("watch repo failed: %v", err)
}
}
}(team.Repos)
}
return committer.Commit() return committer.Commit()
} }

View File

@@ -222,22 +222,19 @@ func (pr *PullRequest) loadProtectedBranch(ctx context.Context) (err error) {
} }
// GetDefaultMergeMessage returns default message used when merging pull request // GetDefaultMergeMessage returns default message used when merging pull request
func (pr *PullRequest) GetDefaultMergeMessage() string { func (pr *PullRequest) GetDefaultMergeMessage() (string, error) {
if pr.HeadRepo == nil { if pr.HeadRepo == nil {
var err error var err error
pr.HeadRepo, err = repo_model.GetRepositoryByID(pr.HeadRepoID) pr.HeadRepo, err = repo_model.GetRepositoryByID(pr.HeadRepoID)
if err != nil { if err != nil {
log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err) return "", fmt.Errorf("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
return ""
} }
} }
if err := pr.LoadIssue(); err != nil { if err := pr.LoadIssue(); err != nil {
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err) return "", fmt.Errorf("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
return ""
} }
if err := pr.LoadBaseRepo(); err != nil { if err := pr.LoadBaseRepo(); err != nil {
log.Error("LoadBaseRepo: %v", err) return "", fmt.Errorf("LoadBaseRepo: %v", err)
return ""
} }
issueReference := "#" issueReference := "#"
@@ -246,10 +243,10 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
} }
if pr.BaseRepoID == pr.HeadRepoID { if pr.BaseRepoID == pr.HeadRepoID {
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch) return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), nil
} }
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch) return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch), nil
} }
// ReviewCount represents a count of Reviews // ReviewCount represents a count of Reviews
@@ -335,19 +332,17 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
} }
// GetDefaultSquashMessage returns default message used when squash and merging pull request // GetDefaultSquashMessage returns default message used when squash and merging pull request
func (pr *PullRequest) GetDefaultSquashMessage() string { func (pr *PullRequest) GetDefaultSquashMessage() (string, error) {
if err := pr.LoadIssue(); err != nil { if err := pr.LoadIssue(); err != nil {
log.Error("LoadIssue: %v", err) return "", fmt.Errorf("LoadIssue: %v", err)
return ""
} }
if err := pr.LoadBaseRepo(); err != nil { if err := pr.LoadBaseRepo(); err != nil {
log.Error("LoadBaseRepo: %v", err) return "", fmt.Errorf("LoadBaseRepo: %v", err)
return ""
} }
if pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) { if pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) {
return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index) return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index), nil
} }
return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index) return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index), nil
} }
// GetGitRefName returns git ref for hidden pull request branch // GetGitRefName returns git ref for hidden pull request branch
@@ -702,3 +697,14 @@ func (pr *PullRequest) GetHeadBranchHTMLURL() string {
} }
return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch) return pr.HeadRepo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(pr.HeadBranch)
} }
// Mergeable returns if the pullrequest is mergeable.
func (pr *PullRequest) Mergeable() bool {
// If a pull request isn't mergable if it's:
// - Being conflict checked.
// - Has a conflict.
// - Received a error while being conflict checked.
// - Is a work-in-progress pull request.
return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict &&
pr.Status != PullRequestStatusError && !pr.IsWorkInProgress()
}

View File

@@ -261,11 +261,15 @@ func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", pr.GetDefaultMergeMessage()) msg, err := pr.GetDefaultMergeMessage()
assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", msg)
pr.BaseRepoID = 1 pr.BaseRepoID = 1
pr.HeadRepoID = 2 pr.HeadRepoID = 2
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", pr.GetDefaultMergeMessage()) msg, err = pr.GetDefaultMergeMessage()
assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", msg)
} }
func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) { func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
@@ -283,9 +287,13 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2, BaseRepo: baseRepo}).(*PullRequest) pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2, BaseRepo: baseRepo}).(*PullRequest)
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", pr.GetDefaultMergeMessage()) msg, err := pr.GetDefaultMergeMessage()
assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", msg)
pr.BaseRepoID = 1 pr.BaseRepoID = 1
pr.HeadRepoID = 2 pr.HeadRepoID = 2
assert.Equal(t, "Merge pull request 'issue3' (!3) from user2/repo1:branch2 into master", pr.GetDefaultMergeMessage()) msg, err = pr.GetDefaultMergeMessage()
assert.NoError(t, err)
assert.Equal(t, "Merge pull request 'issue3' (!3) from user2/repo1:branch2 into master", msg)
} }

View File

@@ -132,7 +132,7 @@ func CheckRepoUnitUser(repo *repo_model.Repository, user *user_model.User, unitT
} }
func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool { func checkRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
if user.IsAdmin { if user != nil && user.IsAdmin {
return true return true
} }
perm, err := getUserRepoPermission(ctx, repo, user) perm, err := getUserRepoPermission(ctx, repo, user)

View File

@@ -6,6 +6,7 @@
package repo package repo
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"time" "time"
@@ -115,6 +116,13 @@ func UpdateMirror(m *Mirror) error {
return updateMirror(db.GetEngine(db.DefaultContext), m) return updateMirror(db.GetEngine(db.DefaultContext), m)
} }
// TouchMirror updates the mirror updatedUnix
func TouchMirror(ctx context.Context, m *Mirror) error {
m.UpdatedUnix = timeutil.TimeStampNow()
_, err := db.GetEngine(ctx).ID(m.ID).Cols("updated_unix").Update(m)
return err
}
// DeleteMirrorByRepoID deletes a mirror by repoID // DeleteMirrorByRepoID deletes a mirror by repoID
func DeleteMirrorByRepoID(repoID int64) error { func DeleteMirrorByRepoID(repoID int64) error {
_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID}) _, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})

View File

@@ -233,14 +233,28 @@ func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Typ
builder.Select("repo_id").From("team_repo").Where( builder.Select("repo_id").From("team_repo").Where(
builder.Eq{ builder.Eq{
"team_id": teamID, "team_id": teamID,
}.And( }.And(builder.Or(
// Check if the user is member of the team.
builder.In( builder.In(
"team_id", builder.Select("team_id").From("team_user").Where( "team_id", builder.Select("team_id").From("team_user").Where(
builder.Eq{ builder.Eq{
"uid": userID, "uid": userID,
}, },
), ),
)).And( ),
// Check if the user is in the owner team of the organisation.
builder.Exists(builder.Select("team_id").From("team_user").
Where(builder.Eq{
"org_id": orgID,
"team_id": builder.Select("id").From("team").Where(
builder.Eq{
"org_id": orgID,
"lower_name": strings.ToLower(ownerTeamName),
}),
"uid": userID,
}),
),
)).And(
builder.In( builder.In(
"team_id", builder.Select("team_id").From("team_unit").Where( "team_id", builder.Select("team_id").From("team_unit").Where(
builder.Eq{ builder.Eq{

View File

@@ -175,8 +175,10 @@ func init() {
checkForActionConsistency := func(t assert.TestingT, bean interface{}) { checkForActionConsistency := func(t assert.TestingT, bean interface{}) {
action := reflectionWrap(bean) action := reflectionWrap(bean)
repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")}) if action.int("RepoID") != 1700 { // dangling intentional
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action) repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")})
assert.Equal(t, parseBool(repoRow["is_private"]), action.bool("IsPrivate"), "action: %+v", action)
}
} }
consistencyCheckMap["user"] = checkForUserConsistency consistencyCheckMap["user"] = checkForUserConsistency

View File

@@ -13,11 +13,13 @@ import (
_ "image/jpeg" // Needed for jpeg support _ "image/jpeg" // Needed for jpeg support
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder" "xorm.io/builder"
) )
@@ -82,6 +84,11 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
} }
// ***** END: Follow ***** // ***** END: Follow *****
if _, err := db.GetEngine(ctx).In("grant_id", builder.Select("id").From("oauth2_grant").Where(builder.Eq{"oauth2_grant.user_id": u.ID})).
Delete(&auth_model.OAuth2AuthorizationCode{}); err != nil {
return err
}
if err = deleteBeans(e, if err = deleteBeans(e,
&AccessToken{UID: u.ID}, &AccessToken{UID: u.ID},
&Collaboration{UserID: u.ID}, &Collaboration{UserID: u.ID},
@@ -99,6 +106,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
&Collaboration{UserID: u.ID}, &Collaboration{UserID: u.ID},
&Stopwatch{UserID: u.ID}, &Stopwatch{UserID: u.ID},
&user_model.Setting{UserID: u.ID}, &user_model.Setting{UserID: u.ID},
&auth_model.OAuth2Application{UID: u.ID},
&auth_model.OAuth2Grant{UserID: u.ID},
); err != nil { ); err != nil {
return fmt.Errorf("deleteBeans: %v", err) return fmt.Errorf("deleteBeans: %v", err)
} }
@@ -130,6 +139,50 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
} }
} }
// ***** START: Branch Protections *****
{
const batchSize = 50
for start := 0; ; start += batchSize {
protections := make([]*ProtectedBranch, 0, batchSize)
// @perf: We can't filter on DB side by u.ID, as those IDs are serialized as JSON strings.
// We could filter down with `WHERE repo_id IN (reposWithPushPermission(u))`,
// though that query will be quite complex and tricky to maintain (compare `getRepoAssignees()`).
// Also, as we didn't update branch protections when removing entries from `access` table,
// it's safer to iterate all protected branches.
if err = e.Limit(batchSize, start).Find(&protections); err != nil {
return fmt.Errorf("findProtectedBranches: %v", err)
}
if len(protections) == 0 {
break
}
for _, p := range protections {
var matched1, matched2, matched3 bool
if len(p.WhitelistUserIDs) != 0 {
p.WhitelistUserIDs, matched1 = util.RemoveIDFromList(
p.WhitelistUserIDs, u.ID)
}
if len(p.ApprovalsWhitelistUserIDs) != 0 {
p.ApprovalsWhitelistUserIDs, matched2 = util.RemoveIDFromList(
p.ApprovalsWhitelistUserIDs, u.ID)
}
if len(p.MergeWhitelistUserIDs) != 0 {
p.MergeWhitelistUserIDs, matched3 = util.RemoveIDFromList(
p.MergeWhitelistUserIDs, u.ID)
}
if matched1 || matched2 || matched3 {
if _, err = e.ID(p.ID).Cols(
"whitelist_user_i_ds",
"merge_whitelist_user_i_ds",
"approvals_whitelist_user_i_ds",
).Update(p); err != nil {
return fmt.Errorf("updateProtectedBranches: %v", err)
}
}
}
}
}
// ***** END: Branch Protections *****
// ***** START: PublicKey ***** // ***** START: PublicKey *****
if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil { if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil {
return fmt.Errorf("deletePublicKeys: %v", err) return fmt.Errorf("deletePublicKeys: %v", err)

View File

@@ -622,7 +622,14 @@ func IsUsableUsername(name string) error {
// CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation // CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation
type CreateUserOverwriteOptions struct { type CreateUserOverwriteOptions struct {
Visibility structs.VisibleType KeepEmailPrivate util.OptionalBool
Visibility *structs.VisibleType
AllowCreateOrganization util.OptionalBool
EmailNotificationsPreference *string
MaxRepoCreation *int
Theme *string
IsRestricted util.OptionalBool
IsActive util.OptionalBool
} }
// CreateUser creates record of a new user. // CreateUser creates record of a new user.
@@ -638,10 +645,36 @@ func CreateUser(u *User, overwriteDefault ...*CreateUserOverwriteOptions) (err e
u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification u.EmailNotificationsPreference = setting.Admin.DefaultEmailNotification
u.MaxRepoCreation = -1 u.MaxRepoCreation = -1
u.Theme = setting.UI.DefaultTheme u.Theme = setting.UI.DefaultTheme
u.IsRestricted = setting.Service.DefaultUserIsRestricted
u.IsActive = !(setting.Service.RegisterEmailConfirm || setting.Service.RegisterManualConfirm)
// overwrite defaults if set // overwrite defaults if set
if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { if len(overwriteDefault) != 0 && overwriteDefault[0] != nil {
u.Visibility = overwriteDefault[0].Visibility overwrite := overwriteDefault[0]
if !overwrite.KeepEmailPrivate.IsNone() {
u.KeepEmailPrivate = overwrite.KeepEmailPrivate.IsTrue()
}
if overwrite.Visibility != nil {
u.Visibility = *overwrite.Visibility
}
if !overwrite.AllowCreateOrganization.IsNone() {
u.AllowCreateOrganization = overwrite.AllowCreateOrganization.IsTrue()
}
if overwrite.EmailNotificationsPreference != nil {
u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference
}
if overwrite.MaxRepoCreation != nil {
u.MaxRepoCreation = *overwrite.MaxRepoCreation
}
if overwrite.Theme != nil {
u.Theme = *overwrite.Theme
}
if !overwrite.IsRestricted.IsNone() {
u.IsRestricted = overwrite.IsRestricted.IsTrue()
}
if !overwrite.IsActive.IsNone() {
u.IsActive = overwrite.IsActive.IsTrue()
}
} }
// validate data // validate data

View File

@@ -497,14 +497,19 @@ func GetSystemOrDefaultWebhook(id int64) (*Webhook, error) {
} }
// GetSystemWebhooks returns all admin system webhooks. // GetSystemWebhooks returns all admin system webhooks.
func GetSystemWebhooks() ([]*Webhook, error) { func GetSystemWebhooks(isActive util.OptionalBool) ([]*Webhook, error) {
return getSystemWebhooks(db.GetEngine(db.DefaultContext)) return getSystemWebhooks(db.GetEngine(db.DefaultContext), isActive)
} }
func getSystemWebhooks(e db.Engine) ([]*Webhook, error) { func getSystemWebhooks(e db.Engine, isActive util.OptionalBool) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5) webhooks := make([]*Webhook, 0, 5)
if isActive.IsNone() {
return webhooks, e.
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
Find(&webhooks)
}
return webhooks, e. return webhooks, e.
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true). Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()).
Find(&webhooks) Find(&webhooks)
} }

View File

@@ -288,6 +288,7 @@ func APIContexter() func(http.Handler) http.Handler {
}, },
Org: &APIOrganization{}, Org: &APIOrganization{},
} }
defer ctx.Close()
ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx) ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
ctx.csrf = Csrfer(csrfOpts, ctx.Context) ctx.csrf = Csrfer(csrfOpts, ctx.Context)

View File

@@ -71,6 +71,16 @@ type Context struct {
Org *Organization Org *Organization
} }
// Close frees all resources hold by Context
func (ctx *Context) Close() error {
var err error
if ctx.Req != nil && ctx.Req.MultipartForm != nil {
err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
}
// TODO: close opened repo, and more
return err
}
// TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString. // TrHTMLEscapeArgs runs Tr but pre-escapes all arguments with html.EscapeString.
// This is useful if the locale message is intended to only produce HTML content. // This is useful if the locale message is intended to only produce HTML content.
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string { func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
@@ -181,6 +191,12 @@ func (ctx *Context) RedirectToFirst(location ...string) {
continue continue
} }
// Unfortunately browsers consider a redirect Location with preceding "//" and "/\" as meaning redirect to "http(s)://REST_OF_PATH"
// Therefore we should ignore these redirect locations to prevent open redirects
if len(loc) > 1 && loc[0] == '/' && (loc[1] == '/' || loc[1] == '\\') {
continue
}
u, err := url.Parse(loc) u, err := url.Parse(loc)
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) { if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
continue continue
@@ -637,6 +653,8 @@ func Contexter() func(next http.Handler) http.Handler {
"RunModeIsProd": setting.IsProd, "RunModeIsProd": setting.IsProd,
}, },
} }
defer ctx.Close()
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules // PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
ctx.PageData = map[string]interface{}{} ctx.PageData = map[string]interface{}{}
ctx.Data["PageData"] = ctx.PageData ctx.Data["PageData"] = ctx.PageData

View File

@@ -229,6 +229,7 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
} }
} }
needsNew = needsNew || ctx.Req.Method == "GET" // If this request is a Get request, it will generate a new token, make sure the token is always up-to-date.
if needsNew { if needsNew {
// FIXME: actionId. // FIXME: actionId.
x.Token = GenerateToken(x.Secret, x.ID, "POST") x.Token = GenerateToken(x.Secret, x.ID, "POST")

View File

@@ -70,12 +70,6 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
org := ctx.Org.Organization org := ctx.Org.Organization
ctx.Data["Org"] = org ctx.Data["Org"] = org
teams, err := org.LoadTeams()
if err != nil {
ctx.ServerError("LoadTeams", err)
}
ctx.Data["OrgTeams"] = teams
// Admin has super access. // Admin has super access.
if ctx.IsSigned && ctx.User.IsAdmin { if ctx.IsSigned && ctx.User.IsAdmin {
ctx.Org.IsOwner = true ctx.Org.IsOwner = true

View File

@@ -38,6 +38,8 @@ func PrivateContexter() func(http.Handler) http.Handler {
Data: map[string]interface{}{}, Data: map[string]interface{}{},
}, },
} }
defer ctx.Close()
ctx.Req = WithPrivateContext(req, ctx) ctx.Req = WithPrivateContext(req, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req) next.ServeHTTP(ctx.Resp, ctx.Req)
}) })

View File

@@ -410,6 +410,12 @@ func RepoIDAssignment() func(ctx *Context) {
// RepoAssignment returns a middleware to handle repository assignment // RepoAssignment returns a middleware to handle repository assignment
func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce {
log.Trace("RepoAssignment was exec already, skipping second call ...")
return
}
ctx.Data["repoAssignmentExecuted"] = true
var ( var (
owner *user_model.User owner *user_model.User
err error err error
@@ -440,6 +446,26 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.Repo.Owner = owner ctx.Repo.Owner = owner
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
// redirect link to wiki
if strings.HasSuffix(repoName, ".wiki") {
// ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
// Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
originalRepoName := ctx.Params(":reponame")
redirectRepoName := strings.TrimSuffix(repoName, ".wiki")
redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
redirectPath := strings.Replace(
ctx.Req.URL.EscapedPath(),
url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
1,
)
if ctx.Req.URL.RawQuery != "" {
redirectPath += "?" + ctx.Req.URL.RawQuery
}
ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
return
}
// Get repository. // Get repository.
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName) repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
if err != nil { if err != nil {
@@ -572,6 +598,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err) ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err)
return return
} }
if ctx.Repo.GitRepo != nil {
ctx.Repo.GitRepo.Close()
}
ctx.Repo.GitRepo = gitRepo ctx.Repo.GitRepo = gitRepo
// We opened it, we should close it // We opened it, we should close it

View File

@@ -67,6 +67,7 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
PatchURL: pr.Issue.PatchURL(), PatchURL: pr.Issue.PatchURL(),
HasMerged: pr.HasMerged, HasMerged: pr.HasMerged,
MergeBase: pr.MergeBase, MergeBase: pr.MergeBase,
Mergeable: pr.Mergeable(),
Deadline: apiIssue.Deadline, Deadline: apiIssue.Deadline,
Created: pr.Issue.CreatedUnix.AsTimePtr(), Created: pr.Issue.CreatedUnix.AsTimePtr(),
Updated: pr.Issue.UpdatedUnix.AsTimePtr(), Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
@@ -190,10 +191,6 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *user_model.User) *api.PullRe
} }
} }
if pr.Status != models.PullRequestStatusChecking {
mergeable := !(pr.Status == models.PullRequestStatusConflict || pr.Status == models.PullRequestStatusError) && !pr.IsWorkInProgress()
apiPullRequest.Mergeable = mergeable
}
if pr.HasMerged { if pr.HasMerged {
apiPullRequest.Merged = pr.MergedUnix.AsTimePtr() apiPullRequest.Merged = pr.MergedUnix.AsTimePtr()
apiPullRequest.MergedCommitID = &pr.MergedCommitID apiPullRequest.MergedCommitID = &pr.MergedCommitID

View File

@@ -71,8 +71,8 @@ func checkAuthorizedKeys(logger log.Logger, autofix bool) error {
"authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"", "authorized_keys file %q is out of date.\nRegenerate it with:\n\t\"%s\"\nor\n\t\"%s\"",
fPath, fPath,
"gitea admin regenerate keys", "gitea admin regenerate keys",
"gitea doctor --run authorized_keys --fix") "gitea doctor --run authorized-keys --fix")
return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized_keys --fix"`) return fmt.Errorf(`authorized_keys is out of date and should be regenerated with "gitea admin regenerate keys" or "gitea doctor --run authorized-keys --fix"`)
} }
logger.Warn("authorized_keys is out of date. Attempting rewrite...") logger.Warn("authorized_keys is out of date. Attempting rewrite...")
err = asymkey_model.RewriteAllPublicKeys() err = asymkey_model.RewriteAllPublicKeys()

View File

@@ -142,6 +142,12 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
Fixer: models.FixIssueLabelWithOutsideLabels, Fixer: models.FixIssueLabelWithOutsideLabels,
FixedMessage: "Removed", FixedMessage: "Removed",
}, },
{
Name: "Action with created_unix set as an empty string",
Counter: models.CountActionCreatedUnixString,
Fixer: models.FixActionCreatedUnixString,
FixedMessage: "Set to zero",
},
} }
// TODO: function to recalc all counters // TODO: function to recalc all counters
@@ -177,6 +183,18 @@ func checkDBConsistency(logger log.Logger, autofix bool) error {
// find access without repository // find access without repository
genericOrphanCheck("Access entries without existing repository", genericOrphanCheck("Access entries without existing repository",
"access", "repository", "access.repo_id=repository.id"), "access", "repository", "access.repo_id=repository.id"),
// find action without repository
genericOrphanCheck("Action entries without existing repository",
"action", "repository", "action.repo_id=repository.id"),
// find OAuth2Grant without existing user
genericOrphanCheck("Orphaned OAuth2Grant without existing User",
"oauth2_grant", "user", "oauth2_grant.user_id=user.id"),
// find OAuth2Application without existing user
genericOrphanCheck("Orphaned OAuth2Application without existing User",
"oauth2_application", "user", "oauth2_application.uid=user.id"),
// find OAuth2AuthorizationCode without existing OAuth2Grant
genericOrphanCheck("Orphaned OAuth2AuthorizationCode without existing OAuth2Grant",
"oauth2_authorization_code", "oauth2_grant", "oauth2_authorization_code.grant_id=oauth2_grant.id"),
) )
for _, c := range consistencyChecks { for _, c := range consistencyChecks {

View File

@@ -54,6 +54,12 @@ func CatFileBatchCheck(ctx context.Context, repoPath string) (WriteCloserError,
<-closed <-closed
} }
// Ensure cancel is called as soon as the provided context is cancelled
go func() {
<-ctx.Done()
cancel()
}()
_, filename, line, _ := runtime.Caller(2) _, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix) filename = strings.TrimPrefix(filename, callerPrefix)
@@ -93,6 +99,12 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
<-closed <-closed
} }
// Ensure cancel is called as soon as the provided context is cancelled
go func() {
<-ctx.Done()
cancel()
}()
_, filename, line, _ := runtime.Caller(2) _, filename, line, _ := runtime.Caller(2)
filename = strings.TrimPrefix(filename, callerPrefix) filename = strings.TrimPrefix(filename, callerPrefix)

View File

@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/util"
) )
var ( var (
@@ -32,10 +33,11 @@ const DefaultLocale = "C"
// Command represents a command with its subcommands or arguments. // Command represents a command with its subcommands or arguments.
type Command struct { type Command struct {
name string name string
args []string args []string
parentContext context.Context parentContext context.Context
desc string desc string
globalArgsLength int
} }
func (c *Command) String() string { func (c *Command) String() string {
@@ -56,9 +58,10 @@ func NewCommandContext(ctx context.Context, args ...string) *Command {
cargs := make([]string, len(GlobalCommandArgs)) cargs := make([]string, len(GlobalCommandArgs))
copy(cargs, GlobalCommandArgs) copy(cargs, GlobalCommandArgs)
return &Command{ return &Command{
name: GitExecutable, name: GitExecutable,
args: append(cargs, args...), args: append(cargs, args...),
parentContext: ctx, parentContext: ctx,
globalArgsLength: len(GlobalCommandArgs),
} }
} }
@@ -145,7 +148,21 @@ func (c *Command) RunWithContext(rc *RunContext) error {
desc := c.desc desc := c.desc
if desc == "" { if desc == "" {
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(c.args, " "), rc.Dir) args := c.args[c.globalArgsLength:]
var argSensitiveURLIndexes []int
for i, arg := range c.args {
if strings.Contains(arg, "://") && strings.Contains(arg, "@") {
argSensitiveURLIndexes = append(argSensitiveURLIndexes, i)
}
}
if len(argSensitiveURLIndexes) > 0 {
args = make([]string, len(c.args))
copy(args, c.args)
for _, urlArgIndex := range argSensitiveURLIndexes {
args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex])
}
}
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir)
} }
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc) ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)

View File

@@ -112,8 +112,8 @@ func SetExecutablePath(path string) error {
// VersionInfo returns git version information // VersionInfo returns git version information
func VersionInfo() string { func VersionInfo() string {
var format = "Git Version: %s" format := "Git Version: %s"
var args = []interface{}{gitVersion.Original()} args := []interface{}{gitVersion.Original()}
// Since git wire protocol has been released from git v2.18 // Since git wire protocol has been released from git v2.18
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
format += ", Wire Protocol %s Enabled" format += ", Wire Protocol %s Enabled"
@@ -148,7 +148,7 @@ func Init(ctx context.Context) error {
// By default partial clones are disabled, enable them from git v2.22 // By default partial clones are disabled, enable them from git v2.22
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
GlobalCommandArgs = append(GlobalCommandArgs, "-c", "uploadpack.allowfilter=true") GlobalCommandArgs = append(GlobalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
} }
// Save current git version on init to gitVersion otherwise it would require an RWMutex // Save current git version on init to gitVersion otherwise it would require an RWMutex

View File

@@ -19,6 +19,7 @@ import (
"time" "time"
"code.gitea.io/gitea/modules/proxy" "code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/util"
) )
// GPGSettings represents the default GPG settings for this repository // GPGSettings represents the default GPG settings for this repository
@@ -97,15 +98,16 @@ func (repo *Repository) IsEmpty() (bool, error) {
// CloneRepoOptions options when clone a repository // CloneRepoOptions options when clone a repository
type CloneRepoOptions struct { type CloneRepoOptions struct {
Timeout time.Duration Timeout time.Duration
Mirror bool Mirror bool
Bare bool Bare bool
Quiet bool Quiet bool
Branch string Branch string
Shared bool Shared bool
NoCheckout bool NoCheckout bool
Depth int Depth int
Filter string Filter string
SkipTLSVerify bool
} }
// Clone clones original repository to target path. // Clone clones original repository to target path.
@@ -128,6 +130,9 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
} }
cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone") cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone")
if opts.SkipTLSVerify {
cmd.AddArguments("-c", "http.sslVerify=false")
}
if opts.Mirror { if opts.Mirror {
cmd.AddArguments("--mirror") cmd.AddArguments("--mirror")
} }
@@ -154,6 +159,12 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
} }
cmd.AddArguments("--", from, to) cmd.AddArguments("--", from, to)
if strings.Contains(from, "://") && strings.Contains(from, "@") {
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.NewStringURLSanitizer(from, true).Replace(from), to, opts.Shared, opts.Mirror, opts.Depth))
} else {
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth))
}
if opts.Timeout <= 0 { if opts.Timeout <= 0 {
opts.Timeout = -1 opts.Timeout = -1
} }
@@ -230,6 +241,11 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
if len(opts.Branch) > 0 { if len(opts.Branch) > 0 {
cmd.AddArguments(opts.Branch) cmd.AddArguments(opts.Branch)
} }
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote), opts.Force, opts.Mirror))
} else {
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror))
}
var outbuf, errbuf strings.Builder var outbuf, errbuf strings.Builder
if opts.Timeout == 0 { if opts.Timeout == 0 {

View File

@@ -119,12 +119,10 @@ type CheckAttributeReader struct {
env []string env []string
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
running chan struct{}
} }
// Init initializes the cmd // Init initializes the cmd
func (c *CheckAttributeReader) Init(ctx context.Context) error { func (c *CheckAttributeReader) Init(ctx context.Context) error {
c.running = make(chan struct{})
cmdArgs := []string{"check-attr", "--stdin", "-z"} cmdArgs := []string{"check-attr", "--stdin", "-z"}
if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil { if len(c.IndexFile) > 0 && CheckGitVersionAtLeast("1.7.8") == nil {
@@ -183,14 +181,7 @@ func (c *CheckAttributeReader) Run() error {
_ = c.stdOut.Close() _ = c.stdOut.Close()
}() }()
stdErr := new(bytes.Buffer) stdErr := new(bytes.Buffer)
err := c.cmd.RunInDirTimeoutEnvFullPipelineFunc(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader, func(_ context.Context, _ context.CancelFunc) error { err := c.cmd.RunInDirTimeoutEnvFullPipeline(c.env, -1, c.Repo.Path, c.stdOut, stdErr, c.stdinReader)
select {
case <-c.running:
default:
close(c.running)
}
return nil
})
if err != nil && // If there is an error we need to return but: if err != nil && // If there is an error we need to return but:
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded) c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
@@ -210,7 +201,7 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
select { select {
case <-c.ctx.Done(): case <-c.ctx.Done():
return nil, c.ctx.Err() return nil, c.ctx.Err()
case <-c.running: default:
} }
if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil { if _, err = c.stdinWriter.Write([]byte(path + "\x00")); err != nil {
@@ -237,11 +228,6 @@ func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err
func (c *CheckAttributeReader) Close() error { func (c *CheckAttributeReader) Close() error {
c.cancel() c.cancel()
err := c.stdinWriter.Close() err := c.stdinWriter.Close()
select {
case <-c.running:
default:
close(c.running)
}
return err return err
} }

View File

@@ -13,6 +13,7 @@ import (
"strings" "strings"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/storer"
) )
// IsObjectExist returns true if given reference exists in the repository. // IsObjectExist returns true if given reference exists in the repository.
@@ -82,7 +83,8 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
} }
// WalkReferences walks all the references from the repository // WalkReferences walks all the references from the repository
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) { // refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
repo, err := OpenRepositoryCtx(ctx, repoPath) repo, err := OpenRepositoryCtx(ctx, repoPath)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -97,9 +99,45 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er
defer iter.Close() defer iter.Close()
err = iter.ForEach(func(ref *plumbing.Reference) error { err = iter.ForEach(func(ref *plumbing.Reference) error {
err := walkfn(string(ref.Name())) err := walkfn(ref.Hash().String(), string(ref.Name()))
i++ i++
return err return err
}) })
return i, err return i, err
} }
// WalkReferences walks all the references from the repository
func (repo *Repository) WalkReferences(arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
i := 0
var iter storer.ReferenceIter
var err error
switch arg {
case ObjectTag:
iter, err = repo.gogitRepo.Tags()
case ObjectBranch:
iter, err = repo.gogitRepo.Branches()
default:
iter, err = repo.gogitRepo.References()
}
if err != nil {
return i, err
}
defer iter.Close()
err = iter.ForEach(func(ref *plumbing.Reference) error {
if i < skip {
i++
return nil
}
err := walkfn(ref.Hash().String(), string(ref.Name()))
i++
if err != nil {
return err
}
if limit != 0 && i >= skip+limit {
return storer.ErrStop
}
return nil
})
return i, err
}

View File

@@ -68,13 +68,29 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
} }
// WalkReferences walks all the references from the repository // WalkReferences walks all the references from the repository
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) { func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
return walkShowRef(ctx, repoPath, "", 0, 0, walkfn) return walkShowRef(ctx, repoPath, "", 0, 0, walkfn)
} }
// WalkReferences walks all the references from the repository
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
var arg string
switch refType {
case ObjectTag:
arg = "--tags"
case ObjectBranch:
arg = "--heads"
default:
arg = ""
}
return walkShowRef(repo.Ctx, repo.Path, arg, skip, limit, walkfn)
}
// callShowRef return refs, if limit = 0 it will not limit // callShowRef return refs, if limit = 0 it will not limit
func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) { func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(branchName string) error { countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(_, branchName string) error {
branchName = strings.TrimPrefix(branchName, prefix) branchName = strings.TrimPrefix(branchName, prefix)
branchNames = append(branchNames, branchName) branchNames = append(branchNames, branchName)
@@ -83,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
return return
} }
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(string) error) (countAll int, err error) { func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe() stdoutReader, stdoutWriter := io.Pipe()
defer func() { defer func() {
_ = stdoutReader.Close() _ = stdoutReader.Close()
@@ -125,11 +141,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
for limit == 0 || i < skip+limit { for limit == 0 || i < skip+limit {
// The output of show-ref is simply a list: // The output of show-ref is simply a list:
// <sha> SP <ref> LF // <sha> SP <ref> LF
_, err := bufReader.ReadSlice(' ') sha, err := bufReader.ReadString(' ')
for err == bufio.ErrBufferFull {
// This shouldn't happen but we'll tolerate it for the sake of peace
_, err = bufReader.ReadSlice(' ')
}
if err == io.EOF { if err == io.EOF {
return i, nil return i, nil
} }
@@ -149,7 +161,12 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
if len(branchName) > 0 { if len(branchName) > 0 {
branchName = branchName[:len(branchName)-1] branchName = branchName[:len(branchName)-1]
} }
err = walkfn(branchName)
if len(sha) > 0 {
sha = sha[:len(sha)-1]
}
err = walkfn(sha, branchName)
if err != nil { if err != nil {
return i, err return i, err
} }

View File

@@ -0,0 +1,21 @@
// 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 git
import (
"context"
"fmt"
)
// WriteCommitGraph write commit graph to speed up repo access
// this requires git v2.18 to be installed
func WriteCommitGraph(ctx context.Context, repoPath string) error {
if CheckGitVersionAtLeast("2.18") == nil {
if _, err := NewCommandContext(ctx, "commit-graph", "write").RunInDir(repoPath); err != nil {
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
}
}
return nil
}

View File

@@ -10,7 +10,6 @@ import (
"fmt" "fmt"
"strings" "strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
@@ -34,69 +33,6 @@ func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error
return err return err
} }
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
t, ok := repo.tagCache.Get(tagID.String())
if ok {
log.Debug("Hit cache: %s", tagID)
tagClone := *t.(*Tag)
tagClone.Name = name // This is necessary because lightweight tags may have same id
return &tagClone, nil
}
tp, err := repo.GetTagType(tagID)
if err != nil {
return nil, err
}
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
commitIDStr, err := repo.GetTagCommitID(name)
if err != nil {
// every tag should have a commit ID so return all errors
return nil, err
}
commitID, err := NewIDFromString(commitIDStr)
if err != nil {
return nil, err
}
// If type is "commit, the tag is a lightweight tag
if ObjectType(tp) == ObjectCommit {
commit, err := repo.GetCommit(commitIDStr)
if err != nil {
return nil, err
}
tag := &Tag{
Name: name,
ID: tagID,
Object: commitID,
Type: tp,
Tagger: commit.Committer,
Message: commit.Message(),
}
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}
// The tag is an annotated tag with a message.
data, err := NewCommandContext(repo.Ctx, "cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
}
tag, err := parseTagData(data)
if err != nil {
return nil, err
}
tag.Name = name
tag.ID = tagID
tag.Type = tp
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}
// GetTagNameBySHA returns the name of a tag from its tag object SHA or commit SHA // GetTagNameBySHA returns the name of a tag from its tag object SHA or commit SHA
func (repo *Repository) GetTagNameBySHA(sha string) (string, error) { func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
if len(sha) < 5 { if len(sha) < 5 {
@@ -159,6 +95,20 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
return tag, nil return tag, nil
} }
// GetTagWithID returns a Git tag by given name and ID
func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
id, err := NewIDFromString(idStr)
if err != nil {
return nil, err
}
tag, err := repo.getTag(id, name)
if err != nil {
return nil, err
}
return tag, nil
}
// GetTagInfos returns all tag infos of the repository. // GetTagInfos returns all tag infos of the repository.
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
// TODO this a slow implementation, makes one git command per tag // TODO this a slow implementation, makes one git command per tag
@@ -192,19 +142,6 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
return tags, tagsTotal, nil return tags, tagsTotal, nil
} }
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
// Get tag type
stdout, err := NewCommandContext(repo.Ctx, "cat-file", "-t", id.String()).RunInDir(repo.Path)
if err != nil {
return "", err
}
if len(stdout) == 0 {
return "", ErrNotExist{ID: id.String()}
}
return strings.TrimSpace(stdout), nil
}
// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag // GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) { func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
id, err := NewIDFromString(sha) id, err := NewIDFromString(sha)

View File

@@ -11,6 +11,8 @@ package git
import ( import (
"strings" "strings"
"code.gitea.io/gitea/modules/log"
"github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing"
) )
@@ -53,3 +55,83 @@ func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
return tagNames, nil return tagNames, nil
} }
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
// Get tag type
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, id)
if err != nil {
if err == plumbing.ErrReferenceNotFound {
return "", &ErrNotExist{ID: id.String()}
}
return "", err
}
return obj.Type().String(), nil
}
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
t, ok := repo.tagCache.Get(tagID.String())
if ok {
log.Debug("Hit cache: %s", tagID)
tagClone := *t.(*Tag)
tagClone.Name = name // This is necessary because lightweight tags may have same id
return &tagClone, nil
}
tp, err := repo.GetTagType(tagID)
if err != nil {
return nil, err
}
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
commitIDStr, err := repo.GetTagCommitID(name)
if err != nil {
// every tag should have a commit ID so return all errors
return nil, err
}
commitID, err := NewIDFromString(commitIDStr)
if err != nil {
return nil, err
}
// If type is "commit, the tag is a lightweight tag
if ObjectType(tp) == ObjectCommit {
commit, err := repo.GetCommit(commitIDStr)
if err != nil {
return nil, err
}
tag := &Tag{
Name: name,
ID: tagID,
Object: commitID,
Type: tp,
Tagger: commit.Committer,
Message: commit.Message(),
}
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}
gogitTag, err := repo.gogitRepo.TagObject(tagID)
if err != nil {
if err == plumbing.ErrReferenceNotFound {
return nil, &ErrNotExist{ID: tagID.String()}
}
return nil, err
}
tag := &Tag{
Name: name,
ID: tagID,
Object: gogitTag.Target,
Type: tp,
Tagger: &gogitTag.Tagger,
Message: gogitTag.Message,
}
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}

View File

@@ -8,6 +8,13 @@
package git package git
import (
"errors"
"io"
"code.gitea.io/gitea/modules/log"
)
// IsTagExist returns true if given tag exists in the repository. // IsTagExist returns true if given tag exists in the repository.
func (repo *Repository) IsTagExist(name string) bool { func (repo *Repository) IsTagExist(name string) bool {
if name == "" { if name == "" {
@@ -23,3 +30,104 @@ func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit) tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
return return
} }
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
func (repo *Repository) GetTagType(id SHA1) (string, error) {
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
defer cancel()
_, err := wr.Write([]byte(id.String() + "\n"))
if err != nil {
return "", err
}
_, typ, _, err := ReadBatchLine(rd)
if IsErrNotExist(err) {
return "", ErrNotExist{ID: id.String()}
}
return typ, nil
}
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
t, ok := repo.tagCache.Get(tagID.String())
if ok {
log.Debug("Hit cache: %s", tagID)
tagClone := *t.(*Tag)
tagClone.Name = name // This is necessary because lightweight tags may have same id
return &tagClone, nil
}
tp, err := repo.GetTagType(tagID)
if err != nil {
return nil, err
}
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
commitIDStr, err := repo.GetTagCommitID(name)
if err != nil {
// every tag should have a commit ID so return all errors
return nil, err
}
commitID, err := NewIDFromString(commitIDStr)
if err != nil {
return nil, err
}
// If type is "commit, the tag is a lightweight tag
if ObjectType(tp) == ObjectCommit {
commit, err := repo.GetCommit(commitIDStr)
if err != nil {
return nil, err
}
tag := &Tag{
Name: name,
ID: tagID,
Object: commitID,
Type: tp,
Tagger: commit.Committer,
Message: commit.Message(),
}
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}
// The tag is an annotated tag with a message.
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
defer cancel()
if _, err := wr.Write([]byte(tagID.String() + "\n")); err != nil {
return nil, err
}
_, typ, size, err := ReadBatchLine(rd)
if err != nil {
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
return nil, ErrNotExist{ID: tagID.String()}
}
return nil, err
}
if typ != "tag" {
return nil, ErrNotExist{ID: tagID.String()}
}
// then we need to parse the tag
// and load the commit
data, err := io.ReadAll(io.LimitReader(rd, size))
if err != nil {
return nil, err
}
_, err = rd.Discard(1)
if err != nil {
return nil, err
}
tag, err := parseTagData(data)
if err != nil {
return nil, err
}
tag.Name = name
tag.ID = tagID
tag.Type = tp
repo.tagCache.Set(tagID.String(), tag)
return tag, nil
}

View File

@@ -40,20 +40,19 @@ steps:
- go test -v -race -coverprofile=coverage.txt -covermode=atomic - go test -v -race -coverprofile=coverage.txt -covermode=atomic
`, `,
want: []string{ want: []string{
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`, `<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
`<span class="w"> `</span></span><span class="line"><span class="cl">`,
</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
`<span class="w"> </span>- <span class="l">go build -v</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w"> </span></span></span>`,
</span>`,
`<span class="w"> `<span class="w">
</span>`, </span>`,
}, },
@@ -76,20 +75,19 @@ steps:
- go test -v -race -coverprofile=coverage.txt -covermode=atomic - go test -v -race -coverprofile=coverage.txt -covermode=atomic
`, `,
want: []string{ want: []string{
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`, `<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`,
`<span class="w"> `</span></span><span class="line"><span class="cl">`,
</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`, `</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
`<span class="w"> </span>- <span class="l">go build -v</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`, `</span></span><span class="line"><span class="cl"><span class="w"> </span></span></span>`,
`<span class="w"> </span>`,
}, },
}, },
} }

View File

@@ -127,13 +127,18 @@ func (hl *HostMatchList) checkIP(ip net.IP) bool {
// MatchHostName checks if the host matches an allow/deny(block) list // MatchHostName checks if the host matches an allow/deny(block) list
func (hl *HostMatchList) MatchHostName(host string) bool { func (hl *HostMatchList) MatchHostName(host string) bool {
hostname, _, err := net.SplitHostPort(host)
if err != nil {
hostname = host
}
if hl == nil { if hl == nil {
return false return false
} }
if hl.checkPattern(host) { if hl.checkPattern(hostname) {
return true return true
} }
if ip := net.ParseIP(host); ip != nil { if ip := net.ParseIP(hostname); ip != nil {
return hl.checkIP(ip) return hl.checkIP(ip)
} }
return false return false

View File

@@ -38,6 +38,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
{"", net.ParseIP("10.0.1.1"), true}, {"", net.ParseIP("10.0.1.1"), true},
{"10.0.1.1", nil, true}, {"10.0.1.1", nil, true},
{"10.0.1.1:8080", nil, true},
{"", net.ParseIP("192.168.1.1"), true}, {"", net.ParseIP("192.168.1.1"), true},
{"192.168.1.1", nil, true}, {"192.168.1.1", nil, true},
{"", net.ParseIP("fd00::1"), true}, {"", net.ParseIP("fd00::1"), true},
@@ -48,6 +49,7 @@ func TestHostOrIPMatchesList(t *testing.T) {
{"mydomain.com", net.IPv4zero, false}, {"mydomain.com", net.IPv4zero, false},
{"sub.mydomain.com", net.IPv4zero, true}, {"sub.mydomain.com", net.IPv4zero, true},
{"sub.mydomain.com:8080", net.IPv4zero, true},
{"", net.ParseIP("169.254.1.1"), true}, {"", net.ParseIP("169.254.1.1"), true},
{"169.254.1.1", nil, true}, {"169.254.1.1", nil, true},

View File

@@ -130,7 +130,7 @@ func Init() {
log.Info("PID: %d Repository Indexer closed", os.Getpid()) log.Info("PID: %d Repository Indexer closed", os.Getpid())
}) })
waitChannel := make(chan time.Duration) waitChannel := make(chan time.Duration, 1)
// Create the Queue // Create the Queue
switch setting.Indexer.RepoType { switch setting.Indexer.RepoType {

View File

@@ -98,7 +98,7 @@ var (
// InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until // InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
// all issue index done. // all issue index done.
func InitIssueIndexer(syncReindex bool) { func InitIssueIndexer(syncReindex bool) {
waitChannel := make(chan time.Duration) waitChannel := make(chan time.Duration, 1)
// Create the Queue // Create the Queue
switch setting.Indexer.IssueType { switch setting.Indexer.IssueType {
@@ -272,7 +272,7 @@ func populateIssueIndexer(ctx context.Context) {
// UpdateRepoIndexer add/update all issues of the repositories // UpdateRepoIndexer add/update all issues of the repositories
func UpdateRepoIndexer(repo *repo_model.Repository) { func UpdateRepoIndexer(repo *repo_model.Repository) {
is, err := models.Issues(&models.IssuesOptions{ is, err := models.Issues(&models.IssuesOptions{
RepoIDs: []int64{repo.ID}, RepoID: repo.ID,
IsClosed: util.OptionalBoolNone, IsClosed: util.OptionalBoolNone,
IsPull: util.OptionalBoolNone, IsPull: util.OptionalBoolNone,
}) })

View File

@@ -5,12 +5,15 @@
package stats package stats
import ( import (
"context"
"path/filepath" "path/filepath"
"testing" "testing"
"time" "time"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
_ "code.gitea.io/gitea/models" _ "code.gitea.io/gitea/models"
@@ -24,6 +27,10 @@ func TestMain(m *testing.M) {
} }
func TestRepoStatsIndex(t *testing.T) { func TestRepoStatsIndex(t *testing.T) {
if err := git.Init(context.Background()); !assert.NoError(t, err) {
return
}
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
setting.Cfg = ini.Empty() setting.Cfg = ini.Empty()
@@ -32,10 +39,14 @@ func TestRepoStatsIndex(t *testing.T) {
err := Init() err := Init()
assert.NoError(t, err) assert.NoError(t, err)
time.Sleep(5 * time.Second)
repo, err := repo_model.GetRepositoryByID(1) repo, err := repo_model.GetRepositoryByID(1)
assert.NoError(t, err) assert.NoError(t, err)
err = UpdateRepoIndexer(repo)
assert.NoError(t, err)
queue.GetManager().FlushAll(context.Background(), 5*time.Second)
status, err := repo_model.GetIndexerStatus(repo, repo_model.RepoIndexerTypeStats) status, err := repo_model.GetIndexerStatus(repo, repo_model.RepoIndexerTypeStats)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha) assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha)

View File

@@ -205,7 +205,7 @@ func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parse
} }
open := pos + 1 open := pos + 1
closes := 0 closes := 0
closure := util.FindClosure(line[pos+1:], '[', ']', false, false) closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint
closes = pos + 1 + closure closes = pos + 1 + closure
next := closes + 1 next := closes + 1
if closure > -1 { if closure > -1 {
@@ -296,7 +296,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con
return nil return nil
} }
open := pos open := pos
closure := util.FindClosure(line[pos:], '[', ']', false, false) closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint
if closure < 0 { if closure < 0 {
return nil return nil
} }

View File

@@ -197,6 +197,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
</li> </li>
</ol> </ol>
</div> </div>
`, `<ul>
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="3"/> If you want to rebase/retry this PR, click this checkbox.</li>
</ul>
<hr/>
<p>This PR has been generated by <a href="https://github.com/renovatebot/renovate" rel="nofollow">Renovate Bot</a>.</p>
`, `,
} }
} }
@@ -269,6 +274,14 @@ Here is a simple footnote,[^1] and here is a longer one.[^bignote]
Add as many paragraphs as you like. Add as many paragraphs as you like.
`, `,
`
- [ ] <!-- rebase-check --> If you want to rebase/retry this PR, click this checkbox.
---
This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!-- test-comment -->`,
} }
func TestTotal_RenderWiki(t *testing.T) { func TestTotal_RenderWiki(t *testing.T) {

View File

@@ -77,9 +77,9 @@ func HelloWorld() {
} }
#+end_src #+end_src
`, `<div class="src src-go"> `, `<div class="src src-go">
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34; <pre><code class="chroma language-go"><span class="line"><span class="cl"><span class="c1">// HelloWorld prints &#34;Hello World&#34;
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span> </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">)</span> </span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">)</span>
<span class="p">}</span></code></pre> </span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
</div>`) </div>`)
} }

View File

@@ -56,7 +56,7 @@ type HookOptions struct {
GitQuarantinePath string GitQuarantinePath string
GitPushOptions GitPushOptions GitPushOptions GitPushOptions
PullRequestID int64 PullRequestID int64
IsDeployKey bool DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
IsWiki bool IsWiki bool
} }

View File

@@ -46,9 +46,9 @@ func ServNoCommand(ctx context.Context, keyID int64) (*asymkey_model.PublicKey,
// ServCommandResults are the results of a call to the private route serv // ServCommandResults are the results of a call to the private route serv
type ServCommandResults struct { type ServCommandResults struct {
IsWiki bool IsWiki bool
IsDeployKey bool DeployKeyID int64
KeyID int64 KeyID int64 // public key
KeyName string KeyName string // this field is ambiguous, it can be the name of DeployKey, or the name of the PublicKey
UserName string UserName string
UserEmail string UserEmail string
UserID int64 UserID int64

View File

@@ -19,6 +19,10 @@ import (
// they use to detect if there is a block and will grow and shrink in // they use to detect if there is a block and will grow and shrink in
// response to demand as per configuration. // response to demand as per configuration.
type WorkerPool struct { type WorkerPool struct {
// This field requires to be the first one in the struct.
// This is to allow 64 bit atomic operations on 32-bit machines.
// See: https://pkg.go.dev/sync/atomic#pkg-note-BUG & Gitea issue 19518
numInQueue int64
lock sync.Mutex lock sync.Mutex
baseCtx context.Context baseCtx context.Context
baseCtxCancel context.CancelFunc baseCtxCancel context.CancelFunc
@@ -32,7 +36,6 @@ type WorkerPool struct {
blockTimeout time.Duration blockTimeout time.Duration
boostTimeout time.Duration boostTimeout time.Duration
boostWorkers int boostWorkers int
numInQueue int64
} }
// WorkerPoolConfiguration is the basic configuration for a WorkerPool // WorkerPoolConfiguration is the basic configuration for a WorkerPool

View File

@@ -43,7 +43,7 @@ var defaultTransformers = []transformer{
{Name: "PASCAL", Transform: xstrings.ToCamelCase}, {Name: "PASCAL", Transform: xstrings.ToCamelCase},
{Name: "LOWER", Transform: strings.ToLower}, {Name: "LOWER", Transform: strings.ToLower},
{Name: "UPPER", Transform: strings.ToUpper}, {Name: "UPPER", Transform: strings.ToUpper},
{Name: "TITLE", Transform: strings.Title}, {Name: "TITLE", Transform: strings.Title}, // nolint
} }
func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository) string { func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository) string {
@@ -62,7 +62,7 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi
{Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLink().SSH, Transformers: nil}, {Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLink().SSH, Transformers: nil},
} }
var expansionMap = make(map[string]string) expansionMap := make(map[string]string)
for _, e := range expansions { for _, e := range expansions {
expansionMap[e.Name] = e.Value expansionMap[e.Name] = e.Value
for _, tr := range e.Transformers { for _, tr := range e.Transformers {
@@ -159,7 +159,7 @@ func generateRepoCommit(repo, templateRepo, generateRepo *repo_model.Repository,
if err := os.WriteFile(path, if err := os.WriteFile(path,
[]byte(generateExpansion(string(content), templateRepo, generateRepo)), []byte(generateExpansion(string(content), templateRepo, generateRepo)),
0644); err != nil { 0o644); err != nil {
return err return err
} }
break break

View File

@@ -72,13 +72,18 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
} }
if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
Timeout: migrateTimeout, Timeout: migrateTimeout,
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
}); err != nil { }); err != nil {
return repo, fmt.Errorf("Clone: %v", err) return repo, fmt.Errorf("Clone: %v", err)
} }
if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
return repo, err
}
if opts.Wiki { if opts.Wiki {
wikiPath := repo_model.WikiPath(u.Name, opts.RepoName) wikiPath := repo_model.WikiPath(u.Name, opts.RepoName)
wikiRemotePath := WikiRemoteURL(opts.CloneAddr) wikiRemotePath := WikiRemoteURL(opts.CloneAddr)
@@ -87,16 +92,21 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err) return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
} }
if err = git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ if err := git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
Mirror: true, Mirror: true,
Quiet: true, Quiet: true,
Timeout: migrateTimeout, Timeout: migrateTimeout,
Branch: "master", Branch: "master",
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
}); err != nil { }); err != nil {
log.Warn("Clone wiki: %v", err) log.Warn("Clone wiki: %v", err)
if err := util.RemoveAll(wikiPath); err != nil { if err := util.RemoveAll(wikiPath); err != nil {
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err) return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
} }
} else {
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
return repo, err
}
} }
} }
} }
@@ -276,23 +286,25 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
} }
} }
} }
tags, err := gitRepo.GetTags(0, 0)
if err != nil { _, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
return fmt.Errorf("unable to GetTags in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err) tagName := strings.TrimPrefix(refname, git.TagPrefix)
} if _, ok := existingRelTags[strings.ToLower(tagName)]; ok {
for _, tagName := range tags { return nil
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
if err := PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
}
} }
}
return nil if err := PushUpdateAddTag(repo, gitRepo, tagName, sha1, refname); err != nil {
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
}
return nil
})
return err
} }
// PushUpdateAddTag must be called for any push actions to add tag // PushUpdateAddTag must be called for any push actions to add tag
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName string) error { func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
tag, err := gitRepo.GetTag(tagName) tag, err := gitRepo.GetTagWithID(sha1, tagName)
if err != nil { if err != nil {
return fmt.Errorf("unable to GetTag: %w", err) return fmt.Errorf("unable to GetTag: %w", err)
} }
@@ -310,7 +322,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
} }
var author *user_model.User var author *user_model.User
var createdAt = time.Unix(1, 0) createdAt := time.Unix(1, 0)
if sig != nil { if sig != nil {
author, err = user_model.GetUserByEmail(sig.Email) author, err = user_model.GetUserByEmail(sig.Email)
@@ -325,7 +337,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
return fmt.Errorf("unable to get CommitsCount: %w", err) return fmt.Errorf("unable to get CommitsCount: %w", err)
} }
var rel = models.Release{ rel := models.Release{
RepoID: repo.ID, RepoID: repo.ID,
TagName: tagName, TagName: tagName,
LowerTagName: strings.ToLower(tagName), LowerTagName: strings.ToLower(tagName),

View File

@@ -15,13 +15,17 @@ import (
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"golang.org/x/text/cases"
"golang.org/x/text/language"
ini "gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
) )
var filenameSuffix = "" var (
var descriptionLock = sync.RWMutex{} filenameSuffix = ""
var logDescriptions = make(map[string]*LogDescription) descriptionLock = sync.RWMutex{}
logDescriptions = make(map[string]*LogDescription)
)
// GetLogDescriptions returns a race safe set of descriptions // GetLogDescriptions returns a race safe set of descriptions
func GetLogDescriptions() map[string]*LogDescription { func GetLogDescriptions() map[string]*LogDescription {
@@ -86,7 +90,7 @@ func RemoveSubLogDescription(key, name string) bool {
type defaultLogOptions struct { type defaultLogOptions struct {
levelName string // LogLevel levelName string // LogLevel
flags string flags string
filename string //path.Join(LogRootPath, "gitea.log") filename string // path.Join(LogRootPath, "gitea.log")
bufferLength int64 bufferLength int64
disableConsole bool disableConsole bool
} }
@@ -243,7 +247,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
Provider: provider, Provider: provider,
Config: config, Config: config,
}) })
log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName) log.Info("%s Log: %s(%s:%s)", cases.Title(language.English).String(key), cases.Title(language.English).String(name), provider, levelName)
} }
AddLogDescription(key, &description) AddLogDescription(key, &description)
@@ -327,7 +331,7 @@ func newLogService() {
Provider: provider, Provider: provider,
Config: config, Config: config,
}) })
log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName) log.Info("Gitea Log Mode: %s(%s:%s)", cases.Title(language.English).String(name), cases.Title(language.English).String(provider), levelName)
} }
AddLogDescription(log.DEFAULT, &description) AddLogDescription(log.DEFAULT, &description)

View File

@@ -8,7 +8,6 @@ package setting
import ( import (
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io"
"math" "math"
"net" "net"
"net/url" "net/url"
@@ -30,6 +29,8 @@ import (
"github.com/unknwon/com" "github.com/unknwon/com"
gossh "golang.org/x/crypto/ssh" gossh "golang.org/x/crypto/ssh"
"golang.org/x/text/cases"
"golang.org/x/text/language"
ini "gopkg.in/ini.v1" ini "gopkg.in/ini.v1"
) )
@@ -90,13 +91,15 @@ var (
// AppDataPath is the default path for storing data. // AppDataPath is the default path for storing data.
// It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data" // It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data"
AppDataPath string AppDataPath string
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
// It maps to ini:"LOCAL_ROOT_URL"
LocalURL string
// Server settings // Server settings
Protocol Scheme Protocol Scheme
Domain string Domain string
HTTPAddr string HTTPAddr string
HTTPPort string HTTPPort string
LocalURL string
RedirectOtherPort bool RedirectOtherPort bool
PortToRedirect string PortToRedirect string
OfflineMode bool OfflineMode bool
@@ -637,7 +640,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
} }
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666") UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32) UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
if err != nil || UnixSocketPermissionParsed > 0777 { if err != nil || UnixSocketPermissionParsed > 0o777 {
log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw) log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
} }
@@ -710,6 +713,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
} }
} }
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL) LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false) RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80") PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
OfflineMode = sec.Key("OFFLINE_MODE").MustBool() OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
@@ -793,16 +797,16 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(",")) SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
if !SSH.Disabled && !SSH.StartBuiltinServer { if !SSH.Disabled && !SSH.StartBuiltinServer {
if err := os.MkdirAll(SSH.RootPath, 0700); err != nil { if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err) log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
} else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil { } else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err) log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
} }
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled { 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")) 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, if err := os.WriteFile(fname,
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0600); err != nil { []byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
log.Fatal("Failed to create '%s': %v", fname, err) log.Fatal("Failed to create '%s': %v", fname, err)
} }
} }
@@ -943,8 +947,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false) unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false)
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("prod") RunMode = Cfg.Section("").Key("RUN_MODE").MustString("Prod")
IsProd = strings.EqualFold(RunMode, "prod") RunMode = cases.Title(language.English).String(strings.ToLower(RunMode))
IsProd = RunMode == "Prod"
// Does not check run user when the install lock is off. // Does not check run user when the install lock is off.
if InstallLock { if InstallLock {
currentUser, match := IsRunUserMatchCurrentUser(RunUser) currentUser, match := IsRunUserMatchCurrentUser(RunUser)
@@ -1004,7 +1009,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true) UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false) UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true) UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(true) UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false)
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt")) HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
if err != nil { if err != nil {
@@ -1074,28 +1079,22 @@ func loadInternalToken(sec *ini.Section) string {
} }
switch tempURI.Scheme { switch tempURI.Scheme {
case "file": case "file":
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600) buf, err := os.ReadFile(tempURI.RequestURI())
if err != nil { if err != nil && !os.IsNotExist(err) {
log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err) log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
} }
defer fp.Close()
buf, err := io.ReadAll(fp)
if err != nil {
log.Fatal("Failed to read InternalTokenURI (%s): %v", uri, err)
}
// No token in the file, generate one and store it. // No token in the file, generate one and store it.
if len(buf) == 0 { if len(buf) == 0 {
token, err := generate.NewInternalToken() token, err := generate.NewInternalToken()
if err != nil { if err != nil {
log.Fatal("Error generate internal token: %v", err) log.Fatal("Error generate internal token: %v", err)
} }
if _, err := io.WriteString(fp, token); err != nil { err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600)
if err != nil {
log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err) log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
} }
return token return token
} }
return strings.TrimSpace(string(buf)) return strings.TrimSpace(string(buf))
default: default:
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri) log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)

View File

@@ -317,64 +317,7 @@ func Listen(host string, port int, ciphers, keyExchanges, macs []string) {
} }
} }
// Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463
//
// Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption
// that the PublicKey().Type() matches the signature algorithm.
//
// Therefore we need to add duplicates for the RSA with different signing algorithms.
signers := make([]ssh.Signer, 0, len(srv.HostSigners))
for _, signer := range srv.HostSigners {
if signer.PublicKey().Type() == "ssh-rsa" {
signers = append(signers,
&wrapSigner{
Signer: signer,
algorithm: gossh.SigAlgoRSASHA2512,
},
&wrapSigner{
Signer: signer,
algorithm: gossh.SigAlgoRSASHA2256,
},
)
}
signers = append(signers, signer)
}
srv.HostSigners = signers
go listen(&srv) go listen(&srv)
}
// wrapSigner wraps a signer and overrides its public key type with the provided algorithm
type wrapSigner struct {
ssh.Signer
algorithm string
}
// PublicKey returns an associated PublicKey instance.
func (s *wrapSigner) PublicKey() gossh.PublicKey {
return &wrapPublicKey{
PublicKey: s.Signer.PublicKey(),
algorithm: s.algorithm,
}
}
// Sign returns raw signature for the given data. This method
// will apply the hash specified for the keytype to the data using
// the algorithm assigned for this key
func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) {
return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm)
}
// wrapPublicKey wraps a PublicKey and overrides its type
type wrapPublicKey struct {
gossh.PublicKey
algorithm string
}
// Type returns the algorithm
func (k *wrapPublicKey) Type() string {
return k.algorithm
} }
// GenKeyPair make a pair of public and private keys for SSH access. // GenKeyPair make a pair of public and private keys for SSH access.

View File

@@ -6,7 +6,6 @@ package storage
import ( import (
"context" "context"
"errors"
"io" "io"
"net/url" "net/url"
"os" "os"
@@ -18,8 +17,6 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
) )
// ErrLocalPathNotSupported represents an error that path is not supported
var ErrLocalPathNotSupported = errors.New("local path is not supported")
var _ ObjectStorage = &LocalStorage{} var _ ObjectStorage = &LocalStorage{}
// LocalStorageType is the type descriptor for local storage // LocalStorageType is the type descriptor for local storage
@@ -62,21 +59,18 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
}, nil }, nil
} }
func (l *LocalStorage) buildLocalPath(p string) string {
return filepath.Join(l.dir, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:])
}
// Open a file // Open a file
func (l *LocalStorage) Open(path string) (Object, error) { func (l *LocalStorage) Open(path string) (Object, error) {
if !isLocalPathValid(path) { return os.Open(l.buildLocalPath(path))
return nil, ErrLocalPathNotSupported
}
return os.Open(filepath.Join(l.dir, path))
} }
// Save a file // Save a file
func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) { func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
if !isLocalPathValid(path) { p := l.buildLocalPath(path)
return 0, ErrLocalPathNotSupported
}
p := filepath.Join(l.dir, path)
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil { if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
return 0, err return 0, err
} }
@@ -116,24 +110,12 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error)
// Stat returns the info of the file // Stat returns the info of the file
func (l *LocalStorage) Stat(path string) (os.FileInfo, error) { func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
return os.Stat(filepath.Join(l.dir, path)) return os.Stat(l.buildLocalPath(path))
}
func isLocalPathValid(p string) bool {
a := path.Clean(p)
if strings.HasPrefix(a, "../") || strings.HasPrefix(a, "..\\") {
return false
}
return a == p
} }
// Delete delete a file // Delete delete a file
func (l *LocalStorage) Delete(path string) error { func (l *LocalStorage) Delete(path string) error {
if !isLocalPathValid(path) { return util.Remove(l.buildLocalPath(path))
return ErrLocalPathNotSupported
}
p := filepath.Join(l.dir, path)
return util.Remove(p)
} }
// URL gets the redirect URL to a file // URL gets the redirect URL to a file

View File

@@ -10,36 +10,44 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestLocalPathIsValid(t *testing.T) { func TestBuildLocalPath(t *testing.T) {
kases := []struct { kases := []struct {
path string localDir string
valid bool path string
expected string
}{ }{
{ {
"a",
"0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", "a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
true,
}, },
{ {
"../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", "a",
false, "../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
}, },
{ {
"a\\0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", "a",
true, "0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
}, },
{ {
"b/../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", "b",
false, "a/../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
}, },
{ {
"..\\a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14", "b",
false, "a\\..\\0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
}, },
} }
for _, k := range kases { for _, k := range kases {
t.Run(k.path, func(t *testing.T) { t.Run(k.path, func(t *testing.T) {
assert.EqualValues(t, k.valid, isLocalPathValid(k.path)) l := LocalStorage{dir: k.localDir}
assert.EqualValues(t, k.expected, l.buildLocalPath(k.path))
}) })
} }
} }

View File

@@ -117,7 +117,7 @@ func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
} }
func (m *MinioStorage) buildMinioPath(p string) string { func (m *MinioStorage) buildMinioPath(p string) string {
return strings.TrimPrefix(path.Join(m.basePath, p), "/") return strings.TrimPrefix(path.Join(m.basePath, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:]), "/")
} }
// Open open a file // Open open a file

View File

@@ -19,6 +19,7 @@ type CreateUserOption struct {
Password string `json:"password" binding:"Required;MaxSize(255)"` Password string `json:"password" binding:"Required;MaxSize(255)"`
MustChangePassword *bool `json:"must_change_password"` MustChangePassword *bool `json:"must_change_password"`
SendNotify bool `json:"send_notify"` SendNotify bool `json:"send_notify"`
Restricted *bool `json:"restricted"`
Visibility string `json:"visibility" binding:"In(,public,limited,private)"` Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
} }

View File

@@ -183,6 +183,8 @@ type EditRepoOption struct {
Archived *bool `json:"archived,omitempty"` Archived *bool `json:"archived,omitempty"`
// set to a string like `8h30m0s` to set the mirror interval time // set to a string like `8h30m0s` to set the mirror interval time
MirrorInterval *string `json:"mirror_interval,omitempty"` MirrorInterval *string `json:"mirror_interval,omitempty"`
// enable prune - remove obsolete remote-tracking references
EnablePrune *bool `json:"enable_prune,omitempty"`
} }
// GenerateRepoOption options when creating repository using a template // GenerateRepoOption options when creating repository using a template

View File

@@ -38,6 +38,8 @@ import (
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff" "code.gitea.io/gitea/services/gitdiff"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"github.com/editorconfig/editorconfig-core-go/v2" "github.com/editorconfig/editorconfig-core-go/v2"
) )
@@ -49,7 +51,7 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
func NewFuncMap() []template.FuncMap { func NewFuncMap() []template.FuncMap {
return []template.FuncMap{map[string]interface{}{ return []template.FuncMap{map[string]interface{}{
"GoVer": func() string { "GoVer": func() string {
return strings.Title(runtime.Version()) return cases.Title(language.English).String(runtime.Version())
}, },
"UseHTTPS": func() bool { "UseHTTPS": func() bool {
return strings.HasPrefix(setting.AppURL, "https") return strings.HasPrefix(setting.AppURL, "https")
@@ -285,7 +287,7 @@ func NewFuncMap() []template.FuncMap {
return util.MergeInto(dict, values...) return util.MergeInto(dict, values...)
}, },
"percentage": func(n int, values ...int) float32 { "percentage": func(n int, values ...int) float32 {
var sum = 0 sum := 0
for i := 0; i < len(values); i++ { for i := 0; i < len(values); i++ {
sum += values[i] sum += values[i]
} }
@@ -378,6 +380,7 @@ func NewFuncMap() []template.FuncMap {
}, },
"Join": strings.Join, "Join": strings.Join,
"QueryEscape": url.QueryEscape, "QueryEscape": url.QueryEscape,
"DotEscape": DotEscape,
}} }}
} }
@@ -386,7 +389,7 @@ func NewFuncMap() []template.FuncMap {
func NewTextFuncMap() []texttmpl.FuncMap { func NewTextFuncMap() []texttmpl.FuncMap {
return []texttmpl.FuncMap{map[string]interface{}{ return []texttmpl.FuncMap{map[string]interface{}{
"GoVer": func() string { "GoVer": func() string {
return strings.Title(runtime.Version()) return cases.Title(language.English).String(runtime.Version())
}, },
"AppName": func() string { "AppName": func() string {
return setting.AppName return setting.AppName
@@ -477,7 +480,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
return dict, nil return dict, nil
}, },
"percentage": func(n int, values ...int) float32 { "percentage": func(n int, values ...int) float32 {
var sum = 0 sum := 0
for i := 0; i < len(values); i++ { for i := 0; i < len(values); i++ {
sum += values[i] sum += values[i]
} }
@@ -501,8 +504,10 @@ func NewTextFuncMap() []texttmpl.FuncMap {
}} }}
} }
var widthRe = regexp.MustCompile(`width="[0-9]+?"`) var (
var heightRe = regexp.MustCompile(`height="[0-9]+?"`) widthRe = regexp.MustCompile(`width="[0-9]+?"`)
heightRe = regexp.MustCompile(`height="[0-9]+?"`)
)
func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) { func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
size := defaultSize size := defaultSize
@@ -629,6 +634,11 @@ func JSEscape(raw string) string {
return template.JSEscapeString(raw) return template.JSEscapeString(raw)
} }
// DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
func DotEscape(raw string) string {
return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
}
// Sha1 returns sha1 sum of string // Sha1 returns sha1 sum of string
func Sha1(str string) string { func Sha1(str string) string {
return base.EncodeSha1(str) return base.EncodeSha1(str)
@@ -736,7 +746,7 @@ func RenderEmoji(text string) template.HTML {
return template.HTML(renderedText) return template.HTML(renderedText)
} }
//ReactionToEmoji renders emoji for use in reactions // ReactionToEmoji renders emoji for use in reactions
func ReactionToEmoji(reaction string) template.HTML { func ReactionToEmoji(reaction string) template.HTML {
val := emoji.FromCode(reaction) val := emoji.FromCode(reaction)
if val != nil { if val != nil {

View File

@@ -39,6 +39,7 @@ func MockContext(t *testing.T, path string) *context.Context {
Resp: context.NewResponse(resp), Resp: context.NewResponse(resp),
Locale: &mockLocale{}, Locale: &mockLocale{},
} }
defer ctx.Close()
requestURL, err := url.Parse(path) requestURL, err := url.Parse(path)
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -8,7 +8,7 @@ import (
"net" "net"
) )
// IsIPPrivate for net.IP.IsPrivate. TODO: replace with `ip.IsPrivate()` if min go version is bumped to 1.17 // IsIPPrivate for net.IP.IsPrivate.
func IsIPPrivate(ip net.IP) bool { func IsIPPrivate(ip net.IP) bool {
if ip4 := ip.To4(); ip4 != nil { if ip4 := ip.To4(); ip4 != nil {
return ip4[0] == 10 || return ip4[0] == 10 ||

18
modules/util/slice.go Normal file
View File

@@ -0,0 +1,18 @@
// 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 util
// RemoveIDFromList removes the given ID from the slice, if found.
// It does not preserve order, and assumes the ID is unique.
func RemoveIDFromList(list []int64, id int64) ([]int64, bool) {
n := len(list) - 1
for i, item := range list {
if item == id {
list[i] = list[n]
return list[:n], true
}
}
return list, false
}

View File

@@ -1435,7 +1435,7 @@ pulls.manually_merged=Слито вручную
pulls.manually_merged_as=Запрос на слияние был объединён вручную, как <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a>. pulls.manually_merged_as=Запрос на слияние был объединён вручную, как <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a>.
pulls.is_closed=Запрос на слияние был закрыт. pulls.is_closed=Запрос на слияние был закрыт.
pulls.has_merged=Слияние этого запроса успешно завершено. pulls.has_merged=Слияние этого запроса успешно завершено.
pulls.title_wip_desc=`<a href="#">Добавьте <strong>%s</strong> в начало заголовка</a> для защиты от случайного досрочного принятия запроса на слияние pulls.title_wip_desc=`<a href="#">Добавьте <strong>%s</strong> в начало заголовка</a> для защиты от случайного досрочного принятия запроса на слияние`
pulls.cannot_merge_work_in_progress=Этот запрос на слияние помечен как в процессе работы. pulls.cannot_merge_work_in_progress=Этот запрос на слияние помечен как в процессе работы.
pulls.still_in_progress=Всё ещё в процессе? pulls.still_in_progress=Всё ещё в процессе?
pulls.add_prefix=Добавить <strong>%s</strong> префикс pulls.add_prefix=Добавить <strong>%s</strong> префикс

View File

@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/password"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/api/v1/utils"
@@ -81,7 +82,6 @@ func CreateUser(ctx *context.APIContext) {
Email: form.Email, Email: form.Email,
Passwd: form.Password, Passwd: form.Password,
MustChangePassword: true, MustChangePassword: true,
IsActive: true,
LoginType: auth.Plain, LoginType: auth.Plain,
} }
if form.MustChangePassword != nil { if form.MustChangePassword != nil {
@@ -107,11 +107,17 @@ func CreateUser(ctx *context.APIContext) {
return return
} }
var overwriteDefault *user_model.CreateUserOverwriteOptions overwriteDefault := &user_model.CreateUserOverwriteOptions{
IsActive: util.OptionalBoolTrue,
}
if form.Restricted != nil {
overwriteDefault.IsRestricted = util.OptionalBoolOf(*form.Restricted)
}
if form.Visibility != "" { if form.Visibility != "" {
overwriteDefault = &user_model.CreateUserOverwriteOptions{ visibility := api.VisibilityModes[form.Visibility]
Visibility: api.VisibilityModes[form.Visibility], overwriteDefault.Visibility = &visibility
}
} }
if err := user_model.CreateUser(u, overwriteDefault); err != nil { if err := user_model.CreateUser(u, overwriteDefault); err != nil {

View File

@@ -35,6 +35,8 @@ func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecor
Render: rnd, Render: rnd,
Data: make(map[string]interface{}), Data: make(map[string]interface{}),
} }
defer c.Close()
return c, resp return c, resp
} }

View File

@@ -214,7 +214,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
targetStatus = models.NotificationStatusRead targetStatus = models.NotificationStatusRead
} }
changed := make([]*structs.NotificationThread, len(nl)) changed := make([]*structs.NotificationThread, 0, len(nl))
for _, n := range nl { for _, n := range nl {
notif, err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus) notif, err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)

View File

@@ -177,23 +177,18 @@ func CreateBranch(ctx *context.APIContext) {
} }
err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName) err := repo_service.CreateNewBranch(ctx.User, ctx.Repo.Repository, opt.OldBranchName, opt.BranchName)
if err != nil { if err != nil {
if models.IsErrBranchDoesNotExist(err) { if models.IsErrBranchDoesNotExist(err) {
ctx.Error(http.StatusNotFound, "", "The old branch does not exist") ctx.Error(http.StatusNotFound, "", "The old branch does not exist")
} }
if models.IsErrTagAlreadyExists(err) { if models.IsErrTagAlreadyExists(err) {
ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.") ctx.Error(http.StatusConflict, "", "The branch with the same tag already exists.")
} else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) { } else if models.IsErrBranchAlreadyExists(err) || git.IsErrPushOutOfDate(err) {
ctx.Error(http.StatusConflict, "", "The branch already exists.") ctx.Error(http.StatusConflict, "", "The branch already exists.")
} else if models.IsErrBranchNameConflict(err) { } else if models.IsErrBranchNameConflict(err) {
ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.") ctx.Error(http.StatusConflict, "", "The branch with the same name already exists.")
} else { } else {
ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err) ctx.Error(http.StatusInternalServerError, "CreateRepoBranch", err)
} }
return return
} }
@@ -263,10 +258,15 @@ func ListBranches(ctx *context.APIContext) {
return return
} }
apiBranches := make([]*api.Branch, len(branches)) apiBranches := make([]*api.Branch, 0, len(branches))
for i := range branches { for i := range branches {
c, err := branches[i].GetCommit() c, err := branches[i].GetCommit()
if err != nil { if err != nil {
// Skip if this branch doesn't exist anymore.
if git.IsErrNotExist(err) {
totalNumOfBranches--
continue
}
ctx.Error(http.StatusInternalServerError, "GetCommit", err) ctx.Error(http.StatusInternalServerError, "GetCommit", err)
return return
} }
@@ -275,11 +275,12 @@ func ListBranches(ctx *context.APIContext) {
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err) ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
return return
} }
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin()) apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
return return
} }
apiBranches = append(apiBranches, apiBranch)
} }
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize) ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
@@ -532,7 +533,6 @@ func CreateBranchProtection(ctx *context.APIContext) {
} }
ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp)) ctx.JSON(http.StatusCreated, convert.ToBranchProtection(bp))
} }
// EditBranchProtection edits a branch protection for a repo // EditBranchProtection edits a branch protection for a repo

View File

@@ -173,6 +173,7 @@ func SearchIssues(ctx *context.APIContext) {
opts.TeamID = team.ID opts.TeamID = team.ID
} }
repoCond := models.SearchRepositoryCondition(opts)
repoIDs, _, err := models.SearchRepositoryIDs(opts) repoIDs, _, err := models.SearchRepositoryIDs(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err) ctx.Error(http.StatusInternalServerError, "SearchRepositoryByName", err)
@@ -233,7 +234,7 @@ func SearchIssues(ctx *context.APIContext) {
Page: ctx.FormInt("page"), Page: ctx.FormInt("page"),
PageSize: limit, PageSize: limit,
}, },
RepoIDs: repoIDs, RepoCond: repoCond,
IsClosed: isClosed, IsClosed: isClosed,
IssueIDs: issueIDs, IssueIDs: issueIDs,
IncludedLabelNames: includedLabelNames, IncludedLabelNames: includedLabelNames,
@@ -245,18 +246,23 @@ func SearchIssues(ctx *context.APIContext) {
UpdatedAfterUnix: since, UpdatedAfterUnix: since,
} }
ctxUserID := int64(0)
if ctx.IsSigned {
ctxUserID = ctx.User.ID
}
// Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested // Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
if ctx.FormBool("created") { if ctx.FormBool("created") {
issuesOpt.PosterID = ctx.User.ID issuesOpt.PosterID = ctxUserID
} }
if ctx.FormBool("assigned") { if ctx.FormBool("assigned") {
issuesOpt.AssigneeID = ctx.User.ID issuesOpt.AssigneeID = ctxUserID
} }
if ctx.FormBool("mentioned") { if ctx.FormBool("mentioned") {
issuesOpt.MentionedID = ctx.User.ID issuesOpt.MentionedID = ctxUserID
} }
if ctx.FormBool("review_requested") { if ctx.FormBool("review_requested") {
issuesOpt.ReviewRequestedID = ctx.User.ID issuesOpt.ReviewRequestedID = ctxUserID
} }
if issues, err = models.Issues(issuesOpt); err != nil { if issues, err = models.Issues(issuesOpt); err != nil {
@@ -455,7 +461,7 @@ func ListIssues(ctx *context.APIContext) {
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
issuesOpt := &models.IssuesOptions{ issuesOpt := &models.IssuesOptions{
ListOptions: listOptions, ListOptions: listOptions,
RepoIDs: []int64{ctx.Repo.Repository.ID}, RepoID: ctx.Repo.Repository.ID,
IsClosed: isClosed, IsClosed: isClosed,
IssueIDs: issueIDs, IssueIDs: issueIDs,
LabelIDs: labelIDs, LabelIDs: labelIDs,
@@ -599,7 +605,7 @@ func CreateIssue(ctx *context.APIContext) {
DeadlineUnix: deadlineUnix, DeadlineUnix: deadlineUnix,
} }
var assigneeIDs = make([]int64, 0) assigneeIDs := make([]int64, 0)
var err error var err error
if ctx.Repo.CanWrite(unit.TypeIssues) { if ctx.Repo.CanWrite(unit.TypeIssues) {
issue.MilestoneID = form.Milestone issue.MilestoneID = form.Milestone

View File

@@ -144,7 +144,7 @@ func GetDeployKey(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/DeployKey" // "$ref": "#/responses/DeployKey"
key, err := asymkey_model.GetDeployKeyByID(db.DefaultContext, ctx.ParamsInt64(":id")) key, err := asymkey_model.GetDeployKeyByID(ctx, ctx.ParamsInt64(":id"))
if err != nil { if err != nil {
if asymkey_model.IsErrDeployKeyNotExist(err) { if asymkey_model.IsErrDeployKeyNotExist(err) {
ctx.NotFound() ctx.NotFound()

View File

@@ -95,7 +95,6 @@ func ListPullRequests(ctx *context.APIContext) {
Labels: ctx.FormStrings("labels"), Labels: ctx.FormStrings("labels"),
MilestoneID: ctx.FormInt64("milestone"), MilestoneID: ctx.FormInt64("milestone"),
}) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "PullRequests", err) ctx.Error(http.StatusInternalServerError, "PullRequests", err)
return return
@@ -724,13 +723,12 @@ func MergePullRequest(ctx *context.APIContext) {
return return
} }
if err = pr.LoadHeadRepo(); err != nil { if err := pr.LoadHeadRepo(); err != nil {
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
return return
} }
err = pr.LoadIssue() if err := pr.LoadIssue(); err != nil {
if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadIssue", err) ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
return return
} }
@@ -744,29 +742,33 @@ func MergePullRequest(ctx *context.APIContext) {
} }
} }
if pr.Issue.IsClosed { manuallMerge := repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged
ctx.NotFound() force := form.ForceMerge != nil && *form.ForceMerge
return
}
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User) if err := pull_service.CheckPullMergable(ctx, ctx.User, &ctx.Repo.Permission, pr, manuallMerge, force); err != nil {
if err != nil { if errors.Is(err, pull_service.ErrIsClosed) {
ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err) ctx.NotFound()
return } else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
} ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
if !allowedMerge { } else if errors.Is(err, pull_service.ErrHasMerged) {
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR") ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
return } else if errors.Is(err, pull_service.ErrIsWorkInProgress) {
} ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
} else if errors.Is(err, pull_service.ErrNotMergableState) {
if pr.HasMerged { ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "") } else if models.IsErrNotAllowedToMerge(err) {
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
} else if asymkey_service.IsErrWontSign(err) {
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
} else {
ctx.InternalServerError(err)
}
return return
} }
// handle manually-merged mark // handle manually-merged mark
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged { if manuallMerge {
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil { if err := pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
if models.IsErrInvalidMergeStyle(err) { if models.IsErrInvalidMergeStyle(err) {
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do))) ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
return return
@@ -782,63 +784,13 @@ func MergePullRequest(ctx *context.APIContext) {
return return
} }
if !pr.CanAutoMerge() { // set defaults to propagate needed fields
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later") if err := form.SetDefaults(pr); err != nil {
ctx.ServerError("SetDefaults", fmt.Errorf("SetDefaults: %v", err))
return return
} }
if pr.IsWorkInProgress() { if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, form.MergeTitleField); err != nil {
ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
return
}
if err := pull_service.CheckPRReadyToMerge(pr, false); err != nil {
if !models.IsErrNotAllowedToMerge(err) {
ctx.Error(http.StatusInternalServerError, "CheckPRReadyToMerge", err)
return
}
if form.ForceMerge != nil && *form.ForceMerge {
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
ctx.Error(http.StatusInternalServerError, "IsUserRepoAdmin", err)
return
} else if !isRepoAdmin {
ctx.Error(http.StatusMethodNotAllowed, "Merge", "Only repository admin can merge if not all checks are ok (force merge)")
}
} else {
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
return
}
}
if _, err := pull_service.IsSignedIfRequired(pr, ctx.User); err != nil {
if !asymkey_service.IsErrWontSign(err) {
ctx.Error(http.StatusInternalServerError, "IsSignedIfRequired", err)
return
}
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
return
}
if len(form.Do) == 0 {
form.Do = string(repo_model.MergeStyleMerge)
}
message := strings.TrimSpace(form.MergeTitleField)
if len(message) == 0 {
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleMerge {
message = pr.GetDefaultMergeMessage()
}
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleSquash {
message = pr.GetDefaultSquashMessage()
}
}
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
if len(form.MergeMessageField) > 0 {
message += "\n\n" + form.MergeMessageField
}
if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message); err != nil {
if models.IsErrInvalidMergeStyle(err) { if models.IsErrInvalidMergeStyle(err) {
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do))) ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
return return

View File

@@ -160,7 +160,7 @@ func Search(ctx *context.APIContext) {
opts.Collaborate = util.OptionalBoolFalse opts.Collaborate = util.OptionalBoolFalse
} }
var mode = ctx.FormString("mode") mode := ctx.FormString("mode")
switch mode { switch mode {
case "source": case "source":
opts.Fork = util.OptionalBoolFalse opts.Fork = util.OptionalBoolFalse
@@ -186,9 +186,9 @@ func Search(ctx *context.APIContext) {
opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private")) opts.IsPrivate = util.OptionalBoolOf(ctx.FormBool("is_private"))
} }
var sortMode = ctx.FormString("sort") sortMode := ctx.FormString("sort")
if len(sortMode) > 0 { if len(sortMode) > 0 {
var sortOrder = ctx.FormString("order") sortOrder := ctx.FormString("order")
if len(sortOrder) == 0 { if len(sortOrder) == 0 {
sortOrder = "asc" sortOrder = "asc"
} }
@@ -264,7 +264,8 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
if repo_model.IsErrRepoAlreadyExist(err) { if repo_model.IsErrRepoAlreadyExist(err) {
ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.")
} else if db.IsErrNameReserved(err) || } else if db.IsErrNameReserved(err) ||
db.IsErrNamePatternNotAllowed(err) { db.IsErrNamePatternNotAllowed(err) ||
models.IsErrIssueLabelTemplateLoad(err) {
ctx.Error(http.StatusUnprocessableEntity, "", err) ctx.Error(http.StatusUnprocessableEntity, "", err)
} else { } else {
ctx.Error(http.StatusInternalServerError, "CreateRepository", err) ctx.Error(http.StatusInternalServerError, "CreateRepository", err)
@@ -624,7 +625,7 @@ func Edit(ctx *context.APIContext) {
} }
if opts.MirrorInterval != nil { if opts.MirrorInterval != nil {
if err := updateMirrorInterval(ctx, opts); err != nil { if err := updateMirror(ctx, opts); err != nil {
return return
} }
} }
@@ -949,37 +950,67 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
return nil return nil
} }
// updateMirrorInterval updates the repo's mirror Interval // updateMirror updates a repo's mirror Interval and EnablePrune
func updateMirrorInterval(ctx *context.APIContext, opts api.EditRepoOption) error { func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
// only update mirror if interval or enable prune are provided
if opts.MirrorInterval == nil && opts.EnablePrune == nil {
return nil
}
// these values only make sense if the repo is a mirror
if !repo.IsMirror {
err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
return err
}
// get the mirror from the repo
mirror, err := repo_model.GetMirrorByRepoID(repo.ID)
if err != nil {
log.Error("Failed to get mirror: %s", err)
ctx.Error(http.StatusInternalServerError, "MirrorInterval", err)
return err
}
// update MirrorInterval
if opts.MirrorInterval != nil { if opts.MirrorInterval != nil {
if !repo.IsMirror {
err := fmt.Errorf("repo is not a mirror, can not change mirror interval") // MirrorInterval should be a duration
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err) interval, err := time.ParseDuration(*opts.MirrorInterval)
return err
}
mirror, err := repo_model.GetMirrorByRepoID(repo.ID)
if err != nil { if err != nil {
log.Error("Failed to get mirror: %s", err)
ctx.Error(http.StatusInternalServerError, "MirrorInterval", err)
return err
}
if interval, err := time.ParseDuration(*opts.MirrorInterval); err == nil {
mirror.Interval = interval
mirror.Repo = repo
if err := repo_model.UpdateMirror(mirror); err != nil {
log.Error("Failed to Set Mirror Interval: %s", err)
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
return err
}
log.Trace("Repository %s/%s Mirror Interval was Updated to %s", ctx.Repo.Owner.Name, repo.Name, interval)
} else {
log.Error("Wrong format for MirrorInternal Sent: %s", err) log.Error("Wrong format for MirrorInternal Sent: %s", err)
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err) ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
return err return err
} }
// Ensure the provided duration is not too short
if interval != 0 && interval < setting.Mirror.MinInterval {
err := fmt.Errorf("invalid mirror interval: %s is below minimum interval: %s", interval, setting.Mirror.MinInterval)
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
return err
}
mirror.Interval = interval
mirror.Repo = repo
mirror.ScheduleNextUpdate()
log.Trace("Repository %s Mirror[%d] Set Interval: %s NextUpdateUnix: %s", repo.FullName(), mirror.ID, interval, mirror.NextUpdateUnix)
} }
// update EnablePrune
if opts.EnablePrune != nil {
mirror.EnablePrune = *opts.EnablePrune
log.Trace("Repository %s Mirror[%d] Set EnablePrune: %t", repo.FullName(), mirror.ID, mirror.EnablePrune)
}
// finally update the mirror in the DB
if err := repo_model.UpdateMirror(mirror); err != nil {
log.Error("Failed to Set Mirror Interval: %s", err)
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
return err
}
return nil return nil
} }

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