Compare commits

..

38 commits

Author SHA1 Message Date
Nulo (he/him)
f649ef486c Save files in local storage as umask (#21198)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Go creates temporary files as 600, but sometimes we want the group to be able to read them (for example,
for another user to back up the storage)

This PR applies the umask to the renamed tmp files in local storage.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-10-15 15:27:03 -03:00
9ee18bd50f gitea.nulo.in: Use NPM cache 2022-10-15 15:27:03 -03:00
010d1e5627 gitea.nulo.in: Use APK cache 2022-10-15 15:27:03 -03:00
5e13cadb4d gitea.nulo.in: Use script to upgrade 2022-10-15 15:27:03 -03:00
24240aad00 gitea.nulo.in: Use Alpine 3.16 2022-10-15 15:27:03 -03:00
1c3fff8db7 Add .woodpecker.yml for gitea.nulo.in 2022-10-15 15:27:03 -03:00
e7009d9fc6 Allow hidden .READMEs 2022-10-15 15:27:03 -03:00
6543
f48fda8eef
Changelog v1.17.3 (#21456) 2022-10-15 15:08:17 +02:00
6543
cd48a007bb
improve code quality (#21464) (#21463)
Backport #21464 and #21465

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-10-15 14:24:39 +02:00
zeripath
6afbef5a8b
Do DB update after merge in hammer context (#21401) (#21416)
Backport #21401

When merge was changed to run in the background context, the db updates
were still running in request context. This means that the merge could
be successful but the db not be updated.

This PR changes both these to run in the hammer context, this is not
complete rollback protection but it's much better.

Fix #21332

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-10-12 15:52:21 +08:00
Gusted
d745780014
Add Num{Issues,Pulls} stats checks (#21404) (#21414)
Backport #21404

Currently `repository.Num{Issues,Pulls}` weren't checked and could
become out-of-consistency. Adds these two checks to `CheckRepoStats`.

Fix incorrect SQL query for `repository.NumClosedPulls`, the check
should be for `repo_num_pulls`.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-10-12 14:47:48 +08:00
Gusted
652abf0ae0
Bump golang.org/x/text (#21412) (#21413)
- Backport #21412
- Update the `golang.org/x/text` dependency, this fixes [a security
issue](https://groups.google.com/g/golang-announce/c/-hjNw559_tE/m/KlGTfid5CAAJ).
2022-10-11 20:00:55 +01:00
zeripath
1f804d35ca
Stop logging CheckPath returns error: context canceled (#21064) (#21405)
Backport #21064

We should only log CheckPath errors if they are not simply due to
context cancellation - and we should add a little more context to the
error message.

Fix #20709

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

Signed-off-by: Andrew Thornton <art27@cantab.net>
2022-10-11 16:35:29 +08:00
KN4CK3R
c83a05f114
Set SemverCompatible to false for Conan packages (#21275) (#21366)
Backport of #21275
2022-10-10 20:46:09 +08:00
KN4CK3R
a3c75ac438
Make NuGet service index publicly accessible (#21242) (#21277)
Backport of #21242

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
2022-10-08 16:23:41 +01:00
M Hickford
14bc4d79c1
Parse OAuth Authorization header when request omits client secret (#21351) (#21374)
Backport #21351

This fixes error "unauthorized_client: invalid client secret" when
client includes secret in Authorization header rather than request body.
OAuth spec permits both:
https://www.rfc-editor.org/rfc/rfc6749#section-2.3.1

Clients in possession of a client password MAY use the HTTP Basic
authentication scheme ... Alternatively, the authorization server MAY
support including the client credentials in the request-body

Sanity validation that client id and client secret in request are
consistent with Authorization header.

Improve error descriptions. Error codes remain the same.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
2022-10-08 16:53:17 +08:00
M Hickford
672d54fafa
Ignore port for OAuth2 loopback redirect URIs (#21293) (#21373)
Backport #21293

Following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3

Fixes #21285
2022-10-08 09:52:35 +08:00
Jason Song
0495544b8a
Tag list should include draft releases with existing tags (#21263) (#21365)
Backport #21263.

Before, a tag for a draft release disappeared in the tag list, fix #21262.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-10-07 18:59:42 +08:00
John Olheiser
1fbc56d732
Fix linked account translation (#21331) (#21334) 2022-10-05 10:25:34 -04:00
wxiaoguang
1a9ba1c65d
Fix missing m.Run() in TestMain (#21341)
Backport #21340, add the missing m.Run()
2022-10-05 12:17:16 +08:00
techknowlogick
cbebcc1c26
Foreign ID conflicts if ID is 0 for each item (#21271) (#21272)
The default is 0 if not defined, and that causes dupe index errors

Backport of #21271
2022-10-02 17:43:30 -04:00
6543
0e677d7b41
Update bluemonday (#21281) (#21287)
Backport #21281

https://github.com/microcosm-cc/bluemonday/releases/tag/v1.0.20

Co-authored-by: Lauris BH <lauris@nix.lv>
2022-09-28 16:15:22 +02:00
KN4CK3R
790770aef3
Fix empty container layer history and UI (#21251) (#21278)
Backport of #21251
2022-09-27 23:10:19 +08:00
KN4CK3R
43b4c38d4f
Use absolute links in feeds (#21229) (#21265)
Backport of #21229

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-09-26 13:49:22 -04:00
wxiaoguang
e79a10793f
Use en-US as fallback when using other default language (#21200) (#21256)
Only en-US has complete translations. When use other language as
default, the en-US should still be used as fallback.

Backport #21200, Close #21199
2022-09-25 22:14:57 +08:00
wxiaoguang
be5411d6b5
Make Clone in VSCode link get updated correctly (#21225) (#21226)
Backport #21225, fix for #21128 (also in 1.17.3), close #21224

The indent was incorrect before, so this PR did some formatting work. 

Bypass Golang's template bug for JS string interpolation. And since
there are JS lint rules for templates, so the string interpolation is
also a must.
2022-09-23 12:10:26 +08:00
KN4CK3R
bdf3be53b0
Respect REQUIRE_SIGNIN_VIEW for packages (#20873) (#21232)
Backport of #20873

When REQUIRE_SIGNIN_VIEW = true, even with public repositories, you can only see them after you login. The packages should not be accessed without login.

Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-09-23 11:25:53 +08:00
delvh
e50473e6bb
Prevent invalid behavior for file reviewing when loading more files (#21230) (#21234)
Backport of #21230

The problem was that many PR review components loaded by `Show more`
received the same ID as previous batches, which confuses browsers (when
clicked). All such occurrences should now be fixed.

Additionally improved the background of the `viewed` checkbox.

Fixes #21228.
Fixes #20681.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2022-09-22 20:45:51 -04:00
wxiaoguang
20c135cd46
Use Go 1.19 fmt for Gitea 1.17, sync emoji data (#21239)
The images used by Gitea's drone pipeline were upgraded to Go 1.19.x
It causes the lint fails because Go 1.19 uses new code format.

This PR partially backport #20758 (including the emoji-data sync),
partially fix the format manually.
2022-09-22 21:58:31 +08:00
wxiaoguang
937ef6fa90
Treat git object mode 40755 as directory (#21195) (#21218)
Backport #21195

Git uses 040000 for tree object, but some users may get 040755 for
unknown reasons, fix #21190

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2022-09-20 12:40:23 -04:00
Abdul Monim
54d4e664c2
Make the vscode clone link respect transport protocol (#20557) (#21128)
Backports #20557

Co-authored-by: Norwin <noerw@users.noreply.github.com>
Co-authored-by: Munim Munna <6266677+monim67@users.noreply.github.com>
2022-09-20 22:01:24 +08:00
KN4CK3R
c571ac6fd3
Allow uppercase ASCII alphabet in PyPI package names (#21095) (#21217)
Backport of #21095

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2022-09-20 17:37:24 +08:00
Lunny Xiao
f663773200
Fix limited user cannot view himself's profile (#21212)
backport #21210, fix #21206

If user and viewer are equal the method should return true.
Also the common organization check was wrong as count can never be less then 0.

Tests are on main branch.
2022-09-20 16:00:46 +08:00
Lunny Xiao
a28677273b
Fix template bug of admin monitor (#21209)
backport #21208
2022-09-20 08:15:08 +08:00
Jason Song
c8d687997d
Fix reaction of issues (#21185) (#21196)
Backport #21185.

Fix #20860.

`CommentID` in `FindReactionsOptions` should be -1 to search reactions
with zero comment id.



8351172b6e/models/issues/reaction.go (L108-L121)

Co-authored-by: Lauris BH <lauris@nix.lv>
2022-09-18 12:04:09 +08:00
wxiaoguang
5cb1037cb7
Fix CSV diff for added/deleted files (#21189) (#21193)
Backport #21189
Fixes #21184
Regression of #19552

Instead of using `GetBlobByPath`, use the already existing instances.
2022-09-17 18:53:04 +08:00
Tyrone Yeh
2dcea782c5
Fix pagination limit parameter problem (#21111)
backport #21109

Co-authored-by: 6543 <6543@obermui.de>
2022-09-08 20:00:42 +08:00
wxiaoguang
31842f12a4
Add MD5 back to template helper functions to avoid breaking (#21102)
In #20932 the MD5 helper function was removed from template context,
it breaks user's customized templates.

This PR adds the MD5 helper function back.
2022-09-07 19:30:52 +08:00
77 changed files with 1188 additions and 854 deletions

View file

@ -4,6 +4,42 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.17.3](https://github.com/go-gitea/gitea/releases/tag/v1.17.3) - 2022-10-15
* SECURITY
* Sanitize and Escape refs in git backend (#21464) (#21463)
* Bump `golang.org/x/text` (#21412) (#21413)
* Update bluemonday (#21281) (#21287)
* ENHANCEMENTS
* Fix empty container layer history and UI (#21251) (#21278)
* Use en-US as fallback when using other default language (#21200) (#21256)
* Make the vscode clone link respect transport protocol (#20557) (#21128)
* BUGFIXES
* Do DB update after merge in hammer context (#21401) (#21416)
* Add Num{Issues,Pulls} stats checks (#21404) (#21414)
* Stop logging CheckPath returns error: context canceled (#21064) (#21405)
* Parse OAuth Authorization header when request omits client secret (#21351) (#21374)
* Ignore port for loopback redirect URIs (#21293) (#21373)
* Set SemverCompatible to false for Conan packages (#21275) (#21366)
* Tag list should include draft releases with existing tags (#21263) (#21365)
* Fix linked account translation (#21331) (#21334)
* Make NuGet service index publicly accessible (#21242) (#21277)
* Foreign ID conflicts if ID is 0 for each item (#21271) (#21272)
* Use absolute links in feeds (#21229) (#21265)
* Prevent invalid behavior for file reviewing when loading more files (#21230) (#21234)
* Respect `REQUIRE_SIGNIN_VIEW` for packages (#20873) (#21232)
* Treat git object mode 40755 as directory (#21195) (#21218)
* Allow uppercase ASCII alphabet in PyPI package names (#21095) (#21217)
* Fix limited user cannot view himself's profile (#21212)
* Fix template bug of admin monitor (#21209)
* Fix reaction of issues (#21185) (#21196)
* Fix CSV diff for added/deleted files (#21189) (#21193)
* Fix pagination limit parameter problem (#21111)
* TESTING
* Fix missing m.Run() in TestMain (#21341)
* BUILD
* Use Go 1.19 fmt for Gitea 1.17, sync emoji data (#21239)
## [1.17.2](https://github.com/go-gitea/gitea/releases/tag/v1.17.2) - 2022-09-06 ## [1.17.2](https://github.com/go-gitea/gitea/releases/tag/v1.17.2) - 2022-09-06
* SECURITY * SECURITY

View file

@ -33,7 +33,7 @@ GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.1
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.0
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10 GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4 MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.0
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
DOCKER_IMAGE ?= gitea/gitea DOCKER_IMAGE ?= gitea/gitea

File diff suppressed because one or more lines are too long

View file

@ -214,8 +214,7 @@ const hdr = `
package emoji package emoji
// Code generated by gen.go. DO NOT EDIT. // Code generated by build/generate-emoji.go. DO NOT EDIT.
// Sourced from %s // Sourced from %s
//
var GemojiData = %#v var GemojiData = %#v
` `

13
go.mod
View file

@ -64,7 +64,7 @@ require (
github.com/mattn/go-isatty v0.0.14 github.com/mattn/go-isatty v0.0.14
github.com/mattn/go-sqlite3 v1.14.12 github.com/mattn/go-sqlite3 v1.14.12
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.19 github.com/microcosm-cc/bluemonday v1.0.20
github.com/minio/minio-go/v7 v7.0.26 github.com/minio/minio-go/v7 v7.0.26
github.com/msteinert/pam v1.0.0 github.com/msteinert/pam v1.0.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
@ -91,11 +91,11 @@ require (
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3 go.jolheiser.com/pwn v0.0.3
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
golang.org/x/net v0.0.0-20220630215102-69896b714898 golang.org/x/net v0.0.0-20220927171203-f486391704dc
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
golang.org/x/text v0.3.7 golang.org/x/text v0.3.8
golang.org/x/tools v0.1.10 golang.org/x/tools v0.1.12
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.66.4 gopkg.in/ini.v1 v1.66.4
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
@ -271,9 +271,8 @@ require (
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.21.0 // indirect go.uber.org/zap v1.21.0 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
google.golang.org/grpc v1.43.0 // indirect google.golang.org/grpc v1.43.0 // indirect

27
go.sum
View file

@ -1146,8 +1146,8 @@ github.com/mholt/acmez v1.0.2 h1:C8wsEBIUVi6e0DYoxqCcFuXtwc4AWXL/jgcDjF7mjVo=
github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM= github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo= github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c= github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=
github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
@ -1716,8 +1716,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -1788,8 +1788,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw= golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -1824,8 +1824,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -1936,8 +1936,8 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@ -1950,8 +1950,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -2045,16 +2046,14 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=

View file

@ -266,7 +266,7 @@ func TestPackageConan(t *testing.T) {
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, pd.SemVer) assert.Nil(t, pd.SemVer)
assert.Equal(t, name, pd.Package.Name) assert.Equal(t, name, pd.Package.Name)
assert.Equal(t, version1, pd.Version.Version) assert.Equal(t, version1, pd.Version.Version)
assert.IsType(t, &conan_module.Metadata{}, pd.Metadata) assert.IsType(t, &conan_module.Metadata{}, pd.Metadata)

View file

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -79,6 +80,18 @@ func TestPackageGeneric(t *testing.T) {
assert.Equal(t, int64(1), pvs[0].DownloadCount) assert.Equal(t, int64(1), pvs[0].DownloadCount)
}) })
t.Run("RequireSignInView", func(t *testing.T) {
defer PrintCurrentTest(t)()
setting.Service.RequireSignInView = true
defer func() {
setting.Service.RequireSignInView = false
}()
req := NewRequest(t, "GET", url)
MakeRequest(t, req, http.StatusUnauthorized)
})
t.Run("Delete", func(t *testing.T) { t.Run("Delete", func(t *testing.T) {
defer PrintCurrentTest(t)() defer PrintCurrentTest(t)()

View file

@ -19,6 +19,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
nuget_module "code.gitea.io/gitea/modules/packages/nuget" nuget_module "code.gitea.io/gitea/modules/packages/nuget"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/packages/nuget" "code.gitea.io/gitea/routers/api/packages/nuget"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -65,39 +66,58 @@ func TestPackageNuGet(t *testing.T) {
t.Run("ServiceIndex", func(t *testing.T) { t.Run("ServiceIndex", func(t *testing.T) {
defer PrintCurrentTest(t)() defer PrintCurrentTest(t)()
req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) privateUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Visibility: structs.VisibleTypePrivate}).(*user_model.User)
req = AddBasicAuthHeader(req, user.Name)
MakeRequest(t, req, http.StatusOK)
req = NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url)) cases := []struct {
req = addNuGetAPIKeyHeader(req, token) Owner string
resp := MakeRequest(t, req, http.StatusOK) UseBasicAuth bool
UseTokenAuth bool
}{
{privateUser.Name, false, false},
{privateUser.Name, true, false},
{privateUser.Name, false, true},
{user.Name, false, false},
{user.Name, true, false},
{user.Name, false, true},
}
var result nuget.ServiceIndexResponse for _, c := range cases {
DecodeJSON(t, resp, &result) url := fmt.Sprintf("/api/packages/%s/nuget", c.Owner)
assert.Equal(t, "3.0.0", result.Version) req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
assert.NotEmpty(t, result.Resources) if c.UseBasicAuth {
req = AddBasicAuthHeader(req, user.Name)
} else if c.UseTokenAuth {
req = addNuGetAPIKeyHeader(req, token)
}
resp := MakeRequest(t, req, http.StatusOK)
root := setting.AppURL + url[1:] var result nuget.ServiceIndexResponse
for _, r := range result.Resources { DecodeJSON(t, resp, &result)
switch r.Type {
case "SearchQueryService": assert.Equal(t, "3.0.0", result.Version)
fallthrough assert.NotEmpty(t, result.Resources)
case "SearchQueryService/3.0.0-beta":
fallthrough root := setting.AppURL + url[1:]
case "SearchQueryService/3.0.0-rc": for _, r := range result.Resources {
assert.Equal(t, root+"/query", r.ID) switch r.Type {
case "RegistrationsBaseUrl": case "SearchQueryService":
fallthrough fallthrough
case "RegistrationsBaseUrl/3.0.0-beta": case "SearchQueryService/3.0.0-beta":
fallthrough fallthrough
case "RegistrationsBaseUrl/3.0.0-rc": case "SearchQueryService/3.0.0-rc":
assert.Equal(t, root+"/registration", r.ID) assert.Equal(t, root+"/query", r.ID)
case "PackageBaseAddress/3.0.0": case "RegistrationsBaseUrl":
assert.Equal(t, root+"/package", r.ID) fallthrough
case "PackagePublish/2.0.0": case "RegistrationsBaseUrl/3.0.0-beta":
assert.Equal(t, root, r.ID) fallthrough
case "RegistrationsBaseUrl/3.0.0-rc":
assert.Equal(t, root+"/registration", r.ID)
case "PackageBaseAddress/3.0.0":
assert.Equal(t, root+"/package", r.ID)
case "PackagePublish/2.0.0":
assert.Equal(t, root, r.ID)
}
} }
} }
}) })

View file

@ -218,6 +218,11 @@ func (a *Action) GetRepoLink() string {
return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName())) return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
} }
// GetRepoAbsoluteLink returns the absolute link to action repository.
func (a *Action) GetRepoAbsoluteLink() string {
return setting.AppURL + url.PathEscape(a.GetRepoUserName()) + "/" + url.PathEscape(a.GetRepoName())
}
// GetRepositoryFromMatch returns a *repo_model.Repository from a username and repo strings // GetRepositoryFromMatch returns a *repo_model.Repository from a username and repo strings
func GetRepositoryFromMatch(ownerName, repoName string) (*repo_model.Repository, error) { func GetRepositoryFromMatch(ownerName, repoName string) (*repo_model.Repository, error) {
var err error var err error

View file

@ -9,6 +9,7 @@ import (
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issue_model "code.gitea.io/gitea/models/issues"
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"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@ -19,7 +20,7 @@ import (
func TestAction_GetRepoPath(t *testing.T) { func TestAction_GetRepoPath(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{}).(*repo_model.Repository) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
action := &Action{RepoID: repo.ID} action := &Action{RepoID: repo.ID}
assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath()) assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath())
@ -27,12 +28,15 @@ func TestAction_GetRepoPath(t *testing.T) {
func TestAction_GetRepoLink(t *testing.T) { func TestAction_GetRepoLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{}).(*repo_model.Repository) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
action := &Action{RepoID: repo.ID} comment := unittest.AssertExistsAndLoadBean(t, &issue_model.Comment{ID: 2}).(*issue_model.Comment)
action := &Action{RepoID: repo.ID, CommentID: comment.ID}
setting.AppSubURL = "/suburl" setting.AppSubURL = "/suburl"
expected := path.Join(setting.AppSubURL, owner.Name, repo.Name) expected := path.Join(setting.AppSubURL, owner.Name, repo.Name)
assert.Equal(t, expected, action.GetRepoLink()) assert.Equal(t, expected, action.GetRepoLink())
assert.Equal(t, repo.HTMLURL(), action.GetRepoAbsoluteLink())
assert.Equal(t, comment.HTMLURL(), action.GetCommentLink())
} }
func TestGetFeeds(t *testing.T) { func TestGetFeeds(t *testing.T) {

View file

@ -10,6 +10,7 @@ import (
"encoding/base32" "encoding/base32"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"net"
"net/url" "net/url"
"strings" "strings"
@ -56,6 +57,18 @@ func (app *OAuth2Application) PrimaryRedirectURI() string {
// ContainsRedirectURI checks if redirectURI is allowed for app // ContainsRedirectURI checks if redirectURI is allowed for app
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
uri, err := url.Parse(redirectURI)
// ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
if err == nil && uri.Scheme == "http" && uri.Port() != "" {
ip := net.ParseIP(uri.Hostname())
if ip != nil && ip.IsLoopback() {
// strip port
uri.Host = uri.Hostname()
if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) {
return true
}
}
}
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true) return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
} }

View file

@ -42,6 +42,26 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
assert.False(t, app.ContainsRedirectURI("d")) assert.False(t, app.ContainsRedirectURI("d"))
} }
func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
app := &OAuth2Application{
RedirectURIs: []string{"http://127.0.0.1/", "http://::1/", "http://192.168.0.1/", "http://intranet/", "https://127.0.0.1/"},
}
// http loopback uris should ignore port
// https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1:3456/"))
assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
assert.True(t, app.ContainsRedirectURI("http://[::1]:3456/"))
// not http
assert.False(t, app.ContainsRedirectURI("https://127.0.0.1:3456/"))
// not loopback
assert.False(t, app.ContainsRedirectURI("http://192.168.0.1:9954/"))
assert.False(t, app.ContainsRedirectURI("http://intranet:3456/"))
// unparseable
assert.False(t, app.ContainsRedirectURI(":"))
}
func TestOAuth2Application_ValidateClientSecret(t *testing.T) { func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application) app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application)

View file

@ -181,6 +181,10 @@ func createReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro
Reaction: opts.Type, Reaction: opts.Type,
UserID: opts.DoerID, UserID: opts.DoerID,
} }
if findOpts.CommentID == 0 {
// explicit search of Issue Reactions where CommentID = 0
findOpts.CommentID = -1
}
existingR, _, err := FindReactions(ctx, findOpts) existingR, _, err := FindReactions(ctx, findOpts)
if err != nil { if err != nil {
@ -256,16 +260,23 @@ func DeleteReaction(ctx context.Context, opts *ReactionOptions) error {
CommentID: opts.CommentID, CommentID: opts.CommentID,
} }
_, err := db.GetEngine(ctx).Where("original_author_id = 0").Delete(reaction) sess := db.GetEngine(ctx).Where("original_author_id = 0")
if opts.CommentID == -1 {
reaction.CommentID = 0
sess.MustCols("comment_id")
}
_, err := sess.Delete(reaction)
return err return err
} }
// DeleteIssueReaction deletes a reaction on issue. // DeleteIssueReaction deletes a reaction on issue.
func DeleteIssueReaction(doerID, issueID int64, content string) error { func DeleteIssueReaction(doerID, issueID int64, content string) error {
return DeleteReaction(db.DefaultContext, &ReactionOptions{ return DeleteReaction(db.DefaultContext, &ReactionOptions{
Type: content, Type: content,
DoerID: doerID, DoerID: doerID,
IssueID: issueID, IssueID: issueID,
CommentID: -1,
}) })
} }

View file

@ -170,6 +170,7 @@ type FindReleasesOptions struct {
IsPreRelease util.OptionalBool IsPreRelease util.OptionalBool
IsDraft util.OptionalBool IsDraft util.OptionalBool
TagNames []string TagNames []string
HasSha1 util.OptionalBool // useful to find draft releases which are created with existing tags
} }
func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond { func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
@ -191,6 +192,13 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
if !opts.IsDraft.IsNone() { if !opts.IsDraft.IsNone() {
cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()}) cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()})
} }
if !opts.HasSha1.IsNone() {
if opts.HasSha1.IsTrue() {
cond = cond.And(builder.Neq{"sha1": ""})
} else {
cond = cond.And(builder.Eq{"sha1": ""})
}
}
return cond return cond
} }

View file

@ -605,15 +605,27 @@ func CheckRepoStats(ctx context.Context) error {
repoStatsCorrectNumStars, repoStatsCorrectNumStars,
"repository count 'num_stars'", "repository count 'num_stars'",
}, },
// Repository.NumIssues
{
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, false),
repoStatsCorrectNumIssues,
"repository count 'num_issues'",
},
// Repository.NumClosedIssues // Repository.NumClosedIssues
{ {
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, false), statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, false),
repoStatsCorrectNumClosedIssues, repoStatsCorrectNumClosedIssues,
"repository count 'num_closed_issues'", "repository count 'num_closed_issues'",
}, },
// Repository.NumPulls
{
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, true),
repoStatsCorrectNumPulls,
"repository count 'num_pulls'",
},
// Repository.NumClosedPulls // Repository.NumClosedPulls
{ {
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, true), statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, true),
repoStatsCorrectNumClosedPulls, repoStatsCorrectNumClosedPulls,
"repository count 'num_closed_pulls'", "repository count 'num_closed_pulls'",
}, },

View file

@ -1265,7 +1265,7 @@ func isUserVisibleToViewerCond(viewer *User) builder.Cond {
// IsUserVisibleToViewer check if viewer is able to see user profile // IsUserVisibleToViewer check if viewer is able to see user profile
func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool { func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
if viewer != nil && viewer.IsAdmin { if viewer != nil && (viewer.IsAdmin || viewer.ID == u.ID) {
return true return true
} }
@ -1304,7 +1304,7 @@ func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
return false return false
} }
if count < 0 { if count == 0 {
// No common organization // No common organization
return false return false
} }

View file

@ -36,20 +36,20 @@ func drawBlock(img *image.Paletted, x, y, size, angle int, points []int) {
// blank // blank
// //
// -------- // --------
// | | // | |
// | | // | |
// | | // | |
// -------- // --------
func b0(img *image.Paletted, x, y, size, angle int) {} func b0(img *image.Paletted, x, y, size, angle int) {}
// full-filled // full-filled
// //
// -------- // --------
// |######| // |######|
// |######| // |######|
// |######| // |######|
// -------- // --------
func b1(img *image.Paletted, x, y, size, angle int) { func b1(img *image.Paletted, x, y, size, angle int) {
for i := x; i < x+size; i++ { for i := x; i < x+size; i++ {
for j := y; j < y+size; j++ { for j := y; j < y+size; j++ {
@ -59,12 +59,13 @@ func b1(img *image.Paletted, x, y, size, angle int) {
} }
// a small block // a small block
// ---------- //
// | | // ----------
// | #### | // | |
// | #### | // | #### |
// | | // | #### |
// ---------- // | |
// ----------
func b2(img *image.Paletted, x, y, size, angle int) { func b2(img *image.Paletted, x, y, size, angle int) {
l := size / 4 l := size / 4
x += l x += l
@ -79,15 +80,15 @@ func b2(img *image.Paletted, x, y, size, angle int) {
// diamond // diamond
// //
// --------- // ---------
// | # | // | # |
// | ### | // | ### |
// | ##### | // | ##### |
// |#######| // |#######|
// | ##### | // | ##### |
// | ### | // | ### |
// | # | // | # |
// --------- // ---------
func b3(img *image.Paletted, x, y, size, angle int) { func b3(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, 0, []int{ drawBlock(img, x, y, size, 0, []int{
@ -101,13 +102,13 @@ func b3(img *image.Paletted, x, y, size, angle int) {
// b4 // b4
// //
// ------- // -------
// |#####| // |#####|
// |#### | // |#### |
// |### | // |### |
// |## | // |## |
// |# | // |# |
// |------ // |------
func b4(img *image.Paletted, x, y, size, angle int) { func b4(img *image.Paletted, x, y, size, angle int) {
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
0, 0, 0, 0,
@ -119,11 +120,11 @@ func b4(img *image.Paletted, x, y, size, angle int) {
// b5 // b5
// //
// --------- // ---------
// | # | // | # |
// | ### | // | ### |
// | ##### | // | ##### |
// |#######| // |#######|
func b5(img *image.Paletted, x, y, size, angle int) { func b5(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -136,11 +137,11 @@ func b5(img *image.Paletted, x, y, size, angle int) {
// b6 // b6
// //
// -------- // --------
// |### | // |### |
// |### | // |### |
// |### | // |### |
// -------- // --------
func b6(img *image.Paletted, x, y, size, angle int) { func b6(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -154,12 +155,12 @@ func b6(img *image.Paletted, x, y, size, angle int) {
// b7 italic cone // b7 italic cone
// //
// --------- // ---------
// | # | // | # |
// | ## | // | ## |
// | #####| // | #####|
// | ####| // | ####|
// |-------- // |--------
func b7(img *image.Paletted, x, y, size, angle int) { func b7(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -173,14 +174,14 @@ func b7(img *image.Paletted, x, y, size, angle int) {
// b8 three small triangles // b8 three small triangles
// //
// ----------- // -----------
// | # | // | # |
// | ### | // | ### |
// | ##### | // | ##### |
// | # # | // | # # |
// | ### ### | // | ### ### |
// |#########| // |#########|
// ----------- // -----------
func b8(img *image.Paletted, x, y, size, angle int) { func b8(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
mm := m / 2 mm := m / 2
@ -212,13 +213,13 @@ func b8(img *image.Paletted, x, y, size, angle int) {
// b9 italic triangle // b9 italic triangle
// //
// --------- // ---------
// |# | // |# |
// | #### | // | #### |
// | #####| // | #####|
// | #### | // | #### |
// | # | // | # |
// --------- // ---------
func b9(img *image.Paletted, x, y, size, angle int) { func b9(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -231,16 +232,16 @@ func b9(img *image.Paletted, x, y, size, angle int) {
// b10 // b10
// //
// ---------- // ----------
// | ####| // | ####|
// | ### | // | ### |
// | ## | // | ## |
// | # | // | # |
// |#### | // |#### |
// |### | // |### |
// |## | // |## |
// |# | // |# |
// ---------- // ----------
func b10(img *image.Paletted, x, y, size, angle int) { func b10(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -260,13 +261,13 @@ func b10(img *image.Paletted, x, y, size, angle int) {
// b11 // b11
// //
// ---------- // ----------
// |#### | // |#### |
// |#### | // |#### |
// |#### | // |#### |
// | | // | |
// | | // | |
// ---------- // ----------
func b11(img *image.Paletted, x, y, size, angle int) { func b11(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -280,13 +281,13 @@ func b11(img *image.Paletted, x, y, size, angle int) {
// b12 // b12
// //
// ----------- // -----------
// | | // | |
// | | // | |
// |#########| // |#########|
// | ##### | // | ##### |
// | # | // | # |
// ----------- // -----------
func b12(img *image.Paletted, x, y, size, angle int) { func b12(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -299,13 +300,13 @@ func b12(img *image.Paletted, x, y, size, angle int) {
// b13 // b13
// //
// ----------- // -----------
// | | // | |
// | | // | |
// | # | // | # |
// | ##### | // | ##### |
// |#########| // |#########|
// ----------- // -----------
func b13(img *image.Paletted, x, y, size, angle int) { func b13(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -318,13 +319,13 @@ func b13(img *image.Paletted, x, y, size, angle int) {
// b14 // b14
// //
// --------- // ---------
// | # | // | # |
// | ### | // | ### |
// |#### | // |#### |
// | | // | |
// | | // | |
// --------- // ---------
func b14(img *image.Paletted, x, y, size, angle int) { func b14(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -337,13 +338,13 @@ func b14(img *image.Paletted, x, y, size, angle int) {
// b15 // b15
// //
// ---------- // ----------
// |##### | // |##### |
// |### | // |### |
// |# | // |# |
// | | // | |
// | | // | |
// ---------- // ----------
func b15(img *image.Paletted, x, y, size, angle int) { func b15(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -356,14 +357,14 @@ func b15(img *image.Paletted, x, y, size, angle int) {
// b16 // b16
// //
// --------- // ---------
// | # | // | # |
// | ##### | // | ##### |
// |#######| // |#######|
// | # | // | # |
// | ##### | // | ##### |
// |#######| // |#######|
// --------- // ---------
func b16(img *image.Paletted, x, y, size, angle int) { func b16(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
drawBlock(img, x, y, size, angle, []int{ drawBlock(img, x, y, size, angle, []int{
@ -383,13 +384,13 @@ func b16(img *image.Paletted, x, y, size, angle int) {
// b17 // b17
// //
// ---------- // ----------
// |##### | // |##### |
// |### | // |### |
// |# | // |# |
// | ##| // | ##|
// | ##| // | ##|
// ---------- // ----------
func b17(img *image.Paletted, x, y, size, angle int) { func b17(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
@ -412,13 +413,13 @@ func b17(img *image.Paletted, x, y, size, angle int) {
// b18 // b18
// //
// ---------- // ----------
// |##### | // |##### |
// |#### | // |#### |
// |### | // |### |
// |## | // |## |
// |# | // |# |
// ---------- // ----------
func b18(img *image.Paletted, x, y, size, angle int) { func b18(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
@ -432,13 +433,13 @@ func b18(img *image.Paletted, x, y, size, angle int) {
// b19 // b19
// //
// ---------- // ----------
// |########| // |########|
// |### ###| // |### ###|
// |# #| // |# #|
// |### ###| // |### ###|
// |########| // |########|
// ---------- // ----------
func b19(img *image.Paletted, x, y, size, angle int) { func b19(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
@ -473,13 +474,13 @@ func b19(img *image.Paletted, x, y, size, angle int) {
// b20 // b20
// //
// ---------- // ----------
// | ## | // | ## |
// |### | // |### |
// |## | // |## |
// |## | // |## |
// |# | // |# |
// ---------- // ----------
func b20(img *image.Paletted, x, y, size, angle int) { func b20(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -494,13 +495,13 @@ func b20(img *image.Paletted, x, y, size, angle int) {
// b21 // b21
// //
// ---------- // ----------
// | #### | // | #### |
// |## #####| // |## #####|
// |## ##| // |## ##|
// |## | // |## |
// |# | // |# |
// ---------- // ----------
func b21(img *image.Paletted, x, y, size, angle int) { func b21(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -522,13 +523,13 @@ func b21(img *image.Paletted, x, y, size, angle int) {
// b22 // b22
// //
// ---------- // ----------
// | #### | // | #### |
// |## ### | // |## ### |
// |## ##| // |## ##|
// |## ##| // |## ##|
// |# #| // |# #|
// ---------- // ----------
func b22(img *image.Paletted, x, y, size, angle int) { func b22(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -550,13 +551,13 @@ func b22(img *image.Paletted, x, y, size, angle int) {
// b23 // b23
// //
// ---------- // ----------
// | #######| // | #######|
// |### #| // |### #|
// |## | // |## |
// |## | // |## |
// |# | // |# |
// ---------- // ----------
func b23(img *image.Paletted, x, y, size, angle int) { func b23(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -578,13 +579,13 @@ func b23(img *image.Paletted, x, y, size, angle int) {
// b24 // b24
// //
// ---------- // ----------
// | ## ###| // | ## ###|
// |### ###| // |### ###|
// |## ## | // |## ## |
// |## ## | // |## ## |
// |# # | // |# # |
// ---------- // ----------
func b24(img *image.Paletted, x, y, size, angle int) { func b24(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -606,13 +607,13 @@ func b24(img *image.Paletted, x, y, size, angle int) {
// b25 // b25
// //
// ---------- // ----------
// |# #| // |# #|
// |## ###| // |## ###|
// |## ## | // |## ## |
// |###### | // |###### |
// |#### | // |#### |
// ---------- // ----------
func b25(img *image.Paletted, x, y, size, angle int) { func b25(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -634,13 +635,13 @@ func b25(img *image.Paletted, x, y, size, angle int) {
// b26 // b26
// //
// ---------- // ----------
// |# #| // |# #|
// |### ###| // |### ###|
// | #### | // | #### |
// |### ###| // |### ###|
// |# #| // |# #|
// ---------- // ----------
func b26(img *image.Paletted, x, y, size, angle int) { func b26(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4
@ -676,13 +677,13 @@ func b26(img *image.Paletted, x, y, size, angle int) {
// b27 // b27
// //
// ---------- // ----------
// |########| // |########|
// |## ###| // |## ###|
// |# #| // |# #|
// |### ##| // |### ##|
// |########| // |########|
// ---------- // ----------
func b27(img *image.Paletted, x, y, size, angle int) { func b27(img *image.Paletted, x, y, size, angle int) {
m := size / 2 m := size / 2
q := size / 4 q := size / 4

View file

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
) )
@ -52,47 +53,11 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
Owner: ctx.ContextUser, Owner: ctx.ContextUser,
} }
if ctx.Package.Owner.IsOrganization() { var err error
org := organization.OrgFromUser(ctx.Package.Owner) ctx.Package.AccessMode, err = determineAccessMode(ctx)
if err != nil {
// 1. Get user max authorize level for the org (may be none, if user is not member of the org) errCb(http.StatusInternalServerError, "determineAccessMode", err)
if ctx.Doer != nil { return
var err error
ctx.Package.AccessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
if err != nil {
errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err)
return
}
// If access mode is less than write check every team for more permissions
if ctx.Package.AccessMode < perm.AccessModeWrite {
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
if err != nil {
errCb(http.StatusInternalServerError, "GetUserOrgTeams", err)
return
}
for _, t := range teams {
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
if ctx.Package.AccessMode < perm {
ctx.Package.AccessMode = perm
}
}
}
}
// 2. If authorize level is none, check if org is visible to user
if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
ctx.Package.AccessMode = perm.AccessModeRead
}
} else {
if ctx.Doer != nil && !ctx.Doer.IsGhost() {
// 1. Check if user is package owner
if ctx.Doer.ID == ctx.Package.Owner.ID {
ctx.Package.AccessMode = perm.AccessModeOwner
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
ctx.Package.AccessMode = perm.AccessModeRead
}
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
ctx.Package.AccessMode = perm.AccessModeRead
}
} }
packageType := ctx.Params("type") packageType := ctx.Params("type")
@ -117,6 +82,57 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
} }
} }
func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
accessMode := perm.AccessModeNone
if setting.Service.RequireSignInView && ctx.Doer == nil {
return accessMode, nil
}
if ctx.Package.Owner.IsOrganization() {
org := organization.OrgFromUser(ctx.Package.Owner)
// 1. Get user max authorize level for the org (may be none, if user is not member of the org)
if ctx.Doer != nil {
var err error
accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
if err != nil {
return accessMode, err
}
// If access mode is less than write check every team for more permissions
if accessMode < perm.AccessModeWrite {
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
if err != nil {
return accessMode, err
}
for _, t := range teams {
perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
if accessMode < perm {
accessMode = perm
}
}
}
}
// 2. If authorize level is none, check if org is visible to user
if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
accessMode = perm.AccessModeRead
}
} else {
if ctx.Doer != nil && !ctx.Doer.IsGhost() {
// 1. Check if user is package owner
if ctx.Doer.ID == ctx.Package.Owner.ID {
accessMode = perm.AccessModeOwner
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
accessMode = perm.AccessModeRead
}
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
accessMode = perm.AccessModeRead
}
}
return accessMode, nil
}
// PackageContexter initializes a package context for a request. // PackageContexter initializes a package context for a request.
func PackageContexter() func(next http.Handler) http.Handler { func PackageContexter() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {

View file

@ -118,7 +118,7 @@ type CanCommitToBranchResults struct {
} }
// CanCommitToBranch returns true if repository is editable and user has proper access level // CanCommitToBranch returns true if repository is editable and user has proper access level
// and branch is not protected for push // and branch is not protected for push
func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) { func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
protectedBranch, err := git_model.GetProtectedBranchBy(ctx, r.Repository.ID, r.BranchName) protectedBranch, err := git_model.GetProtectedBranchBy(ctx, r.Repository.ID, r.BranchName)
if err != nil { if err != nil {
@ -524,7 +524,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
} }
ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
IncludeTags: true, IncludeDrafts: true,
IncludeTags: true,
HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
}) })
if err != nil { if err != nil {
ctx.ServerError("GetReleaseCountByRepoID", err) ctx.ServerError("GetReleaseCountByRepoID", err)

View file

@ -322,7 +322,7 @@ func TestGuessDelimiter(t *testing.T) {
}, },
// case 3 - tab delimited // case 3 - tab delimited
{ {
csv: "1 2", csv: "1\t2",
expectedDelimiter: '\t', expectedDelimiter: '\t',
}, },
// case 4 - pipe delimited // case 4 - pipe delimited

File diff suppressed because it is too large Load diff

View file

@ -40,6 +40,7 @@ type Command struct {
parentContext context.Context parentContext context.Context
desc string desc string
globalArgsLength int globalArgsLength int
brokenArgs []string
} }
func (c *Command) String() string { func (c *Command) String() string {
@ -50,6 +51,7 @@ func (c *Command) String() string {
} }
// NewCommand creates and returns a new Git Command based on given command and arguments. // NewCommand creates and returns a new Git Command based on given command and arguments.
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommand(ctx context.Context, args ...string) *Command { func NewCommand(ctx context.Context, args ...string) *Command {
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it // Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
cargs := make([]string, len(globalCommandArgs)) cargs := make([]string, len(globalCommandArgs))
@ -63,11 +65,13 @@ func NewCommand(ctx context.Context, args ...string) *Command {
} }
// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args // NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandNoGlobals(args ...string) *Command { func NewCommandNoGlobals(args ...string) *Command {
return NewCommandContextNoGlobals(DefaultContext, args...) return NewCommandContextNoGlobals(DefaultContext, args...)
} }
// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args // NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command { func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command {
return &Command{ return &Command{
name: GitExecutable, name: GitExecutable,
@ -89,12 +93,28 @@ func (c *Command) SetDescription(desc string) *Command {
return c return c
} }
// AddArguments adds new argument(s) to the command. // AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted.
// User-provided arguments should be passed to AddDynamicArguments instead.
func (c *Command) AddArguments(args ...string) *Command { func (c *Command) AddArguments(args ...string) *Command {
c.args = append(c.args, args...) c.args = append(c.args, args...)
return c return c
} }
// AddDynamicArguments adds new dynamic argument(s) to the command.
// The arguments may come from user input and can not be trusted, so no leading '-' is allowed to avoid passing options
func (c *Command) AddDynamicArguments(args ...string) *Command {
for _, arg := range args {
if arg != "" && arg[0] == '-' {
c.brokenArgs = append(c.brokenArgs, arg)
}
}
if len(c.brokenArgs) != 0 {
return c
}
c.args = append(c.args, args...)
return c
}
// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored. // RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored.
type RunOpts struct { type RunOpts struct {
Env []string Env []string
@ -138,8 +158,14 @@ func CommonCmdServEnvs() []string {
return commonBaseEnvs() return commonBaseEnvs()
} }
var ErrBrokenCommand = errors.New("git command is broken")
// Run runs the command with the RunOpts // Run runs the command with the RunOpts
func (c *Command) Run(opts *RunOpts) error { func (c *Command) Run(opts *RunOpts) error {
if len(c.brokenArgs) != 0 {
log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " "))
return ErrBrokenCommand
}
if opts == nil { if opts == nil {
opts = &RunOpts{} opts = &RunOpts{}
} }

View file

@ -26,4 +26,19 @@ func TestRunWithContextStd(t *testing.T) {
assert.Contains(t, err.Error(), "exit status 129 - unknown option:") assert.Contains(t, err.Error(), "exit status 129 - unknown option:")
assert.Empty(t, stdout) assert.Empty(t, stdout)
} }
cmd = NewCommand(context.Background())
cmd.AddDynamicArguments("-test")
assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
cmd = NewCommand(context.Background())
cmd.AddDynamicArguments("--test")
assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
subCmd := "version"
cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
stdout, stderr, err = cmd.RunStdString(&RunOpts{})
assert.NoError(t, err)
assert.Empty(t, stderr)
assert.Contains(t, stdout, "git version")
} }

View file

@ -163,7 +163,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
// CommitsCountFiles returns number of total commits of until given revision. // CommitsCountFiles returns number of total commits of until given revision.
func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath []string) (int64, error) { func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath []string) (int64, error) {
cmd := NewCommand(ctx, "rev-list", "--count") cmd := NewCommand(ctx, "rev-list", "--count")
cmd.AddArguments(revision...) cmd.AddDynamicArguments(revision...)
if len(relpath) > 0 { if len(relpath) > 0 {
cmd.AddArguments("--") cmd.AddArguments("--")
cmd.AddArguments(relpath...) cmd.AddArguments(relpath...)

View file

@ -68,8 +68,7 @@ func NewParser(r io.Reader, format Format) *Parser {
// //
// It could, for example return something like: // It could, for example return something like:
// //
// { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" } // { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" }
//
func (p *Parser) Next() map[string]string { func (p *Parser) Next() map[string]string {
if !p.scanner.Scan() { if !p.scanner.Scan() {
return nil return nil
@ -89,8 +88,7 @@ func (p *Parser) Err() error {
// parseRef parses out all key-value pairs from a single reference block, such as // parseRef parses out all key-value pairs from a single reference block, such as
// //
// "objecttype tag\0refname:short v1.16.4\0object f460b7543ed500e49c133c2cd85c8c55ee9dbe27" // "objecttype tag\0refname:short v1.16.4\0object f460b7543ed500e49c133c2cd85c8c55ee9dbe27"
//
func (p *Parser) parseRef(refBlock string) (map[string]string, error) { func (p *Parser) parseRef(refBlock string) (map[string]string, error) {
if refBlock == "" { if refBlock == "" {
// must be at EOF // must be at EOF

View file

@ -44,7 +44,7 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
case "160000": case "160000":
entry.entryMode = EntryModeCommit entry.entryMode = EntryModeCommit
pos += 14 // skip over "160000 object " pos += 14 // skip over "160000 object "
case "040000": case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
entry.entryMode = EntryModeTree entry.entryMode = EntryModeTree
pos += 12 // skip over "040000 tree " pos += 12 // skip over "040000 tree "
default: default:
@ -119,7 +119,7 @@ loop:
entry.entryMode = EntryModeSymlink entry.entryMode = EntryModeSymlink
case "160000": case "160000":
entry.entryMode = EntryModeCommit entry.entryMode = EntryModeCommit
case "40000": case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons
entry.entryMode = EntryModeTree entry.entryMode = EntryModeTree
default: default:
log.Debug("Unknown mode: %v", string(mode)) log.Debug("Unknown mode: %v", string(mode))

View file

@ -191,8 +191,8 @@ func (c *CheckAttributeReader) Run() error {
// CheckPath check attr for given path // CheckPath check attr for given path
func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err error) { func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err error) {
defer func() { defer func() {
if err != nil { if err != nil && err != c.ctx.Err() {
log.Error("CheckPath returns error: %v", err) log.Error("Unexpected error when checking path %s in %s. Error: %v", path, c.Repo.Path, err)
} }
}() }()

View file

@ -158,7 +158,7 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// add previous arguments except for --grep and --all // add previous arguments except for --grep and --all
hashCmd.AddArguments(args...) hashCmd.AddArguments(args...)
// add keyword as <commit> // add keyword as <commit>
hashCmd.AddArguments(v) hashCmd.AddDynamicArguments(v)
// search with given constraints for commit matching sha hash of v // search with given constraints for commit matching sha hash of v
hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path}) hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
@ -208,14 +208,15 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}() }()
go func() { go func() {
stderr := strings.Builder{} stderr := strings.Builder{}
err := NewCommand(repo.Ctx, "log", revision, "--follow", gitCmd := NewCommand(repo.Ctx, "log", prettyLogFormat, "--follow",
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page), "--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page))
prettyLogFormat, "--", file). gitCmd.AddDynamicArguments(revision)
Run(&RunOpts{ gitCmd.AddArguments("--", file)
Dir: repo.Path, err := gitCmd.Run(&RunOpts{
Stdout: stdoutWriter, Dir: repo.Path,
Stderr: &stderr, Stdout: stdoutWriter,
}) Stderr: &stderr,
})
if err != nil { if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String())) _ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
} else { } else {

View file

@ -59,15 +59,15 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close() _ = stdoutWriter.Close()
}() }()
args := []string{"log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since))
if len(branch) == 0 { if len(branch) == 0 {
args = append(args, "--branches=*") gitCmd.AddArguments("--branches=*")
} else { } else {
args = append(args, "--first-parent", branch) gitCmd.AddArguments("--first-parent").AddDynamicArguments(branch)
} }
stderr := new(strings.Builder) stderr := new(strings.Builder)
err = NewCommand(repo.Ctx, args...).Run(&RunOpts{ err = gitCmd.Run(&RunOpts{
Env: []string{}, Env: []string{},
Dir: repo.Path, Dir: repo.Path,
Stdout: stdoutWriter, Stdout: stdoutWriter,

View file

@ -19,8 +19,10 @@ import (
type Signature = object.Signature type Signature = object.Signature
// Helper to get a signature from the commit line, which looks like these: // Helper to get a signature from the commit line, which looks like these:
// author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200 //
// author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200 // author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
// author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200
//
// but without the "author " at the beginning (this method should) // but without the "author " at the beginning (this method should)
// be used for author and committer. // be used for author and committer.
// //

View file

@ -37,8 +37,10 @@ func (s *Signature) Decode(b []byte) {
} }
// Helper to get a signature from the commit line, which looks like these: // Helper to get a signature from the commit line, which looks like these:
// author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200 //
// author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200 // author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200
// author Patrick Gundlach <gundlach@speedata.de> Thu, 07 Apr 2005 22:13:13 +0200
//
// but without the "author " at the beginning (this method should) // but without the "author " at the beginning (this method should)
// be used for author and committer. // be used for author and committer.
func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {

View file

@ -24,19 +24,17 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
page = 1 page = 1
} }
args := make([]string, 0, 12+len(branches)+len(files)) graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
args = append(args, "--graph", "--date-order", "--decorate=full")
if hidePRRefs { if hidePRRefs {
args = append(args, "--exclude="+git.PullPrefix+"*") graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
} }
if len(branches) == 0 { if len(branches) == 0 {
args = append(args, "--all") graphCmd.AddArguments("--all")
} }
args = append(args, graphCmd.AddArguments(
"-C", "-C",
"-M", "-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page), fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
@ -44,15 +42,12 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
fmt.Sprintf("--pretty=format:%s", format)) fmt.Sprintf("--pretty=format:%s", format))
if len(branches) > 0 { if len(branches) > 0 {
args = append(args, branches...) graphCmd.AddDynamicArguments(branches...)
} }
args = append(args, "--")
if len(files) > 0 { if len(files) > 0 {
args = append(args, files...) graphCmd.AddArguments("--")
graphCmd.AddArguments(files...)
} }
graphCmd := git.NewCommand(r.Ctx, "log")
graphCmd.AddArguments(args...)
graph := NewGraph() graph := NewGraph()
stderr := new(strings.Builder) stderr := new(strings.Builder)

View file

@ -93,6 +93,7 @@ func NewFileLogger() LoggerProvider {
// Init file logger with json config. // Init file logger with json config.
// config like: // config like:
//
// { // {
// "filename":"log/gogs.log", // "filename":"log/gogs.log",
// "maxsize":1<<30, // "maxsize":1<<30,

View file

@ -48,6 +48,7 @@ func NewSMTPLogger() LoggerProvider {
// Init smtp writer with json config. // Init smtp writer with json config.
// config like: // config like:
//
// { // {
// "Username":"example@gmail.com", // "Username":"example@gmail.com",
// "password:"password", // "password:"password",

View file

@ -7,6 +7,7 @@ package markup_test
import ( import (
"context" "context"
"io" "io"
"os"
"strings" "strings"
"testing" "testing"
@ -32,6 +33,7 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil { if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err) log.Fatal("git init failed, err: %v", err)
} }
os.Exit(m.Run())
} }
func TestRender_Commits(t *testing.T) { func TestRender_Commits(t *testing.T) {
@ -336,7 +338,7 @@ func TestRender_emoji(t *testing.T) {
`<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span><span class="emoji" aria-label="grinning face with smiling eyes">😄</span> 2 emoji next to each other</p>`) `<p>Some text with <span class="emoji" aria-label="grinning face with smiling eyes">😄</span><span class="emoji" aria-label="grinning face with smiling eyes">😄</span> 2 emoji next to each other</p>`)
test( test(
"😎🤪🔐🤑❓", "😎🤪🔐🤑❓",
`<p><span class="emoji" aria-label="smiling face with sunglasses">😎</span><span class="emoji" aria-label="zany face">🤪</span><span class="emoji" aria-label="locked with key">🔐</span><span class="emoji" aria-label="money-mouth face">🤑</span><span class="emoji" aria-label="question mark">❓</span></p>`) `<p><span class="emoji" aria-label="smiling face with sunglasses">😎</span><span class="emoji" aria-label="zany face">🤪</span><span class="emoji" aria-label="locked with key">🔐</span><span class="emoji" aria-label="money-mouth face">🤑</span><span class="emoji" aria-label="red question mark">❓</span></p>`)
// should match nothing // should match nothing
test( test(

View file

@ -6,6 +6,7 @@ package markdown_test
import ( import (
"context" "context"
"os"
"strings" "strings"
"testing" "testing"
@ -37,6 +38,7 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil { if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err) log.Fatal("git init failed, err: %v", err)
} }
os.Exit(m.Run())
} }
func TestRender_StandardLinks(t *testing.T) { func TestRender_StandardLinks(t *testing.T) {

View file

@ -141,7 +141,7 @@ func (r *stripRenderer) AddOptions(...renderer.Option) {
} }
// StripMarkdown parses markdown content by removing all markup and code blocks // StripMarkdown parses markdown content by removing all markup and code blocks
// in order to extract links and other references // in order to extract links and other references
func StripMarkdown(rawBytes []byte) (string, []string) { func StripMarkdown(rawBytes []byte) (string, []string) {
buf, links := StripMarkdownBytes(rawBytes) buf, links := StripMarkdownBytes(rawBytes)
return string(buf), links return string(buf), links
@ -153,7 +153,7 @@ var (
) )
// StripMarkdownBytes parses markdown content by removing all markup and code blocks // StripMarkdownBytes parses markdown content by removing all markup and code blocks
// in order to extract links and other references // in order to extract links and other references
func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) { func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) {
once.Do(func() { once.Do(func() {
gdMarkdown := goldmark.New( gdMarkdown := goldmark.New(

View file

@ -95,7 +95,9 @@ func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
if i := strings.Index(cmd, "#(nop) "); i != -1 { if i := strings.Index(cmd, "#(nop) "); i != -1 {
cmd = strings.TrimSpace(cmd[i+7:]) cmd = strings.TrimSpace(cmd[i+7:])
} }
imageLayers = append(imageLayers, cmd) if cmd != "" {
imageLayers = append(imageLayers, cmd)
}
} }
metadata := &Metadata{ metadata := &Metadata{

View file

@ -25,7 +25,7 @@ func DumpMemProfileForUsername(pprofDataPath, username string) error {
} }
// DumpCPUProfileForUsername dumps a CPU profile at pprofDataPath as cpuprofile_<username>_<temporary id> // DumpCPUProfileForUsername dumps a CPU profile at pprofDataPath as cpuprofile_<username>_<temporary id>
// it returns the stop function which stops, writes and closes the CPU profile file // the stop function it returns stops, writes and closes the CPU profile file
func DumpCPUProfileForUsername(pprofDataPath, username string) (func(), error) { func DumpCPUProfileForUsername(pprofDataPath, username string) (func(), error) {
f, err := os.CreateTemp(pprofDataPath, fmt.Sprintf("cpuprofile_%s_", username)) f, err := os.CreateTemp(pprofDataPath, fmt.Sprintf("cpuprofile_%s_", username))
if err != nil { if err != nil {

View file

@ -31,8 +31,8 @@ import (
) )
/* /*
GitHub, GitLab, Gogs: *.wiki.git GitHub, GitLab, Gogs: *.wiki.git
BitBucket: *.git/wiki BitBucket: *.git/wiki
*/ */
var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"} var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"}

View file

@ -154,6 +154,7 @@ func NewFuncMap() []template.FuncMap {
"DiffTypeToStr": DiffTypeToStr, "DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr, "DiffLineTypeToStr": DiffLineTypeToStr,
"ShortSha": base.ShortSha, "ShortSha": base.ShortSha,
"MD5": base.EncodeMD5,
"ActionContent2Commits": ActionContent2Commits, "ActionContent2Commits": ActionContent2Commits,
"PathEscape": url.PathEscape, "PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments, "PathEscapeSegments": util.PathEscapeSegments,

View file

@ -41,14 +41,14 @@ func NewLocaleStore() *LocaleStore {
} }
// AddLocaleByIni adds locale by ini into the store // AddLocaleByIni adds locale by ini into the store
func (ls *LocaleStore) AddLocaleByIni(langName, langDesc string, localeFile interface{}, otherLocaleFiles ...interface{}) error { func (ls *LocaleStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error {
if _, ok := ls.localeMap[langName]; ok { if _, ok := ls.localeMap[langName]; ok {
return ErrLocaleAlreadyExist return ErrLocaleAlreadyExist
} }
iniFile, err := ini.LoadSources(ini.LoadOptions{ iniFile, err := ini.LoadSources(ini.LoadOptions{
IgnoreInlineComment: true, IgnoreInlineComment: true,
UnescapeValueCommentSymbols: true, UnescapeValueCommentSymbols: true,
}, localeFile, otherLocaleFiles...) }, source, moreSource)
if err == nil { if err == nil {
iniFile.BlockMode = false iniFile.BlockMode = false
lc := &locale{store: ls, langName: langName, langDesc: langDesc, messages: iniFile} lc := &locale{store: ls, langName: langName, langDesc: langDesc, messages: iniFile}

View file

@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func Test_Tr(t *testing.T) { func TestLocaleStore(t *testing.T) {
testData1 := []byte(` testData1 := []byte(`
.dot.name = Dot Name .dot.name = Dot Name
fmt = %[1]s %[2]s fmt = %[1]s %[2]s
@ -28,8 +28,8 @@ sub = Changed Sub String
`) `)
ls := NewLocaleStore() ls := NewLocaleStore()
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1)) assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil))
assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2)) assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil))
ls.SetDefaultLang("lang1") ls.SetDefaultLang("lang1")
result := ls.Tr("lang1", "fmt", "a", "b") result := ls.Tr("lang1", "fmt", "a", "b")
@ -54,3 +54,21 @@ sub = Changed Sub String
assert.Equal(t, []string{"lang1", "lang2"}, langs) assert.Equal(t, []string{"lang1", "lang2"}, langs)
assert.Equal(t, []string{"Lang1", "Lang2"}, descs) assert.Equal(t, []string{"Lang1", "Lang2"}, descs)
} }
func TestLocaleStoreMoreSource(t *testing.T) {
testData1 := []byte(`
a=11
b=12
`)
testData2 := []byte(`
b=21
c=22
`)
ls := NewLocaleStore()
assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2))
assert.Equal(t, "11", ls.Tr("lang1", "a"))
assert.Equal(t, "21", ls.Tr("lang1", "b"))
assert.Equal(t, "22", ls.Tr("lang1", "c"))
}

View file

@ -60,9 +60,9 @@ func InitLocales() {
log.Fatal("Failed to list locale files: %v", err) log.Fatal("Failed to list locale files: %v", err)
} }
localFiles := make(map[string][]byte, len(localeNames)) localeData := make(map[string][]byte, len(localeNames))
for _, name := range localeNames { for _, name := range localeNames {
localFiles[name], err = options.Locale(name) localeData[name], err = options.Locale(name)
if err != nil { if err != nil {
log.Fatal("Failed to load %s locale file. %v", name, err) log.Fatal("Failed to load %s locale file. %v", name, err)
} }
@ -75,8 +75,16 @@ func InitLocales() {
matcher = language.NewMatcher(supportedTags) matcher = language.NewMatcher(supportedTags)
for i := range setting.Names { for i := range setting.Names {
var localeDataBase []byte
if i == 0 && setting.Langs[0] != "en-US" {
// Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback.
localeDataBase = localeData["locale_en-US.ini"]
if localeDataBase == nil {
log.Fatal("Failed to load locale_en-US.ini file.")
}
}
key := "locale_" + setting.Langs[i] + ".ini" key := "locale_" + setting.Langs[i] + ".ini"
if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil { if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil {
log.Error("Failed to set messages to %s: %v", setting.Langs[i], err) log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
} }
} }

View file

@ -17,13 +17,13 @@ func isSnakeCaseLowerOrNumber(c byte) bool {
// ToSnakeCase convert the input string to snake_case format. // ToSnakeCase convert the input string to snake_case format.
// //
// Some samples. // Some samples.
// "FirstName" => "first_name"
// "HTTPServer" => "http_server"
// "NoHTTPS" => "no_https"
// "GO_PATH" => "go_path"
// "GO PATH" => "go_path" // space is converted to underscore.
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
// //
// "FirstName" => "first_name"
// "HTTPServer" => "http_server"
// "NoHTTPS" => "no_https"
// "GO_PATH" => "go_path"
// "GO PATH" => "go_path" // space is converted to underscore.
// "GO-PATH" => "go_path" // hyphen is converted to underscore.
func ToSnakeCase(input string) string { func ToSnakeCase(input string) string {
if len(input) == 0 { if len(input) == 0 {
return "" return ""

View file

@ -66,7 +66,7 @@ func Routes() *web.Route {
r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata) r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata)
r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile) r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile)
r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage) r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/conan", func() { r.Group("/conan", func() {
r.Group("/v1", func() { r.Group("/v1", func() {
r.Get("/ping", conan.Ping) r.Get("/ping", conan.Ping)
@ -154,7 +154,7 @@ func Routes() *web.Route {
}, conan.ExtractPathParameters) }, conan.ExtractPathParameters)
}) })
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/generic", func() { r.Group("/generic", func() {
r.Group("/{packagename}/{packageversion}/{filename}", func() { r.Group("/{packagename}/{packageversion}/{filename}", func() {
r.Get("", generic.DownloadPackageFile) r.Get("", generic.DownloadPackageFile)
@ -163,33 +163,35 @@ func Routes() *web.Route {
r.Delete("", generic.DeletePackage) r.Delete("", generic.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/helm", func() { r.Group("/helm", func() {
r.Get("/index.yaml", helm.Index) r.Get("/index.yaml", helm.Index)
r.Get("/{filename}", helm.DownloadPackageFile) r.Get("/{filename}", helm.DownloadPackageFile)
r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage) r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/maven", func() { r.Group("/maven", func() {
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile) r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile)
r.Get("/*", maven.DownloadPackageFile) r.Get("/*", maven.DownloadPackageFile)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/nuget", func() { r.Group("/nuget", func() {
r.Get("/index.json", nuget.ServiceIndex) r.Get("/index.json", nuget.ServiceIndex) // Needs to be unauthenticated for the NuGet client.
r.Get("/query", nuget.SearchService)
r.Group("/registration/{id}", func() {
r.Get("/index.json", nuget.RegistrationIndex)
r.Get("/{version}", nuget.RegistrationLeaf)
})
r.Group("/package/{id}", func() {
r.Get("/index.json", nuget.EnumeratePackageVersions)
r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
})
r.Group("", func() { r.Group("", func() {
r.Put("/", nuget.UploadPackage) r.Get("/query", nuget.SearchService)
r.Put("/symbolpackage", nuget.UploadSymbolPackage) r.Group("/registration/{id}", func() {
r.Delete("/{id}/{version}", nuget.DeletePackage) r.Get("/index.json", nuget.RegistrationIndex)
}, reqPackageAccess(perm.AccessModeWrite)) r.Get("/{version}", nuget.RegistrationLeaf)
r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) })
r.Group("/package/{id}", func() {
r.Get("/index.json", nuget.EnumeratePackageVersions)
r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
})
r.Group("", func() {
r.Put("/", nuget.UploadPackage)
r.Put("/symbolpackage", nuget.UploadSymbolPackage)
r.Delete("/{id}/{version}", nuget.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite))
r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile)
}, reqPackageAccess(perm.AccessModeRead))
}) })
r.Group("/npm", func() { r.Group("/npm", func() {
r.Group("/@{scope}/{id}", func() { r.Group("/@{scope}/{id}", func() {
@ -216,12 +218,12 @@ func Routes() *web.Route {
r.Delete("", npm.DeletePackageTag) r.Delete("", npm.DeletePackageTag)
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}) })
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/pypi", func() { r.Group("/pypi", func() {
r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile) r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile)
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile) r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
r.Get("/simple/{id}", pypi.PackageMetadata) r.Get("/simple/{id}", pypi.PackageMetadata)
}) }, reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() { r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages) r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest) r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
@ -233,7 +235,7 @@ func Routes() *web.Route {
r.Delete("/yank", rubygems.DeletePackage) r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite)) }, reqPackageAccess(perm.AccessModeWrite))
}) })
}, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead)) }, context_service.UserAssignmentWeb(), context.PackageAssignment())
return r return r
} }

View file

@ -342,8 +342,7 @@ func uploadFile(ctx *context.Context, fileFilter stringSet, fileKey string) {
Name: rref.Name, Name: rref.Name,
Version: rref.Version, Version: rref.Version,
}, },
SemverCompatible: true, Creator: ctx.Doer,
Creator: ctx.Doer,
} }
pfci := &packages_service.PackageFileCreationInfo{ pfci := &packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{ PackageFileInfo: packages_service.PackageFileInfo{

View file

@ -24,7 +24,7 @@ import (
// https://www.python.org/dev/peps/pep-0503/#normalized-names // https://www.python.org/dev/peps/pep-0503/#normalized-names
var normalizer = strings.NewReplacer(".", "-", "_", "-") var normalizer = strings.NewReplacer(".", "-", "_", "-")
var nameMatcher = regexp.MustCompile(`\A[a-z0-9\.\-_]+\z`) var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`)
// https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions // https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
var versionMatcher = regexp.MustCompile(`^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$`) var versionMatcher = regexp.MustCompile(`^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$`)

View file

@ -7,59 +7,59 @@
// //
// This documentation describes the Gitea API. // This documentation describes the Gitea API.
// //
// Schemes: http, https // Schemes: http, https
// BasePath: /api/v1 // BasePath: /api/v1
// Version: {{AppVer | JSEscape | Safe}} // Version: {{AppVer | JSEscape | Safe}}
// License: MIT http://opensource.org/licenses/MIT // License: MIT http://opensource.org/licenses/MIT
// //
// Consumes: // Consumes:
// - application/json // - application/json
// - text/plain // - text/plain
// //
// Produces: // Produces:
// - application/json // - application/json
// - text/html // - text/html
// //
// Security: // Security:
// - BasicAuth : // - BasicAuth :
// - Token : // - Token :
// - AccessToken : // - AccessToken :
// - AuthorizationHeaderToken : // - AuthorizationHeaderToken :
// - SudoParam : // - SudoParam :
// - SudoHeader : // - SudoHeader :
// - TOTPHeader : // - TOTPHeader :
// //
// SecurityDefinitions: // SecurityDefinitions:
// BasicAuth: // BasicAuth:
// type: basic // type: basic
// Token: // Token:
// type: apiKey // type: apiKey
// name: token // name: token
// in: query // in: query
// AccessToken: // AccessToken:
// type: apiKey // type: apiKey
// name: access_token // name: access_token
// in: query // in: query
// AuthorizationHeaderToken: // AuthorizationHeaderToken:
// type: apiKey // type: apiKey
// name: Authorization // name: Authorization
// in: header // in: header
// description: API tokens must be prepended with "token" followed by a space. // description: API tokens must be prepended with "token" followed by a space.
// SudoParam: // SudoParam:
// type: apiKey // type: apiKey
// name: sudo // name: sudo
// in: query // in: query
// description: Sudo API request as the user provided as the key. Admin privileges are required. // description: Sudo API request as the user provided as the key. Admin privileges are required.
// SudoHeader: // SudoHeader:
// type: apiKey // type: apiKey
// name: Sudo // name: Sudo
// in: header // in: header
// description: Sudo API request as the user provided as the key. Admin privileges are required. // description: Sudo API request as the user provided as the key. Admin privileges are required.
// TOTPHeader: // TOTPHeader:
// type: apiKey // type: apiKey
// name: X-GITEA-OTP // name: X-GITEA-OTP
// in: header // in: header
// description: Must be used in combination with BasicAuth if two-factor authentication is enabled. // description: Must be used in combination with BasicAuth if two-factor authentication is enabled.
// //
// swagger:meta // swagger:meta
package v1 package v1

View file

@ -34,7 +34,7 @@ func GetGitAllRefs(ctx *context.APIContext) {
// required: true // required: true
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/Reference" // # "$ref": "#/responses/Reference" TODO: swagger doesnt support different output formats by ref
// "$ref": "#/responses/ReferenceList" // "$ref": "#/responses/ReferenceList"
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"
@ -67,7 +67,7 @@ func GetGitRefs(ctx *context.APIContext) {
// required: true // required: true
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/Reference" // # "$ref": "#/responses/Reference" TODO: swagger doesnt support different output formats by ref
// "$ref": "#/responses/ReferenceList" // "$ref": "#/responses/ReferenceList"
// "404": // "404":
// "$ref": "#/responses/notFound" // "$ref": "#/responses/notFound"

View file

@ -566,6 +566,8 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// swagger:operation GET /user/times user userCurrentTrackedTimes // swagger:operation GET /user/times user userCurrentTrackedTimes
// --- // ---
// summary: List the current user's tracked times // summary: List the current user's tracked times
// produces:
// - application/json
// parameters: // parameters:
// - name: page // - name: page
// in: query // in: query
@ -575,9 +577,6 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// in: query // in: query
// description: page size of results // description: page size of results
// type: integer // type: integer
// produces:
// - application/json
// parameters:
// - name: since // - name: since
// in: query // in: query
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format // description: Only show times updated after the given time. This is a timestamp in RFC 3339 format

View file

@ -585,7 +585,6 @@ func Edit(ctx *context.APIContext) {
// description: name of the repo to edit // description: name of the repo to edit
// type: string // type: string
// required: true // required: true
// required: true
// - name: body // - name: body
// in: body // in: body
// description: "Properties of a repo that you can edit" // description: "Properties of a repo that you can edit"

View file

@ -48,11 +48,6 @@ func AddEmail(ctx *context.APIContext) {
// produces: // produces:
// - application/json // - application/json
// parameters: // parameters:
// - name: options
// in: body
// schema:
// "$ref": "#/definitions/CreateEmailOption"
// parameters:
// - name: body // - name: body
// in: body // in: body
// schema: // schema:

View file

@ -588,7 +588,8 @@ func OIDCKeys(ctx *context.Context) {
// AccessTokenOAuth manages all access token requests by the client // AccessTokenOAuth manages all access token requests by the client
func AccessTokenOAuth(ctx *context.Context) { func AccessTokenOAuth(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.AccessTokenForm) form := *web.GetForm(ctx).(*forms.AccessTokenForm)
if form.ClientID == "" { // if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
if form.ClientID == "" || form.ClientSecret == "" {
authHeader := ctx.Req.Header.Get("Authorization") authHeader := ctx.Req.Header.Get("Authorization")
authContent := strings.SplitN(authHeader, " ", 2) authContent := strings.SplitN(authHeader, " ", 2)
if len(authContent) == 2 && authContent[0] == "Basic" { if len(authContent) == 2 && authContent[0] == "Basic" {
@ -608,7 +609,21 @@ func AccessTokenOAuth(ctx *context.Context) {
}) })
return return
} }
if form.ClientID != "" && form.ClientID != pair[0] {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "client_id in request body inconsistent with Authorization header",
})
return
}
form.ClientID = pair[0] form.ClientID = pair[0]
if form.ClientSecret != "" && form.ClientSecret != pair[1] {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeInvalidRequest,
ErrorDescription: "client_secret in request body inconsistent with Authorization header",
})
return
}
form.ClientSecret = pair[1] form.ClientSecret = pair[1]
} }
} }
@ -686,9 +701,13 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
return return
} }
if !app.ValidateClientSecret([]byte(form.ClientSecret)) { if !app.ValidateClientSecret([]byte(form.ClientSecret)) {
errorDescription := "invalid client secret"
if form.ClientSecret == "" {
errorDescription = "invalid empty client secret"
}
handleAccessTokenError(ctx, AccessTokenError{ handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient, ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
ErrorDescription: "client is not authorized", ErrorDescription: errorDescription,
}) })
return return
} }

View file

@ -24,27 +24,27 @@ import (
) )
func toBranchLink(act *models.Action) string { func toBranchLink(act *models.Action) string {
return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch()) return act.GetRepoAbsoluteLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
} }
func toTagLink(act *models.Action) string { func toTagLink(act *models.Action) string {
return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag()) return act.GetRepoAbsoluteLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
} }
func toIssueLink(act *models.Action) string { func toIssueLink(act *models.Action) string {
return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0]) return act.GetRepoAbsoluteLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
} }
func toPullLink(act *models.Action) string { func toPullLink(act *models.Action) string {
return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0]) return act.GetRepoAbsoluteLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
} }
func toSrcLink(act *models.Action) string { func toSrcLink(act *models.Action) string {
return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch()) return act.GetRepoAbsoluteLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
} }
func toReleaseLink(act *models.Action) string { func toReleaseLink(act *models.Action) string {
return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch()) return act.GetRepoAbsoluteLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
} }
// renderMarkdown creates a minimal markdown render context from an action. // renderMarkdown creates a minimal markdown render context from an action.
@ -79,17 +79,17 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
title = act.ActUser.DisplayName() + " " title = act.ActUser.DisplayName() + " "
switch act.OpType { switch act.OpType {
case models.ActionCreateRepo: case models.ActionCreateRepo:
title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoAbsoluteLink(), act.ShortRepoPath())
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
case models.ActionRenameRepo: case models.ActionRenameRepo:
title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
case models.ActionCommitRepo: case models.ActionCommitRepo:
link.Href = toBranchLink(act) link.Href = toBranchLink(act)
if len(act.Content) != 0 { if len(act.Content) != 0 {
title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
} else { } else {
title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
} }
case models.ActionCreateIssue: case models.ActionCreateIssue:
link.Href = toIssueLink(act) link.Href = toIssueLink(act)
@ -98,11 +98,11 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
link.Href = toPullLink(act) link.Href = toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionTransferRepo: case models.ActionTransferRepo:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
case models.ActionPushTag: case models.ActionPushTag:
link.Href = toTagLink(act) link.Href = toTagLink(act)
title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoAbsoluteLink(), link.Href, act.GetTag(), act.ShortRepoPath())
case models.ActionCommentIssue: case models.ActionCommentIssue:
issueLink := toIssueLink(act) issueLink := toIssueLink(act)
if link.Href == "#" { if link.Href == "#" {
@ -140,26 +140,26 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
} }
title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionDeleteTag: case models.ActionDeleteTag:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoAbsoluteLink(), act.GetTag(), act.ShortRepoPath())
case models.ActionDeleteBranch: case models.ActionDeleteBranch:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoAbsoluteLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
case models.ActionMirrorSyncPush: case models.ActionMirrorSyncPush:
srcLink := toSrcLink(act) srcLink := toSrcLink(act)
if link.Href == "#" { if link.Href == "#" {
link.Href = srcLink link.Href = srcLink
} }
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncCreate: case models.ActionMirrorSyncCreate:
srcLink := toSrcLink(act) srcLink := toSrcLink(act)
if link.Href == "#" { if link.Href == "#" {
link.Href = srcLink link.Href = srcLink
} }
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncDelete: case models.ActionMirrorSyncDelete:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoAbsoluteLink(), act.GetBranch(), act.ShortRepoPath())
case models.ActionApprovePullRequest: case models.ActionApprovePullRequest:
pullLink := toPullLink(act) pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath()) title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
@ -174,16 +174,16 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
if link.Href == "#" { if link.Href == "#" {
link.Href = releaseLink link.Href = releaseLink
} }
title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content) title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoAbsoluteLink(), releaseLink, act.ShortRepoPath(), act.Content)
case models.ActionPullReviewDismissed: case models.ActionPullReviewDismissed:
pullLink := toPullLink(act) pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1]) title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
case models.ActionStarRepo: case models.ActionStarRepo:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath()) title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
case models.ActionWatchRepo: case models.ActionWatchRepo:
link.Href = act.GetRepoLink() link.Href = act.GetRepoAbsoluteLink()
title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath()) title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
default: default:
return nil, fmt.Errorf("unknown action type: %v", act.OpType) return nil, fmt.Errorf("unknown action type: %v", act.OpType)
} }
@ -193,14 +193,14 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
switch act.OpType { switch act.OpType {
case models.ActionCommitRepo, models.ActionMirrorSyncPush: case models.ActionCommitRepo, models.ActionMirrorSyncPush:
push := templates.ActionContent2Commits(act) push := templates.ActionContent2Commits(act)
repoLink := act.GetRepoLink() repoLink := act.GetRepoAbsoluteLink()
for _, commit := range push.Commits { for _, commit := range push.Commits {
if len(desc) != 0 { if len(desc) != 0 {
desc += "\n\n" desc += "\n\n"
} }
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s", desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)), html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), commit.Sha1)),
commit.Sha1, commit.Sha1,
templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil), templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil),
) )
@ -209,7 +209,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
if push.Len > 1 { if push.Len > 1 {
link = &feeds.Link{Href: fmt.Sprintf("%s/%s", setting.AppSubURL, push.CompareURL)} link = &feeds.Link{Href: fmt.Sprintf("%s/%s", setting.AppSubURL, push.CompareURL)}
} else if push.Len == 1 { } else if push.Len == 1 {
link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), push.Commits[0].Sha1)} link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), push.Commits[0].Sha1)}
} }
case models.ActionCreateIssue, models.ActionCreatePullRequest: case models.ActionCreateIssue, models.ActionCreatePullRequest:

View file

@ -83,7 +83,7 @@ func Commits(ctx *context.Context) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
ctx.Data["RefName"] = ctx.Repo.RefName ctx.Data["RefName"] = ctx.Repo.RefName
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager := context.NewPagination(int(commitsCount), pageSize, page, 5)
pager.SetDefaultParams(ctx) pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager

View file

@ -113,17 +113,17 @@ func setCsvCompareContext(ctx *context.Context) {
Error string Error string
} }
ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseCommit, headCommit *git.Commit) CsvDiffResult { ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseBlob, headBlob *git.Blob) CsvDiffResult {
if diffFile == nil || baseCommit == nil || headCommit == nil { if diffFile == nil {
return CsvDiffResult{nil, ""} return CsvDiffResult{nil, ""}
} }
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large")) errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
csvReaderFromCommit := func(ctx *markup.RenderContext, c *git.Commit) (*csv.Reader, io.Closer, error) { csvReaderFromCommit := func(ctx *markup.RenderContext, blob *git.Blob) (*csv.Reader, io.Closer, error) {
blob, err := c.GetBlobByPath(diffFile.Name) if blob == nil {
if err != nil { // It's ok for blob to be nil (file added or deleted)
return nil, nil, err return nil, nil, nil
} }
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() { if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
@ -139,28 +139,28 @@ func setCsvCompareContext(ctx *context.Context) {
return csvReader, reader, err return csvReader, reader, err
} }
baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseCommit) baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseBlob)
if baseBlobCloser != nil { if baseBlobCloser != nil {
defer baseBlobCloser.Close() defer baseBlobCloser.Close()
} }
if err == errTooLarge {
return CsvDiffResult{nil, err.Error()}
}
if err != nil { if err != nil {
log.Error("CreateCsvDiff error whilst creating baseReader from file %s in commit %s in %s: %v", diffFile.Name, baseCommit.ID.String(), ctx.Repo.Repository.Name, err) if err == errTooLarge {
return CsvDiffResult{nil, "unable to load file from base commit"} return CsvDiffResult{nil, err.Error()}
}
log.Error("error whilst creating csv.Reader from file %s in base commit %s in %s: %v", diffFile.Name, baseBlob.ID.String(), ctx.Repo.Repository.Name, err)
return CsvDiffResult{nil, "unable to load file"}
} }
headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headCommit) headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headBlob)
if headBlobCloser != nil { if headBlobCloser != nil {
defer headBlobCloser.Close() defer headBlobCloser.Close()
} }
if err == errTooLarge {
return CsvDiffResult{nil, err.Error()}
}
if err != nil { if err != nil {
log.Error("CreateCsvDiff error whilst creating headReader from file %s in commit %s in %s: %v", diffFile.Name, headCommit.ID.String(), ctx.Repo.Repository.Name, err) if err == errTooLarge {
return CsvDiffResult{nil, "unable to load file from head commit"} return CsvDiffResult{nil, err.Error()}
}
log.Error("error whilst creating csv.Reader from file %s in head commit %s in %s: %v", diffFile.Name, headBlob.ID.String(), ctx.Repo.Repository.Name, err)
return CsvDiffResult{nil, "unable to load file"}
} }
sections, err := gitdiff.CreateCsvDiff(diffFile, baseReader, headReader) sections, err := gitdiff.CreateCsvDiff(diffFile, baseReader, headReader)

View file

@ -116,9 +116,17 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
opts := models.FindReleasesOptions{ opts := models.FindReleasesOptions{
ListOptions: listOptions, ListOptions: listOptions,
IncludeDrafts: writeAccess && !isTagList, }
IncludeTags: isTagList, if isTagList {
// for the tags list page, show all releases with real tags (having real commit-id),
// the drafts should also be included because a real tag might be used as a draft.
opts.IncludeDrafts = true
opts.IncludeTags = true
opts.HasSha1 = util.OptionalBoolTrue
} else {
// only show draft releases for users who can write, read-only users shouldn't see draft releases.
opts.IncludeDrafts = writeAccess
} }
releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts) releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)

View file

@ -1448,8 +1448,6 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
} else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" { } else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" {
diffFile.Language = language diffFile.Language = language
} }
} else {
log.Error("Unexpected error: %v", err)
} }
} }

View file

@ -26,7 +26,6 @@ import (
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"github.com/google/uuid" "github.com/google/uuid"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )

View file

@ -78,8 +78,9 @@ type GiteaDownloader struct {
} }
// NewGiteaDownloader creates a gitea Downloader via gitea API // NewGiteaDownloader creates a gitea Downloader via gitea API
// Use either a username/password or personal token. token is preferred //
// Note: Public access only allows very basic access // Use either a username/password or personal token. token is preferred
// Note: Public access only allows very basic access
func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GiteaDownloader, error) { func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GiteaDownloader, error) {
giteaClient, err := gitea_sdk.NewClient( giteaClient, err := gitea_sdk.NewClient(
baseURL, baseURL,

View file

@ -412,6 +412,10 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
}, },
} }
if is.ForeignReference.ForeignIndex == "0" {
is.ForeignReference.ForeignIndex = strconv.FormatInt(is.Index, 10)
}
if err := g.remapUser(issue, &is); err != nil { if err := g.remapUser(issue, &is); err != nil {
return err return err
} }

View file

@ -71,8 +71,9 @@ type GitlabDownloader struct {
} }
// NewGitlabDownloader creates a gitlab Downloader via gitlab API // NewGitlabDownloader creates a gitlab Downloader via gitlab API
// Use either a username/password, personal token entered into the username field, or anonymous/public access //
// Note: Public access only allows very basic access // Use either a username/password, personal token entered into the username field, or anonymous/public access
// Note: Public access only allows very basic access
func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GitlabDownloader, error) { func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GitlabDownloader, error) {
gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient())) gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
// Only use basic auth if token is blank and password is NOT // Only use basic auth if token is blank and password is NOT
@ -374,7 +375,8 @@ type gitlabIssueContext struct {
} }
// GetIssues returns issues according start and limit // GetIssues returns issues according start and limit
// Note: issue label description and colors are not supported by the go-gitlab library at this time //
// Note: issue label description and colors are not supported by the go-gitlab library at this time
func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) { func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
state := "all" state := "all"
sort := "asc" sort := "asc"

View file

@ -28,6 +28,7 @@ import (
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/references"
@ -165,9 +166,10 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
}() }()
// TODO: make it able to do this in a database session // Run the merge in the hammer context to prevent cancellation
mergeCtx := context.Background() hammerCtx := graceful.GetManager().HammerContext()
pr.MergedCommitID, err = rawMerge(mergeCtx, pr, doer, mergeStyle, expectedHeadCommitID, message)
pr.MergedCommitID, err = rawMerge(hammerCtx, pr, doer, mergeStyle, expectedHeadCommitID, message)
if err != nil { if err != nil {
return err return err
} }
@ -176,18 +178,18 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
pr.Merger = doer pr.Merger = doer
pr.MergerID = doer.ID pr.MergerID = doer.ID
if _, err := pr.SetMerged(ctx); err != nil { if _, err := pr.SetMerged(hammerCtx); err != nil {
log.Error("setMerged [%d]: %v", pr.ID, err) log.Error("setMerged [%d]: %v", pr.ID, err)
} }
if err := pr.LoadIssueCtx(ctx); err != nil { if err := pr.LoadIssueCtx(hammerCtx); err != nil {
log.Error("loadIssue [%d]: %v", pr.ID, err) log.Error("loadIssue [%d]: %v", pr.ID, err)
} }
if err := pr.Issue.LoadRepo(ctx); err != nil { if err := pr.Issue.LoadRepo(hammerCtx); err != nil {
log.Error("loadRepo for issue [%d]: %v", pr.ID, err) log.Error("loadRepo for issue [%d]: %v", pr.ID, err)
} }
if err := pr.Issue.Repo.GetOwner(ctx); err != nil { if err := pr.Issue.Repo.GetOwner(hammerCtx); err != nil {
log.Error("GetOwner for issue repo [%d]: %v", pr.ID, err) log.Error("GetOwner for issue repo [%d]: %v", pr.ID, err)
} }
@ -197,17 +199,17 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true)) cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true))
// Resolve cross references // Resolve cross references
refs, err := pr.ResolveCrossReferences(ctx) refs, err := pr.ResolveCrossReferences(hammerCtx)
if err != nil { if err != nil {
log.Error("ResolveCrossReferences: %v", err) log.Error("ResolveCrossReferences: %v", err)
return nil return nil
} }
for _, ref := range refs { for _, ref := range refs {
if err = ref.LoadIssueCtx(ctx); err != nil { if err = ref.LoadIssueCtx(hammerCtx); err != nil {
return err return err
} }
if err = ref.Issue.LoadRepo(ctx); err != nil { if err = ref.Issue.LoadRepo(hammerCtx); err != nil {
return err return err
} }
close := ref.RefAction == references.XRefActionCloses close := ref.RefAction == references.XRefActionCloses

View file

@ -157,7 +157,7 @@
<tbody> <tbody>
{{range .Queue.Workers}} {{range .Queue.Workers}}
<tr> <tr>
<td>{{.Workers}}{{if .IsFlusher}}<span title="{{.i18n.Tr "admin.monitor.queue.flush"}}">{{svg "octicon-sync"}}</span>{{end}}</td> <td>{{.Workers}}{{if .IsFlusher}}<span title="{{$.i18n.Tr "admin.monitor.queue.flush"}}">{{svg "octicon-sync"}}</span>{{end}}</td>
<td>{{DateFmtLong .Start}}</td> <td>{{DateFmtLong .Start}}</td>
<td>{{if .HasTimeout}}{{DateFmtLong .Timeout}}{{else}}-{{end}}</td> <td>{{if .HasTimeout}}{{DateFmtLong .Timeout}}{{else}}-{{end}}</td>
<td> <td>

View file

@ -43,7 +43,7 @@
{{if .PackageDescriptor.Metadata.ImageLayers}} {{if .PackageDescriptor.Metadata.ImageLayers}}
<h4 class="ui top attached header">{{.i18n.Tr "packages.container.layers"}}</h4> <h4 class="ui top attached header">{{.i18n.Tr "packages.container.layers"}}</h4>
<div class="ui attached segment"> <div class="ui attached segment">
<table id="notice-table" class="ui very basic compact table"> <table class="ui very basic compact table">
<tbody> <tbody>
{{range .PackageDescriptor.Metadata.ImageLayers}} {{range .PackageDescriptor.Metadata.ImageLayers}}
<tr> <tr>
@ -57,7 +57,7 @@
{{if .PackageDescriptor.Metadata.Labels}} {{if .PackageDescriptor.Metadata.Labels}}
<h4 class="ui top attached header">{{.i18n.Tr "packages.container.labels"}}</h4> <h4 class="ui top attached header">{{.i18n.Tr "packages.container.labels"}}</h4>
<div class="ui attached segment"> <div class="ui attached segment">
<table id="notice-table" class="ui very basic compact table"> <table class="ui very basic compact table container-labels">
<thead> <thead>
<tr> <tr>
<th>{{.i18n.Tr "packages.container.labels.key"}}</th> <th>{{.i18n.Tr "packages.container.labels.key"}}</th>

View file

@ -1,6 +1,11 @@
{{/*Golang's template has a bug with string interpolation containing slashes,
the double slash will be treated as a comment there.
But there are also JS lint rules for template that require to use string interpolation in 1.17*/}}
<script> <script>
// synchronously set clone button states and urls here to avoid flickering // synchronously set clone button states and urls here to avoid flickering
// on page load. initRepoCloneLink calls this when proto changes. // on page load. initRepoCloneLink calls this when proto changes.
// this applies the protocol-dependant clone url to all elements with the
// `js-clone-url` and `js-clone-url-vsc` classes.
// TODO: This localStorage setting should be moved to backend user config // TODO: This localStorage setting should be moved to backend user config
// so it's available during rendering, then this inline script can be removed. // so it's available during rendering, then this inline script can be removed.
(window.updateCloneStates = function() { (window.updateCloneStates = function() {
@ -19,5 +24,9 @@
for (const el of document.getElementsByClassName('js-clone-url')) { for (const el of document.getElementsByClassName('js-clone-url')) {
el[el.nodeName === 'INPUT' ? 'value' : 'textContent'] = link; el[el.nodeName === 'INPUT' ? 'value' : 'textContent'] = link;
} }
const sep = '//';
for (const el of document.getElementsByClassName('js-clone-url-vsc')) {
el.href = `vscode:${sep}vscode.git/clone?url=${encodeURIComponent(link)}`;
}
})(); })();
</script> </script>

View file

@ -57,7 +57,8 @@
{{end}} {{end}}
</ol> </ol>
<div id="diff-file-boxes"> <div id="diff-file-boxes">
{{range $i, $file := .Diff.Files}} {{range $file := .Diff.Files}}
{{/*notice: the index of Diff.Files should not be used for element ID, because the index will be restarted from 0 when doing load-more for PRs with a lot of files*/}}
{{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}} {{$blobBase := call $.GetBlobByPathForCommit $.BaseCommit $file.OldName}}
{{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}} {{$blobHead := call $.GetBlobByPathForCommit $.HeadCommit $file.Name}}
{{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}} {{$isImage := or (call $.IsBlobAnImage $blobBase) (call $.IsBlobAnImage $blobHead)}}
@ -93,8 +94,8 @@
<div class="diff-file-header-actions df ac"> <div class="diff-file-header-actions df ac">
{{if $showFileViewToggle}} {{if $showFileViewToggle}}
<div class="ui compact icon buttons"> <div class="ui compact icon buttons">
<span class="ui tiny basic button tooltip file-view-toggle" data-toggle-selector="#diff-source-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code"}}</span> <span class="ui tiny basic button tooltip file-view-toggle" data-toggle-selector="#diff-source-{{$file.NameHash}}" data-content="{{$.i18n.Tr "repo.file_view_source"}}" data-position="bottom center">{{svg "octicon-code"}}</span>
<span class="ui tiny basic button tooltip file-view-toggle active" data-toggle-selector="#diff-rendered-{{$i}}" data-content="{{$.i18n.Tr "repo.file_view_rendered"}}" data-position="bottom center">{{svg "octicon-file"}}</span> <span class="ui tiny basic button tooltip file-view-toggle active" data-toggle-selector="#diff-rendered-{{$file.NameHash}}" data-content="{{$.i18n.Tr "repo.file_view_rendered"}}" data-position="bottom center">{{svg "octicon-file"}}</span>
</div> </div>
{{end}} {{end}}
{{if $file.IsProtected}} {{if $file.IsProtected}}
@ -115,15 +116,14 @@
{{if $file.HasChangedSinceLastReview}} {{if $file.HasChangedSinceLastReview}}
<span class="changed-since-last-review unselectable">{{$.i18n.Tr "repo.pulls.has_changed_since_last_review"}}</span> <span class="changed-since-last-review unselectable">{{$.i18n.Tr "repo.pulls.has_changed_since_last_review"}}</span>
{{end}} {{end}}
<div data-link="{{$.Issue.Link}}/viewed-files" data-headcommit="{{$.PullHeadCommitID}}" class="viewed-file-form unselectable{{if $file.IsViewed}} viewed-file-checked-form{{end}}"> <label data-link="{{$.Issue.Link}}/viewed-files" data-headcommit="{{$.PullHeadCommitID}}" class="viewed-file-form unselectable{{if $file.IsViewed}} viewed-file-checked-form{{end}}">
<input type="checkbox" name="{{$file.GetDiffFileName}}" id="viewed-file-checkbox-{{$i}}" autocomplete="off" {{if $file.IsViewed}}checked{{end}}></input> <input type="checkbox" name="{{$file.GetDiffFileName}}" autocomplete="off"{{if $file.IsViewed}} checked{{end}}> {{$.i18n.Tr "repo.pulls.has_viewed_file"}}
<label for="viewed-file-checkbox-{{$i}}">{{$.i18n.Tr "repo.pulls.has_viewed_file"}}</label> </label>
</div>
{{end}} {{end}}
</div> </div>
</h4> </h4>
<div class="diff-file-body ui attached unstackable table segment" {{if $file.IsViewed}}data-folded="true"{{end}}> <div class="diff-file-body ui attached unstackable table segment" {{if $file.IsViewed}}data-folded="true"{{end}}>
<div id="diff-source-{{$i}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}"> <div id="diff-source-{{$file.NameHash}}" class="file-body file-code unicode-escaped code-diff{{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}{{if $showFileViewToggle}} hide{{end}}">
{{if or $file.IsIncomplete $file.IsBin}} {{if or $file.IsIncomplete $file.IsBin}}
<div class="diff-file-body binary" style="padding: 5px 10px;"> <div class="diff-file-body binary" style="padding: 5px 10px;">
{{if $file.IsIncomplete}} {{if $file.IsIncomplete}}
@ -148,12 +148,12 @@
{{end}} {{end}}
</div> </div>
{{if $showFileViewToggle}} {{if $showFileViewToggle}}
<div id="diff-rendered-{{$i}}" class="file-body file-code {{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}"> <div id="diff-rendered-{{$file.NameHash}}" class="file-body file-code {{if $.IsSplitStyle}} code-diff-split{{else}} code-diff-unified{{end}}">
<table class="chroma w-100"> <table class="chroma w-100">
{{if $isImage}} {{if $isImage}}
{{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}} {{template "repo/diff/image_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
{{else}} {{else}}
{{template "repo/diff/csv_diff" dict "file" . "root" $}} {{template "repo/diff/csv_diff" dict "file" . "root" $ "blobBase" $blobBase "blobHead" $blobHead}}
{{end}} {{end}}
</table> </table>
</div> </div>

View file

@ -1,6 +1,6 @@
<tr> <tr>
<td> <td>
{{$result := call .root.CreateCsvDiff .file .root.BaseCommit .root.HeadCommit}} {{$result := call .root.CreateCsvDiff .file .blobBase .blobHead}}
{{if $result.Error}} {{if $result.Error}}
<div class="ui center">{{$result.Error}}</div> <div class="ui center">{{$result.Error}}</div>
{{else if $result.Sections}} {{else if $result.Sections}}

View file

@ -116,19 +116,19 @@
<!-- Only show clone panel in repository home page --> <!-- Only show clone panel in repository home page -->
{{if eq $n 0}} {{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel"> <div class="ui action tiny input" id="clone-panel">
{{template "repo/clone_buttons" .}} {{template "repo/clone_buttons" .}}
{{template "repo/clone_script" .}} <button id="download-btn" class="ui basic small compact jump dropdown icon button tooltip" data-content="{{.i18n.Tr "repo.download_archive"}}" data-position="top right">
<button id="download-btn" class="ui basic small compact jump dropdown icon button tooltip" data-content="{{.i18n.Tr "repo.download_archive"}}" data-position="top right"> {{svg "octicon-download"}}
{{svg "octicon-download"}} <div class="menu">
<div class="menu"> {{if not $.DisableDownloadSourceArchives}}
{{if not $.DisableDownloadSourceArchives}} <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.i18n.Tr "repo.download_zip"}}</a>
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.zip" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.i18n.Tr "repo.download_zip"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.i18n.Tr "repo.download_tar"}}</a>
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.tar.gz" rel="nofollow">{{svg "octicon-file-zip" 16 "mr-3"}}{{.i18n.Tr "repo.download_tar"}}</a> <a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "mr-3"}}{{.i18n.Tr "repo.download_bundle"}}</a>
<a class="item archive-link" href="{{$.RepoLink}}/archive/{{PathEscapeSegments $.RefName}}.bundle" rel="nofollow">{{svg "octicon-package" 16 "mr-3"}}{{.i18n.Tr "repo.download_bundle"}}</a> {{end}}
{{end}} <a class="item js-clone-url-vsc" href="vscode://vscode.git/clone?url={{.CloneButtonOriginLink.HTTPS}}">{{svg "gitea-vscode" 16 "mr-3"}}{{.i18n.Tr "repo.clone_in_vsc"}}</a>
<a class="item" href="vscode://vscode.git/clone?url={{$.RepoCloneLink.HTTPS}}">{{svg "gitea-vscode" 16 "mr-3"}}{{.i18n.Tr "repo.clone_in_vsc"}}</a> </div>
</div> </button>
</button> {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}}
</div> </div>
{{end}} {{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }} {{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }}

View file

@ -77,14 +77,12 @@
<span class="ui green label">{{$.i18n.Tr "repo.release.stable"}}</span> <span class="ui green label">{{$.i18n.Tr "repo.release.stable"}}</span>
{{end}} {{end}}
<span class="tag text blue"> <span class="tag text blue">
<a class="df ac je" href="{{if .IsDraft}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "mr-2"}}{{.TagName}}</a> <a class="df ac je" href="{{if not .Sha1}}#{{else}}{{$.RepoLink}}/src/tag/{{.TagName | PathEscapeSegments}}{{end}}" rel="nofollow">{{svg "octicon-tag" 16 "mr-2"}}{{.TagName}}</a>
</span> </span>
{{if not .IsDraft}} {{if .Sha1}}
<span class="commit"> <span class="commit">
<a class="mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}}</a> <a class="mono" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "mr-2"}}{{ShortSha .Sha1}}</a>
</span> </span>
{{end}}
{{if .Sha1 }}
{{template "repo/branch_dropdown" dict "root" $ "release" .}} {{template "repo/branch_dropdown" dict "root" $ "release" .}}
{{end}} {{end}}
{{end}} {{end}}

View file

@ -12420,6 +12420,18 @@
"summary": "List the current user's tracked times", "summary": "List the current user's tracked times",
"operationId": "userCurrentTrackedTimes", "operationId": "userCurrentTrackedTimes",
"parameters": [ "parameters": [
{
"type": "integer",
"description": "page number of results to return (1-based)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "page size of results",
"name": "limit",
"in": "query"
},
{ {
"type": "string", "type": "string",
"format": "date-time", "format": "date-time",
@ -15619,7 +15631,6 @@
"$ref": "#/definitions/FileCommitResponse" "$ref": "#/definitions/FileCommitResponse"
}, },
"content": { "content": {
"type": "object",
"x-go-name": "Content" "x-go-name": "Content"
}, },
"verification": { "verification": {
@ -18661,7 +18672,6 @@
"x-go-name": "Ignored" "x-go-name": "Ignored"
}, },
"reason": { "reason": {
"type": "object",
"x-go-name": "Reason" "x-go-name": "Reason"
}, },
"repository_url": { "repository_url": {

View file

@ -33,7 +33,7 @@
</div> </div>
<div class="content"> <div class="content">
<strong>{{$provider}}</strong> <strong>{{$provider}}</strong>
{{if $loginSource.IsActive}}<span class="text red">{{$.i18n.Tr "settings.active"}}</span>{{end}} {{if $loginSource.IsActive}}<span class="text red">{{$.i18n.Tr "repo.settings.active"}}</span>{{end}}
</div> </div>
</div> </div>
{{end}} {{end}}

View file

@ -0,0 +1,9 @@
.container-labels {
td:nth-child(1) {
vertical-align: top;
}
td:nth-child(2) {
overflow-wrap: anywhere;
}
}

View file

@ -272,13 +272,22 @@ a.blob-excerpt:hover {
} }
.viewed-file-form { .viewed-file-form {
margin: 0 3px; display: flex;
padding: 0 3px; align-items: center;
border-radius: 3px; border: 1px none;
padding: 4px 8px;
margin: -8px 0; // just like other buttons in the diff box header
border-radius: .285rem; // just like .ui.tiny.button
font-size: .857rem; // just like .ui.tiny.button
}
.viewed-file-form input {
margin-right: 4px;
} }
.viewed-file-checked-form { .viewed-file-checked-form {
background-color: var(--color-primary-light-4); background-color: var(--color-primary-light-6);
border: 1px solid var(--color-primary-light-4);
} }
#viewed-files-summary { #viewed-files-summary {

View file

@ -34,5 +34,6 @@
@import "_admin"; @import "_admin";
@import "_explore"; @import "_explore";
@import "_review"; @import "_review";
@import "_package";
@import "./helpers.less"; @import "./helpers.less";