mirror of
https://github.com/go-gitea/gitea
synced 2026-02-06 13:01:12 +00:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ded9a218f7 | ||
|
|
03f06d5ac1 | ||
|
|
8cd6be1723 | ||
|
|
f882747209 | ||
|
|
92796dcc8b | ||
|
|
48450939c7 | ||
|
|
7dcf9dd4d8 | ||
|
|
73189f0a19 | ||
|
|
92f72d678c | ||
|
|
7e26f2b626 | ||
|
|
3d34cdabb9 | ||
|
|
f51a19c537 | ||
|
|
068e96fbd2 | ||
|
|
721e422fa7 | ||
|
|
6f323d13dd | ||
|
|
0e95e7460e | ||
|
|
c057590a3a | ||
|
|
a8534ac4a4 | ||
|
|
e23ad87b55 | ||
|
|
e93a4a0174 | ||
|
|
601766d1fa | ||
|
|
ee6d5124bd | ||
|
|
8188cdfcd2 | ||
|
|
82d50af721 | ||
|
|
6117c8b15a | ||
|
|
ba16df8da3 | ||
|
|
87630a6583 | ||
|
|
56716f5834 | ||
|
|
65b5c8e532 | ||
|
|
9dc53ba65f | ||
|
|
d25c74f353 | ||
|
|
795913e3c7 | ||
|
|
e609ef9585 | ||
|
|
f321cdced7 | ||
|
|
f241201484 | ||
|
|
43bddc1405 | ||
|
|
9414260d67 | ||
|
|
3c07ed0911 | ||
|
|
995ae06a6e | ||
|
|
14342047ad | ||
|
|
d6d62c071f | ||
|
|
7a2daae7c3 | ||
|
|
5bc3fbd511 | ||
|
|
b0a057f1c0 | ||
|
|
43a8547df6 | ||
|
|
291787a5ef | ||
|
|
e504410708 | ||
|
|
2ccf940464 | ||
|
|
169c08e20a | ||
|
|
d5856fece7 | ||
|
|
0571ddc368 | ||
|
|
6b7ce726c2 | ||
|
|
92b5f48c40 | ||
|
|
8043fbce09 | ||
|
|
556e2d5506 | ||
|
|
675c14aba6 | ||
|
|
4b4adb1cc9 | ||
|
|
19df07f021 | ||
|
|
5a84558e7c | ||
|
|
46053c092d | ||
|
|
3f032759ed |
+20
-7
@@ -851,7 +851,8 @@ steps:
|
||||
image: plugins/hugo:latest
|
||||
pull: always
|
||||
commands:
|
||||
- apk add --no-cache make bash curl
|
||||
# https://github.com/drone-plugins/drone-hugo/issues/36
|
||||
- apk upgrade --no-cache libcurl && apk add --no-cache make bash curl
|
||||
- cd docs
|
||||
- make trans-copy clean build
|
||||
|
||||
@@ -902,8 +903,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-amd64
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-amd64
|
||||
- ${DRONE_TAG:1:4}-linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -920,8 +924,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-amd64-rootless
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-amd64-rootless
|
||||
- ${DRONE_TAG:1:4}-linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -1126,8 +1133,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-arm64
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-arm64
|
||||
- ${DRONE_TAG:1:4}-linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -1144,8 +1154,11 @@ steps:
|
||||
image: techknowlogick/drone-docker:latest
|
||||
settings:
|
||||
dockerfile: Dockerfile.rootless
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
auto_tag_suffix: linux-arm64-rootless
|
||||
tags:
|
||||
- ${DRONE_TAG##v}-linux-arm64-rootless
|
||||
- ${DRONE_TAG:1:4}-linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.io
|
||||
@@ -1299,7 +1312,7 @@ steps:
|
||||
image: plugins/manifest
|
||||
pull: always
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
ignore_missing: true
|
||||
spec: docker/manifest.rootless.tmpl
|
||||
password:
|
||||
@@ -1310,7 +1323,7 @@ steps:
|
||||
- name: manifest
|
||||
image: plugins/manifest
|
||||
settings:
|
||||
auto_tag: true
|
||||
auto_tag: false
|
||||
ignore_missing: true
|
||||
spec: docker/manifest.tmpl
|
||||
password:
|
||||
|
||||
@@ -4,6 +4,60 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.17.4](https://github.com/go-gitea/gitea/releases/tag/1.17.4) - 2022-12-21
|
||||
|
||||
* SECURITY
|
||||
* Do not allow Ghost access to limited visible user/org (#21849) (#21875)
|
||||
* Fix package access for admins and inactive users (#21580) (#21592)
|
||||
* ENHANCEMENTS
|
||||
* Fix button in branch list, avoid unexpected page jump before restore branch actually done (#21562) (#21927)
|
||||
* Fix vertical align of committer avatar rendered by email address (#21884) (#21919)
|
||||
* Fix setting HTTP headers after write (#21833) (#21874)
|
||||
* Ignore line anchor links with leading zeroes (#21728) (#21777)
|
||||
* Enable Monaco automaticLayout (#21516)
|
||||
* BUGFIXES
|
||||
* Do not list active repositories as unadopted (#22034) (#22167)
|
||||
* Correctly handle moved files in apply patch (#22118) (#22136)
|
||||
* Fix condition for is_internal (#22095) (#22131)
|
||||
* Fix permission check on issue/pull lock (#22114)
|
||||
* Fix sorting admin user list by last login (#22081) (#22106)
|
||||
* Workaround for container registry push/pull errors (#21862) (#22069)
|
||||
* Fix issue/PR numbers (#22037) (#22045)
|
||||
* Handle empty author names (#21902) (#22028)
|
||||
* Fix ListBranches to handle empty case (#21921) (#22025)
|
||||
* Fix enabling partial clones on 1.17 (#21809)
|
||||
* Prevent panic in doctor command when running default checks (#21791) (#21808)
|
||||
* Upgrade golang.org/x/crypto (#21792) (#21794)
|
||||
* Init git module before database migration (#21764) (#21766)
|
||||
* Set last login when activating account (#21731) (#21754)
|
||||
* Add HEAD fix to gitea doctor (#21352) (#21751)
|
||||
* Fix UI language switching bug (#21597) (#21748)
|
||||
* Remove semver compatible flag and change pypi to an array of test cases (#21708) (#21729)
|
||||
* Allow local package identifiers for PyPI packages (#21690) (#21726)
|
||||
* Fix repository adoption on Windows (#21646) (#21651)
|
||||
* Sync git hooks when config file path changed (#21619) (#21625)
|
||||
* Added check for disabled Packages (#21540) (#21614)
|
||||
* Fix `Timestamp.IsZero` (#21593) (#21604)
|
||||
* Fix issues count bug (#21600)
|
||||
* Support binary deploy in npm packages (#21589)
|
||||
* Update milestone counters when issue is deleted (#21459) (#21586)
|
||||
* SessionUser protection against nil pointer dereference (#21581)
|
||||
* Case-insensitive NuGet symbol file GUID (#21409) (#21575)
|
||||
* Suppress `ExternalLoginUserNotExist` error (#21504) (#21572)
|
||||
* Prevent Authorization header for presigned LFS urls (#21531) (#21569)
|
||||
* Update binding to fix bugs (#21560)
|
||||
* Fix generating compare link (#21519) (#21530)
|
||||
* Ignore error when retrieving changed PR review files (#21487) (#21524)
|
||||
* Fix incorrect notification commit url (#21479) (#21483)
|
||||
* Display total commit count in hook message (#21400) (#21481)
|
||||
* Enforce grouped NuGet search results (#21442) (#21480)
|
||||
* Return 404 when user is not found on avatar (#21476) (#21477)
|
||||
* Normalize NuGet package version on upload (#22186) (#22201)
|
||||
* MISC
|
||||
* Check for zero time instant in TimeStamp.IsZero() (#22171) (#22173)
|
||||
* Fix warn in database structs sync (#22111)
|
||||
* Allow for resolution of NPM registry paths that match upstream (#21568) (#21723)
|
||||
|
||||
## [1.17.3](https://github.com/go-gitea/gitea/releases/tag/v1.17.3) - 2022-10-15
|
||||
|
||||
* SECURITY
|
||||
|
||||
@@ -5,7 +5,7 @@ go 1.18
|
||||
require (
|
||||
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681
|
||||
gitea.com/go-chi/cache v0.2.0
|
||||
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
|
||||
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||
@@ -90,11 +90,11 @@ require (
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
go.jolheiser.com/hcaptcha v0.0.4
|
||||
go.jolheiser.com/pwn v0.0.3
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc
|
||||
golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10
|
||||
golang.org/x/text v0.3.8
|
||||
golang.org/x/sys v0.2.0
|
||||
golang.org/x/text v0.4.0
|
||||
golang.org/x/tools v0.1.12
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.66.4
|
||||
|
||||
@@ -69,8 +69,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU
|
||||
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
|
||||
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4=
|
||||
gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
|
||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||
gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w=
|
||||
gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE=
|
||||
@@ -1676,8 +1676,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
|
||||
golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891 h1:WhEPFM1Ck5gaKybeSWvzI7Y/cd8K9K5tJGRxXMACOBA=
|
||||
golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -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-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-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1936,12 +1936,12 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1951,8 +1951,8 @@ 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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
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-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -6,10 +6,12 @@ package integrations
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -433,6 +435,10 @@ func TestPackageContainer(t *testing.T) {
|
||||
|
||||
assert.Equal(t, fmt.Sprintf("%d", len(blobContent)), resp.Header().Get("Content-Length"))
|
||||
assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest"))
|
||||
|
||||
req = NewRequest(t, "HEAD", fmt.Sprintf("%s/blobs/%s", url, blobDigest))
|
||||
addTokenAuthHeader(req, anonymousToken)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("GetBlob", func(t *testing.T) {
|
||||
@@ -545,6 +551,32 @@ func TestPackageContainer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/go-gitea/gitea/issues/19586
|
||||
t.Run("ParallelUpload", func(t *testing.T) {
|
||||
defer PrintCurrentTest(t)()
|
||||
|
||||
url := fmt.Sprintf("%sv2/%s/parallel", setting.AppURL, user.Name)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
|
||||
content := []byte{byte(i)}
|
||||
digest := fmt.Sprintf("sha256:%x", sha256.Sum256(content))
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/blobs/uploads?digest=%s", url, digest), bytes.NewReader(content))
|
||||
addTokenAuthHeader(req, userToken)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
assert.Equal(t, digest, resp.Header().Get("Docker-Content-Digest"))
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
t.Run("OwnerNameChange", func(t *testing.T) {
|
||||
defer PrintCurrentTest(t)()
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ func TestPackageNpm(t *testing.T) {
|
||||
packageTag2 := "release"
|
||||
packageAuthor := "KN4CK3R"
|
||||
packageDescription := "Test Description"
|
||||
packageBinName := "cli"
|
||||
packageBinPath := "./cli.sh"
|
||||
|
||||
data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA"
|
||||
upload := `{
|
||||
@@ -51,6 +53,9 @@ func TestPackageNpm(t *testing.T) {
|
||||
"author": {
|
||||
"name": "` + packageAuthor + `"
|
||||
},
|
||||
"bin": {
|
||||
"` + packageBinName + `": "` + packageBinPath + `"
|
||||
},
|
||||
"dist": {
|
||||
"integrity": "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==",
|
||||
"shasum": "aaa7eaf852a948b0aa05afeda35b1badca155d90"
|
||||
@@ -118,10 +123,16 @@ func TestPackageNpm(t *testing.T) {
|
||||
b, _ := base64.StdEncoding.DecodeString(data)
|
||||
assert.Equal(t, b, resp.Body.Bytes())
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("%s/-/%s", root, filename))
|
||||
req = addTokenAuthHeader(req, token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
assert.Equal(t, b, resp.Body.Bytes())
|
||||
|
||||
pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, pvs, 1)
|
||||
assert.Equal(t, int64(1), pvs[0].DownloadCount)
|
||||
assert.Equal(t, int64(2), pvs[0].DownloadCount)
|
||||
})
|
||||
|
||||
t.Run("PackageMetadata", func(t *testing.T) {
|
||||
@@ -150,6 +161,7 @@ func TestPackageNpm(t *testing.T) {
|
||||
assert.Equal(t, packageName, pmv.Name)
|
||||
assert.Equal(t, packageDescription, pmv.Description)
|
||||
assert.Equal(t, packageAuthor, pmv.Author.Name)
|
||||
assert.Equal(t, packageBinPath, pmv.Bin[packageBinName])
|
||||
assert.Equal(t, "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==", pmv.Dist.Integrity)
|
||||
assert.Equal(t, "aaa7eaf852a948b0aa05afeda35b1badca155d90", pmv.Dist.Shasum)
|
||||
assert.Equal(t, fmt.Sprintf("%s%s/-/%s/%s", setting.AppURL, root[1:], packageVersion, filename), pmv.Dist.Tarball)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
@@ -43,23 +44,29 @@ func TestPackageNuGet(t *testing.T) {
|
||||
symbolFilename := "test.pdb"
|
||||
symbolID := "d910bb6948bd4c6cb40155bcf52c3c94"
|
||||
|
||||
var buf bytes.Buffer
|
||||
archive := zip.NewWriter(&buf)
|
||||
w, _ := archive.Create("package.nuspec")
|
||||
w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>` + packageName + `</id>
|
||||
<version>` + packageVersion + `</version>
|
||||
<authors>` + packageAuthors + `</authors>
|
||||
<description>` + packageDescription + `</description>
|
||||
<group targetFramework=".NETStandard2.0">
|
||||
<dependency id="Microsoft.CSharp" version="4.5.0" />
|
||||
</group>
|
||||
</metadata>
|
||||
</package>`))
|
||||
archive.Close()
|
||||
content := buf.Bytes()
|
||||
createPackage := func(id, version string) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
archive := zip.NewWriter(&buf)
|
||||
w, _ := archive.Create("package.nuspec")
|
||||
w.Write([]byte(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>` + id + `</id>
|
||||
<version>` + version + `</version>
|
||||
<authors>` + packageAuthors + `</authors>
|
||||
<description>` + packageDescription + `</description>
|
||||
<dependencies>
|
||||
<group targetFramework=".NETStandard2.0">
|
||||
<dependency id="Microsoft.CSharp" version="4.5.0" />
|
||||
</group>
|
||||
</dependencies>
|
||||
</metadata>
|
||||
</package>`))
|
||||
archive.Close()
|
||||
return &buf
|
||||
}
|
||||
|
||||
content, _ := ioutil.ReadAll(createPackage(packageName, packageVersion))
|
||||
|
||||
url := fmt.Sprintf("/api/packages/%s/nuget", user.Name)
|
||||
|
||||
@@ -159,7 +166,7 @@ func TestPackageNuGet(t *testing.T) {
|
||||
t.Run("SymbolPackage", func(t *testing.T) {
|
||||
defer PrintCurrentTest(t)()
|
||||
|
||||
createPackage := func(id, packageType string) io.Reader {
|
||||
createSymbolPackage := func(id, packageType string) io.Reader {
|
||||
var buf bytes.Buffer
|
||||
archive := zip.NewWriter(&buf)
|
||||
|
||||
@@ -185,15 +192,15 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||
return &buf
|
||||
}
|
||||
|
||||
req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage("unknown-package", "SymbolsPackage"))
|
||||
req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage("unknown-package", "SymbolsPackage"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "DummyPackage"))
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "DummyPackage"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
@@ -237,7 +244,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||
}
|
||||
}
|
||||
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
|
||||
req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusConflict)
|
||||
})
|
||||
@@ -279,7 +286,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, symbolID, symbolFilename))
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFffff/%s", url, symbolFilename, symbolID, symbolFilename))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
@@ -315,6 +322,43 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
|
||||
assert.Equal(t, c.ExpectedTotal, result.TotalHits, "case %d: unexpected total hits", i)
|
||||
assert.Len(t, result.Data, c.ExpectedResults, "case %d: unexpected result count", i)
|
||||
}
|
||||
|
||||
t.Run("EnforceGrouped", func(t *testing.T) {
|
||||
defer PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequestWithBody(t, "PUT", url, createPackage(packageName+".dummy", "1.0.0"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
req = NewRequestWithBody(t, "PUT", url, createPackage(packageName, "1.0.99"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s", url, packageName))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
var result nuget.SearchResultResponse
|
||||
DecodeJSON(t, resp, &result)
|
||||
|
||||
assert.EqualValues(t, 3, result.TotalHits)
|
||||
assert.Len(t, result.Data, 2)
|
||||
for _, sr := range result.Data {
|
||||
if sr.ID == packageName {
|
||||
assert.Len(t, sr.Versions, 2)
|
||||
} else {
|
||||
assert.Len(t, sr.Versions, 1)
|
||||
}
|
||||
}
|
||||
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName+".dummy", "1.0.0"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, "1.0.99"))
|
||||
req = AddBasicAuthHeader(req, user.Name)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("RegistrationService", func(t *testing.T) {
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestPackagePyPI(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
|
||||
|
||||
packageName := "test-package"
|
||||
packageVersion := "1.0.1"
|
||||
packageVersion := "1!1.0.1+r1234"
|
||||
packageAuthor := "KN4CK3R"
|
||||
packageDescription := "Test Description"
|
||||
|
||||
@@ -71,7 +71,7 @@ func TestPackagePyPI(t *testing.T) {
|
||||
|
||||
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, pd.SemVer)
|
||||
assert.Nil(t, pd.SemVer)
|
||||
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
|
||||
assert.Equal(t, packageName, pd.Package.Name)
|
||||
assert.Equal(t, packageVersion, pd.Version.Version)
|
||||
@@ -99,7 +99,7 @@ func TestPackagePyPI(t *testing.T) {
|
||||
|
||||
pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, pd.SemVer)
|
||||
assert.Nil(t, pd.SemVer)
|
||||
assert.IsType(t, &pypi.Metadata{}, pd.Metadata)
|
||||
assert.Equal(t, packageName, pd.Package.Name)
|
||||
assert.Equal(t, packageVersion, pd.Version.Version)
|
||||
@@ -163,7 +163,7 @@ func TestPackagePyPI(t *testing.T) {
|
||||
nodes := htmlDoc.doc.Find("a").Nodes
|
||||
assert.Len(t, nodes, 2)
|
||||
|
||||
hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, packageName, packageVersion, hashSHA256))
|
||||
hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256))
|
||||
|
||||
for _, a := range nodes {
|
||||
for _, att := range a.Attr {
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
func TestPackageAPI(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session)
|
||||
@@ -143,6 +144,27 @@ func TestPackageAPI(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestPackageAccess(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
|
||||
inactive := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9}).(*user_model.User)
|
||||
|
||||
uploadPackage := func(doer, owner *user_model.User, expectedStatus int) {
|
||||
url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/file.bin", owner.Name)
|
||||
req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1}))
|
||||
AddBasicAuthHeader(req, doer.Name)
|
||||
MakeRequest(t, req, expectedStatus)
|
||||
}
|
||||
|
||||
uploadPackage(user, inactive, http.StatusUnauthorized)
|
||||
uploadPackage(inactive, inactive, http.StatusUnauthorized)
|
||||
uploadPackage(inactive, user, http.StatusUnauthorized)
|
||||
uploadPackage(admin, inactive, http.StatusCreated)
|
||||
uploadPackage(admin, user, http.StatusCreated)
|
||||
}
|
||||
|
||||
func TestPackageCleanup(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -430,19 +431,19 @@ var tokenCounter int64
|
||||
|
||||
func getTokenForLoggedInUser(t testing.TB, session *TestSession) string {
|
||||
t.Helper()
|
||||
tokenCounter++
|
||||
req := NewRequest(t, "GET", "/user/settings/applications")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{
|
||||
"_csrf": doc.GetCSRF(),
|
||||
"name": fmt.Sprintf("api-testing-token-%d", tokenCounter),
|
||||
"name": fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)),
|
||||
})
|
||||
resp = session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
req = NewRequest(t, "GET", "/user/settings/applications")
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
token := htmlDoc.doc.Find(".ui.info p").Text()
|
||||
assert.NotEmpty(t, token)
|
||||
return token
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,11 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
||||
reqOne := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/"+path.Base(commitURL)+"/status")
|
||||
testRepoCommitsWithStatus(t, session.MakeRequest(t, req, http.StatusOK), session.MakeRequest(t, reqOne, http.StatusOK), state)
|
||||
|
||||
// By short SHA
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/"+path.Base(commitURL)[:10]+"/statuses")
|
||||
reqOne = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/"+path.Base(commitURL)[:10]+"/status")
|
||||
testRepoCommitsWithStatus(t, session.MakeRequest(t, req, http.StatusOK), session.MakeRequest(t, reqOne, http.StatusOK), state)
|
||||
|
||||
// By Ref
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/master/statuses")
|
||||
reqOne = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/commits/master/status")
|
||||
|
||||
+1
-1
@@ -287,7 +287,7 @@ func (a *Action) GetRefLink() string {
|
||||
return a.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.BranchPrefix))
|
||||
case strings.HasPrefix(a.RefName, git.TagPrefix):
|
||||
return a.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.TagPrefix))
|
||||
case len(a.RefName) == 40 && git.IsValidSHAPattern(a.RefName):
|
||||
case len(a.RefName) == git.SHAFullLength && git.IsValidSHAPattern(a.RefName):
|
||||
return a.GetRepoLink() + "/src/commit/" + a.RefName
|
||||
default:
|
||||
// FIXME: we will just assume it's a branch - this was the old way - at some point we may want to enforce that there is always a ref here.
|
||||
|
||||
@@ -65,11 +65,17 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) {
|
||||
|
||||
// PaddedKeyID show KeyID padded to 16 characters
|
||||
func (key *GPGKey) PaddedKeyID() string {
|
||||
if len(key.KeyID) > 15 {
|
||||
return key.KeyID
|
||||
return PaddedKeyID(key.KeyID)
|
||||
}
|
||||
|
||||
// PaddedKeyID show KeyID padded to 16 characters
|
||||
func PaddedKeyID(keyID string) string {
|
||||
if len(keyID) > 15 {
|
||||
return keyID
|
||||
}
|
||||
|
||||
zeros := "0000000000000000"
|
||||
return zeros[0:16-len(key.KeyID)] + key.KeyID
|
||||
return zeros[0:16-len(keyID)] + keyID
|
||||
}
|
||||
|
||||
// ListGPGKeys returns a list of public keys belongs to given user.
|
||||
|
||||
@@ -19,8 +19,12 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
)
|
||||
|
||||
// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
|
||||
const DefaultAvatarPixelSize = 28
|
||||
const (
|
||||
// DefaultAvatarClass is the default class of a rendered avatar
|
||||
DefaultAvatarClass = "ui avatar vm"
|
||||
// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
|
||||
DefaultAvatarPixelSize = 28
|
||||
)
|
||||
|
||||
// EmailHash represents a pre-generated hash map (mainly used by LibravatarURL, it queries email server's DNS records)
|
||||
type EmailHash struct {
|
||||
|
||||
@@ -291,6 +291,10 @@ func NewCommitStatus(opts NewCommitStatusOptions) error {
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: no user specified", repoPath, opts.SHA)
|
||||
}
|
||||
|
||||
if _, err := git.NewIDFromString(opts.SHA); err != nil {
|
||||
return fmt.Errorf("NewCommitStatus[%s, %s]: invalid sha: %w", repoPath, opts.SHA, err)
|
||||
}
|
||||
|
||||
// Get the next Status Index
|
||||
idx, err := GetNextCommitStatusIndex(opts.Repo.ID, opts.SHA)
|
||||
if err != nil {
|
||||
|
||||
@@ -881,7 +881,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
|
||||
}
|
||||
}
|
||||
case CommentTypeReopen, CommentTypeClose:
|
||||
if err = updateIssueClosedNum(ctx, opts.Issue); err != nil {
|
||||
if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
+3
-16
@@ -722,7 +722,8 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
|
||||
}
|
||||
}
|
||||
|
||||
if err := updateIssueClosedNum(ctx, issue); err != nil {
|
||||
// update repository's issue closed number
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1006,12 +1007,7 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
|
||||
}
|
||||
}
|
||||
|
||||
if opts.IsPull {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID)
|
||||
} else {
|
||||
_, err = e.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", opts.Issue.RepoID)
|
||||
}
|
||||
if err != nil {
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.IsPull, false); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -2104,15 +2100,6 @@ func (issue *Issue) BlockingDependencies(ctx context.Context) (issueDeps []*Depe
|
||||
return issueDeps, err
|
||||
}
|
||||
|
||||
func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) {
|
||||
if issue.IsPull {
|
||||
err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls")
|
||||
} else {
|
||||
err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
||||
func FindAndUpdateIssueMentions(ctx context.Context, issue *Issue, doer *user_model.User, content string) (mentions []*user_model.User, err error) {
|
||||
rawMentions := references.FindAllMentionsMarkdown(content)
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
@@ -496,6 +497,13 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
|
||||
return nil
|
||||
}
|
||||
|
||||
// Some migration tasks depend on the git command
|
||||
if git.DefaultContext == nil {
|
||||
if err = git.InitSimple(context.Background()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate
|
||||
for i, m := range migrations[v-minDBVersion:] {
|
||||
log.Info("Migration[%d]: %s", v+int64(i), m.Description())
|
||||
|
||||
@@ -448,8 +448,9 @@ func CountOrgs(opts FindOrgOptions) (int64, error) {
|
||||
|
||||
// HasOrgOrUserVisible tells if the given user can see the given org or user
|
||||
func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool {
|
||||
// Not SignedUser
|
||||
if user == nil {
|
||||
// If user is nil, it's an anonymous user/request.
|
||||
// The Ghost user is handled like an anonymous user.
|
||||
if user == nil || user.IsGhost() {
|
||||
return orgOrUser.Visibility == structs.VisibleTypePublic
|
||||
}
|
||||
|
||||
|
||||
@@ -289,7 +289,7 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
|
||||
|
||||
sess := db.GetEngine(ctx).
|
||||
Table("package_version").
|
||||
Join("LEFT", "package_version pv2", "package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))").
|
||||
Join("LEFT", "package_version pv2", "package_version.package_id = pv2.package_id AND pv2.is_internal = ? AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))", false).
|
||||
Join("INNER", "package", "package.id = package_version.package_id").
|
||||
Where(cond)
|
||||
|
||||
|
||||
+6
-11
@@ -562,24 +562,19 @@ func repoStatsCorrectIssueNumComments(ctx context.Context, id int64) error {
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumIssues(ctx context.Context, id int64) error {
|
||||
return repoStatsCorrectNum(ctx, id, false, "num_issues")
|
||||
return repo_model.UpdateRepoIssueNumbers(ctx, id, false, false)
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumPulls(ctx context.Context, id int64) error {
|
||||
return repoStatsCorrectNum(ctx, id, true, "num_pulls")
|
||||
}
|
||||
|
||||
func repoStatsCorrectNum(ctx context.Context, id int64, isPull bool, field string) error {
|
||||
_, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=?) WHERE id=?", id, isPull, id)
|
||||
return err
|
||||
return repo_model.UpdateRepoIssueNumbers(ctx, id, true, false)
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumClosedIssues(ctx context.Context, id int64) error {
|
||||
return repo_model.StatsCorrectNumClosed(ctx, id, false, "num_closed_issues")
|
||||
return repo_model.UpdateRepoIssueNumbers(ctx, id, false, true)
|
||||
}
|
||||
|
||||
func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error {
|
||||
return repo_model.StatsCorrectNumClosed(ctx, id, true, "num_closed_pulls")
|
||||
return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true)
|
||||
}
|
||||
|
||||
func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) {
|
||||
@@ -607,7 +602,7 @@ func CheckRepoStats(ctx context.Context) error {
|
||||
},
|
||||
// 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),
|
||||
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=?)", false),
|
||||
repoStatsCorrectNumIssues,
|
||||
"repository count 'num_issues'",
|
||||
},
|
||||
@@ -619,7 +614,7 @@ func CheckRepoStats(ctx context.Context) error {
|
||||
},
|
||||
// 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),
|
||||
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=?)", true),
|
||||
repoStatsCorrectNumPulls,
|
||||
"repository count 'num_pulls'",
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@ type PushMirror struct {
|
||||
Repo *Repository `xorm:"-"`
|
||||
RemoteName string
|
||||
|
||||
SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"`
|
||||
Interval time.Duration
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
|
||||
|
||||
+26
-35
@@ -23,6 +23,8 @@ import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
||||
// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
|
||||
@@ -319,13 +321,7 @@ func (repo *Repository) LoadUnits(ctx context.Context) (err error) {
|
||||
|
||||
// UnitEnabled if this repository has the given unit enabled
|
||||
func (repo *Repository) UnitEnabled(tp unit.Type) (result bool) {
|
||||
if err := db.WithContext(func(ctx *db.Context) error {
|
||||
result = repo.UnitEnabledCtx(ctx, tp)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Error("repo.UnitEnabled: %v", err)
|
||||
}
|
||||
return
|
||||
return repo.UnitEnabledCtx(db.DefaultContext, tp)
|
||||
}
|
||||
|
||||
// UnitEnabled if this repository has the given unit enabled
|
||||
@@ -760,33 +756,28 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64,
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// StatsCorrectNumClosed update repository's issue related numbers
|
||||
func StatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error {
|
||||
_, err := db.Exec(ctx, "UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id)
|
||||
// UpdateRepoIssueNumbers updates one of a repositories amount of (open|closed) (issues|PRs) with the current count
|
||||
func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed bool) error {
|
||||
field := "num_"
|
||||
if isClosed {
|
||||
field += "closed_"
|
||||
}
|
||||
if isPull {
|
||||
field += "pulls"
|
||||
} else {
|
||||
field += "issues"
|
||||
}
|
||||
|
||||
subQuery := builder.Select("count(*)").
|
||||
From("issue").Where(builder.Eq{
|
||||
"repo_id": repoID,
|
||||
"is_pull": isPull,
|
||||
}.And(builder.If(isClosed, builder.Eq{"is_closed": isClosed})))
|
||||
|
||||
// builder.Update(cond) will generate SQL like UPDATE ... SET cond
|
||||
query := builder.Update(builder.Eq{field: subQuery}).
|
||||
From("repository").
|
||||
Where(builder.Eq{"id": repoID})
|
||||
_, err := db.Exec(ctx, query)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateRepoIssueNumbers update repository issue numbers
|
||||
func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed bool) error {
|
||||
e := db.GetEngine(ctx)
|
||||
if isPull {
|
||||
if _, err := e.ID(repoID).Decr("num_pulls").Update(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
if isClosed {
|
||||
if _, err := e.ID(repoID).Decr("num_closed_pulls").Update(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, err := e.ID(repoID).Decr("num_issues").Update(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
if isClosed {
|
||||
if _, err := e.ID(repoID).Decr("num_closed_issues").Update(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
||||
&user_model.Setting{UserID: u.ID},
|
||||
&pull_model.AutoMerge{DoerID: u.ID},
|
||||
&pull_model.ReviewState{UserID: u.ID},
|
||||
&user_model.Redirect{RedirectUserID: u.ID},
|
||||
); err != nil {
|
||||
return fmt.Errorf("deleteBeans: %v", err)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ package appstate
|
||||
|
||||
// RuntimeState contains app state for runtime, and we can save remote version for update checker here in future
|
||||
type RuntimeState struct {
|
||||
LastAppPath string `json:"last_app_path"`
|
||||
LastAppPath string `json:"last_app_path"`
|
||||
LastCustomConf string `json:"last_custom_conf"`
|
||||
}
|
||||
|
||||
// Name returns the item name
|
||||
|
||||
@@ -388,7 +388,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if len(refName) == 40 {
|
||||
} else if len(refName) == git.SHAFullLength {
|
||||
ctx.Repo.CommitID = refName
|
||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName)
|
||||
if err != nil {
|
||||
|
||||
+49
-13
@@ -34,6 +34,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/templates"
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/services/auth"
|
||||
@@ -322,9 +323,9 @@ func (ctx *Context) plainTextInternal(skip, status int, bs []byte) {
|
||||
if statusPrefix == 4 || statusPrefix == 5 {
|
||||
log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
|
||||
}
|
||||
ctx.Resp.WriteHeader(status)
|
||||
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
|
||||
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
ctx.Resp.WriteHeader(status)
|
||||
if _, err := ctx.Resp.Write(bs); err != nil {
|
||||
log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
|
||||
}
|
||||
@@ -345,16 +346,45 @@ func (ctx *Context) RespHeader() http.Header {
|
||||
return ctx.Resp.Header()
|
||||
}
|
||||
|
||||
type ServeHeaderOptions struct {
|
||||
ContentType string // defaults to "application/octet-stream"
|
||||
ContentTypeCharset string
|
||||
Disposition string // defaults to "attachment"
|
||||
Filename string
|
||||
CacheDuration time.Duration // defaults to 5 minutes
|
||||
}
|
||||
|
||||
// SetServeHeaders sets necessary content serve headers
|
||||
func (ctx *Context) SetServeHeaders(filename string) {
|
||||
ctx.Resp.Header().Set("Content-Description", "File Transfer")
|
||||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
|
||||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+filename)
|
||||
ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
|
||||
ctx.Resp.Header().Set("Expires", "0")
|
||||
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
|
||||
ctx.Resp.Header().Set("Pragma", "public")
|
||||
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
|
||||
func (ctx *Context) SetServeHeaders(opts *ServeHeaderOptions) {
|
||||
header := ctx.Resp.Header()
|
||||
|
||||
contentType := typesniffer.ApplicationOctetStream
|
||||
if opts.ContentType != "" {
|
||||
if opts.ContentTypeCharset != "" {
|
||||
contentType = opts.ContentType + "; charset=" + strings.ToLower(opts.ContentTypeCharset)
|
||||
} else {
|
||||
contentType = opts.ContentType
|
||||
}
|
||||
}
|
||||
header.Set("Content-Type", contentType)
|
||||
header.Set("X-Content-Type-Options", "nosniff")
|
||||
|
||||
if opts.Filename != "" {
|
||||
disposition := opts.Disposition
|
||||
if disposition == "" {
|
||||
disposition = "attachment"
|
||||
}
|
||||
|
||||
backslashEscapedName := strings.ReplaceAll(strings.ReplaceAll(opts.Filename, `\`, `\\`), `"`, `\"`) // \ -> \\, " -> \"
|
||||
header.Set("Content-Disposition", fmt.Sprintf(`%s; filename="%s"; filename*=UTF-8''%s`, disposition, backslashEscapedName, url.PathEscape(opts.Filename)))
|
||||
header.Set("Access-Control-Expose-Headers", "Content-Disposition")
|
||||
}
|
||||
|
||||
duration := opts.CacheDuration
|
||||
if duration == 0 {
|
||||
duration = 5 * time.Minute
|
||||
}
|
||||
httpcache.AddCacheControlToHeader(header, duration)
|
||||
}
|
||||
|
||||
// ServeContent serves content to http request
|
||||
@@ -366,7 +396,9 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
|
||||
modTime = v
|
||||
}
|
||||
}
|
||||
ctx.SetServeHeaders(name)
|
||||
ctx.SetServeHeaders(&ServeHeaderOptions{
|
||||
Filename: name,
|
||||
})
|
||||
http.ServeContent(ctx.Resp, ctx.Req, name, modTime, r)
|
||||
}
|
||||
|
||||
@@ -378,13 +410,17 @@ func (ctx *Context) ServeFile(file string, names ...string) {
|
||||
} else {
|
||||
name = path.Base(file)
|
||||
}
|
||||
ctx.SetServeHeaders(name)
|
||||
ctx.SetServeHeaders(&ServeHeaderOptions{
|
||||
Filename: name,
|
||||
})
|
||||
http.ServeFile(ctx.Resp, ctx.Req, file)
|
||||
}
|
||||
|
||||
// ServeStream serves file via io stream
|
||||
func (ctx *Context) ServeStream(rd io.Reader, name string) {
|
||||
ctx.SetServeHeaders(name)
|
||||
ctx.SetServeHeaders(&ServeHeaderOptions{
|
||||
Filename: name,
|
||||
})
|
||||
_, err := io.Copy(ctx.Resp, rd)
|
||||
if err != nil {
|
||||
ctx.ServerError("Download file failed", err)
|
||||
|
||||
@@ -83,12 +83,15 @@ 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
|
||||
return perm.AccessModeNone, nil
|
||||
}
|
||||
|
||||
if ctx.Doer != nil && !ctx.Doer.IsGhost() && (!ctx.Doer.IsActive || ctx.Doer.ProhibitLogin) {
|
||||
return perm.AccessModeNone, nil
|
||||
}
|
||||
|
||||
accessMode := perm.AccessModeNone
|
||||
if ctx.Package.Owner.IsOrganization() {
|
||||
org := organization.OrgFromUser(ctx.Package.Owner)
|
||||
|
||||
|
||||
@@ -807,7 +807,7 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
|
||||
}
|
||||
// For legacy and API support only full commit sha
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) > 0 && len(parts[0]) == 40 {
|
||||
if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
|
||||
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
|
||||
return parts[0]
|
||||
}
|
||||
@@ -843,7 +843,7 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
|
||||
return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
|
||||
case RepoRefCommit:
|
||||
parts := strings.Split(path, "/")
|
||||
if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= 40 {
|
||||
if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
|
||||
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
|
||||
return parts[0]
|
||||
}
|
||||
@@ -952,7 +952,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
||||
return
|
||||
}
|
||||
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
|
||||
} else if len(refName) >= 7 && len(refName) <= 40 {
|
||||
} else if len(refName) >= 7 && len(refName) <= git.SHAFullLength {
|
||||
ctx.Repo.IsViewCommit = true
|
||||
ctx.Repo.CommitID = refName
|
||||
|
||||
@@ -962,7 +962,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
||||
return
|
||||
}
|
||||
// If short commit ID add canonical link header
|
||||
if len(refName) < 40 {
|
||||
if len(refName) < git.SHAFullLength {
|
||||
ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
|
||||
util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
|
||||
}
|
||||
|
||||
@@ -205,6 +205,9 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
|
||||
// find stopwatches without existing issue
|
||||
genericOrphanCheck("Orphaned Stopwatches without existing Issue",
|
||||
"stopwatch", "issue", "stopwatch.issue_id=`issue`.id"),
|
||||
// find redirects without existing user.
|
||||
genericOrphanCheck("Orphaned Redirects without existing redirect user",
|
||||
"user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"),
|
||||
)
|
||||
|
||||
for _, c := range consistencyChecks {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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 doctor
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error {
|
||||
numRepos := 0
|
||||
numHeadsBroken := 0
|
||||
numDefaultBranchesBroken := 0
|
||||
numReposUpdated := 0
|
||||
err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
|
||||
numRepos++
|
||||
_, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
|
||||
|
||||
head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()})
|
||||
|
||||
// what we expect: default branch is valid, and HEAD points to it
|
||||
if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
|
||||
return nil
|
||||
}
|
||||
|
||||
if headErr != nil {
|
||||
numHeadsBroken++
|
||||
}
|
||||
if defaultBranchErr != nil {
|
||||
numDefaultBranchesBroken++
|
||||
}
|
||||
|
||||
// if default branch is broken, let the user fix that in the UI
|
||||
if defaultBranchErr != nil {
|
||||
logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// if we're not autofixing, that's all we can do
|
||||
if !autofix {
|
||||
return nil
|
||||
}
|
||||
|
||||
// otherwise, let's try fixing HEAD
|
||||
err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()})
|
||||
if err != nil {
|
||||
logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
|
||||
return nil
|
||||
}
|
||||
numReposUpdated++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
logger.Critical("Error when fixing repo HEADs: %v", err)
|
||||
}
|
||||
|
||||
if autofix {
|
||||
logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated)
|
||||
} else {
|
||||
if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 {
|
||||
logger.Info("All %d repos have their HEADs in the correct state", numRepos)
|
||||
} else {
|
||||
if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 {
|
||||
logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos)
|
||||
} else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 {
|
||||
logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos)
|
||||
} else {
|
||||
logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(&Check{
|
||||
Title: "Synchronize repo HEADs",
|
||||
Name: "synchronize-repo-heads",
|
||||
IsDefault: true,
|
||||
Run: synchronizeRepoHeads,
|
||||
Priority: 7,
|
||||
})
|
||||
}
|
||||
+20
-6
@@ -169,8 +169,11 @@ func (c *Command) Run(opts *RunOpts) error {
|
||||
if opts == nil {
|
||||
opts = &RunOpts{}
|
||||
}
|
||||
if opts.Timeout <= 0 {
|
||||
opts.Timeout = defaultCommandExecutionTimeout
|
||||
|
||||
// We must not change the provided options
|
||||
timeout := opts.Timeout
|
||||
if timeout <= 0 {
|
||||
timeout = defaultCommandExecutionTimeout
|
||||
}
|
||||
|
||||
if len(opts.Dir) == 0 {
|
||||
@@ -205,7 +208,7 @@ func (c *Command) Run(opts *RunOpts) error {
|
||||
if opts.UseContextTimeout {
|
||||
ctx, cancel, finished = process.GetManager().AddContext(c.parentContext, desc)
|
||||
} else {
|
||||
ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
|
||||
ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, timeout, desc)
|
||||
}
|
||||
defer finished()
|
||||
|
||||
@@ -306,9 +309,20 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS
|
||||
}
|
||||
stdoutBuf := &bytes.Buffer{}
|
||||
stderrBuf := &bytes.Buffer{}
|
||||
opts.Stdout = stdoutBuf
|
||||
opts.Stderr = stderrBuf
|
||||
err := c.Run(opts)
|
||||
|
||||
// We must not change the provided options as it could break future calls - therefore make a copy.
|
||||
newOpts := &RunOpts{
|
||||
Env: opts.Env,
|
||||
Timeout: opts.Timeout,
|
||||
UseContextTimeout: opts.UseContextTimeout,
|
||||
Dir: opts.Dir,
|
||||
Stdout: stdoutBuf,
|
||||
Stderr: stderrBuf,
|
||||
Stdin: opts.Stdin,
|
||||
PipelineFunc: opts.PipelineFunc,
|
||||
}
|
||||
|
||||
err := c.Run(newOpts)
|
||||
stderr = stderrBuf.Bytes()
|
||||
if err != nil {
|
||||
return nil, stderr, &runStdError{err: err, stderr: bytesToString(stderr)}
|
||||
|
||||
@@ -190,11 +190,6 @@ func InitOnceWithSync(ctx context.Context) (err error) {
|
||||
globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
|
||||
}
|
||||
|
||||
// By default partial clones are disabled, enable them from git v2.22
|
||||
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
|
||||
globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
|
||||
}
|
||||
|
||||
// Explicitly disable credential helper, otherwise Git credentials might leak
|
||||
if CheckGitVersionAtLeast("2.9") == nil {
|
||||
globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
|
||||
|
||||
@@ -53,7 +53,7 @@ func (repo *Repository) IsReferenceExist(name string) bool {
|
||||
|
||||
// IsBranchExist returns true if given branch exists in current repository.
|
||||
func (repo *Repository) IsBranchExist(name string) bool {
|
||||
if name == "" {
|
||||
if repo == nil || name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ func (repo *Repository) RemoveReference(name string) error {
|
||||
|
||||
// ConvertToSHA1 returns a Hash object from a potential ID string
|
||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
|
||||
if len(commitID) == 40 {
|
||||
if len(commitID) == SHAFullLength {
|
||||
sha1, err := NewIDFromString(commitID)
|
||||
if err == nil {
|
||||
return sha1, nil
|
||||
|
||||
@@ -138,7 +138,7 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Co
|
||||
|
||||
// ConvertToSHA1 returns a Hash object from a potential ID string
|
||||
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
|
||||
if len(commitID) == 40 && IsValidSHAPattern(commitID) {
|
||||
if len(commitID) == SHAFullLength && IsValidSHAPattern(commitID) {
|
||||
sha1, err := NewIDFromString(commitID)
|
||||
if err == nil {
|
||||
return sha1, nil
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
// ReadTreeToIndex reads a treeish to the index
|
||||
func (repo *Repository) ReadTreeToIndex(treeish string, indexFilename ...string) error {
|
||||
if len(treeish) != 40 {
|
||||
if len(treeish) != SHAFullLength {
|
||||
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", treeish).RunStdString(&RunOpts{Dir: repo.Path})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
// IsTagExist returns true if given tag exists in the repository.
|
||||
func (repo *Repository) IsTagExist(name string) bool {
|
||||
if name == "" {
|
||||
if repo == nil || name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
|
||||
|
||||
// GetTree find the tree object in the repository.
|
||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
||||
if len(idStr) != 40 {
|
||||
if len(idStr) != SHAFullLength {
|
||||
res, _, err := NewCommand(repo.Ctx, "rev-parse", "--verify", idStr).RunStdString(&RunOpts{Dir: repo.Path})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -67,7 +67,7 @@ func (repo *Repository) getTree(id SHA1) (*Tree, error) {
|
||||
|
||||
// GetTree find the tree object in the repository.
|
||||
func (repo *Repository) GetTree(idStr string) (*Tree, error) {
|
||||
if len(idStr) != 40 {
|
||||
if len(idStr) != SHAFullLength {
|
||||
res, err := repo.GetRefCommitID(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
+4
-1
@@ -18,6 +18,9 @@ const EmptySHA = "0000000000000000000000000000000000000000"
|
||||
// EmptyTreeSHA is the SHA of an empty tree
|
||||
const EmptyTreeSHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
||||
|
||||
// SHAFullLength is the full length of a git SHA
|
||||
const SHAFullLength = 40
|
||||
|
||||
// SHAPattern can be used to determine if a string is an valid sha
|
||||
var shaPattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)
|
||||
|
||||
@@ -51,7 +54,7 @@ func MustIDFromString(s string) SHA1 {
|
||||
func NewIDFromString(s string) (SHA1, error) {
|
||||
var id SHA1
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) != 40 {
|
||||
if len(s) != SHAFullLength {
|
||||
return id, fmt.Errorf("Length must be 40: %s", s)
|
||||
}
|
||||
b, err := hex.DecodeString(s)
|
||||
|
||||
@@ -10,6 +10,7 @@ package git
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
@@ -30,7 +31,9 @@ type Signature = object.Signature
|
||||
func newSignatureFromCommitline(line []byte) (_ *Signature, err error) {
|
||||
sig := new(Signature)
|
||||
emailStart := bytes.IndexByte(line, '<')
|
||||
sig.Name = string(line[:emailStart-1])
|
||||
if emailStart > 0 { // Empty name has already occurred, even if it shouldn't
|
||||
sig.Name = strings.TrimSpace(string(line[:emailStart-1]))
|
||||
}
|
||||
emailEnd := bytes.IndexByte(line, '>')
|
||||
sig.Email = string(line[emailStart+1 : emailEnd])
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -51,7 +52,9 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
sig.Name = string(line[:emailStart-1])
|
||||
if emailStart > 0 { // Empty name has already occurred, even if it shouldn't
|
||||
sig.Name = strings.TrimSpace(string(line[:emailStart-1]))
|
||||
}
|
||||
sig.Email = string(line[emailStart+1 : emailEnd])
|
||||
|
||||
hasTime := emailEnd+2 < len(line)
|
||||
|
||||
@@ -608,15 +608,16 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_
|
||||
}
|
||||
|
||||
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppURL + commits.CompareURL,
|
||||
Commits: apiCommits,
|
||||
HeadCommit: apiHeadCommit,
|
||||
Repo: convert.ToRepo(repo, perm.AccessModeOwner),
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppURL + commits.CompareURL,
|
||||
Commits: apiCommits,
|
||||
TotalCommits: commits.Len,
|
||||
HeadCommit: apiHeadCommit,
|
||||
Repo: convert.ToRepo(repo, perm.AccessModeOwner),
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
@@ -838,15 +839,16 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r
|
||||
}
|
||||
|
||||
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppURL + commits.CompareURL,
|
||||
Commits: apiCommits,
|
||||
HeadCommit: apiHeadCommit,
|
||||
Repo: convert.ToRepo(repo, perm.AccessModeOwner),
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
Ref: opts.RefFullName,
|
||||
Before: opts.OldCommitID,
|
||||
After: opts.NewCommitID,
|
||||
CompareURL: setting.AppURL + commits.CompareURL,
|
||||
Commits: apiCommits,
|
||||
TotalCommits: commits.Len,
|
||||
HeadCommit: apiHeadCommit,
|
||||
Repo: convert.ToRepo(repo, perm.AccessModeOwner),
|
||||
Pusher: apiPusher,
|
||||
Sender: apiPusher,
|
||||
}); err != nil {
|
||||
log.Error("PrepareWebhooks: %v", err)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,13 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
|
||||
return s.store.Open(KeyToRelativePath(key))
|
||||
}
|
||||
|
||||
// FIXME: Workaround to be removed in v1.20
|
||||
// https://github.com/go-gitea/gitea/issues/19586
|
||||
func (s *ContentStore) Has(key BlobHash256Key) error {
|
||||
_, err := s.store.Stat(KeyToRelativePath(key))
|
||||
return err
|
||||
}
|
||||
|
||||
// Save stores a package blob
|
||||
func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error {
|
||||
_, err := s.store.Save(KeyToRelativePath(key), r, size)
|
||||
|
||||
@@ -66,7 +66,8 @@ type PackageMetadata struct {
|
||||
License string `json:"license,omitempty"`
|
||||
}
|
||||
|
||||
// PackageMetadataVersion https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version
|
||||
// PackageMetadataVersion documentation: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version
|
||||
// PackageMetadataVersion response: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
|
||||
type PackageMetadataVersion struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
@@ -80,6 +81,7 @@ type PackageMetadataVersion struct {
|
||||
Dependencies map[string]string `json:"dependencies,omitempty"`
|
||||
DevDependencies map[string]string `json:"devDependencies,omitempty"`
|
||||
PeerDependencies map[string]string `json:"peerDependencies,omitempty"`
|
||||
Bin map[string]string `json:"bin,omitempty"`
|
||||
OptionalDependencies map[string]string `json:"optionalDependencies,omitempty"`
|
||||
Readme string `json:"readme,omitempty"`
|
||||
Dist PackageDistribution `json:"dist"`
|
||||
@@ -192,6 +194,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
DevelopmentDependencies: meta.DevDependencies,
|
||||
PeerDependencies: meta.PeerDependencies,
|
||||
OptionalDependencies: meta.OptionalDependencies,
|
||||
Bin: meta.Bin,
|
||||
Readme: meta.Readme,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ func TestParsePackage(t *testing.T) {
|
||||
packageVersion := "1.0.1-pre"
|
||||
packageTag := "latest"
|
||||
packageAuthor := "KN4CK3R"
|
||||
packageBin := "gitea"
|
||||
packageDescription := "Test Description"
|
||||
data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA"
|
||||
integrity := "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg=="
|
||||
@@ -236,6 +237,9 @@ func TestParsePackage(t *testing.T) {
|
||||
Dependencies: map[string]string{
|
||||
"package": "1.2.0",
|
||||
},
|
||||
Bin: map[string]string{
|
||||
"bin": packageBin,
|
||||
},
|
||||
Dist: PackageDistribution{
|
||||
Integrity: integrity,
|
||||
},
|
||||
@@ -264,6 +268,7 @@ func TestParsePackage(t *testing.T) {
|
||||
assert.Equal(t, packageDescription, p.Metadata.Description)
|
||||
assert.Equal(t, packageDescription, p.Metadata.Readme)
|
||||
assert.Equal(t, packageAuthor, p.Metadata.Author)
|
||||
assert.Equal(t, packageBin, p.Metadata.Bin["bin"])
|
||||
assert.Equal(t, "MIT", p.Metadata.License)
|
||||
assert.Equal(t, "https://gitea.io/", p.Metadata.ProjectURL)
|
||||
assert.Contains(t, p.Metadata.Dependencies, "package")
|
||||
|
||||
@@ -20,5 +20,6 @@ type Metadata struct {
|
||||
DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"`
|
||||
PeerDependencies map[string]string `json:"peer_dependencies,omitempty"`
|
||||
OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"`
|
||||
Bin map[string]string `json:"bin,omitempty"`
|
||||
Readme string `json:"readme,omitempty"`
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ package nuget
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -181,7 +183,23 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
|
||||
return &Package{
|
||||
PackageType: packageType,
|
||||
ID: p.Metadata.ID,
|
||||
Version: v.String(),
|
||||
Version: toNormalizedVersion(v),
|
||||
Metadata: m,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#normalized-version-numbers
|
||||
// https://github.com/NuGet/NuGet.Client/blob/dccbd304b11103e08b97abf4cf4bcc1499d9235a/src/NuGet.Core/NuGet.Versioning/VersionFormatter.cs#L121
|
||||
func toNormalizedVersion(v *version.Version) string {
|
||||
var buf bytes.Buffer
|
||||
segments := v.Segments64()
|
||||
fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2])
|
||||
if len(segments) > 3 && segments[3] > 0 {
|
||||
fmt.Fprintf(&buf, ".%d", segments[3])
|
||||
}
|
||||
pre := v.Prerelease()
|
||||
if pre != "" {
|
||||
fmt.Fprint(&buf, "-", pre)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -147,6 +147,19 @@ func TestParseNuspecMetaData(t *testing.T) {
|
||||
assert.Len(t, deps, 1)
|
||||
assert.Equal(t, dependencyID, deps[0].ID)
|
||||
assert.Equal(t, dependencyVersion, deps[0].Version)
|
||||
|
||||
t.Run("NormalizedVersion", func(t *testing.T) {
|
||||
np, err := ParseNuspecMetaData(strings.NewReader(`<?xml version="1.0" encoding="utf-8"?>
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>test</id>
|
||||
<version>1.04.5.2.5-rc.1+metadata</version>
|
||||
</metadata>
|
||||
</package>`))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, np)
|
||||
assert.Equal(t, "1.4.5.2-rc.1", np.Version)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("Symbols Package", func(t *testing.T) {
|
||||
|
||||
+10
-9
@@ -267,15 +267,16 @@ func (p *ReleasePayload) JSONPayload() ([]byte, error) {
|
||||
|
||||
// PushPayload represents a payload information of push event.
|
||||
type PushPayload struct {
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
CompareURL string `json:"compare_url"`
|
||||
Commits []*PayloadCommit `json:"commits"`
|
||||
HeadCommit *PayloadCommit `json:"head_commit"`
|
||||
Repo *Repository `json:"repository"`
|
||||
Pusher *User `json:"pusher"`
|
||||
Sender *User `json:"sender"`
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
CompareURL string `json:"compare_url"`
|
||||
Commits []*PayloadCommit `json:"commits"`
|
||||
TotalCommits int `json:"total_commits"`
|
||||
HeadCommit *PayloadCommit `json:"head_commit"`
|
||||
Repo *Repository `json:"repository"`
|
||||
Pusher *User `json:"pusher"`
|
||||
Sender *User `json:"sender"`
|
||||
}
|
||||
|
||||
// JSONPayload FIXME
|
||||
|
||||
@@ -458,6 +458,19 @@ func NewFuncMap() []template.FuncMap {
|
||||
return items
|
||||
},
|
||||
"HasPrefix": strings.HasPrefix,
|
||||
"CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string {
|
||||
var curBranch string
|
||||
if repo.ID != baseRepo.ID {
|
||||
curBranch += fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name))
|
||||
}
|
||||
curBranch += util.PathEscapeSegments(branchName)
|
||||
|
||||
return fmt.Sprintf("%s/compare/%s...%s",
|
||||
baseRepo.Link(),
|
||||
util.PathEscapeSegments(baseRepo.DefaultBranch),
|
||||
curBranch,
|
||||
)
|
||||
},
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -634,7 +647,7 @@ func SVG(icon string, others ...interface{}) template.HTML {
|
||||
|
||||
// Avatar renders user avatars. args: user, size (int), class (string)
|
||||
func Avatar(item interface{}, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
|
||||
|
||||
switch t := item.(type) {
|
||||
case *user_model.User:
|
||||
@@ -665,7 +678,7 @@ func AvatarByAction(action *models.Action, others ...interface{}) template.HTML
|
||||
|
||||
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
|
||||
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
|
||||
|
||||
src := repo.RelAvatarLink()
|
||||
if src != "" {
|
||||
@@ -676,7 +689,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM
|
||||
|
||||
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
|
||||
func AvatarByEmail(email, name string, others ...interface{}) template.HTML {
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
|
||||
size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...)
|
||||
src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor)
|
||||
|
||||
if src != "" {
|
||||
|
||||
@@ -13,8 +13,13 @@ import (
|
||||
// TimeStamp defines a timestamp
|
||||
type TimeStamp int64
|
||||
|
||||
// mock is NOT concurrency-safe!!
|
||||
var mock time.Time
|
||||
var (
|
||||
// mock is NOT concurrency-safe!!
|
||||
mock time.Time
|
||||
|
||||
// Used for IsZero, to check if timestamp is the zero time instant.
|
||||
timeZeroUnix = time.Time{}.Unix()
|
||||
)
|
||||
|
||||
// Set sets the time to a mocked time.Time
|
||||
func Set(now time.Time) {
|
||||
@@ -103,5 +108,5 @@ func (ts TimeStamp) FormatDate() string {
|
||||
|
||||
// IsZero is zero time
|
||||
func (ts TimeStamp) IsZero() bool {
|
||||
return ts.AsTimeInLocation(time.Local).IsZero()
|
||||
return int64(ts) == 0 || int64(ts) == timeZeroUnix
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ func Routes() *web.Route {
|
||||
authGroup := auth.NewGroup(authMethods...)
|
||||
r.Use(func(ctx *context.Context) {
|
||||
ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||
ctx.IsSigned = ctx.Doer != nil
|
||||
})
|
||||
|
||||
r.Group("/{username}", func() {
|
||||
@@ -190,7 +191,7 @@ func Routes() *web.Route {
|
||||
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)
|
||||
r.Get("/symbols/{filename}/{guid:[0-9a-fA-F]{32}[fF]{8}}/{filename2}", nuget.DownloadSymbolFile)
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
})
|
||||
r.Group("/npm", func() {
|
||||
@@ -198,11 +199,13 @@ func Routes() *web.Route {
|
||||
r.Get("", npm.PackageMetadata)
|
||||
r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage)
|
||||
r.Get("/-/{version}/{filename}", npm.DownloadPackageFile)
|
||||
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
|
||||
})
|
||||
r.Group("/{id}", func() {
|
||||
r.Get("", npm.PackageMetadata)
|
||||
r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage)
|
||||
r.Get("/-/{version}/{filename}", npm.DownloadPackageFile)
|
||||
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
|
||||
})
|
||||
r.Group("/-/package/@{scope}/{id}/dist-tags", func() {
|
||||
r.Get("", npm.ListPackageTags)
|
||||
@@ -256,6 +259,7 @@ func ContainerRoutes() *web.Route {
|
||||
authGroup := auth.NewGroup(authMethods...)
|
||||
r.Use(func(ctx *context.Context) {
|
||||
ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||
ctx.IsSigned = ctx.Doer != nil
|
||||
})
|
||||
|
||||
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
|
||||
|
||||
@@ -7,8 +7,11 @@ package container
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
@@ -19,6 +22,8 @@ import (
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
)
|
||||
|
||||
var uploadVersionMutex sync.Mutex
|
||||
|
||||
// saveAsPackageBlob creates a package blob from an upload
|
||||
// The uploaded blob gets stored in a special upload version to link them to the package/image
|
||||
func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_service.PackageInfo) (*packages_model.PackageBlob, error) {
|
||||
@@ -28,6 +33,11 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
|
||||
|
||||
contentStore := packages_module.NewContentStore()
|
||||
|
||||
var uploadVersion *packages_model.PackageVersion
|
||||
|
||||
// FIXME: Replace usage of mutex with database transaction
|
||||
// https://github.com/go-gitea/gitea/pull/21862
|
||||
uploadVersionMutex.Lock()
|
||||
err := db.WithTx(func(ctx context.Context) error {
|
||||
created := true
|
||||
p := &packages_model.Package{
|
||||
@@ -68,11 +78,30 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
|
||||
}
|
||||
}
|
||||
|
||||
uploadVersion = pv
|
||||
|
||||
return nil
|
||||
})
|
||||
uploadVersionMutex.Unlock()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.WithTx(func(ctx context.Context) error {
|
||||
pb, exists, err = packages_model.GetOrInsertBlob(ctx, pb)
|
||||
if err != nil {
|
||||
log.Error("Error inserting package blob: %v", err)
|
||||
return err
|
||||
}
|
||||
// FIXME: Workaround to be removed in v1.20
|
||||
// https://github.com/go-gitea/gitea/issues/19586
|
||||
if exists {
|
||||
err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256))
|
||||
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), hsr, hsr.Size()); err != nil {
|
||||
log.Error("Error saving package blob in content store: %v", err)
|
||||
@@ -83,7 +112,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
|
||||
filename := strings.ToLower(fmt.Sprintf("sha256_%s", pb.HashSHA256))
|
||||
|
||||
pf := &packages_model.PackageFile{
|
||||
VersionID: pv.ID,
|
||||
VersionID: uploadVersion.ID,
|
||||
BlobID: pb.ID,
|
||||
Name: filename,
|
||||
LowerName: filename,
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -193,7 +194,7 @@ func InitiateUploadBlob(ctx *context.Context) {
|
||||
mount := ctx.FormTrim("mount")
|
||||
from := ctx.FormTrim("from")
|
||||
if mount != "" {
|
||||
blob, _ := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
blob, _ := workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
Image: from,
|
||||
Digest: mount,
|
||||
})
|
||||
@@ -361,7 +362,7 @@ func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescri
|
||||
return nil, container_model.ErrContainerBlobNotExist
|
||||
}
|
||||
|
||||
return container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
OwnerID: ctx.Package.Owner.ID,
|
||||
Image: ctx.Params("image"),
|
||||
Digest: digest,
|
||||
@@ -503,7 +504,7 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe
|
||||
return nil, container_model.ErrContainerBlobNotExist
|
||||
}
|
||||
|
||||
return container_model.GetContainerBlob(ctx, opts)
|
||||
return workaroundGetContainerBlob(ctx, opts)
|
||||
}
|
||||
|
||||
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#checking-if-content-exists-in-the-registry
|
||||
@@ -643,3 +644,23 @@ func GetTagList(ctx *context.Context) {
|
||||
Tags: tags,
|
||||
})
|
||||
}
|
||||
|
||||
// FIXME: Workaround to be removed in v1.20
|
||||
// https://github.com/go-gitea/gitea/issues/19586
|
||||
func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) {
|
||||
blob, err := container_model.GetContainerBlob(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(blob.Blob.HashSHA256))
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
log.Debug("Package registry inconsistent: blob %s does not exist on file system", blob.Blob.HashSHA256)
|
||||
return nil, container_model.ErrContainerBlobNotExist
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return blob, nil
|
||||
}
|
||||
|
||||
@@ -6,8 +6,10 @@ package container
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
@@ -403,6 +405,15 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack
|
||||
log.Error("Error inserting package blob: %v", err)
|
||||
return nil, false, "", err
|
||||
}
|
||||
// FIXME: Workaround to be removed in v1.20
|
||||
// https://github.com/go-gitea/gitea/issues/19586
|
||||
if exists {
|
||||
err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(pb.HashSHA256))
|
||||
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||
log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256)
|
||||
exists = false
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
contentStore := packages_module.NewContentStore()
|
||||
if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), buf, buf.Size()); err != nil {
|
||||
|
||||
@@ -67,6 +67,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
|
||||
PeerDependencies: metadata.PeerDependencies,
|
||||
OptionalDependencies: metadata.OptionalDependencies,
|
||||
Readme: metadata.Readme,
|
||||
Bin: metadata.Bin,
|
||||
Dist: npm_module.PackageDistribution{
|
||||
Shasum: pd.Files[0].Blob.HashSHA1,
|
||||
Integrity: "sha512-" + base64.StdEncoding.EncodeToString(hashBytes),
|
||||
|
||||
@@ -105,6 +105,49 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
ctx.ServeStream(s, pf.Name)
|
||||
}
|
||||
|
||||
// DownloadPackageFileByName finds the version and serves the contents of a package
|
||||
func DownloadPackageFileByName(ctx *context.Context) {
|
||||
filename := ctx.Params("filename")
|
||||
|
||||
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
|
||||
OwnerID: ctx.Package.Owner.ID,
|
||||
Type: packages_model.TypeNpm,
|
||||
Name: packages_model.SearchValue{
|
||||
ExactMatch: true,
|
||||
Value: packageNameFromParams(ctx),
|
||||
},
|
||||
HasFileWithName: filename,
|
||||
IsInternal: false,
|
||||
})
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
if len(pvs) != 1 {
|
||||
apiError(ctx, http.StatusNotFound, nil)
|
||||
return
|
||||
}
|
||||
|
||||
s, pf, err := packages_service.GetFileStreamByPackageVersion(
|
||||
ctx,
|
||||
pvs[0],
|
||||
&packages_service.PackageFileInfo{
|
||||
Filename: filename,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageFileNotExist {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
ctx.ServeStream(s, pf.Name)
|
||||
}
|
||||
|
||||
// UploadPackage creates a new package
|
||||
func UploadPackage(ctx *context.Context) {
|
||||
npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
|
||||
|
||||
@@ -5,15 +5,11 @@
|
||||
package nuget
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
nuget_module "code.gitea.io/gitea/modules/packages/nuget"
|
||||
|
||||
"github.com/hashicorp/go-version"
|
||||
)
|
||||
|
||||
// ServiceIndexResponse https://docs.microsoft.com/en-us/nuget/api/service-index#resources
|
||||
@@ -113,8 +109,8 @@ func createRegistrationIndexResponse(l *linkBuilder, pds []*packages_model.Packa
|
||||
{
|
||||
RegistrationPageURL: l.GetRegistrationIndexURL(pds[0].Package.Name),
|
||||
Count: len(pds),
|
||||
Lower: normalizeVersion(pds[0].SemVer),
|
||||
Upper: normalizeVersion(pds[len(pds)-1].SemVer),
|
||||
Lower: pds[0].Version.Version,
|
||||
Upper: pds[len(pds)-1].Version.Version,
|
||||
Items: items,
|
||||
},
|
||||
},
|
||||
@@ -191,7 +187,7 @@ type PackageVersionsResponse struct {
|
||||
func createPackageVersionsResponse(pds []*packages_model.PackageDescriptor) *PackageVersionsResponse {
|
||||
versions := make([]string, 0, len(pds))
|
||||
for _, pd := range pds {
|
||||
versions = append(versions, normalizeVersion(pd.SemVer))
|
||||
versions = append(versions, pd.Version.Version)
|
||||
}
|
||||
|
||||
return &PackageVersionsResponse{
|
||||
@@ -224,20 +220,13 @@ type SearchResultVersion struct {
|
||||
}
|
||||
|
||||
func createSearchResultResponse(l *linkBuilder, totalHits int64, pds []*packages_model.PackageDescriptor) *SearchResultResponse {
|
||||
grouped := make(map[string][]*packages_model.PackageDescriptor)
|
||||
for _, pd := range pds {
|
||||
grouped[pd.Package.Name] = append(grouped[pd.Package.Name], pd)
|
||||
}
|
||||
|
||||
data := make([]*SearchResult, 0, len(pds))
|
||||
|
||||
if len(pds) > 0 {
|
||||
groupID := pds[0].Package.Name
|
||||
group := make([]*packages_model.PackageDescriptor, 0, 10)
|
||||
|
||||
for i := 0; i < len(pds); i++ {
|
||||
if groupID != pds[i].Package.Name {
|
||||
data = append(data, createSearchResult(l, group))
|
||||
groupID = pds[i].Package.Name
|
||||
group = group[:0]
|
||||
}
|
||||
group = append(group, pds[i])
|
||||
}
|
||||
for _, group := range grouped {
|
||||
data = append(data, createSearchResult(l, group))
|
||||
}
|
||||
|
||||
@@ -273,15 +262,3 @@ func createSearchResult(l *linkBuilder, pds []*packages_model.PackageDescriptor)
|
||||
RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name),
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeVersion removes the metadata
|
||||
func normalizeVersion(v *version.Version) string {
|
||||
var buf bytes.Buffer
|
||||
segments := v.Segments64()
|
||||
fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2])
|
||||
pre := v.Prerelease()
|
||||
if pre != "" {
|
||||
fmt.Fprintf(&buf, "-%s", pre)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ func processUploadedFile(ctx *context.Context, expectedType nuget_module.Package
|
||||
// DownloadSymbolFile https://github.com/dotnet/symstore/blob/main/docs/specs/Simple_Symbol_Query_Protocol.md#request
|
||||
func DownloadSymbolFile(ctx *context.Context) {
|
||||
filename := ctx.Params("filename")
|
||||
guid := ctx.Params("guid")
|
||||
guid := ctx.Params("guid")[:32]
|
||||
filename2 := ctx.Params("filename2")
|
||||
|
||||
if filename != filename2 {
|
||||
|
||||
@@ -22,12 +22,19 @@ import (
|
||||
packages_service "code.gitea.io/gitea/services/packages"
|
||||
)
|
||||
|
||||
// https://www.python.org/dev/peps/pep-0503/#normalized-names
|
||||
// https://peps.python.org/pep-0426/#name
|
||||
var normalizer = strings.NewReplacer(".", "-", "_", "-")
|
||||
var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`)
|
||||
var nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`)
|
||||
|
||||
// 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]*))?$`)
|
||||
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
|
||||
var versionMatcher = regexp.MustCompile(`\Av?` +
|
||||
`(?:[0-9]+!)?` + // epoch
|
||||
`[0-9]+(?:\.[0-9]+)*` + // release segment
|
||||
`(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release
|
||||
`(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release
|
||||
`(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release
|
||||
`(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version
|
||||
`\z`)
|
||||
|
||||
func apiError(ctx *context.Context, status int, obj interface{}) {
|
||||
helper.LogAndProcessError(ctx, status, obj, func(message string) {
|
||||
@@ -123,7 +130,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
|
||||
packageName := normalizer.Replace(ctx.Req.FormValue("name"))
|
||||
packageVersion := ctx.Req.FormValue("version")
|
||||
if !nameMatcher.MatchString(packageName) || !versionMatcher.MatchString(packageVersion) {
|
||||
if !isValidNameAndVersion(packageName, packageVersion) {
|
||||
apiError(ctx, http.StatusBadRequest, "invalid name or version")
|
||||
return
|
||||
}
|
||||
@@ -141,7 +148,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
Name: packageName,
|
||||
Version: packageVersion,
|
||||
},
|
||||
SemverCompatible: true,
|
||||
SemverCompatible: false,
|
||||
Creator: ctx.Doer,
|
||||
Metadata: &pypi_module.Metadata{
|
||||
Author: ctx.Req.FormValue("author"),
|
||||
@@ -172,3 +179,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
func isValidNameAndVersion(packageName, packageVersion string) bool {
|
||||
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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 pypi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsValidNameAndVersion(t *testing.T) {
|
||||
// The test cases below were created from the following Python PEPs:
|
||||
// https://peps.python.org/pep-0426/#name
|
||||
// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
|
||||
|
||||
// Valid Cases
|
||||
assert.True(t, isValidNameAndVersion("A", "1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("Test.Name.1234", "1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("test_name", "1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "v1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "2012.4"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1.0.1-alpha"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1.0.1a1"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1.0b2.r345.dev456"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1!1.0.1"))
|
||||
assert.True(t, isValidNameAndVersion("test-name", "1.0.1+local.1"))
|
||||
|
||||
// Invalid Cases
|
||||
assert.False(t, isValidNameAndVersion(".test-name", "1.0.1"))
|
||||
assert.False(t, isValidNameAndVersion("test!name", "1.0.1"))
|
||||
assert.False(t, isValidNameAndVersion("-test-name", "1.0.1"))
|
||||
assert.False(t, isValidNameAndVersion("test-name-", "1.0.1"))
|
||||
assert.False(t, isValidNameAndVersion("test-name", "a1.0.1"))
|
||||
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
|
||||
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
|
||||
}
|
||||
@@ -75,7 +75,9 @@ func enumeratePackages(ctx *context.Context, filename string, pvs []*packages_mo
|
||||
})
|
||||
}
|
||||
|
||||
ctx.SetServeHeaders(filename + ".gz")
|
||||
ctx.SetServeHeaders(&context.ServeHeaderOptions{
|
||||
Filename: filename + ".gz",
|
||||
})
|
||||
|
||||
zw := gzip.NewWriter(ctx.Resp)
|
||||
defer zw.Close()
|
||||
@@ -113,7 +115,9 @@ func ServePackageSpecification(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetServeHeaders(filename)
|
||||
ctx.SetServeHeaders(&context.ServeHeaderOptions{
|
||||
Filename: filename,
|
||||
})
|
||||
|
||||
zw := zlib.NewWriter(ctx.Resp)
|
||||
defer zw.Close()
|
||||
|
||||
+4
-10
@@ -230,13 +230,10 @@ func reqExploreSignIn() func(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) {
|
||||
func reqBasicAuth() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if ctx.IsSigned && setting.Service.EnableReverseProxyAuth && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName {
|
||||
return
|
||||
}
|
||||
if !ctx.Context.IsBasicAuth {
|
||||
ctx.Error(http.StatusUnauthorized, "reqBasicOrRevProxyAuth", "auth required")
|
||||
ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
|
||||
return
|
||||
}
|
||||
ctx.CheckForOTP()
|
||||
@@ -595,9 +592,6 @@ func buildAuthGroup() *auth.Group {
|
||||
&auth.HTTPSign{},
|
||||
&auth.Basic{}, // FIXME: this should be removed once we don't allow basic auth in API
|
||||
)
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
group.Add(&auth.ReverseProxy{})
|
||||
}
|
||||
specialAdd(group)
|
||||
|
||||
return group
|
||||
@@ -681,7 +675,7 @@ func Routes() *web.Route {
|
||||
m.Combo("").Get(user.ListAccessTokens).
|
||||
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
|
||||
m.Combo("/{id}").Delete(user.DeleteAccessToken)
|
||||
}, reqBasicOrRevProxyAuth())
|
||||
}, reqBasicAuth())
|
||||
}, context_service.UserAssignmentAPI())
|
||||
})
|
||||
|
||||
@@ -890,7 +884,7 @@ func Routes() *web.Route {
|
||||
m.Group("/{index}", func() {
|
||||
m.Combo("").Get(repo.GetIssue).
|
||||
Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue).
|
||||
Delete(reqToken(), reqAdmin(), repo.DeleteIssue)
|
||||
Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue)
|
||||
m.Group("/comments", func() {
|
||||
m.Combo("").Get(repo.ListIssueComments).
|
||||
Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment)
|
||||
|
||||
@@ -252,42 +252,50 @@ func ListBranches(ctx *context.APIContext) {
|
||||
// "200":
|
||||
// "$ref": "#/responses/BranchList"
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
skip, _ := listOptions.GetStartEnd()
|
||||
branches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
var totalNumOfBranches int
|
||||
var apiBranches []*api.Branch
|
||||
|
||||
apiBranches := make([]*api.Branch, 0, len(branches))
|
||||
for i := range branches {
|
||||
c, err := branches[i].GetCommit()
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
|
||||
skip, _ := listOptions.GetStartEnd()
|
||||
branches, total, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize)
|
||||
if err != nil {
|
||||
// Skip if this branch doesn't exist anymore.
|
||||
if git.IsErrNotExist(err) {
|
||||
totalNumOfBranches--
|
||||
continue
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
||||
return
|
||||
}
|
||||
|
||||
apiBranches = make([]*api.Branch, 0, len(branches))
|
||||
for i := range branches {
|
||||
c, err := branches[i].GetCommit()
|
||||
if err != nil {
|
||||
// Skip if this branch doesn't exist anymore.
|
||||
if git.IsErrNotExist(err) {
|
||||
total--
|
||||
continue
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||
return
|
||||
branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branches[i].Name)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err)
|
||||
return
|
||||
}
|
||||
apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
apiBranches = append(apiBranches, apiBranch)
|
||||
}
|
||||
branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branches[i].Name)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||
return
|
||||
}
|
||||
apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin())
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||
return
|
||||
}
|
||||
apiBranches = append(apiBranches, apiBranch)
|
||||
|
||||
totalNumOfBranches = total
|
||||
}
|
||||
|
||||
ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize)
|
||||
ctx.SetTotalCountHeader(int64(totalNumOfBranches))
|
||||
ctx.JSON(http.StatusOK, &apiBranches)
|
||||
ctx.JSON(http.StatusOK, apiBranches)
|
||||
}
|
||||
|
||||
// GetBranchProtection gets a branch protection
|
||||
|
||||
@@ -169,15 +169,16 @@ func TestHook(ctx *context.APIContext) {
|
||||
|
||||
commitID := ctx.Repo.Commit.ID.String()
|
||||
if err := webhook_service.PrepareWebhook(hook, ctx.Repo.Repository, webhook.HookEventPush, &api.PushPayload{
|
||||
Ref: ref,
|
||||
Before: commitID,
|
||||
After: commitID,
|
||||
CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
|
||||
Commits: []*api.PayloadCommit{commit},
|
||||
HeadCommit: commit,
|
||||
Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
|
||||
Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
|
||||
Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
|
||||
Ref: ref,
|
||||
Before: commitID,
|
||||
After: commitID,
|
||||
CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
|
||||
Commits: []*api.PayloadCommit{commit},
|
||||
TotalCommits: 1,
|
||||
HeadCommit: commit,
|
||||
Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
|
||||
Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
|
||||
Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
|
||||
}); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err)
|
||||
return
|
||||
|
||||
@@ -488,6 +488,11 @@ func EditPullRequest(ctx *context.APIContext) {
|
||||
issue := pr.Issue
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
|
||||
if err := issue.LoadAttributes(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !issue.IsPoster(ctx.Doer.ID) && !ctx.Repo.CanWrite(unit.TypePullRequests) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
|
||||
@@ -184,6 +184,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||
ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
|
||||
return
|
||||
}
|
||||
sha = utils.MustConvertToSHA1(ctx.Context, sha)
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
@@ -30,7 +30,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
|
||||
return refSHA
|
||||
}
|
||||
}
|
||||
return ref
|
||||
return MustConvertToSHA1(ctx.Context, ref)
|
||||
}
|
||||
|
||||
// GetGitRefs return git references based on filter
|
||||
@@ -55,3 +55,30 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str
|
||||
}
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
// ConvertToSHA1 returns a full-length SHA1 from a potential ID string
|
||||
func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
|
||||
if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
|
||||
sha1, err := git.NewIDFromString(commitID)
|
||||
if err == nil {
|
||||
return sha1, nil
|
||||
}
|
||||
}
|
||||
|
||||
gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, ctx.Repo.Repository.RepoPath())
|
||||
if err != nil {
|
||||
return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
return gitRepo.ConvertToSHA1(commitID)
|
||||
}
|
||||
|
||||
// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
|
||||
func MustConvertToSHA1(ctx *context.Context, commitID string) string {
|
||||
sha, err := ConvertToSHA1(ctx, commitID)
|
||||
if err != nil {
|
||||
return commitID
|
||||
}
|
||||
return sha.String()
|
||||
}
|
||||
|
||||
+19
-30
@@ -7,7 +7,6 @@ package common
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -53,50 +52,44 @@ func ServeData(ctx *context.Context, filePath string, size int64, reader io.Read
|
||||
buf = buf[:n]
|
||||
}
|
||||
|
||||
httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute)
|
||||
|
||||
if size >= 0 {
|
||||
ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size))
|
||||
} else {
|
||||
log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size)
|
||||
}
|
||||
|
||||
fileName := path.Base(filePath)
|
||||
sniffedType := typesniffer.DetectContentType(buf)
|
||||
isPlain := sniffedType.IsText() || ctx.FormBool("render")
|
||||
mimeType := ""
|
||||
charset := ""
|
||||
|
||||
if setting.MimeTypeMap.Enabled {
|
||||
fileExtension := strings.ToLower(filepath.Ext(fileName))
|
||||
mimeType = setting.MimeTypeMap.Map[fileExtension]
|
||||
opts := &context.ServeHeaderOptions{
|
||||
Filename: path.Base(filePath),
|
||||
}
|
||||
|
||||
if mimeType == "" {
|
||||
sniffedType := typesniffer.DetectContentType(buf)
|
||||
isPlain := sniffedType.IsText() || ctx.FormBool("render")
|
||||
|
||||
if setting.MimeTypeMap.Enabled {
|
||||
fileExtension := strings.ToLower(filepath.Ext(filePath))
|
||||
opts.ContentType = setting.MimeTypeMap.Map[fileExtension]
|
||||
}
|
||||
|
||||
if opts.ContentType == "" {
|
||||
if sniffedType.IsBrowsableBinaryType() {
|
||||
mimeType = sniffedType.GetMimeType()
|
||||
opts.ContentType = sniffedType.GetMimeType()
|
||||
} else if isPlain {
|
||||
mimeType = "text/plain"
|
||||
opts.ContentType = "text/plain"
|
||||
} else {
|
||||
mimeType = typesniffer.ApplicationOctetStream
|
||||
opts.ContentType = typesniffer.ApplicationOctetStream
|
||||
}
|
||||
}
|
||||
|
||||
if isPlain {
|
||||
var charset string
|
||||
charset, err = charsetModule.DetectEncoding(buf)
|
||||
if err != nil {
|
||||
log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err)
|
||||
charset = "utf-8"
|
||||
}
|
||||
opts.ContentTypeCharset = strings.ToLower(charset)
|
||||
}
|
||||
|
||||
if charset != "" {
|
||||
ctx.Resp.Header().Set("Content-Type", mimeType+"; charset="+strings.ToLower(charset))
|
||||
} else {
|
||||
ctx.Resp.Header().Set("Content-Type", mimeType)
|
||||
}
|
||||
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
|
||||
|
||||
isSVG := sniffedType.IsSvgImage()
|
||||
|
||||
// serve types that can present a security risk with CSP
|
||||
@@ -109,16 +102,12 @@ func ServeData(ctx *context.Context, filePath string, size int64, reader io.Read
|
||||
ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'")
|
||||
}
|
||||
|
||||
disposition := "inline"
|
||||
opts.Disposition = "inline"
|
||||
if isSVG && !setting.UI.SVG.Enabled {
|
||||
disposition = "attachment"
|
||||
opts.Disposition = "attachment"
|
||||
}
|
||||
|
||||
// encode filename per https://datatracker.ietf.org/doc/html/rfc5987
|
||||
encodedFileName := `filename*=UTF-8''` + url.PathEscape(fileName)
|
||||
|
||||
ctx.Resp.Header().Set("Content-Disposition", disposition+"; "+encodedFileName)
|
||||
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
|
||||
ctx.SetServeHeaders(opts)
|
||||
|
||||
_, err = ctx.Resp.Write(buf)
|
||||
if err != nil {
|
||||
|
||||
+13
-3
@@ -74,21 +74,31 @@ func InitGitServices() {
|
||||
mustInit(repo_service.Init)
|
||||
}
|
||||
|
||||
func syncAppPathForGit(ctx context.Context) error {
|
||||
func syncAppConfForGit(ctx context.Context) error {
|
||||
runtimeState := new(appstate.RuntimeState)
|
||||
if err := appstate.AppState.Get(runtimeState); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updated := false
|
||||
if runtimeState.LastAppPath != setting.AppPath {
|
||||
log.Info("AppPath changed from '%s' to '%s'", runtimeState.LastAppPath, setting.AppPath)
|
||||
runtimeState.LastAppPath = setting.AppPath
|
||||
updated = true
|
||||
}
|
||||
if runtimeState.LastCustomConf != setting.CustomConf {
|
||||
log.Info("CustomConf changed from '%s' to '%s'", runtimeState.LastCustomConf, setting.CustomConf)
|
||||
runtimeState.LastCustomConf = setting.CustomConf
|
||||
updated = true
|
||||
}
|
||||
|
||||
if updated {
|
||||
log.Info("re-sync repository hooks ...")
|
||||
mustInitCtx(ctx, repo_service.SyncRepositoryHooks)
|
||||
|
||||
log.Info("re-write ssh public keys ...")
|
||||
mustInit(asymkey_model.RewriteAllPublicKeys)
|
||||
|
||||
runtimeState.LastAppPath = setting.AppPath
|
||||
return appstate.AppState.Set(runtimeState)
|
||||
}
|
||||
return nil
|
||||
@@ -153,7 +163,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
||||
mustInit(repo_migrations.Init)
|
||||
eventsource.GetManager().Init()
|
||||
|
||||
mustInitCtx(ctx, syncAppPathForGit)
|
||||
mustInitCtx(ctx, syncAppConfForGit)
|
||||
|
||||
mustInit(ssh.Init)
|
||||
|
||||
|
||||
@@ -613,7 +613,9 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
||||
// update external user information
|
||||
if gothUser != nil {
|
||||
if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
if !user_model.IsErrExternalLoginUserNotExist(err) {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,6 +775,13 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) {
|
||||
return
|
||||
}
|
||||
|
||||
// Register last login
|
||||
user.SetLastLogin()
|
||||
if err := user_model.UpdateUserCols(ctx, user, "last_login_unix"); err != nil {
|
||||
ctx.ServerError("UpdateUserCols", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Flash.Success(ctx.Tr("auth.account_activated"))
|
||||
ctx.Redirect(setting.AppSubURL + "/")
|
||||
}
|
||||
|
||||
@@ -1061,7 +1061,9 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
|
||||
|
||||
// update external user information
|
||||
if err := externalaccount.UpdateExternalUser(u, gothUser); err != nil {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
if !user_model.IsErrExternalLoginUserNotExist(err) {
|
||||
log.Error("UpdateExternalUser failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := resetLocale(ctx, u); err != nil {
|
||||
|
||||
@@ -58,6 +58,10 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
|
||||
orderBy = "`user`.updated_unix ASC"
|
||||
case "reversealphabetically":
|
||||
orderBy = "`user`.name DESC"
|
||||
case "lastlogin":
|
||||
orderBy = "`user`.last_login_unix ASC"
|
||||
case "reverselastlogin":
|
||||
orderBy = "`user`.last_login_unix DESC"
|
||||
case UserSearchDefaultSortType: // "alphabetically"
|
||||
default:
|
||||
orderBy = "`user`.name ASC"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package feed
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
@@ -57,7 +56,6 @@ func showUserFeed(ctx *context.Context, formatType string) {
|
||||
|
||||
// writeFeed write a feeds.Feed as atom or rss to ctx.Resp
|
||||
func writeFeed(ctx *context.Context, feed *feeds.Feed, formatType string) {
|
||||
ctx.Resp.WriteHeader(http.StatusOK)
|
||||
if formatType == "atom" {
|
||||
ctx.Resp.Header().Set("Content-Type", "application/atom+xml;charset=utf-8")
|
||||
if err := feed.WriteAtom(ctx.Resp); err != nil {
|
||||
|
||||
@@ -284,7 +284,7 @@ func Diff(ctx *context.Context) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(commitID) != 40 {
|
||||
if len(commitID) != git.SHAFullLength {
|
||||
commitID = commit.ID.String()
|
||||
}
|
||||
|
||||
|
||||
@@ -1273,15 +1273,16 @@ func TestWebhook(ctx *context.Context) {
|
||||
|
||||
commitID := commit.ID.String()
|
||||
p := &api.PushPayload{
|
||||
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
|
||||
Before: commitID,
|
||||
After: commitID,
|
||||
CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
|
||||
Commits: []*api.PayloadCommit{apiCommit},
|
||||
HeadCommit: apiCommit,
|
||||
Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
|
||||
Pusher: apiUser,
|
||||
Sender: apiUser,
|
||||
Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
|
||||
Before: commitID,
|
||||
After: commitID,
|
||||
CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
|
||||
Commits: []*api.PayloadCommit{apiCommit},
|
||||
TotalCommits: 1,
|
||||
HeadCommit: apiCommit,
|
||||
Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
|
||||
Pusher: apiUser,
|
||||
Sender: apiUser,
|
||||
}
|
||||
if err := webhook_service.PrepareWebhook(w, ctx.Repo.Repository, webhook.HookEventPush, p); err != nil {
|
||||
ctx.Flash.Error("PrepareWebhook: " + err.Error())
|
||||
|
||||
@@ -31,6 +31,10 @@ func AvatarByUserName(ctx *context.Context) {
|
||||
if strings.ToLower(userName) != "ghost" {
|
||||
var err error
|
||||
if user, err = user_model.GetUserByName(ctx, userName); err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
ctx.NotFound("GetUserByName", err)
|
||||
return
|
||||
}
|
||||
ctx.ServerError("Invalid user: "+userName, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,14 +100,18 @@ func KeysPost(ctx *context.Context) {
|
||||
loadKeysData(ctx)
|
||||
ctx.Data["Err_Content"] = true
|
||||
ctx.Data["Err_Signature"] = true
|
||||
ctx.Data["KeyID"] = err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
||||
keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
||||
ctx.Data["KeyID"] = keyID
|
||||
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
||||
ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
|
||||
case asymkey_model.IsErrGPGNoEmailFound(err):
|
||||
loadKeysData(ctx)
|
||||
|
||||
ctx.Data["Err_Content"] = true
|
||||
ctx.Data["Err_Signature"] = true
|
||||
ctx.Data["KeyID"] = err.(asymkey_model.ErrGPGNoEmailFound).ID
|
||||
keyID := err.(asymkey_model.ErrGPGNoEmailFound).ID
|
||||
ctx.Data["KeyID"] = keyID
|
||||
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
||||
ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
|
||||
default:
|
||||
ctx.ServerError("AddPublicKey", err)
|
||||
@@ -139,7 +143,9 @@ func KeysPost(ctx *context.Context) {
|
||||
loadKeysData(ctx)
|
||||
ctx.Data["VerifyingID"] = form.KeyID
|
||||
ctx.Data["Err_Signature"] = true
|
||||
ctx.Data["KeyID"] = err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
||||
keyID := err.(asymkey_model.ErrGPGInvalidTokenSignature).ID
|
||||
ctx.Data["KeyID"] = keyID
|
||||
ctx.Data["PaddedKeyID"] = asymkey_model.PaddedKeyID(keyID)
|
||||
ctx.RenderWithErr(ctx.Tr("settings.gpg_invalid_token_signature"), tplSettingsKeys, &form)
|
||||
default:
|
||||
ctx.ServerError("VerifyGPG", err)
|
||||
|
||||
+4
-3
@@ -566,6 +566,8 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Post("/delete", admin.DeleteNotices)
|
||||
m.Post("/empty", admin.EmptyNotices)
|
||||
})
|
||||
}, func(ctx *context.Context) {
|
||||
ctx.Data["EnablePackages"] = setting.Packages.Enabled
|
||||
}, adminReq)
|
||||
// ***** END: Admin *****
|
||||
|
||||
@@ -597,7 +599,6 @@ func RegisterRoutes(m *web.Route) {
|
||||
reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases)
|
||||
reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases)
|
||||
reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki)
|
||||
reqRepoIssueWriter := context.RequireRepoWriter(unit.TypeIssues)
|
||||
reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues)
|
||||
reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests)
|
||||
reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests)
|
||||
@@ -891,8 +892,8 @@ func RegisterRoutes(m *web.Route) {
|
||||
})
|
||||
})
|
||||
m.Post("/reactions/{action}", bindIgnErr(forms.ReactionForm{}), repo.ChangeIssueReaction)
|
||||
m.Post("/lock", reqRepoIssueWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue)
|
||||
m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue)
|
||||
m.Post("/lock", reqRepoIssuesOrPullsWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue)
|
||||
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
|
||||
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
|
||||
}, context.RepoMustNotBeArchived())
|
||||
m.Group("/{index}", func() {
|
||||
|
||||
@@ -39,6 +39,10 @@ func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataSto
|
||||
|
||||
// SessionUser returns the user object corresponding to the "uid" session variable.
|
||||
func SessionUser(sess SessionStore) *user_model.User {
|
||||
if sess == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get user ID
|
||||
uid := sess.Get("uid")
|
||||
if uid == nil {
|
||||
|
||||
@@ -1505,8 +1505,13 @@ func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_
|
||||
}
|
||||
|
||||
changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit)
|
||||
// There are way too many possible errors.
|
||||
// Examples are various git errors such as the commit the review was based on was gc'ed and hence doesn't exist anymore as well as unrecoverable errors where we should serve a 500 response
|
||||
// Due to the current architecture and physical limitation of needing to compare explicit error messages, we can only choose one approach without the code getting ugly
|
||||
// For SOME of the errors such as the gc'ed commit, it would be best to mark all files as changed
|
||||
// But as that does not work for all potential errors, we simply mark all files as unchanged and drop the error which always works, even if not as good as possible
|
||||
if err != nil {
|
||||
return diff, err
|
||||
log.Error("Could not get changed files between %s and %s for pull request %d in repo with path %s. Assuming no changes. Error: %w", review.CommitSHA, latestCommit, pull.Index, gitRepo.Path, err)
|
||||
}
|
||||
|
||||
filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState)
|
||||
|
||||
+13
-1
@@ -220,9 +220,21 @@ func deleteIssue(issue *issues_model.Issue) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, issue.IsClosed); err != nil {
|
||||
// update the total issue numbers
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil {
|
||||
return err
|
||||
}
|
||||
// if the issue is closed, update the closed issue numbers
|
||||
if issue.IsClosed {
|
||||
if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil {
|
||||
return fmt.Errorf("error updating counters for milestone id %d: %w",
|
||||
issue.MilestoneID, err)
|
||||
}
|
||||
|
||||
if err := models.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
|
||||
return err
|
||||
|
||||
@@ -438,14 +438,21 @@ func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, downloa
|
||||
}
|
||||
|
||||
if download {
|
||||
rep.Actions["download"] = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header}
|
||||
var link *lfs_module.Link
|
||||
if setting.LFS.ServeDirect {
|
||||
// If we have a signed url (S3, object storage), redirect to this directly.
|
||||
u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid)
|
||||
if u != nil && err == nil {
|
||||
rep.Actions["download"] = &lfs_module.Link{Href: u.String(), Header: header}
|
||||
// Presigned url does not need the Authorization header
|
||||
// https://github.com/go-gitea/gitea/issues/21525
|
||||
delete(header, "Authorization")
|
||||
link = &lfs_module.Link{Href: u.String(), Header: header}
|
||||
}
|
||||
}
|
||||
if link == nil {
|
||||
link = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header}
|
||||
}
|
||||
rep.Actions["download"] = link
|
||||
}
|
||||
if upload {
|
||||
rep.Actions["upload"] = &lfs_module.Link{Href: rc.UploadLink(pointer), Header: header}
|
||||
|
||||
@@ -200,19 +200,19 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
|
||||
return nil, fmt.Errorf("ReadFile(%s): %v", headFile, err)
|
||||
}
|
||||
commitID := string(commitIDBytes)
|
||||
if len(commitID) < 40 {
|
||||
if len(commitID) < git.SHAFullLength {
|
||||
return nil, fmt.Errorf(`ReadFile(%s): invalid commit-ID "%s"`, headFile, commitID)
|
||||
}
|
||||
cmd := commitID[:40] + ".." + pr.BaseBranch
|
||||
cmd := commitID[:git.SHAFullLength] + ".." + pr.BaseBranch
|
||||
|
||||
// Get the commit from BaseBranch where the pull request got merged
|
||||
mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse", cmd).
|
||||
RunStdString(&git.RunOpts{Dir: "", Env: []string{"GIT_INDEX_FILE=" + indexTmpPath, "GIT_DIR=" + pr.BaseRepo.RepoPath()}})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("git rev-list --ancestry-path --merges --reverse: %v", err)
|
||||
} else if len(mergeCommit) < 40 {
|
||||
} else if len(mergeCommit) < git.SHAFullLength {
|
||||
// PR was maybe fast-forwarded, so just use last commit of PR
|
||||
mergeCommit = commitID[:40]
|
||||
mergeCommit = commitID[:git.SHAFullLength]
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
@@ -221,9 +221,9 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com
|
||||
}
|
||||
defer gitRepo.Close()
|
||||
|
||||
commit, err := gitRepo.GetCommit(mergeCommit[:40])
|
||||
commit, err := gitRepo.GetCommit(mergeCommit[:git.SHAFullLength])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetMergeCommit[%v]: %v", mergeCommit[:40], err)
|
||||
return nil, fmt.Errorf("GetMergeCommit[%v]: %v", mergeCommit[:git.SHAFullLength], err)
|
||||
}
|
||||
|
||||
return commit, nil
|
||||
|
||||
@@ -833,7 +833,7 @@ func MergedManually(pr *issues_model.PullRequest, doer *user_model.User, baseGit
|
||||
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged}
|
||||
}
|
||||
|
||||
if len(commitID) < 40 {
|
||||
if len(commitID) < git.SHAFullLength {
|
||||
return fmt.Errorf("Wrong commit ID")
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ var patchErrorSuffices = []string{
|
||||
": patch does not apply",
|
||||
": already exists in working directory",
|
||||
"unrecognized input",
|
||||
": No such file or directory",
|
||||
": does not exist in index",
|
||||
}
|
||||
|
||||
// TestPatch will test whether a simple patch will apply
|
||||
@@ -415,6 +417,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
|
||||
scanner := bufio.NewScanner(stderrReader)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
log.Trace("PullRequest[%d].testPatch: stderr: %s", pr.ID, line)
|
||||
if strings.HasPrefix(line, prefix) {
|
||||
conflict = true
|
||||
filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
|
||||
|
||||
@@ -167,7 +167,7 @@ func createTemporaryRepo(ctx context.Context, pr *issues_model.PullRequest) (str
|
||||
var headBranch string
|
||||
if pr.Flow == issues_model.PullRequestFlowGithub {
|
||||
headBranch = git.BranchPrefix + pr.HeadBranch
|
||||
} else if len(pr.HeadCommitID) == 40 { // for not created pull request
|
||||
} else if len(pr.HeadCommitID) == git.SHAFullLength { // for not created pull request
|
||||
headBranch = pr.HeadCommitID
|
||||
} else {
|
||||
headBranch = pr.GetGitRefName()
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -220,21 +221,21 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error
|
||||
return util.RemoveAll(repoPath)
|
||||
}
|
||||
|
||||
type unadoptedRrepositories struct {
|
||||
type unadoptedRepositories struct {
|
||||
repositories []string
|
||||
index int
|
||||
start int
|
||||
end int
|
||||
}
|
||||
|
||||
func (unadopted *unadoptedRrepositories) add(repository string) {
|
||||
func (unadopted *unadoptedRepositories) add(repository string) {
|
||||
if unadopted.index >= unadopted.start && unadopted.index < unadopted.end {
|
||||
unadopted.repositories = append(unadopted.repositories, repository)
|
||||
}
|
||||
unadopted.index++
|
||||
}
|
||||
|
||||
func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRrepositories) error {
|
||||
func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error {
|
||||
if len(repoNamesToCheck) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -266,7 +267,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
|
||||
}
|
||||
for _, repoName := range repoNamesToCheck {
|
||||
if _, ok := repoNames[repoName]; !ok {
|
||||
unadopted.add(filepath.Join(userName, repoName))
|
||||
unadopted.add(path.Join(userName, repoName)) // These are not used as filepaths - but as reponames - therefore use path.Join not filepath.Join
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -294,7 +295,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
|
||||
var repoNamesToCheck []string
|
||||
|
||||
start := (opts.Page - 1) * opts.PageSize
|
||||
unadopted := &unadoptedRrepositories{
|
||||
unadopted := &unadoptedRepositories{
|
||||
repositories: make([]string, 0, opts.PageSize),
|
||||
start: start,
|
||||
end: start + opts.PageSize,
|
||||
@@ -339,7 +340,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
|
||||
}
|
||||
|
||||
repoNamesToCheck = append(repoNamesToCheck, name)
|
||||
if len(repoNamesToCheck) > setting.Database.IterateBufferSize {
|
||||
if len(repoNamesToCheck) >= setting.Database.IterateBufferSize {
|
||||
if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
func TestCheckUnadoptedRepositories_Add(t *testing.T) {
|
||||
start := 10
|
||||
end := 20
|
||||
unadopted := &unadoptedRrepositories{
|
||||
unadopted := &unadoptedRepositories{
|
||||
start: start,
|
||||
end: end,
|
||||
index: 0,
|
||||
@@ -39,7 +39,7 @@ func TestCheckUnadoptedRepositories(t *testing.T) {
|
||||
//
|
||||
// Non existent user
|
||||
//
|
||||
unadopted := &unadoptedRrepositories{start: 0, end: 100}
|
||||
unadopted := &unadoptedRepositories{start: 0, end: 100}
|
||||
err := checkUnadoptedRepositories("notauser", []string{"repo"}, unadopted)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(unadopted.repositories))
|
||||
@@ -50,14 +50,14 @@ func TestCheckUnadoptedRepositories(t *testing.T) {
|
||||
userName := "user2"
|
||||
repoName := "repo2"
|
||||
unadoptedRepoName := "unadopted"
|
||||
unadopted = &unadoptedRrepositories{start: 0, end: 100}
|
||||
unadopted = &unadoptedRepositories{start: 0, end: 100}
|
||||
err = checkUnadoptedRepositories(userName, []string{repoName, unadoptedRepoName}, unadopted)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{path.Join(userName, unadoptedRepoName)}, unadopted.repositories)
|
||||
//
|
||||
// Existing (adopted) repository is not returned
|
||||
//
|
||||
unadopted = &unadoptedRrepositories{start: 0, end: 100}
|
||||
unadopted = &unadoptedRepositories{start: 0, end: 100}
|
||||
err = checkUnadoptedRepositories(userName, []string{repoName}, unadopted)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, len(unadopted.repositories))
|
||||
|
||||
@@ -30,9 +30,12 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
if _, err := gitRepo.GetCommit(sha); err != nil {
|
||||
if commit, err := gitRepo.GetCommit(sha); err != nil {
|
||||
gitRepo.Close()
|
||||
return fmt.Errorf("GetCommit[%s]: %v", sha, err)
|
||||
} else if len(sha) != git.SHAFullLength {
|
||||
// use complete commit sha
|
||||
sha = commit.ID.String()
|
||||
}
|
||||
gitRepo.Close()
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
|
||||
copy(treeURL[apiURLLen:], "/git/trees/")
|
||||
|
||||
// 40 is the size of the sha1 hash in hexadecimal format.
|
||||
copyPos := len(treeURL) - 40
|
||||
copyPos := len(treeURL) - git.SHAFullLength
|
||||
|
||||
if perPage <= 0 || perPage > setting.API.DefaultGitTreesPerPage {
|
||||
perPage = setting.API.DefaultGitTreesPerPage
|
||||
|
||||
@@ -220,10 +220,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
log.Error("updateIssuesCommit: %v", err)
|
||||
}
|
||||
|
||||
if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
||||
oldCommitID := opts.OldCommitID
|
||||
if oldCommitID == git.EmptySHA && len(commits.Commits) > 0 {
|
||||
oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1)
|
||||
@@ -251,6 +247,10 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
commits.CompareURL = ""
|
||||
}
|
||||
|
||||
if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
|
||||
notification.NotifyPushCommits(pusher, repo, opts, commits)
|
||||
|
||||
if err = git_model.RemoveDeletedBranchByName(repo.ID, branch); err != nil {
|
||||
|
||||
@@ -67,14 +67,14 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
)
|
||||
|
||||
var titleLink, linkText string
|
||||
if len(p.Commits) == 1 {
|
||||
if p.TotalCommits == 1 {
|
||||
commitDesc = "1 new commit"
|
||||
titleLink = p.Commits[0].URL
|
||||
linkText = fmt.Sprintf("view commit %s", p.Commits[0].ID[:7])
|
||||
linkText = "view commit"
|
||||
} else {
|
||||
commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
|
||||
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
||||
titleLink = p.CompareURL
|
||||
linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
|
||||
linkText = "view commits"
|
||||
}
|
||||
if titleLink == "" {
|
||||
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
|
||||
|
||||
@@ -82,7 +82,7 @@ func TestDingTalkPayload(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DingtalkPayload).ActionCard.Text)
|
||||
assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DingtalkPayload).ActionCard.Title)
|
||||
assert.Equal(t, "view commit 2020558...2020558", pl.(*DingtalkPayload).ActionCard.SingleTitle)
|
||||
assert.Equal(t, "view commits", pl.(*DingtalkPayload).ActionCard.SingleTitle)
|
||||
assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
|
||||
})
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ type (
|
||||
Wait bool `json:"wait"`
|
||||
Content string `json:"content"`
|
||||
Username string `json:"username"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
TTS bool `json:"tts"`
|
||||
Embeds []DiscordEmbed `json:"embeds"`
|
||||
}
|
||||
@@ -141,11 +141,11 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
)
|
||||
|
||||
var titleLink string
|
||||
if len(p.Commits) == 1 {
|
||||
if p.TotalCommits == 1 {
|
||||
commitDesc = "1 new commit"
|
||||
titleLink = p.Commits[0].URL
|
||||
} else {
|
||||
commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
|
||||
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
||||
titleLink = p.CompareURL
|
||||
}
|
||||
if titleLink == "" {
|
||||
|
||||
@@ -82,12 +82,13 @@ func pushTestPayload() *api.PushPayload {
|
||||
}
|
||||
|
||||
return &api.PushPayload{
|
||||
Ref: "refs/heads/test",
|
||||
Before: "2020558fe2e34debb818a514715839cabd25e777",
|
||||
After: "2020558fe2e34debb818a514715839cabd25e778",
|
||||
CompareURL: "",
|
||||
HeadCommit: commit,
|
||||
Commits: []*api.PayloadCommit{commit, commit},
|
||||
Ref: "refs/heads/test",
|
||||
Before: "2020558fe2e34debb818a514715839cabd25e777",
|
||||
After: "2020558fe2e34debb818a514715839cabd25e778",
|
||||
CompareURL: "",
|
||||
HeadCommit: commit,
|
||||
Commits: []*api.PayloadCommit{commit, commit},
|
||||
TotalCommits: 2,
|
||||
Repo: &api.Repository{
|
||||
HTMLURL: "http://localhost:3000/test/repo",
|
||||
Name: "repo",
|
||||
|
||||
@@ -154,10 +154,10 @@ func (m *MatrixPayloadUnsafe) Release(p *api.ReleasePayload) (api.Payloader, err
|
||||
func (m *MatrixPayloadUnsafe) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
var commitDesc string
|
||||
|
||||
if len(p.Commits) == 1 {
|
||||
if p.TotalCommits == 1 {
|
||||
commitDesc = "1 commit"
|
||||
} else {
|
||||
commitDesc = fmt.Sprintf("%d commits", len(p.Commits))
|
||||
commitDesc = fmt.Sprintf("%d commits", p.TotalCommits)
|
||||
}
|
||||
|
||||
repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
|
||||
|
||||
@@ -124,11 +124,11 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
)
|
||||
|
||||
var titleLink string
|
||||
if len(p.Commits) == 1 {
|
||||
if p.TotalCommits == 1 {
|
||||
commitDesc = "1 new commit"
|
||||
titleLink = p.Commits[0].URL
|
||||
} else {
|
||||
commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
|
||||
commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
|
||||
titleLink = p.CompareURL
|
||||
}
|
||||
if titleLink == "" {
|
||||
@@ -155,7 +155,7 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) {
|
||||
text,
|
||||
titleLink,
|
||||
greenColor,
|
||||
&MSTeamsFact{"Commit count:", fmt.Sprintf("%d", len(p.Commits))},
|
||||
&MSTeamsFact{"Commit count:", fmt.Sprintf("%d", p.TotalCommits)},
|
||||
), nil
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user