mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-03 08:02:36 +09:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a044ec8b53 | ||
|
|
f93d72c09b | ||
|
|
2f22337125 | ||
|
|
781ad8a79e | ||
|
|
cada7202aa | ||
|
|
0b331e2213 | ||
|
|
0734ca0132 | ||
|
|
0b83cc21be | ||
|
|
b68e605d56 | ||
|
|
42991dc89a | ||
|
|
160de9fbda | ||
|
|
d644289fcb | ||
|
|
fd9ff7cd6f |
27
CHANGELOG.md
27
CHANGELOG.md
@@ -4,13 +4,15 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
## [1.16.0-rc1](https://github.com/go-gitea/gitea/releases/tag/v1.16.0-rc1) - 2022-01-19
|
## [1.16.0](https://github.com/go-gitea/gitea/releases/tag/v1.16.0) - 2022-01-30
|
||||||
|
|
||||||
* BREAKING
|
* BREAKING
|
||||||
* Remove golang vendored directory (#18277)
|
* Remove golang vendored directory (#18277)
|
||||||
* Paginate releases page & set default page size to 10 (#16857)
|
* Paginate releases page & set default page size to 10 (#16857)
|
||||||
* Only allow webhook to send requests to allowed hosts (#17482)
|
* Only allow webhook to send requests to allowed hosts (#17482)
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
* Disable content sniffing on `PlainTextBytes` (#18359) (#18365)
|
||||||
|
* Only view milestones from current repo (#18414) (#18417)
|
||||||
* Sanitize user-input on file name (#17666)
|
* Sanitize user-input on file name (#17666)
|
||||||
* Use `hostmatcher` to replace `matchlist` to improve blocking of bad hosts in Webhooks (#17605)
|
* Use `hostmatcher` to replace `matchlist` to improve blocking of bad hosts in Webhooks (#17605)
|
||||||
* FEATURES
|
* FEATURES
|
||||||
@@ -228,6 +230,16 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* Add left padding for chunk header of split diff view (#13397)
|
* Add left padding for chunk header of split diff view (#13397)
|
||||||
* Allow U2F 2FA without TOTP (#11573)
|
* Allow U2F 2FA without TOTP (#11573)
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
* GitLab reviews may not have the updated_at field set (#18450) (#18461)
|
||||||
|
* Fix detection of no commits when the default branch is not master (#18422) (#18423)
|
||||||
|
* Fix broken oauth2 authentication source edit page (#18412) (#18419)
|
||||||
|
* Place inline diff comment dialogs on split diff in 4th and 8th columns (#18403) (#18404)
|
||||||
|
* Fix restore without topic failure (#18387) (#18400)
|
||||||
|
* Fix commit's time (#18375) (#18392)
|
||||||
|
* Fix partial cloning a repo (#18373) (#18377)
|
||||||
|
* Stop trimming preceding and suffixing spaces from editor filenames (#18334)
|
||||||
|
* Prevent showing webauthn error for every time visiting `/user/settings/security` (#18386)
|
||||||
|
* Fix mime-type detection for HTTP server (#18370) (#18371)
|
||||||
* Stop trimming preceding and suffixing spaces from editor filenames (#18334)
|
* Stop trimming preceding and suffixing spaces from editor filenames (#18334)
|
||||||
* Restore propagation of ErrDependenciesLeft (#18325)
|
* Restore propagation of ErrDependenciesLeft (#18325)
|
||||||
* Fix PR comments UI (#18323)
|
* Fix PR comments UI (#18323)
|
||||||
@@ -299,6 +311,19 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
|
|||||||
* MISC
|
* MISC
|
||||||
* Update JS dependencies (#17611)
|
* Update JS dependencies (#17611)
|
||||||
|
|
||||||
|
## [1.15.11](https://github.com/go-gitea/gitea/releases/tag/v1.15.11) - 2022-01-29
|
||||||
|
|
||||||
|
* SECURITY
|
||||||
|
* Only view milestones from current repo (#18414) (#18418)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix broken when no commits and default branch is not master (#18422) (#18424)
|
||||||
|
* Fix commit's time (#18375) (#18409)
|
||||||
|
* Fix restore without topic failure (#18387) (#18401)
|
||||||
|
* Fix mermaid import in 1.15 (it uses ESModule now) (#18382)
|
||||||
|
* Update to go/text 0.3.7 (#18336)
|
||||||
|
* MISC
|
||||||
|
* Upgrade EasyMDE to 2.16.1 (#18278) (#18279)
|
||||||
|
|
||||||
## [1.15.10](https://github.com/go-gitea/gitea/releases/tag/v1.15.10) - 2022-01-14
|
## [1.15.10](https://github.com/go-gitea/gitea/releases/tag/v1.15.10) - 2022-01-14
|
||||||
|
|
||||||
* BUGFIXES
|
* BUGFIXES
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ params:
|
|||||||
description: Git with a cup of tea
|
description: Git with a cup of tea
|
||||||
author: The Gitea Authors
|
author: The Gitea Authors
|
||||||
website: https://docs.gitea.io
|
website: https://docs.gitea.io
|
||||||
version: 1.15.10
|
version: 1.16.0
|
||||||
minGoVersion: 1.16
|
minGoVersion: 1.16
|
||||||
goVersion: 1.17
|
goVersion: 1.17
|
||||||
minNodeVersion: 12.17
|
minNodeVersion: 12.17
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -30,7 +30,7 @@ require (
|
|||||||
github.com/denisenkom/go-mssqldb v0.10.0
|
github.com/denisenkom/go-mssqldb v0.10.0
|
||||||
github.com/djherbis/buffer v1.2.0
|
github.com/djherbis/buffer v1.2.0
|
||||||
github.com/djherbis/nio/v3 v3.0.1
|
github.com/djherbis/nio/v3 v3.0.1
|
||||||
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
|
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/editorconfig/editorconfig-core-go/v2 v2.4.2
|
github.com/editorconfig/editorconfig-core-go/v2 v2.4.2
|
||||||
github.com/emirpasic/gods v1.12.0
|
github.com/emirpasic/gods v1.12.0
|
||||||
@@ -54,7 +54,7 @@ require (
|
|||||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/go-github/v39 v39.2.0
|
github.com/google/go-github/v39 v39.2.0
|
||||||
github.com/google/uuid v1.2.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/feeds v1.1.1
|
github.com/gorilla/feeds v1.1.1
|
||||||
github.com/gorilla/mux v1.8.0 // indirect
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
@@ -145,8 +145,6 @@ replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.
|
|||||||
|
|
||||||
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
|
||||||
|
|
||||||
replace github.com/duo-labs/webauthn => github.com/authelia/webauthn v0.0.0-20211225121951-80d1f2a572e4
|
|
||||||
|
|
||||||
replace github.com/satori/go.uuid v1.2.0 => github.com/gofrs/uuid v4.2.0+incompatible
|
replace github.com/satori/go.uuid v1.2.0 => github.com/gofrs/uuid v4.2.0+incompatible
|
||||||
|
|
||||||
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
exclude github.com/gofrs/uuid v3.2.0+incompatible
|
||||||
|
|||||||
9
go.sum
9
go.sum
@@ -131,8 +131,6 @@ github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:o
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/authelia/webauthn v0.0.0-20211225121951-80d1f2a572e4 h1:u3eFvgr4A8IjlAokbFt6XY6VdurX7DEYnQMQ4K2yobc=
|
|
||||||
github.com/authelia/webauthn v0.0.0-20211225121951-80d1f2a572e4/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
|
|
||||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||||
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
|
||||||
@@ -276,6 +274,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
|||||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||||
|
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951 h1:17esZ09oW+29rklBtCVphIguql2u3NxYH2OasFPPZoo=
|
||||||
|
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951/go.mod h1:nHy3JdztZWcsjenDeBuE8gn171OAwg12LBN027UP5AE=
|
||||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
@@ -491,7 +491,6 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
|||||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||||
github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k=
|
github.com/goccy/go-json v0.7.4 h1:B44qRUFwz/vxPKPISQ1KhvzRi9kZ28RAf6YtjriBZ5k=
|
||||||
github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.7.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
|
|
||||||
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
@@ -585,8 +584,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
|||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
|||||||
@@ -123,6 +123,17 @@ func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
|
||||||
|
return func(t *testing.T) {
|
||||||
|
assert.NoError(t, git.CloneWithArgs(context.Background(), u.String(), dstLocalPath, allowLFSFilters(), git.CloneRepoOptions{
|
||||||
|
Filter: "blob:none",
|
||||||
|
}))
|
||||||
|
exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, exist)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func doGitCloneFail(u *url.URL) func(*testing.T) {
|
func doGitCloneFail(u *url.URL) func(*testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
tmpDir, err := os.MkdirTemp("", "doGitCloneFail")
|
tmpDir, err := os.MkdirTemp("", "doGitCloneFail")
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ func testGit(t *testing.T, u *url.URL) {
|
|||||||
|
|
||||||
t.Run("Clone", doGitClone(dstPath, u))
|
t.Run("Clone", doGitClone(dstPath, u))
|
||||||
|
|
||||||
|
dstPath2, err := os.MkdirTemp("", httpContext.Reponame)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer util.RemoveAll(dstPath2)
|
||||||
|
|
||||||
|
t.Run("Partial Clone", doPartialGitClone(dstPath2, u))
|
||||||
|
|
||||||
little, big := standardCommitAndPushTest(t, dstPath)
|
little, big := standardCommitAndPushTest(t, dstPath)
|
||||||
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
littleLFS, bigLFS := lfsCommitAndPushTest(t, dstPath)
|
||||||
rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
rawTest(t, &httpContext, little, big, littleLFS, bigLFS)
|
||||||
|
|||||||
@@ -134,22 +134,6 @@ func GetMilestoneByRepoIDANDName(repoID int64, name string) (*Milestone, error)
|
|||||||
return &mile, nil
|
return &mile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMilestoneByID returns the milestone via id .
|
|
||||||
func GetMilestoneByID(id int64) (*Milestone, error) {
|
|
||||||
return getMilestoneByID(db.GetEngine(db.DefaultContext), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMilestoneByID(e db.Engine, id int64) (*Milestone, error) {
|
|
||||||
var m Milestone
|
|
||||||
has, err := e.ID(id).Get(&m)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if !has {
|
|
||||||
return nil, ErrMilestoneNotExist{ID: id, RepoID: 0}
|
|
||||||
}
|
|
||||||
return &m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateMilestone updates information of given milestone.
|
// UpdateMilestone updates information of given milestone.
|
||||||
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
func UpdateMilestone(m *Milestone, oldIsClosed bool) error {
|
||||||
ctx, committer, err := db.TxContext()
|
ctx, committer, err := db.TxContext()
|
||||||
|
|||||||
@@ -291,6 +291,7 @@ func (ctx *Context) PlainTextBytes(status int, bs []byte) {
|
|||||||
}
|
}
|
||||||
ctx.Resp.WriteHeader(status)
|
ctx.Resp.WriteHeader(status)
|
||||||
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
||||||
|
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
||||||
if _, err := ctx.Resp.Write(bs); err != nil {
|
if _, err := ctx.Resp.Write(bs); err != nil {
|
||||||
log.Error("Write bytes failed: %v", err)
|
log.Error("Write bytes failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,27 +59,28 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
|
|||||||
ctx, _, finished := process.GetManager().AddContext(repo.Ctx, fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path))
|
ctx, _, finished := process.GetManager().AddContext(repo.Ctx, fmt.Sprintf("GetRawDiffForFile: [repo_path: %s]", repo.Path))
|
||||||
defer finished()
|
defer finished()
|
||||||
|
|
||||||
var cmd *exec.Cmd
|
cmd := exec.CommandContext(ctx, GitExecutable, GlobalCommandArgs...)
|
||||||
|
|
||||||
switch diffType {
|
switch diffType {
|
||||||
case RawDiffNormal:
|
case RawDiffNormal:
|
||||||
if len(startCommit) != 0 {
|
if len(startCommit) != 0 {
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"diff", "-M", startCommit, endCommit}, fileArgs...)...)
|
||||||
} else if commit.ParentCount() == 0 {
|
} else if commit.ParentCount() == 0 {
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"show", endCommit}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"show", endCommit}, fileArgs...)...)
|
||||||
} else {
|
} else {
|
||||||
c, _ := commit.Parent(0)
|
c, _ := commit.Parent(0)
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"diff", "-M", c.ID.String(), endCommit}, fileArgs...)...)
|
||||||
}
|
}
|
||||||
case RawDiffPatch:
|
case RawDiffPatch:
|
||||||
if len(startCommit) != 0 {
|
if len(startCommit) != 0 {
|
||||||
query := fmt.Sprintf("%s...%s", endCommit, startCommit)
|
query := fmt.Sprintf("%s...%s", endCommit, startCommit)
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"format-patch", "--no-signature", "--stdout", "--root", query}, fileArgs...)...)
|
||||||
} else if commit.ParentCount() == 0 {
|
} else if commit.ParentCount() == 0 {
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"format-patch", "--no-signature", "--stdout", "--root", endCommit}, fileArgs...)...)
|
||||||
} else {
|
} else {
|
||||||
c, _ := commit.Parent(0)
|
c, _ := commit.Parent(0)
|
||||||
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
|
query := fmt.Sprintf("%s...%s", endCommit, c.ID.String())
|
||||||
cmd = exec.CommandContext(ctx, GitExecutable, append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...)
|
cmd.Args = append(cmd.Args, append([]string{"format-patch", "--no-signature", "--stdout", query}, fileArgs...)...)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid diffType: %s", diffType)
|
return fmt.Errorf("invalid diffType: %s", diffType)
|
||||||
|
|||||||
@@ -79,16 +79,21 @@ func InitRepository(repoPath string, bare bool) error {
|
|||||||
|
|
||||||
// IsEmpty Check if repository is empty.
|
// IsEmpty Check if repository is empty.
|
||||||
func (repo *Repository) IsEmpty() (bool, error) {
|
func (repo *Repository) IsEmpty() (bool, error) {
|
||||||
var errbuf strings.Builder
|
var errbuf, output strings.Builder
|
||||||
if err := NewCommand("log", "-1").RunInDirPipeline(repo.Path, nil, &errbuf); err != nil {
|
if err := NewCommandContext(repo.Ctx, "rev-list", "--all", "--count", "--max-count=1").RunWithContext(&RunContext{
|
||||||
if strings.Contains(errbuf.String(), "fatal: bad default revision 'HEAD'") ||
|
Timeout: -1,
|
||||||
strings.Contains(errbuf.String(), "fatal: your current branch 'master' does not have any commits yet") {
|
Dir: repo.Path,
|
||||||
return true, nil
|
Stdout: &output,
|
||||||
}
|
Stderr: &errbuf,
|
||||||
|
}); err != nil {
|
||||||
return true, fmt.Errorf("check empty: %v - %s", err, errbuf.String())
|
return true, fmt.Errorf("check empty: %v - %s", err, errbuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
c, err := strconv.Atoi(strings.TrimSpace(output.String()))
|
||||||
|
if err != nil {
|
||||||
|
return true, fmt.Errorf("check empty: convert %s to count failed: %v", output.String(), err)
|
||||||
|
}
|
||||||
|
return c == 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneRepoOptions options when clone a repository
|
// CloneRepoOptions options when clone a repository
|
||||||
@@ -101,6 +106,7 @@ type CloneRepoOptions struct {
|
|||||||
Shared bool
|
Shared bool
|
||||||
NoCheckout bool
|
NoCheckout bool
|
||||||
Depth int
|
Depth int
|
||||||
|
Filter string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone clones original repository to target path.
|
// Clone clones original repository to target path.
|
||||||
@@ -141,7 +147,9 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
|
|||||||
if opts.Depth > 0 {
|
if opts.Depth > 0 {
|
||||||
cmd.AddArguments("--depth", strconv.Itoa(opts.Depth))
|
cmd.AddArguments("--depth", strconv.Itoa(opts.Depth))
|
||||||
}
|
}
|
||||||
|
if opts.Filter != "" {
|
||||||
|
cmd.AddArguments("--filter", opts.Filter)
|
||||||
|
}
|
||||||
if len(opts.Branch) > 0 {
|
if len(opts.Branch) > 0 {
|
||||||
cmd.AddArguments("-b", opts.Branch)
|
cmd.AddArguments("-b", opts.Branch)
|
||||||
}
|
}
|
||||||
|
|||||||
41
modules/public/mime_types.go
Normal file
41
modules/public/mime_types.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package public
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// wellKnownMimeTypesLower comes from Golang's builtin mime package: `builtinTypesLower`, see the comment of detectWellKnownMimeType
|
||||||
|
var wellKnownMimeTypesLower = map[string]string{
|
||||||
|
".avif": "image/avif",
|
||||||
|
".css": "text/css; charset=utf-8",
|
||||||
|
".gif": "image/gif",
|
||||||
|
".htm": "text/html; charset=utf-8",
|
||||||
|
".html": "text/html; charset=utf-8",
|
||||||
|
".jpeg": "image/jpeg",
|
||||||
|
".jpg": "image/jpeg",
|
||||||
|
".js": "text/javascript; charset=utf-8",
|
||||||
|
".json": "application/json",
|
||||||
|
".mjs": "text/javascript; charset=utf-8",
|
||||||
|
".pdf": "application/pdf",
|
||||||
|
".png": "image/png",
|
||||||
|
".svg": "image/svg+xml",
|
||||||
|
".wasm": "application/wasm",
|
||||||
|
".webp": "image/webp",
|
||||||
|
".xml": "text/xml; charset=utf-8",
|
||||||
|
|
||||||
|
// well, there are some types missing from the builtin list
|
||||||
|
".txt": "text/plain; charset=utf-8",
|
||||||
|
}
|
||||||
|
|
||||||
|
// detectWellKnownMimeType will return the mime-type for a well-known file ext name
|
||||||
|
// The purpose of this function is to bypass the unstable behavior of Golang's mime.TypeByExtension
|
||||||
|
// mime.TypeByExtension would use OS's mime-type config to overwrite the well-known types (see its document).
|
||||||
|
// If the user's OS has incorrect mime-type config, it would make Gitea can not respond a correct Content-Type to browsers.
|
||||||
|
// For example, if Gitea returns `text/plain` for a `.js` file, the browser couldn't run the JS due to security reasons.
|
||||||
|
// detectWellKnownMimeType makes the Content-Type for well-known files stable.
|
||||||
|
func detectWellKnownMimeType(ext string) string {
|
||||||
|
ext = strings.ToLower(ext)
|
||||||
|
return wellKnownMimeTypesLower[ext]
|
||||||
|
}
|
||||||
@@ -95,6 +95,15 @@ func parseAcceptEncoding(val string) map[string]bool {
|
|||||||
return types
|
return types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setWellKnownContentType will set the Content-Type if the file is a well-known type.
|
||||||
|
// See the comments of detectWellKnownMimeType
|
||||||
|
func setWellKnownContentType(w http.ResponseWriter, file string) {
|
||||||
|
mimeType := detectWellKnownMimeType(filepath.Ext(file))
|
||||||
|
if mimeType != "" {
|
||||||
|
w.Header().Set("Content-Type", mimeType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
|
func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.FileSystem, file string) bool {
|
||||||
// use clean to keep the file is a valid path with no . or ..
|
// use clean to keep the file is a valid path with no . or ..
|
||||||
f, err := fs.Open(path.Clean(file))
|
f, err := fs.Open(path.Clean(file))
|
||||||
@@ -125,6 +134,8 @@ func (opts *Options) handle(w http.ResponseWriter, req *http.Request, fs http.Fi
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setWellKnownContentType(w, file)
|
||||||
|
|
||||||
serveContent(w, req, fi, fi.ModTime(), f)
|
serveContent(w, req, fi, fi.ModTime(), f)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,12 @@ package public
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,24 +63,16 @@ func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modt
|
|||||||
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
|
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
|
||||||
if encodings["gzip"] {
|
if encodings["gzip"] {
|
||||||
if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok {
|
if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok {
|
||||||
rd := bytes.NewReader(cf.GzipBytes())
|
rdGzip := bytes.NewReader(cf.GzipBytes())
|
||||||
w.Header().Set("Content-Encoding", "gzip")
|
// all static files are managed by Gitea, so we can make sure every file has the correct ext name
|
||||||
ctype := mime.TypeByExtension(filepath.Ext(fi.Name()))
|
// then we can get the correct Content-Type, we do not need to do http.DetectContentType on the decompressed data
|
||||||
if ctype == "" {
|
mimeType := detectWellKnownMimeType(filepath.Ext(fi.Name()))
|
||||||
// read a chunk to decide between utf-8 text and binary
|
if mimeType == "" {
|
||||||
var buf [512]byte
|
mimeType = "application/octet-stream"
|
||||||
grd, _ := gzip.NewReader(rd)
|
|
||||||
n, _ := io.ReadFull(grd, buf[:])
|
|
||||||
ctype = http.DetectContentType(buf[:n])
|
|
||||||
_, err := rd.Seek(0, io.SeekStart) // rewind to output whole file
|
|
||||||
if err != nil {
|
|
||||||
log.Error("rd.Seek error: %v", err)
|
|
||||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", ctype)
|
w.Header().Set("Content-Type", mimeType)
|
||||||
http.ServeContent(w, req, fi.Name(), modtime, rd)
|
w.Header().Set("Content-Encoding", "gzip")
|
||||||
|
http.ServeContent(w, req, fi.Name(), modtime, rdGzip)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -491,7 +491,10 @@ func serviceRPC(h serviceHandler, service string) {
|
|||||||
defer finished()
|
defer finished()
|
||||||
|
|
||||||
var stderr bytes.Buffer
|
var stderr bytes.Buffer
|
||||||
cmd := exec.CommandContext(ctx, git.GitExecutable, service, "--stateless-rpc", h.dir)
|
args := make([]string, len(git.GlobalCommandArgs))
|
||||||
|
copy(args, git.GlobalCommandArgs)
|
||||||
|
args = append(args, []string{service, "--stateless-rpc", h.dir}...)
|
||||||
|
cmd := exec.CommandContext(ctx, git.GitExecutable, args...)
|
||||||
cmd.Dir = h.dir
|
cmd.Dir = h.dir
|
||||||
cmd.Env = append(os.Environ(), h.environ...)
|
cmd.Env = append(os.Environ(), h.environ...)
|
||||||
cmd.Stdout = h.w
|
cmd.Stdout = h.w
|
||||||
|
|||||||
@@ -802,7 +802,7 @@ func NewIssue(ctx *context.Context) {
|
|||||||
|
|
||||||
milestoneID := ctx.FormInt64("milestone")
|
milestoneID := ctx.FormInt64("milestone")
|
||||||
if milestoneID > 0 {
|
if milestoneID > 0 {
|
||||||
milestone, err := models.GetMilestoneByID(milestoneID)
|
milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetMilestoneByID: %d: %v", milestoneID, err)
|
log.Error("GetMilestoneByID: %d: %v", milestoneID, err)
|
||||||
} else {
|
} else {
|
||||||
@@ -889,7 +889,7 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull
|
|||||||
// Check milestone.
|
// Check milestone.
|
||||||
milestoneID := form.MilestoneID
|
milestoneID := form.MilestoneID
|
||||||
if milestoneID > 0 {
|
if milestoneID > 0 {
|
||||||
milestone, err := models.GetMilestoneByID(milestoneID)
|
milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetMilestoneByID", err)
|
ctx.ServerError("GetMilestoneByID", err)
|
||||||
return nil, nil, 0, 0
|
return nil, nil, 0, 0
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ func DeleteMilestone(ctx *context.Context) {
|
|||||||
// MilestoneIssuesAndPulls lists all the issues and pull requests of the milestone
|
// MilestoneIssuesAndPulls lists all the issues and pull requests of the milestone
|
||||||
func MilestoneIssuesAndPulls(ctx *context.Context) {
|
func MilestoneIssuesAndPulls(ctx *context.Context) {
|
||||||
milestoneID := ctx.ParamsInt64(":id")
|
milestoneID := ctx.ParamsInt64(":id")
|
||||||
milestone, err := models.GetMilestoneByID(milestoneID)
|
milestone, err := models.GetMilestoneByRepoID(ctx.Repo.Repository.ID, milestoneID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrMilestoneNotExist(err) {
|
if models.IsErrMilestoneNotExist(err) {
|
||||||
ctx.NotFound("GetMilestoneByID", err)
|
ctx.NotFound("GetMilestoneByID", err)
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GitlabDownloaderFactory defines a gitlab downloader factory
|
// GitlabDownloaderFactory defines a gitlab downloader factory
|
||||||
type GitlabDownloaderFactory struct {
|
type GitlabDownloaderFactory struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// New returns a Downloader related to this factory according MigrateOptions
|
// New returns a Downloader related to this factory according MigrateOptions
|
||||||
func (f *GitlabDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
|
func (f *GitlabDownloaderFactory) New(ctx context.Context, opts base.MigrateOptions) (base.Downloader, error) {
|
||||||
@@ -184,16 +183,17 @@ func (g *GitlabDownloader) GetTopics() ([]string, error) {
|
|||||||
|
|
||||||
// GetMilestones returns milestones
|
// GetMilestones returns milestones
|
||||||
func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
|
func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
|
||||||
var perPage = g.maxPerPage
|
perPage := g.maxPerPage
|
||||||
var state = "all"
|
state := "all"
|
||||||
var milestones = make([]*base.Milestone, 0, perPage)
|
milestones := make([]*base.Milestone, 0, perPage)
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
ms, _, err := g.client.Milestones.ListMilestones(g.repoID, &gitlab.ListMilestonesOptions{
|
ms, _, err := g.client.Milestones.ListMilestones(g.repoID, &gitlab.ListMilestonesOptions{
|
||||||
State: &state,
|
State: &state,
|
||||||
ListOptions: gitlab.ListOptions{
|
ListOptions: gitlab.ListOptions{
|
||||||
Page: i,
|
Page: i,
|
||||||
PerPage: perPage,
|
PerPage: perPage,
|
||||||
}}, nil, gitlab.WithContext(g.ctx))
|
},
|
||||||
|
}, nil, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ func (g *GitlabDownloader) GetMilestones() ([]*base.Milestone, error) {
|
|||||||
if m.Description != "" {
|
if m.Description != "" {
|
||||||
desc = m.Description
|
desc = m.Description
|
||||||
}
|
}
|
||||||
var state = "open"
|
state := "open"
|
||||||
var closedAt *time.Time
|
var closedAt *time.Time
|
||||||
if m.State != "" {
|
if m.State != "" {
|
||||||
state = m.State
|
state = m.State
|
||||||
@@ -255,8 +255,8 @@ func (g *GitlabDownloader) normalizeColor(val string) string {
|
|||||||
|
|
||||||
// GetLabels returns labels
|
// GetLabels returns labels
|
||||||
func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
|
func (g *GitlabDownloader) GetLabels() ([]*base.Label, error) {
|
||||||
var perPage = g.maxPerPage
|
perPage := g.maxPerPage
|
||||||
var labels = make([]*base.Label, 0, perPage)
|
labels := make([]*base.Label, 0, perPage)
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
|
ls, _, err := g.client.Labels.ListLabels(g.repoID, &gitlab.ListLabelsOptions{ListOptions: gitlab.ListOptions{
|
||||||
Page: i,
|
Page: i,
|
||||||
@@ -327,8 +327,8 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
|
|||||||
|
|
||||||
// GetReleases returns releases
|
// GetReleases returns releases
|
||||||
func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
|
func (g *GitlabDownloader) GetReleases() ([]*base.Release, error) {
|
||||||
var perPage = g.maxPerPage
|
perPage := g.maxPerPage
|
||||||
var releases = make([]*base.Release, 0, perPage)
|
releases := make([]*base.Release, 0, perPage)
|
||||||
for i := 1; ; i++ {
|
for i := 1; ; i++ {
|
||||||
ls, _, err := g.client.Releases.ListReleases(g.repoID, &gitlab.ListReleasesOptions{
|
ls, _, err := g.client.Releases.ListReleases(g.repoID, &gitlab.ListReleasesOptions{
|
||||||
Page: i,
|
Page: i,
|
||||||
@@ -381,7 +381,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var allIssues = make([]*base.Issue, 0, perPage)
|
allIssues := make([]*base.Issue, 0, perPage)
|
||||||
|
|
||||||
issues, _, err := g.client.Issues.ListProjectIssues(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
|
issues, _, err := g.client.Issues.ListProjectIssues(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -389,7 +389,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
|
|||||||
}
|
}
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
|
|
||||||
var labels = make([]*base.Label, 0, len(issue.Labels))
|
labels := make([]*base.Label, 0, len(issue.Labels))
|
||||||
for _, l := range issue.Labels {
|
for _, l := range issue.Labels {
|
||||||
labels = append(labels, &base.Label{
|
labels = append(labels, &base.Label{
|
||||||
Name: l,
|
Name: l,
|
||||||
@@ -402,7 +402,7 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
var reactions []*base.Reaction
|
var reactions []*base.Reaction
|
||||||
var awardPage = 1
|
awardPage := 1
|
||||||
for {
|
for {
|
||||||
awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
|
awards, _, err := g.client.AwardEmoji.ListIssueAwardEmoji(g.repoID, issue.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -456,9 +456,9 @@ func (g *GitlabDownloader) GetComments(opts base.GetCommentOptions) ([]*base.Com
|
|||||||
return nil, false, fmt.Errorf("unexpected context: %+v", opts.Context)
|
return nil, false, fmt.Errorf("unexpected context: %+v", opts.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allComments = make([]*base.Comment, 0, g.maxPerPage)
|
allComments := make([]*base.Comment, 0, g.maxPerPage)
|
||||||
|
|
||||||
var page = 1
|
page := 1
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var comments []*gitlab.Discussion
|
var comments []*gitlab.Discussion
|
||||||
@@ -503,7 +503,6 @@ func (g *GitlabDownloader) GetComments(opts base.GetCommentOptions) ([]*base.Com
|
|||||||
Created: *c.CreatedAt,
|
Created: *c.CreatedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if resp.NextPage == 0 {
|
if resp.NextPage == 0 {
|
||||||
break
|
break
|
||||||
@@ -526,7 +525,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var allPRs = make([]*base.PullRequest, 0, perPage)
|
allPRs := make([]*base.PullRequest, 0, perPage)
|
||||||
|
|
||||||
prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
|
prs, _, err := g.client.MergeRequests.ListProjectMergeRequests(g.repoID, opt, nil, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -534,7 +533,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
|
|||||||
}
|
}
|
||||||
for _, pr := range prs {
|
for _, pr := range prs {
|
||||||
|
|
||||||
var labels = make([]*base.Label, 0, len(pr.Labels))
|
labels := make([]*base.Label, 0, len(pr.Labels))
|
||||||
for _, l := range pr.Labels {
|
for _, l := range pr.Labels {
|
||||||
labels = append(labels, &base.Label{
|
labels = append(labels, &base.Label{
|
||||||
Name: l,
|
Name: l,
|
||||||
@@ -547,12 +546,12 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
|
|||||||
pr.State = "closed"
|
pr.State = "closed"
|
||||||
}
|
}
|
||||||
|
|
||||||
var mergeTime = pr.MergedAt
|
mergeTime := pr.MergedAt
|
||||||
if merged && pr.MergedAt == nil {
|
if merged && pr.MergedAt == nil {
|
||||||
mergeTime = pr.UpdatedAt
|
mergeTime = pr.UpdatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
var closeTime = pr.ClosedAt
|
closeTime := pr.ClosedAt
|
||||||
if merged && pr.ClosedAt == nil {
|
if merged && pr.ClosedAt == nil {
|
||||||
closeTime = pr.UpdatedAt
|
closeTime = pr.UpdatedAt
|
||||||
}
|
}
|
||||||
@@ -568,7 +567,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
var reactions []*base.Reaction
|
var reactions []*base.Reaction
|
||||||
var awardPage = 1
|
awardPage := 1
|
||||||
for {
|
for {
|
||||||
awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
|
awards, _, err := g.client.AwardEmoji.ListMergeRequestAwardEmoji(g.repoID, pr.IID, &gitlab.ListAwardEmojiOptions{Page: awardPage, PerPage: perPage}, gitlab.WithContext(g.ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -641,13 +640,22 @@ func (g *GitlabDownloader) GetReviews(context base.IssueContext) ([]*base.Review
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var reviews = make([]*base.Review, 0, len(approvals.ApprovedBy))
|
var createdAt time.Time
|
||||||
|
if approvals.CreatedAt != nil {
|
||||||
|
createdAt = *approvals.CreatedAt
|
||||||
|
} else if approvals.UpdatedAt != nil {
|
||||||
|
createdAt = *approvals.UpdatedAt
|
||||||
|
} else {
|
||||||
|
createdAt = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
reviews := make([]*base.Review, 0, len(approvals.ApprovedBy))
|
||||||
for _, user := range approvals.ApprovedBy {
|
for _, user := range approvals.ApprovedBy {
|
||||||
reviews = append(reviews, &base.Review{
|
reviews = append(reviews, &base.Review{
|
||||||
IssueIndex: context.LocalID(),
|
IssueIndex: context.LocalID(),
|
||||||
ReviewerID: int64(user.User.ID),
|
ReviewerID: int64(user.User.ID),
|
||||||
ReviewerName: user.User.Username,
|
ReviewerName: user.User.Username,
|
||||||
CreatedAt: *approvals.UpdatedAt,
|
CreatedAt: createdAt,
|
||||||
// All we get are approvals
|
// All we get are approvals
|
||||||
State: base.ReviewStateApproved,
|
State: base.ReviewStateApproved,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
base "code.gitea.io/gitea/modules/migration"
|
base "code.gitea.io/gitea/modules/migration"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/xanzy/go-gitlab"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGitlabDownloadRepo(t *testing.T) {
|
func TestGitlabDownloadRepo(t *testing.T) {
|
||||||
@@ -310,12 +313,14 @@ func TestGitlabDownloadRepo(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assertReviewsEqual(t, []*base.Review{
|
assertReviewsEqual(t, []*base.Review{
|
||||||
{
|
{
|
||||||
|
IssueIndex: 1,
|
||||||
ReviewerID: 4102996,
|
ReviewerID: 4102996,
|
||||||
ReviewerName: "zeripath",
|
ReviewerName: "zeripath",
|
||||||
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
|
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
|
||||||
State: "APPROVED",
|
State: "APPROVED",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
IssueIndex: 1,
|
||||||
ReviewerID: 527793,
|
ReviewerID: 527793,
|
||||||
ReviewerName: "axifive",
|
ReviewerName: "axifive",
|
||||||
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
|
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC),
|
||||||
@@ -327,6 +332,7 @@ func TestGitlabDownloadRepo(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assertReviewsEqual(t, []*base.Review{
|
assertReviewsEqual(t, []*base.Review{
|
||||||
{
|
{
|
||||||
|
IssueIndex: 2,
|
||||||
ReviewerID: 4575606,
|
ReviewerID: 4575606,
|
||||||
ReviewerName: "real6543",
|
ReviewerName: "real6543",
|
||||||
CreatedAt: time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC),
|
CreatedAt: time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC),
|
||||||
@@ -334,3 +340,137 @@ func TestGitlabDownloadRepo(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}, rvs)
|
}, rvs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
|
||||||
|
// mux is the HTTP request multiplexer used with the test server.
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
// server is a test HTTP server used to provide mock API responses.
|
||||||
|
server := httptest.NewServer(mux)
|
||||||
|
|
||||||
|
// client is the Gitlab client being tested.
|
||||||
|
client, err := gitlab.NewClient("", gitlab.WithBaseURL(server.URL))
|
||||||
|
if err != nil {
|
||||||
|
server.Close()
|
||||||
|
t.Fatalf("Failed to create client: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mux, server, client
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitlabClientMockTeardown(server *httptest.Server) {
|
||||||
|
server.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
type reviewTestCase struct {
|
||||||
|
repoID, prID, reviewerID int
|
||||||
|
reviewerName string
|
||||||
|
createdAt, updatedAt *time.Time
|
||||||
|
expectedCreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertTestCase(t reviewTestCase) (func(w http.ResponseWriter, r *http.Request), base.Review) {
|
||||||
|
var updatedAtField string
|
||||||
|
if t.updatedAt == nil {
|
||||||
|
updatedAtField = ""
|
||||||
|
} else {
|
||||||
|
updatedAtField = `"updated_at": "` + t.updatedAt.Format(time.RFC3339) + `",`
|
||||||
|
}
|
||||||
|
|
||||||
|
var createdAtField string
|
||||||
|
if t.createdAt == nil {
|
||||||
|
createdAtField = ""
|
||||||
|
} else {
|
||||||
|
createdAtField = `"created_at": "` + t.createdAt.Format(time.RFC3339) + `",`
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprint(w, `
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"iid": `+strconv.Itoa(t.prID)+`,
|
||||||
|
"project_id": `+strconv.Itoa(t.repoID)+`,
|
||||||
|
"title": "Approvals API",
|
||||||
|
"description": "Test",
|
||||||
|
"state": "opened",
|
||||||
|
`+createdAtField+`
|
||||||
|
`+updatedAtField+`
|
||||||
|
"merge_status": "cannot_be_merged",
|
||||||
|
"approvals_required": 2,
|
||||||
|
"approvals_left": 1,
|
||||||
|
"approved_by": [
|
||||||
|
{
|
||||||
|
"user": {
|
||||||
|
"name": "Administrator",
|
||||||
|
"username": "`+t.reviewerName+`",
|
||||||
|
"id": `+strconv.Itoa(t.reviewerID)+`,
|
||||||
|
"state": "active",
|
||||||
|
"avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon",
|
||||||
|
"web_url": "http://localhost:3000/root"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`)
|
||||||
|
}
|
||||||
|
review := base.Review{
|
||||||
|
IssueIndex: int64(t.prID),
|
||||||
|
ReviewerID: int64(t.reviewerID),
|
||||||
|
ReviewerName: t.reviewerName,
|
||||||
|
CreatedAt: t.expectedCreatedAt,
|
||||||
|
State: "APPROVED",
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler, review
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitlabGetReviews(t *testing.T) {
|
||||||
|
mux, server, client := gitlabClientMockSetup(t)
|
||||||
|
defer gitlabClientMockTeardown(server)
|
||||||
|
|
||||||
|
repoID := 1324
|
||||||
|
|
||||||
|
downloader := &GitlabDownloader{
|
||||||
|
ctx: context.Background(),
|
||||||
|
client: client,
|
||||||
|
repoID: repoID,
|
||||||
|
}
|
||||||
|
|
||||||
|
createdAt := time.Date(2020, 4, 19, 19, 24, 21, 0, time.UTC)
|
||||||
|
|
||||||
|
for _, testCase := range []reviewTestCase{
|
||||||
|
{
|
||||||
|
repoID: repoID,
|
||||||
|
prID: 1,
|
||||||
|
reviewerID: 801,
|
||||||
|
reviewerName: "someone1",
|
||||||
|
createdAt: nil,
|
||||||
|
updatedAt: &createdAt,
|
||||||
|
expectedCreatedAt: createdAt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
repoID: repoID,
|
||||||
|
prID: 2,
|
||||||
|
reviewerID: 802,
|
||||||
|
reviewerName: "someone2",
|
||||||
|
createdAt: &createdAt,
|
||||||
|
updatedAt: nil,
|
||||||
|
expectedCreatedAt: createdAt,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
repoID: repoID,
|
||||||
|
prID: 3,
|
||||||
|
reviewerID: 803,
|
||||||
|
reviewerName: "someone3",
|
||||||
|
createdAt: nil,
|
||||||
|
updatedAt: nil,
|
||||||
|
expectedCreatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
mock, review := convertTestCase(testCase)
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/api/v4/projects/%d/merge_requests/%d/approvals", testCase.repoID, testCase.prID), mock)
|
||||||
|
|
||||||
|
rvs, err := downloader.GetReviews(base.BasicIssueContext(testCase.prID))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assertReviewsEqual(t, []*base.Review{&review}, rvs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -223,15 +223,15 @@ func assertRepositoryEqual(t *testing.T, expected, actual *base.Repository) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func assertReviewEqual(t *testing.T, expected, actual *base.Review) {
|
func assertReviewEqual(t *testing.T, expected, actual *base.Review) {
|
||||||
assert.Equal(t, expected.ID, actual.ID)
|
assert.Equal(t, expected.ID, actual.ID, "ID")
|
||||||
assert.Equal(t, expected.IssueIndex, actual.IssueIndex)
|
assert.Equal(t, expected.IssueIndex, actual.IssueIndex, "IsssueIndex")
|
||||||
assert.Equal(t, expected.ReviewerID, actual.ReviewerID)
|
assert.Equal(t, expected.ReviewerID, actual.ReviewerID, "ReviewerID")
|
||||||
assert.Equal(t, expected.ReviewerName, actual.ReviewerName)
|
assert.Equal(t, expected.ReviewerName, actual.ReviewerName, "ReviewerName")
|
||||||
assert.Equal(t, expected.Official, actual.Official)
|
assert.Equal(t, expected.Official, actual.Official, "Official")
|
||||||
assert.Equal(t, expected.CommitID, actual.CommitID)
|
assert.Equal(t, expected.CommitID, actual.CommitID, "CommitID")
|
||||||
assert.Equal(t, expected.Content, actual.Content)
|
assert.Equal(t, expected.Content, actual.Content, "Content")
|
||||||
assertTimeEqual(t, expected.CreatedAt, actual.CreatedAt)
|
assert.WithinDuration(t, expected.CreatedAt, actual.CreatedAt, 10*time.Second)
|
||||||
assert.Equal(t, expected.State, actual.State)
|
assert.Equal(t, expected.State, actual.State, "State")
|
||||||
assertReviewCommentsEqual(t, expected.Comments, actual.Comments)
|
assertReviewCommentsEqual(t, expected.Comments, actual.Comments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,6 +97,9 @@ func (r *RepositoryRestorer) GetTopics() ([]string, error) {
|
|||||||
|
|
||||||
bs, err := os.ReadFile(p)
|
bs, err := os.ReadFile(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -286,6 +286,10 @@
|
|||||||
<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
|
<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if $cfg.SkipLocalTwoFA}}checked{{end}}>
|
||||||
<p class="help">{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
|
<p class="help">{{.i18n.Tr "admin.auths.skip_local_two_fa_helper"}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="oauth2_use_custom_url inline field">
|
||||||
|
<div class="ui checkbox">
|
||||||
|
<label><strong>{{.i18n.Tr "admin.auths.oauth2_use_custom_url"}}</strong></label>
|
||||||
<input id="oauth2_use_custom_url" name="oauth2_use_custom_url" type="checkbox" {{if $cfg.CustomURLMapping}}checked{{end}}>
|
<input id="oauth2_use_custom_url" name="oauth2_use_custom_url" type="checkbox" {{if $cfg.CustomURLMapping}}checked{{end}}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -75,7 +75,11 @@
|
|||||||
<pre class="commit-body" style="display: none;">{{RenderCommitBody .Message $commitRepoLink $.Repository.ComposeMetas}}</pre>
|
<pre class="commit-body" style="display: none;">{{RenderCommitBody .Message $commitRepoLink $.Repository.ComposeMetas}}</pre>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td class="text right aligned">{{TimeSince .Author.When $.Lang}}</td>
|
{{if .Committer}}
|
||||||
|
<td class="text right aligned">{{TimeSince .Committer.When $.Lang}}</td>
|
||||||
|
{{else}}
|
||||||
|
<td class="text right aligned">{{TimeSince .Author.When $.Lang}}</td>
|
||||||
|
{{end}}
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
</span>
|
</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</th>
|
</th>
|
||||||
<th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Author}}{{TimeSince .LatestCommit.Author.When $.Lang}}{{end}}{{end}}</th>
|
<th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When $.Lang}}{{end}}{{end}}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -496,9 +496,11 @@ export function initRepoPullRequestReview() {
|
|||||||
<tr class="add-comment" data-line-type="${lineType}">
|
<tr class="add-comment" data-line-type="${lineType}">
|
||||||
${isSplit ? `
|
${isSplit ? `
|
||||||
<td class="lines-num"></td>
|
<td class="lines-num"></td>
|
||||||
|
<td class="lines-escape"></td>
|
||||||
<td class="lines-type-marker"></td>
|
<td class="lines-type-marker"></td>
|
||||||
<td class="add-comment-left"></td>
|
<td class="add-comment-left"></td>
|
||||||
<td class="lines-num"></td>
|
<td class="lines-num"></td>
|
||||||
|
<td class="lines-escape"></td>
|
||||||
<td class="lines-type-marker"></td>
|
<td class="lines-type-marker"></td>
|
||||||
<td class="add-comment-right"></td>
|
<td class="add-comment-right"></td>
|
||||||
` : `
|
` : `
|
||||||
|
|||||||
@@ -150,13 +150,12 @@ export function initUserAuthWebAuthnRegister() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!detectWebAuthnSupport()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#webauthn-error').modal({allowMultiple: false});
|
$('#webauthn-error').modal({allowMultiple: false});
|
||||||
$('#register-webauthn').on('click', (e) => {
|
$('#register-webauthn').on('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
if (!detectWebAuthnSupport()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
webAuthnRegisterRequest();
|
webAuthnRegisterRequest();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user