Compare commits

...

25 Commits

Author SHA1 Message Date
Lunny Xiao
08c6ea6728 Changelog for 1.24.6 (#35457)
Signed-off-by: techknowlogick <matti@mdranta.net>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2025-09-10 20:45:54 -07:00
Giteabot
67977f0b1c Fix a compare page 404 bug when the pull request disabled (#35441) (#35453)
Backport #35441 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-09-10 22:39:50 +08:00
Lunny Xiao
78fbcf35ad Fix push commits comments when changing the pull request target branch (#35386) (#35443)
Backport #35386 

When changing the pull request target branch, the pushed commits
comments will not be changed resulted the number are inconsistent
between commits tab number and the pushed commits comments number.

This PR will remove all the previous pushed commits comments and
calculate new comments when changing the target branch.

Before:

<img width="928" height="585" alt="image"

src="https://github.com/user-attachments/assets/35e4d31f-31a1-4d14-83b0-1786721ab0d9"
/>

After:
<img width="816" height="623" alt="image"

src="https://github.com/user-attachments/assets/24b6dafe-9238-4e7e-833d-68472457afab"
/>
2025-09-10 03:26:39 +02:00
Giteabot
8f5b1d27d4 Fix bug when issue disabled, pull request number in the commit message cannot be redirected (#35420) (#35442)
Backport #35420 by @lunny

Fix #35419

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-09-09 20:09:24 +00:00
Giteabot
89c99a4dcb fix: add author.name field to Swift Package Registry API response (#35410) (#35431)
Backport #35410 by ahanoff

Fixes #35159

Swift Package Manager expects an 'author.name' field in package
metadata, but Gitea was only providing schema.org format fields
(givenName, middleName, familyName). This caused SPM to fail with
keyNotFound error when fetching package metadata.

Changes:
- Add 'name' field to Person struct (inherited from
https://schema.org/Thing)
- Populate 'name' field in API response using existing String() method
- Maintains backward compatibility with existing schema.org fields
- Provides both formats for maximum compatibility

The fix ensures Swift Package Manager can successfully resolve packages
while preserving full schema.org compliance.

Co-authored-by: Akhan Zhakiyanov <ahanoff@gmail.com>
2025-09-08 03:37:53 +08:00
Giteabot
3c7e7a19dd fix(webhook/discord): fixed username cannot be empty error (#35412) (#35417)
Backport #35412 by @ulnanlu

username field is not required by discord and used to override the
default username. sending it as blank causes a 400 error. it should be
omitted instead when it's not set.

Ref:
https://discord.com/developers/docs/resources/webhook#execute-webhook-jsonform-params

Closes #35411

Co-authored-by: ulnanlu <220980518+ulnanlu@users.noreply.github.com>
2025-09-05 18:44:39 +00:00
Giteabot
8313b5d998 Switch bitnami images to bitnamilegacy on CI (#35402) (#35403)
Backport #35402 by @silverwind

As per https://github.com/bitnami/containers/issues/83267, my
understanding is that the `docker.io/bitnami` images will be deleted on
September 29.

To prevent this failure mode, use the images on the `bitnamilegacy` org
instead which are snapshots from when they stopped updating them.
Long-term, we should replace these images with official ones.

Co-authored-by: silverwind <me@silverwind.io>
2025-09-03 23:15:50 +08:00
Lunny Xiao
6ca73bf662 Upgrade xz to v0.5.15 (#35385)
backport #35377
2025-08-30 10:47:34 -07:00
Lunny Xiao
5e10def7f7 Fix review request webhook bug (#35339)
partially backport #35337
Fix #35327
2025-08-28 21:26:29 -07:00
Giteabot
1b8efb6fc7 Allow foreachref parser to grow its buffer (#35365) (#35376)
Backport #35365 by meyfa-lawo

Signed-off-by: Fabian Meyer <fabian.meyer@lawo.com>
Co-authored-by: Fabian Meyer <fabian.meyer@lawo.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-08-29 10:29:34 +08:00
Giteabot
8f89e1e174 Fix atom/rss mixed error (#35345) (#35347)
Backport #35345 by @lunny

Fix #35342

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-08-26 01:38:36 +00:00
Giteabot
cbc595b9d9 Allow deleting comment with content via API like web did (#35346) (#35354)
Backport #35346 by @lunny

Fix #35296

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-08-25 09:29:44 -07:00
Lunny Xiao
cc5ccf44dc fix: remove duplicate IDs (#35210) (#35325)
backport #35210
2025-08-22 16:19:55 -07:00
Lunny Xiao
f91e35b8b7 Upgrade mermaid to 11.10.0 (#35333)
backport #35329
2025-08-22 18:26:23 -04:00
Giteabot
f52ed422dc Fix some overflows in test (#35315) (#35321)
Backport #35315 by @lunny

Fix #35311

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-08-20 09:48:13 -07:00
Giteabot
0266ee5de7 Fix LFS range size header response (#35277) (#35293)
Backport #35277 by @LePau

Fix #35276

Signed-off-by: LePau <101608950+LePau@users.noreply.github.com>
Co-authored-by: LePau <101608950+LePau@users.noreply.github.com>
2025-08-17 15:59:03 +03:00
Giteabot
ac03e65cf4 Fix GitHub release assets URL validation (#35287) (#35290)
Backport #35287 by @alexblackie

GitHub changed where the attachments on releases are stored, which means
repo migrations with releases now fail because the redirect URLs don't
match the base URL validation. We need to update the base URL check to
check for the `release-assets` subdomain as well.

Co-authored-by: Alex Blackie <alex@blackie.ca>
2025-08-15 19:43:59 -07:00
Giteabot
f3e6672c09 Fix token lifetime, closes #35230 (#35271) (#35281)
Backport #35271 by @TimB87

This is an attempt to fix #35230 as discussed in the ticket.

Co-authored-by: Tim Biermann <tbier@posteo.de>
2025-08-14 14:52:09 -07:00
Lunny Xiao
136ec9ef81 Add changelog for 1.24.5 (#35261) 2025-08-13 08:58:05 -07:00
Giteabot
79018ae726 modules/setting/actions.go: fixed typo: ì->i (#35253) (#35254)
Backport #35253 by @TimB87

Hello,

I spotted this minor typo.

Co-authored-by: Tim Biermann <tbier@posteo.de>
2025-08-12 11:21:56 +00:00
Giteabot
e11176192a Fix a bug where lfs gc never worked. (#35198) (#35255)
Backport #35198 by @lunny

Fix #31113

After #22385 introduced LFS GC, it never worked due to a bug in the INI
library: fields in structs embedded more than one level deep are not
populated from the INI file.

This PR fixes the issue by replacing the multi-level embedded struct
with a single-level struct for parsing the cron.gc_lfs configuration.

Added a new test for retrieving cron settings to demonstrate the bug in
the INI package.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-08-12 10:57:50 +03:00
Giteabot
4e0269e890 Reload issue when sending webhook to make num comments is right. (#35243) (#35248)
Backport #35243 by @lunny

Fix #35229

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-08-11 10:12:47 -07:00
Lunny Xiao
04114c637a Fix bug when review pull request commits (#35192) (#35246)
The commit range in the UI follows a half-open, half-closed convention:
(,]. When reviewing a range of commits, the beforeCommitID should be set
to the commit immediately preceding the first selected commit. For
single-commit reviews, we must identify and use the previous commit of
that specific commit.

The endpoint ViewPullFilesStartingFromCommit is currently unused and can
be safely removed.

Fix #35157
Replace #35184
Partially extract from #35077
Backport #35192
2025-08-11 13:18:03 +02:00
6543
e5540bfa81 Nix flake build static with sqlite support (#35149) (#35225)
Backport #35149

with `nix develop -c $SHELL` you can enter the dev environment. now with
`make clean-all generate build -j1` you will get a static linked binary
that has sqlite support

outside of an nix dev shell if you set `STATIC=true` you also will get a
static binary
2025-08-07 23:37:46 +03:00
Giteabot
d22d6ca0d8 Vertically center "Show Resolved" (#35211) (#35218)
Backport #35211 by @silverwind

Before, "Show Resolved" slightly off-center:

<img width="174" height="60" alt="Screenshot 2025-08-04 at 15 07 13"
src="https://github.com/user-attachments/assets/a165f721-4749-4ea3-bde0-141ad43a6142"
/>

After: centered:

<img width="176" height="63" alt="Screenshot 2025-08-04 at 15 07 22"
src="https://github.com/user-attachments/assets/f87e16bc-d067-4040-b940-9adb4cf182c1"
/>

Co-authored-by: silverwind <me@silverwind.io>
2025-08-06 10:55:48 +02:00
44 changed files with 655 additions and 338 deletions

View File

@@ -31,7 +31,7 @@ jobs:
minio: minio:
# as github actions doesn't support "entrypoint", we need to use a non-official image # as github actions doesn't support "entrypoint", we need to use a non-official image
# that has a custom entrypoint set to "minio server /data" # that has a custom entrypoint set to "minio server /data"
image: bitnami/minio:2023.8.31 image: bitnamilegacy/minio:2023.8.31
env: env:
MINIO_ROOT_USER: 123456 MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678 MINIO_ROOT_PASSWORD: 12345678
@@ -113,7 +113,7 @@ jobs:
ports: ports:
- 6379:6379 - 6379:6379
minio: minio:
image: bitnami/minio:2021.3.17 image: bitnamilegacy/minio:2021.3.17
env: env:
MINIO_ACCESS_KEY: 123456 MINIO_ACCESS_KEY: 123456
MINIO_SECRET_KEY: 12345678 MINIO_SECRET_KEY: 12345678
@@ -155,7 +155,7 @@ jobs:
services: services:
mysql: mysql:
# the bitnami mysql image has more options than the official one, it's easier to customize # the bitnami mysql image has more options than the official one, it's easier to customize
image: bitnami/mysql:8.0 image: bitnamilegacy/mysql:8.0
env: env:
ALLOW_EMPTY_PASSWORD: true ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: testgitea MYSQL_DATABASE: testgitea

View File

@@ -4,7 +4,35 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com). been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.24.4](https://github.com/go-gitea/gitea/releases/tag/1.24.4) - 2025-08-03 ## [1.24.6](https://github.com/go-gitea/gitea/releases/tag/1.24.6) - 2025-09-10
* SECURITY
* Upgrade xz to v0.5.15 (#35385)
* BUGFIXES
* Fix a compare page 404 bug when the pull request disabled (#35441) (#35453)
* Fix bug when issue disabled, pull request number in the commit message cannot be redirected (#35420) (#35442)
* Add author.name field to Swift Package Registry API response (#35410) (#35431)
* Remove usernames when empty in discord webhook (#35412) (#35417)
* Allow foreachref parser to grow its buffer (#35365) (#35376)
* Allow deleting comment with content via API like web did (#35346) (#35354)
* Fix atom/rss mixed error (#35345) (#35347)
* Fix review request webhook bug (#35339)
* Remove duplicate html IDs (#35210) (#35325)
* Fix LFS range size header response (#35277) (#35293)
* Fix GitHub release assets URL validation (#35287) (#35290)
* Fix token lifetime, closes #35230 (#35271) (#35281)
* Fix push commits comments when changing the pull request target branch (#35386) (#35443)
## [1.24.5](https://github.com/go-gitea/gitea/releases/tag/v1.24.5) - 2025-08-12
* BUGFIXES
* Fix a bug where lfs gc never worked. (#35198) (#35255)
* Reload issue when sending webhook to make num comments is right. (#35243) (#35248)
* Fix bug when review pull request commits (#35192) (#35246)
* MISC
* Vertically center "Show Resolved" (#35211) (#35218)
## [1.24.4](https://github.com/go-gitea/gitea/releases/tag/v1.24.4) - 2025-08-03
* BUGFIXES * BUGFIXES
* Fix various bugs (1.24) (#35186) * Fix various bugs (1.24) (#35186)
@@ -16,7 +44,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Don't use full-file highlight when there is a git diff textconv (#35114) (#35119) * Don't use full-file highlight when there is a git diff textconv (#35114) (#35119)
* Increase gap on latest commit (#35104) (#35113) * Increase gap on latest commit (#35104) (#35113)
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/1.24.3) - 2025-07-15 ## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/v1.24.3) - 2025-07-15
* BUGFIXES * BUGFIXES
* Fix form property assignment edge case (#35073) (#35078) * Fix form property assignment edge case (#35073) (#35078)
@@ -48,7 +76,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Skip updating timestamp when sync branch (#34875) * Skip updating timestamp when sync branch (#34875)
* Fix required contexts and commit status matching bug (#34815) (#34829) * Fix required contexts and commit status matching bug (#34815) (#34829)
## [1.24.2](https://github.com/go-gitea/gitea/releases/tag/1.24.2) - 2025-06-20 ## [1.24.2](https://github.com/go-gitea/gitea/releases/tag/v1.24.2) - 2025-06-20
* BUGFIXES * BUGFIXES
* Fix container range bug (#34795) (#34796) * Fix container range bug (#34795) (#34796)
@@ -56,7 +84,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* BUILD * BUILD
* Bump poetry feature to new url for dev container (#34787) (#34790) * Bump poetry feature to new url for dev container (#34787) (#34790)
## [1.24.1](https://github.com/go-gitea/gitea/releases/tag/1.24.1) - 2025-06-18 ## [1.24.1](https://github.com/go-gitea/gitea/releases/tag/v1.24.1) - 2025-06-18
* ENHANCEMENTS * ENHANCEMENTS
* Improve alignment of commit status icon on commit page (#34750) (#34757) * Improve alignment of commit status icon on commit page (#34750) (#34757)
@@ -76,7 +104,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Hide href attribute of a tag if there is no target_url (#34556) (#34684) * Hide href attribute of a tag if there is no target_url (#34556) (#34684)
* Fix tag target (#34781) #34783 * Fix tag target (#34781) #34783
## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26 ## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/v1.24.0) - 2025-05-26
* BREAKING * BREAKING
* Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076) * Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
@@ -446,7 +474,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Bump x/net (#32896) (#32900) * Bump x/net (#32896) (#32900)
* Only activity tab needs heatmap data loading (#34652) * Only activity tab needs heatmap data loading (#34652)
## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/1.23.8) - 2025-05-11 ## [1.23.8](https://github.com/go-gitea/gitea/releases/tag/v1.23.8) - 2025-05-11
* SECURITY * SECURITY
* Fix a bug when uploading file via lfs ssh command (#34408) (#34411) * Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
@@ -473,7 +501,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* Bump go version in go.mod (#34160) * Bump go version in go.mod (#34160)
* remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158) * remove hardcoded 'code' string in clone_panel.tmpl (#34153) (#34158)
## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/1.23.7) - 2025-04-07 ## [1.23.7](https://github.com/go-gitea/gitea/releases/tag/v1.23.7) - 2025-04-07
* Enhancements * Enhancements
* Add a config option to block "expensive" pages for anonymous users (#34024) (#34071) * Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
@@ -571,7 +599,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.com).
* BUGFIXES * BUGFIXES
* Fix a bug caused by status webhook template #33512 * Fix a bug caused by status webhook template #33512
## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04 ## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/v1.23.2) - 2025-02-04
* BREAKING * BREAKING
* Add tests for webhook and fix some webhook bugs (#33396) (#33442) * Add tests for webhook and fix some webhook bugs (#33396) (#33442)
@@ -3101,7 +3129,7 @@ Key highlights of this release encompass significant changes categorized under `
* Improve decryption failure message (#24573) (#24575) * Improve decryption failure message (#24573) (#24575)
* Makefile: Use portable !, not GNUish -not, with find(1). (#24565) (#24572) * Makefile: Use portable !, not GNUish -not, with find(1). (#24565) (#24572)
## [1.19.3](https://github.com/go-gitea/gitea/releases/tag/1.19.3) - 2023-05-03 ## [1.19.3](https://github.com/go-gitea/gitea/releases/tag/v1.19.3) - 2023-05-03
* SECURITY * SECURITY
* Use golang 1.20.4 to fix CVE-2023-24539, CVE-2023-24540, and CVE-2023-29400 * Use golang 1.20.4 to fix CVE-2023-24539, CVE-2023-24540, and CVE-2023-29400
@@ -3114,7 +3142,7 @@ Key highlights of this release encompass significant changes categorized under `
* Fix incorrect CurrentUser check for docker rootless (#24435) * Fix incorrect CurrentUser check for docker rootless (#24435)
* Getting the tag list does not require being signed in (#24413) (#24416) * Getting the tag list does not require being signed in (#24413) (#24416)
## [1.19.2](https://github.com/go-gitea/gitea/releases/tag/1.19.2) - 2023-04-26 ## [1.19.2](https://github.com/go-gitea/gitea/releases/tag/v1.19.2) - 2023-04-26
* SECURITY * SECURITY
* Require repo scope for PATs for private repos and basic authentication (#24362) (#24364) * Require repo scope for PATs for private repos and basic authentication (#24362) (#24364)
@@ -3613,7 +3641,7 @@ Key highlights of this release encompass significant changes categorized under `
* Display attachments of review comment when comment content is blank (#23035) (#23046) * Display attachments of review comment when comment content is blank (#23035) (#23046)
* Return empty url for submodule tree entries (#23043) (#23048) * Return empty url for submodule tree entries (#23043) (#23048)
## [1.18.4](https://github.com/go-gitea/gitea/releases/tag/1.18.4) - 2023-02-20 ## [1.18.4](https://github.com/go-gitea/gitea/releases/tag/v1.18.4) - 2023-02-20
* SECURITY * SECURITY
* Provide the ability to set password hash algorithm parameters (#22942) (#22943) * Provide the ability to set password hash algorithm parameters (#22942) (#22943)
@@ -4040,7 +4068,7 @@ Key highlights of this release encompass significant changes categorized under `
* Fix the mode of custom dir to 0700 in docker-rootless (#20861) (#20867) * Fix the mode of custom dir to 0700 in docker-rootless (#20861) (#20867)
* Fix UI mis-align for PR commit history (#20845) (#20859) * Fix UI mis-align for PR commit history (#20845) (#20859)
## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/1.17.1) - 2022-08-17 ## [1.17.1](https://github.com/go-gitea/gitea/releases/tag/v1.17.1) - 2022-08-17
* SECURITY * SECURITY
* Correctly escape within tribute.js (#20831) (#20832) * Correctly escape within tribute.js (#20831) (#20832)

View File

@@ -47,6 +47,17 @@ ifeq ($(HAS_GO), yes)
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
endif endif
CGO_ENABLED ?= 0
ifneq (,$(findstring sqlite,$(TAGS))$(findstring pam,$(TAGS)))
CGO_ENABLED = 1
endif
STATIC ?=
EXTLDFLAGS ?=
ifneq ($(STATIC),)
EXTLDFLAGS = -extldflags "-static"
endif
ifeq ($(GOOS),windows) ifeq ($(GOOS),windows)
IS_WINDOWS := yes IS_WINDOWS := yes
else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
@@ -740,7 +751,10 @@ security-check:
go run $(GOVULNCHECK_PACKAGE) -show color ./... go run $(GOVULNCHECK_PACKAGE) -show color ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) $(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@ ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
$(error pam support set via TAGS doesn't support static builds)
endif
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
.PHONY: release .PHONY: release
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check

View File

@@ -11,8 +11,16 @@
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
in in
{ {
devShells.default = pkgs.mkShell { devShells.default =
buildInputs = with pkgs; [ with pkgs;
let
# only bump toolchain versions here
go = go_1_24;
nodejs = nodejs_24;
python3 = python312;
in
pkgs.mkShell {
buildInputs = [
# generic # generic
git git
git-lfs git-lfs
@@ -22,21 +30,25 @@
gzip gzip
# frontend # frontend
nodejs_22 nodejs
# linting # linting
python312 python3
poetry poetry
# backend # backend
go_1_24 go
glibc.static
gofumpt gofumpt
sqlite sqlite
]; ];
shellHook = '' CFLAGS = "-I${glibc.static.dev}/include";
export GO="${pkgs.go_1_24}/bin/go" LDFLAGS = "-L ${glibc.static}/lib";
export GOROOT="${pkgs.go_1_24}/share/go" GO = "${go}/bin/go";
''; GOROOT = "${go}/share/go";
TAGS = "sqlite sqlite_unlock_notify";
STATIC = "true";
}; };
} }
); );

2
go.mod
View File

@@ -109,7 +109,7 @@ require (
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0
github.com/syndtr/goleveldb v1.0.0 github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0 github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.12 github.com/ulikunitz/xz v0.5.15
github.com/urfave/cli/v2 v2.27.6 github.com/urfave/cli/v2 v2.27.6
github.com/wneessen/go-mail v0.6.2 github.com/wneessen/go-mail v0.6.2
github.com/xeipuuv/gojsonschema v1.2.0 github.com/xeipuuv/gojsonschema v1.2.0

4
go.sum
View File

@@ -757,8 +757,8 @@ github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGB
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=

View File

@@ -85,8 +85,8 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) {
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin)) assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin))
// Disvard invalid input. // Discard invalid input.
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(unittest.NonexistentID))) assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(-1)))
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID}) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID})
} }

View File

@@ -30,6 +30,10 @@ type Parser struct {
func NewParser(r io.Reader, format Format) *Parser { func NewParser(r io.Reader, format Format) *Parser {
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
// default MaxScanTokenSize = 64 kiB may be too small for some references,
// so allow the buffer to grow up to 4x if needed
scanner.Buffer(nil, 4*bufio.MaxScanTokenSize)
// in addition to the reference delimiter we specified in the --format, // in addition to the reference delimiter we specified in the --format,
// `git for-each-ref` will always add a newline after every reference. // `git for-each-ref` will always add a newline after every reference.
refDelim := make([]byte, 0, len(format.refDelim)+1) refDelim := make([]byte, 0, len(format.refDelim)+1)
@@ -70,6 +74,9 @@ func NewParser(r io.Reader, format Format) *Parser {
// { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" } // { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" }
func (p *Parser) Next() map[string]string { func (p *Parser) Next() map[string]string {
if !p.scanner.Scan() { if !p.scanner.Scan() {
if err := p.scanner.Err(); err != nil {
p.err = err
}
return nil return nil
} }
fields, err := p.parseRef(p.scanner.Text()) fields, err := p.parseRef(p.scanner.Text())

View File

@@ -82,6 +82,7 @@ type ProgrammingLanguage struct {
// https://schema.org/Person // https://schema.org/Person
type Person struct { type Person struct {
Type string `json:"@type,omitempty"` Type string `json:"@type,omitempty"`
Name string `json:"name,omitempty"` // inherited from https://schema.org/Thing
GivenName string `json:"givenName,omitempty"` GivenName string `json:"givenName,omitempty"`
MiddleName string `json:"middleName,omitempty"` MiddleName string `json:"middleName,omitempty"`
FamilyName string `json:"familyName,omitempty"` FamilyName string `json:"familyName,omitempty"`
@@ -184,11 +185,17 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) {
p.Metadata.Description = ssc.Description p.Metadata.Description = ssc.Description
p.Metadata.Keywords = ssc.Keywords p.Metadata.Keywords = ssc.Keywords
p.Metadata.License = ssc.License p.Metadata.License = ssc.License
p.Metadata.Author = Person{ author := Person{
Name: ssc.Author.Name,
GivenName: ssc.Author.GivenName, GivenName: ssc.Author.GivenName,
MiddleName: ssc.Author.MiddleName, MiddleName: ssc.Author.MiddleName,
FamilyName: ssc.Author.FamilyName, FamilyName: ssc.Author.FamilyName,
} }
// If Name is not provided, generate it from individual name components
if author.Name == "" {
author.Name = author.String()
}
p.Metadata.Author = author
p.Metadata.RepositoryURL = ssc.CodeRepository p.Metadata.RepositoryURL = ssc.CodeRepository
if !validation.IsValidURL(p.Metadata.RepositoryURL) { if !validation.IsValidURL(p.Metadata.RepositoryURL) {

View File

@@ -97,10 +97,49 @@ func TestParsePackage(t *testing.T) {
assert.Equal(t, packageDescription, p.Metadata.Description) assert.Equal(t, packageDescription, p.Metadata.Description)
assert.ElementsMatch(t, []string{"swift", "package"}, p.Metadata.Keywords) assert.ElementsMatch(t, []string{"swift", "package"}, p.Metadata.Keywords)
assert.Equal(t, packageLicense, p.Metadata.License) assert.Equal(t, packageLicense, p.Metadata.License)
assert.Equal(t, packageAuthor, p.Metadata.Author.Name)
assert.Equal(t, packageAuthor, p.Metadata.Author.GivenName) assert.Equal(t, packageAuthor, p.Metadata.Author.GivenName)
assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL) assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL)
assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs) assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs)
}) })
t.Run("WithExplicitNameField", func(t *testing.T) {
data := createArchive(map[string][]byte{
"Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"),
})
authorName := "John Doe"
p, err := ParsePackage(
data,
data.Size(),
strings.NewReader(`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","author":{"name":"`+authorName+`","givenName":"John","familyName":"Doe"}}`),
)
assert.NotNil(t, p)
assert.NoError(t, err)
assert.Equal(t, authorName, p.Metadata.Author.Name)
assert.Equal(t, "John", p.Metadata.Author.GivenName)
assert.Equal(t, "Doe", p.Metadata.Author.FamilyName)
})
t.Run("NameFieldGeneration", func(t *testing.T) {
data := createArchive(map[string][]byte{
"Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"),
})
// Test with only individual name components - Name should be auto-generated
p, err := ParsePackage(
data,
data.Size(),
strings.NewReader(`{"author":{"givenName":"John","middleName":"Q","familyName":"Doe"}}`),
)
assert.NotNil(t, p)
assert.NoError(t, err)
assert.Equal(t, "John Q Doe", p.Metadata.Author.Name)
assert.Equal(t, "John", p.Metadata.Author.GivenName)
assert.Equal(t, "Q", p.Metadata.Author.MiddleName)
assert.Equal(t, "Doe", p.Metadata.Author.FamilyName)
})
} }
func TestTrimmedVersionString(t *testing.T) { func TestTrimmedVersionString(t *testing.T) {
@@ -142,3 +181,43 @@ func TestTrimmedVersionString(t *testing.T) {
assert.Equal(t, c.Expected, TrimmedVersionString(c.Version)) assert.Equal(t, c.Expected, TrimmedVersionString(c.Version))
} }
} }
func TestPersonNameString(t *testing.T) {
cases := []struct {
Name string
Person Person
Expected string
}{
{
Name: "GivenNameOnly",
Person: Person{GivenName: "John"},
Expected: "John",
},
{
Name: "GivenAndFamily",
Person: Person{GivenName: "John", FamilyName: "Doe"},
Expected: "John Doe",
},
{
Name: "FullName",
Person: Person{GivenName: "John", MiddleName: "Q", FamilyName: "Doe"},
Expected: "John Q Doe",
},
{
Name: "MiddleAndFamily",
Person: Person{MiddleName: "Q", FamilyName: "Doe"},
Expected: "Q Doe",
},
{
Name: "Empty",
Person: Person{},
Expected: "",
},
}
for _, c := range cases {
t.Run(c.Name, func(t *testing.T) {
assert.Equal(t, c.Expected, c.Person.String())
})
}
}

View File

@@ -24,7 +24,7 @@ var (
ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"` ZombieTaskTimeout time.Duration `ini:"ZOMBIE_TASK_TIMEOUT"`
EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"` EndlessTaskTimeout time.Duration `ini:"ENDLESS_TASK_TIMEOUT"`
AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"` AbandonedJobTimeout time.Duration `ini:"ABANDONED_JOB_TIMEOUT"`
SkipWorkflowStrings []string `ìni:"SKIP_WORKFLOW_STRINGS"` SkipWorkflowStrings []string `ini:"SKIP_WORKFLOW_STRINGS"`
}{ }{
Enabled: true, Enabled: true,
DefaultActionsURL: defaultActionsURLGitHub, DefaultActionsURL: defaultActionsURLGitHub,

View File

@@ -41,3 +41,56 @@ EXTEND = true
assert.Equal(t, "white rabbit", extended.Second) assert.Equal(t, "white rabbit", extended.Second)
assert.True(t, extended.Extend) assert.True(t, extended.Extend)
} }
// Test_getCronSettings2 tests that getCronSettings can not handle two levels of embedding
func Test_getCronSettings2(t *testing.T) {
type BaseStruct struct {
Enabled bool
RunAtStart bool
Schedule string
}
type Extended struct {
BaseStruct
Extend bool
}
type Extended2 struct {
Extended
Third string
}
iniStr := `
[cron.test]
ENABLED = TRUE
RUN_AT_START = TRUE
SCHEDULE = @every 1h
EXTEND = true
THIRD = white rabbit
`
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
extended := &Extended2{
Extended: Extended{
BaseStruct: BaseStruct{
Enabled: false,
RunAtStart: false,
Schedule: "@every 72h",
},
Extend: false,
},
Third: "black rabbit",
}
_, err = getCronSettings(cfg, "test", extended)
assert.NoError(t, err)
// This confirms the first level of embedding works
assert.Equal(t, "white rabbit", extended.Third)
assert.True(t, extended.Extend)
// This confirms 2 levels of embedding doesn't work
assert.False(t, extended.Enabled)
assert.False(t, extended.RunAtStart)
assert.Equal(t, "@every 72h", extended.Schedule)
}

View File

@@ -13,6 +13,6 @@ func TestCountFmt(t *testing.T) {
assert.Equal(t, "125", countFmt(125)) assert.Equal(t, "125", countFmt(125))
assert.Equal(t, "1.3k", countFmt(int64(1317))) assert.Equal(t, "1.3k", countFmt(int64(1317)))
assert.Equal(t, "21.3M", countFmt(21317675)) assert.Equal(t, "21.3M", countFmt(21317675))
assert.Equal(t, "45.7G", countFmt(45721317675)) assert.Equal(t, "45.7G", countFmt(int64(45721317675)))
assert.Empty(t, countFmt("test")) assert.Empty(t, countFmt("test"))
} }

36
package-lock.json generated
View File

@@ -35,7 +35,7 @@
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.22", "katex": "0.16.22",
"license-checker-webpack-plugin": "0.2.1", "license-checker-webpack-plugin": "0.2.1",
"mermaid": "11.6.0", "mermaid": "11.10.0",
"mini-css-extract-plugin": "2.9.2", "mini-css-extract-plugin": "2.9.2",
"minimatch": "10.0.1", "minimatch": "10.0.1",
"monaco-editor": "0.52.2", "monaco-editor": "0.52.2",
@@ -1540,9 +1540,9 @@
} }
}, },
"node_modules/@mermaid-js/parser": { "node_modules/@mermaid-js/parser": {
"version": "0.4.0", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz",
"integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"langium": "3.3.1" "langium": "3.3.1"
@@ -6154,9 +6154,9 @@
} }
}, },
"node_modules/dompurify": { "node_modules/dompurify": {
"version": "3.2.4", "version": "3.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
"integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"license": "(MPL-2.0 OR Apache-2.0)", "license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": { "optionalDependencies": {
"@types/trusted-types": "^2.0.7" "@types/trusted-types": "^2.0.7"
@@ -9249,14 +9249,14 @@
} }
}, },
"node_modules/mermaid": { "node_modules/mermaid": {
"version": "11.6.0", "version": "11.10.0",
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.10.0.tgz",
"integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", "integrity": "sha512-oQsFzPBy9xlpnGxUqLbVY8pvknLlsNIJ0NWwi8SUJjhbP1IT0E0o1lfhU4iYV3ubpy+xkzkaOyDUQMn06vQElQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@braintree/sanitize-url": "^7.0.4", "@braintree/sanitize-url": "^7.0.4",
"@iconify/utils": "^2.1.33", "@iconify/utils": "^2.1.33",
"@mermaid-js/parser": "^0.4.0", "@mermaid-js/parser": "^0.6.2",
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"cytoscape": "^3.29.3", "cytoscape": "^3.29.3",
"cytoscape-cose-bilkent": "^4.1.0", "cytoscape-cose-bilkent": "^4.1.0",
@@ -9265,11 +9265,11 @@
"d3-sankey": "^0.12.3", "d3-sankey": "^0.12.3",
"dagre-d3-es": "7.0.11", "dagre-d3-es": "7.0.11",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dompurify": "^3.2.4", "dompurify": "^3.2.5",
"katex": "^0.16.9", "katex": "^0.16.22",
"khroma": "^2.1.0", "khroma": "^2.1.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"marked": "^15.0.7", "marked": "^16.0.0",
"roughjs": "^4.6.6", "roughjs": "^4.6.6",
"stylis": "^4.3.6", "stylis": "^4.3.6",
"ts-dedent": "^2.2.0", "ts-dedent": "^2.2.0",
@@ -9277,15 +9277,15 @@
} }
}, },
"node_modules/mermaid/node_modules/marked": { "node_modules/mermaid/node_modules/marked": {
"version": "15.0.7", "version": "16.2.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz", "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.0.tgz",
"integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", "integrity": "sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg==",
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"marked": "bin/marked.js" "marked": "bin/marked.js"
}, },
"engines": { "engines": {
"node": ">= 18" "node": ">= 20"
} }
}, },
"node_modules/micromark": { "node_modules/micromark": {

View File

@@ -34,7 +34,7 @@
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.22", "katex": "0.16.22",
"license-checker-webpack-plugin": "0.2.1", "license-checker-webpack-plugin": "0.2.1",
"mermaid": "11.6.0", "mermaid": "11.10.0",
"mini-css-extract-plugin": "2.9.2", "mini-css-extract-plugin": "2.9.2",
"minimatch": "10.0.1", "minimatch": "10.0.1",
"monaco-editor": "0.52.2", "monaco-editor": "0.52.2",

View File

@@ -230,6 +230,7 @@ func PackageVersionMetadata(ctx *context.Context) {
}, },
Author: swift_module.Person{ Author: swift_module.Person{
Type: "Person", Type: "Person",
Name: metadata.Author.String(),
GivenName: metadata.Author.GivenName, GivenName: metadata.Author.GivenName,
MiddleName: metadata.Author.MiddleName, MiddleName: metadata.Author.MiddleName,
FamilyName: metadata.Author.FamilyName, FamilyName: metadata.Author.FamilyName,

View File

@@ -721,8 +721,8 @@ func deleteIssueComment(ctx *context.APIContext) {
if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
ctx.Status(http.StatusForbidden) ctx.Status(http.StatusForbidden)
return return
} else if comment.Type != issues_model.CommentTypeComment { } else if !comment.Type.HasContentSupport() {
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusBadRequest)
return return
} }

View File

@@ -8,11 +8,18 @@ import (
) )
// RenderBranchFeed render format for branch or file // RenderBranchFeed render format for branch or file
func RenderBranchFeed(ctx *context.Context) { func RenderBranchFeed(ctx *context.Context, feedType string) {
_, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
if ctx.Repo.TreePath == "" { if ctx.Repo.TreePath == "" {
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) ShowBranchFeed(ctx, ctx.Repo.Repository, feedType)
} else { } else {
ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) ShowFileFeed(ctx, ctx.Repo.Repository, feedType)
} }
} }
func RenderBranchFeedRSS(ctx *context.Context) {
RenderBranchFeed(ctx, "rss")
}
func RenderBranchFeedAtom(ctx *context.Context) {
RenderBranchFeed(ctx, "atom")
}

View File

@@ -523,7 +523,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo {
// Treat as pull request if both references are branches // Treat as pull request if both references are branches
if ctx.Data["PageIsComparePull"] == nil { if ctx.Data["PageIsComparePull"] == nil {
ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch && permBase.CanReadIssuesOrPulls(true)
} }
if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) {
@@ -735,6 +735,7 @@ func CompareDiff(ctx *context.Context) {
return return
} }
ctx.Data["PageIsViewCode"] = true
ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes
ctx.Data["DirectComparison"] = ci.DirectComparison ctx.Data["DirectComparison"] = ci.DirectComparison
ctx.Data["OtherCompareSeparator"] = ".." ctx.Data["OtherCompareSeparator"] = ".."

View File

@@ -643,8 +643,17 @@ func ViewPullCommits(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplPullCommits) ctx.HTML(http.StatusOK, tplPullCommits)
} }
func indexCommit(commits []*git.Commit, commitID string) *git.Commit {
for i := range commits {
if commits[i].ID.String() == commitID {
return commits[i]
}
}
return nil
}
// ViewPullFiles render pull request changed files list page // ViewPullFiles render pull request changed files list page
func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommit string, willShowSpecifiedCommitRange, willShowSpecifiedCommit bool) { func viewPullFiles(ctx *context.Context, beforeCommitID, afterCommitID string) {
ctx.Data["PageIsPullList"] = true ctx.Data["PageIsPullList"] = true
ctx.Data["PageIsPullFiles"] = true ctx.Data["PageIsPullFiles"] = true
@@ -654,11 +663,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
} }
pull := issue.PullRequest pull := issue.PullRequest
var ( gitRepo := ctx.Repo.GitRepo
startCommitID string
endCommitID string
gitRepo = ctx.Repo.GitRepo
)
prInfo := preparePullViewPullInfo(ctx, issue) prInfo := preparePullViewPullInfo(ctx, issue)
if ctx.Written() { if ctx.Written() {
@@ -668,77 +673,68 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
return return
} }
// Validate the given commit sha to show (if any passed)
if willShowSpecifiedCommit || willShowSpecifiedCommitRange {
foundStartCommit := len(specifiedStartCommit) == 0
foundEndCommit := len(specifiedEndCommit) == 0
if !(foundStartCommit && foundEndCommit) {
for _, commit := range prInfo.Commits {
if commit.ID.String() == specifiedStartCommit {
foundStartCommit = true
}
if commit.ID.String() == specifiedEndCommit {
foundEndCommit = true
}
if foundStartCommit && foundEndCommit {
break
}
}
}
if !(foundStartCommit && foundEndCommit) {
ctx.NotFound(nil)
return
}
}
if ctx.Written() {
return
}
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName()) headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
if err != nil { if err != nil {
ctx.ServerError("GetRefCommitID", err) ctx.ServerError("GetRefCommitID", err)
return return
} }
ctx.Data["IsShowingOnlySingleCommit"] = willShowSpecifiedCommit isSingleCommit := beforeCommitID == "" && afterCommitID != ""
ctx.Data["IsShowingOnlySingleCommit"] = isSingleCommit
isShowAllCommits := (beforeCommitID == "" || beforeCommitID == prInfo.MergeBase) && (afterCommitID == "" || afterCommitID == headCommitID)
ctx.Data["IsShowingAllCommits"] = isShowAllCommits
if willShowSpecifiedCommit || willShowSpecifiedCommitRange { if afterCommitID == "" || afterCommitID == headCommitID {
if len(specifiedEndCommit) > 0 { afterCommitID = headCommitID
endCommitID = specifiedEndCommit
} else {
endCommitID = headCommitID
} }
if len(specifiedStartCommit) > 0 { afterCommit := indexCommit(prInfo.Commits, afterCommitID)
startCommitID = specifiedStartCommit if afterCommit == nil {
} else { ctx.HTTPError(http.StatusBadRequest, "after commit not found in PR commits")
startCommitID = prInfo.MergeBase return
}
var beforeCommit *git.Commit
if !isSingleCommit {
if beforeCommitID == "" || beforeCommitID == prInfo.MergeBase {
beforeCommitID = prInfo.MergeBase
// mergebase commit is not in the list of the pull request commits
beforeCommit, err = gitRepo.GetCommit(beforeCommitID)
if err != nil {
ctx.ServerError("GetCommit", err)
return
} }
ctx.Data["IsShowingAllCommits"] = false
} else { } else {
endCommitID = headCommitID beforeCommit = indexCommit(prInfo.Commits, beforeCommitID)
startCommitID = prInfo.MergeBase if beforeCommit == nil {
ctx.Data["IsShowingAllCommits"] = true ctx.HTTPError(http.StatusBadRequest, "before commit not found in PR commits")
return
}
}
} else {
beforeCommit, err = afterCommit.Parent(0)
if err != nil {
ctx.ServerError("Parent", err)
return
}
beforeCommitID = beforeCommit.ID.String()
} }
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name
ctx.Data["AfterCommitID"] = endCommitID ctx.Data["MergeBase"] = prInfo.MergeBase
ctx.Data["BeforeCommitID"] = startCommitID ctx.Data["AfterCommitID"] = afterCommitID
ctx.Data["BeforeCommitID"] = beforeCommitID
fileOnly := ctx.FormBool("file-only")
maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
files := ctx.FormStrings("files") files := ctx.FormStrings("files")
fileOnly := ctx.FormBool("file-only")
if fileOnly && (len(files) == 2 || len(files) == 1) { if fileOnly && (len(files) == 2 || len(files) == 1) {
maxLines, maxFiles = -1, -1 maxLines, maxFiles = -1, -1
} }
diffOptions := &gitdiff.DiffOptions{ diffOptions := &gitdiff.DiffOptions{
AfterCommitID: endCommitID, BeforeCommitID: beforeCommitID,
AfterCommitID: afterCommitID,
SkipTo: ctx.FormString("skip-to"), SkipTo: ctx.FormString("skip-to"),
MaxLines: maxLines, MaxLines: maxLines,
MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
@@ -746,10 +742,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)),
} }
if !willShowSpecifiedCommit {
diffOptions.BeforeCommitID = startCommitID
}
diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...) diff, err := gitdiff.GetDiffForRender(ctx, ctx.Repo.RepoLink, gitRepo, diffOptions, files...)
if err != nil { if err != nil {
ctx.ServerError("GetDiff", err) ctx.ServerError("GetDiff", err)
@@ -761,7 +753,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
// as the viewed information is designed to be loaded only on latest PR // as the viewed information is designed to be loaded only on latest PR
// diff and if you're signed in. // diff and if you're signed in.
var reviewState *pull_model.ReviewState var reviewState *pull_model.ReviewState
if ctx.IsSigned && !willShowSpecifiedCommit && !willShowSpecifiedCommitRange { if ctx.IsSigned && isShowAllCommits {
reviewState, err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions) reviewState, err = gitdiff.SyncUserSpecificDiff(ctx, ctx.Doer.ID, pull, gitRepo, diff, diffOptions)
if err != nil { if err != nil {
ctx.ServerError("SyncUserSpecificDiff", err) ctx.ServerError("SyncUserSpecificDiff", err)
@@ -769,7 +761,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
} }
} }
diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, startCommitID, endCommitID) diffShortStat, err := gitdiff.GetDiffShortStat(ctx.Repo.GitRepo, beforeCommitID, afterCommitID)
if err != nil { if err != nil {
ctx.ServerError("GetDiffShortStat", err) ctx.ServerError("GetDiffShortStat", err)
return return
@@ -816,7 +808,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
if !fileOnly { if !fileOnly {
// note: use mergeBase is set to false because we already have the merge base from the pull request info // note: use mergeBase is set to false because we already have the merge base from the pull request info
diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, startCommitID, endCommitID) diffTree, err := gitdiff.GetDiffTree(ctx, gitRepo, false, beforeCommitID, afterCommitID)
if err != nil { if err != nil {
ctx.ServerError("GetDiffTree", err) ctx.ServerError("GetDiffTree", err)
return return
@@ -836,17 +828,6 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.Data["Diff"] = diff ctx.Data["Diff"] = diff
ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0 ctx.Data["DiffNotAvailable"] = diffShortStat.NumFiles == 0
baseCommit, err := ctx.Repo.GitRepo.GetCommit(startCommitID)
if err != nil {
ctx.ServerError("GetCommit", err)
return
}
commit, err := gitRepo.GetCommit(endCommitID)
if err != nil {
ctx.ServerError("GetCommit", err)
return
}
if ctx.IsSigned && ctx.Doer != nil { if ctx.IsSigned && ctx.Doer != nil {
if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, issue, ctx.Doer); err != nil { if ctx.Data["CanMarkConversation"], err = issues_model.CanMarkConversation(ctx, issue, ctx.Doer); err != nil {
ctx.ServerError("CanMarkConversation", err) ctx.ServerError("CanMarkConversation", err)
@@ -854,7 +835,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
} }
} }
setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name) setCompareContext(ctx, beforeCommit, afterCommit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository) assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository)
if err != nil { if err != nil {
@@ -901,7 +882,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool { ctx.Data["CanBlockUser"] = func(blocker, blockee *user_model.User) bool {
return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee) return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
} }
if !willShowSpecifiedCommit && !willShowSpecifiedCommitRange && pull.Flow == issues_model.PullRequestFlowGithub { if isShowAllCommits && pull.Flow == issues_model.PullRequestFlowGithub {
if err := pull.LoadHeadRepo(ctx); err != nil { if err := pull.LoadHeadRepo(ctx); err != nil {
ctx.ServerError("LoadHeadRepo", err) ctx.ServerError("LoadHeadRepo", err)
return return
@@ -930,19 +911,17 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi
} }
func ViewPullFilesForSingleCommit(ctx *context.Context) { func ViewPullFilesForSingleCommit(ctx *context.Context) {
viewPullFiles(ctx, "", ctx.PathParam("sha"), true, true) // it doesn't support showing files from mergebase to the special commit
// otherwise it will be ambiguous
viewPullFiles(ctx, "", ctx.PathParam("sha"))
} }
func ViewPullFilesForRange(ctx *context.Context) { func ViewPullFilesForRange(ctx *context.Context) {
viewPullFiles(ctx, ctx.PathParam("shaFrom"), ctx.PathParam("shaTo"), true, false) viewPullFiles(ctx, ctx.PathParam("shaFrom"), ctx.PathParam("shaTo"))
}
func ViewPullFilesStartingFromCommit(ctx *context.Context) {
viewPullFiles(ctx, "", ctx.PathParam("sha"), true, false)
} }
func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) { func ViewPullFilesForAllCommitsOfPr(ctx *context.Context) {
viewPullFiles(ctx, "", "", false, false) viewPullFiles(ctx, "", "")
} }
// UpdatePullRequest merge PR's baseBranch into headBranch // UpdatePullRequest merge PR's baseBranch into headBranch

View File

@@ -1217,10 +1217,11 @@ func registerWebRoutes(m *web.Router) {
// end "/{username}/{reponame}": view milestone, label, issue, pull, etc // end "/{username}/{reponame}": view milestone, label, issue, pull, etc
m.Group("/{username}/{reponame}/{type:issues}", func() { m.Group("/{username}/{reponame}/{type:issues}", func() {
// these handlers also check unit permissions internally
m.Get("", repo.Issues) m.Get("", repo.Issues)
m.Get("/{index}", repo.ViewIssue) m.Get("/{index}", repo.ViewIssue) // also do pull-request redirection (".../issues/{PR-number}" -> ".../pulls/{PR-number}")
}, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypeExternalTracker)) }, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker))
// end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker // end "/{username}/{reponame}": issue list, issue view (pull-request redirection), external tracker
m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc
m.Group("/issues", func() { m.Group("/issues", func() {
@@ -1509,7 +1510,7 @@ func registerWebRoutes(m *web.Router) {
m.Group("/commits", func() { m.Group("/commits", func() {
m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits) m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
m.Get("/list", repo.GetPullCommits) m.Get("/list", repo.GetPullCommits)
m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit) m.Get("/{sha:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
}) })
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest) m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest) m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
@@ -1518,8 +1519,7 @@ func registerWebRoutes(m *web.Router) {
m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest) m.Post("/cleanup", context.RepoMustNotBeArchived(), repo.CleanUpPullRequest)
m.Group("/files", func() { m.Group("/files", func() {
m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr) m.Get("", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
m.Get("/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit) m.Get("/{shaFrom:[a-f0-9]{7,64}}..{shaTo:[a-f0-9]{7,64}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Group("/reviews", func() { m.Group("/reviews", func() {
m.Get("/new_comment", repo.RenderNewCodeCommentForm) m.Get("/new_comment", repo.RenderNewCodeCommentForm)
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment) m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
@@ -1593,8 +1593,8 @@ func registerWebRoutes(m *web.Router) {
m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, context.RepoRefByDefaultBranch(), repo.CherryPick) m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, context.RepoRefByDefaultBranch(), repo.CherryPick)
}, repo.MustBeNotEmpty) }, repo.MustBeNotEmpty)
m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeedRSS)
m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeedAtom)
m.Group("/src", func() { m.Group("/src", func() {
m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404 m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404

View File

@@ -53,7 +53,7 @@ func CreateAuthorizationToken(taskID, runID, jobID int64) (string, error) {
claims := actionsClaims{ claims := actionsClaims{
RegisteredClaims: jwt.RegisteredClaims{ RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)), ExpiresAt: jwt.NewNumericDate(now.Add(1*time.Hour + setting.Actions.EndlessTaskTimeout)),
NotBefore: jwt.NewNumericDate(now), NotBefore: jwt.NewNumericDate(now),
}, },
Scp: fmt.Sprintf("Actions.Results:%d:%d", runID, jobID), Scp: fmt.Sprintf("Actions.Results:%d:%d", runID, jobID),

View File

@@ -260,11 +260,6 @@ func (n *actionsNotifier) CreateIssueComment(ctx context.Context, doer *user_mod
func (n *actionsNotifier) UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) { func (n *actionsNotifier) UpdateComment(ctx context.Context, doer *user_model.User, c *issues_model.Comment, oldContent string) {
ctx = withMethod(ctx, "UpdateComment") ctx = withMethod(ctx, "UpdateComment")
if err := c.LoadIssue(ctx); err != nil {
log.Error("LoadIssue: %v", err)
return
}
if c.Issue.IsPull { if c.Issue.IsPull {
notifyIssueCommentChange(ctx, doer, c, oldContent, webhook_module.HookEventPullRequestComment, api.HookIssueCommentEdited) notifyIssueCommentChange(ctx, doer, c, oldContent, webhook_module.HookEventPullRequestComment, api.HookIssueCommentEdited)
return return
@@ -275,11 +270,6 @@ func (n *actionsNotifier) UpdateComment(ctx context.Context, doer *user_model.Us
func (n *actionsNotifier) DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) { func (n *actionsNotifier) DeleteComment(ctx context.Context, doer *user_model.User, comment *issues_model.Comment) {
ctx = withMethod(ctx, "DeleteComment") ctx = withMethod(ctx, "DeleteComment")
if err := comment.LoadIssue(ctx); err != nil {
log.Error("LoadIssue: %v", err)
return
}
if comment.Issue.IsPull { if comment.Issue.IsPull {
notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventPullRequestComment, api.HookIssueCommentDeleted) notifyIssueCommentChange(ctx, doer, comment, "", webhook_module.HookEventPullRequestComment, api.HookIssueCommentDeleted)
return return
@@ -288,6 +278,7 @@ func (n *actionsNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
} }
func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, comment *issues_model.Comment, oldContent string, event webhook_module.HookEventType, action api.HookIssueCommentAction) { func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, comment *issues_model.Comment, oldContent string, event webhook_module.HookEventType, action api.HookIssueCommentAction) {
comment.Issue = nil // force issue to be loaded
if err := comment.LoadIssue(ctx); err != nil { if err := comment.LoadIssue(ctx); err != nil {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return

View File

@@ -250,7 +250,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git.
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to load pull issue. Error: %w", err) return nil, fmt.Errorf("failed to load pull issue. Error: %w", err)
} }
comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i], forcePush.Value())
if err == nil && comment != nil { if err == nil && comment != nil {
notify_service.PullRequestPushCommits(ctx, pusher, pr, comment) notify_service.PullRequestPushCommits(ctx, pusher, pr, comment)
} }

View File

@@ -171,19 +171,20 @@ func registerDeleteOldSystemNotices() {
}) })
} }
type GCLFSConfig struct {
BaseConfig
OlderThan time.Duration
LastUpdatedMoreThanAgo time.Duration
NumberToCheckPerRepo int64
ProportionToCheckPerRepo float64
}
func registerGCLFS() { func registerGCLFS() {
if !setting.LFS.StartServer { if !setting.LFS.StartServer {
return return
} }
type GCLFSConfig struct {
OlderThanConfig
LastUpdatedMoreThanAgo time.Duration
NumberToCheckPerRepo int64
ProportionToCheckPerRepo float64
}
RegisterTaskFatal("gc_lfs", &GCLFSConfig{ RegisterTaskFatal("gc_lfs", &GCLFSConfig{
OlderThanConfig: OlderThanConfig{
BaseConfig: BaseConfig{ BaseConfig: BaseConfig{
Enabled: false, Enabled: false,
RunAtStart: false, RunAtStart: false,
@@ -198,7 +199,7 @@ func registerGCLFS() {
// It is likely that a week is potentially excessive but it should definitely be enough that any // It is likely that a week is potentially excessive but it should definitely be enough that any
// unassociated LFS object is genuinely unassociated. // unassociated LFS object is genuinely unassociated.
OlderThan: 24 * time.Hour * 7, OlderThan: 24 * time.Hour * 7,
},
// Only GC things that haven't been looked at in the past 3 days // Only GC things that haven't been looked at in the past 3 days
LastUpdatedMoreThanAgo: 24 * time.Hour * 3, LastUpdatedMoreThanAgo: 24 * time.Hour * 3,
NumberToCheckPerRepo: 100, NumberToCheckPerRepo: 100,

View File

@@ -0,0 +1,51 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cron
import (
"testing"
"time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func Test_GCLFSConfig(t *testing.T) {
cfg, err := setting.NewConfigProviderFromData(`
[cron.gc_lfs]
ENABLED = true
RUN_AT_START = true
SCHEDULE = "@every 2h"
OLDER_THAN = "1h"
LAST_UPDATED_MORE_THAN_AGO = "7h"
NUMBER_TO_CHECK_PER_REPO = 10
PROPORTION_TO_CHECK_PER_REPO = 0.1
`)
assert.NoError(t, err)
defer test.MockVariableValue(&setting.CfgProvider, cfg)()
config := &GCLFSConfig{
BaseConfig: BaseConfig{
Enabled: false,
RunAtStart: false,
Schedule: "@every 24h",
},
OlderThan: 24 * time.Hour * 7,
LastUpdatedMoreThanAgo: 24 * time.Hour * 3,
NumberToCheckPerRepo: 100,
ProportionToCheckPerRepo: 0.6,
}
_, err = setting.GetCronSettings("gc_lfs", config)
assert.NoError(t, err)
assert.True(t, config.Enabled)
assert.True(t, config.RunAtStart)
assert.Equal(t, "@every 2h", config.Schedule)
assert.Equal(t, 1*time.Hour, config.OlderThan)
assert.Equal(t, 7*time.Hour, config.LastUpdatedMoreThanAgo)
assert.Equal(t, int64(10), config.NumberToCheckPerRepo)
assert.InDelta(t, 0.1, config.ProportionToCheckPerRepo, 0.001)
}

View File

@@ -80,6 +80,12 @@ func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_m
return nil, err return nil, err
} }
// reload issue to ensure it has the latest data, especially the number of comments
issue, err = issues_model.GetIssueByID(ctx, issue.ID)
if err != nil {
return nil, err
}
notify_service.CreateIssueComment(ctx, doer, repo, issue, comment, mentions) notify_service.CreateIssueComment(ctx, doer, repo, issue, comment, mentions)
return comment, nil return comment, nil

View File

@@ -111,7 +111,7 @@ func DownloadHandler(ctx *context.Context) {
} }
} }
ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size-fromByte)) ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size))
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Range") ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Range")
} }
} }

View File

@@ -354,7 +354,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(ctx context.Context, rel *gith
// Prevent open redirect // Prevent open redirect
if !hasBaseURL(redirectURL, g.baseURL) && if !hasBaseURL(redirectURL, g.baseURL) &&
!hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") { !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") &&
!hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") {
WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL) WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL)
return io.NopCloser(strings.NewReader(redirectURL)), nil return io.NopCloser(strings.NewReader(redirectURL)), nil

View File

@@ -14,42 +14,28 @@ import (
) )
// getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID // getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID
// isForcePush will be true if oldCommit isn't on the branch
// Commit on baseBranch will skip // Commit on baseBranch will skip
func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) { func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, err error) {
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
if err != nil { if err != nil {
return nil, false, err return nil, err
} }
defer closer.Close() defer closer.Close()
oldCommit, err := gitRepo.GetCommit(oldCommitID) oldCommit, err := gitRepo.GetCommit(oldCommitID)
if err != nil { if err != nil {
return nil, false, err return nil, err
} }
newCommit, err := gitRepo.GetCommit(newCommitID) newCommit, err := gitRepo.GetCommit(newCommitID)
if err != nil { if err != nil {
return nil, false, err return nil, err
}
isForcePush, err = newCommit.IsForcePush(oldCommitID)
if err != nil {
return nil, false, err
}
if isForcePush {
commitIDs = make([]string, 2)
commitIDs[0] = oldCommitID
commitIDs[1] = newCommitID
return commitIDs, isForcePush, err
} }
// Find commits between new and old commit excluding base branch commits // Find commits between new and old commit excluding base branch commits
commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch) commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch)
if err != nil { if err != nil {
return nil, false, err return nil, err
} }
commitIDs = make([]string, 0, len(commits)) commitIDs = make([]string, 0, len(commits))
@@ -57,38 +43,40 @@ func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldC
commitIDs = append(commitIDs, commits[i].ID.String()) commitIDs = append(commitIDs, commits[i].ID.String())
} }
return commitIDs, isForcePush, err return commitIDs, err
} }
// CreatePushPullComment create push code to pull base comment // CreatePushPullComment create push code to pull base comment
func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (comment *issues_model.Comment, err error) { func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *issues_model.PullRequest, oldCommitID, newCommitID string, isForcePush bool) (comment *issues_model.Comment, err error) {
if pr.HasMerged || oldCommitID == "" || newCommitID == "" { if pr.HasMerged || oldCommitID == "" || newCommitID == "" {
return nil, nil return nil, nil
} }
ops := &issues_model.CreateCommentOptions{ opts := &issues_model.CreateCommentOptions{
Type: issues_model.CommentTypePullRequestPush, Type: issues_model.CommentTypePullRequestPush,
Doer: pusher, Doer: pusher,
Repo: pr.BaseRepo, Repo: pr.BaseRepo,
IsForcePush: isForcePush,
Issue: pr.Issue,
} }
var data issues_model.PushActionContent var data issues_model.PushActionContent
if opts.IsForcePush {
data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) data.CommitIDs = []string{oldCommitID, newCommitID}
} else {
data.CommitIDs, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
ops.Issue = pr.Issue
dataJSON, err := json.Marshal(data) dataJSON, err := json.Marshal(data)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ops.Content = string(dataJSON) opts.Content = string(dataJSON)
comment, err = issues_model.CreateComment(ctx, opts)
comment, err = issues_model.CreateComment(ctx, ops)
return comment, err return comment, err
} }

View File

@@ -28,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/globallock"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -142,36 +141,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
return err return err
} }
compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), if _, err := CreatePushPullComment(ctx, issue.Poster, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false); err != nil {
git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false)
if err != nil {
return err
}
if len(compareInfo.Commits) == 0 {
return nil
}
data := issues_model.PushActionContent{IsForcePush: false}
data.CommitIDs = make([]string, 0, len(compareInfo.Commits))
for i := len(compareInfo.Commits) - 1; i >= 0; i-- {
data.CommitIDs = append(data.CommitIDs, compareInfo.Commits[i].ID.String())
}
dataJSON, err := json.Marshal(data)
if err != nil {
return err
}
ops := &issues_model.CreateCommentOptions{
Type: issues_model.CommentTypePullRequestPush,
Doer: issue.Poster,
Repo: repo,
Issue: pr.Issue,
IsForcePush: false,
Content: string(dataJSON),
}
if _, err = issues_model.CreateComment(ctx, ops); err != nil {
return err return err
} }
@@ -193,6 +163,20 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers) issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
// Request reviews, these should be requested before other notifications because they will add request reviews record
// on database
permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster)
for _, reviewer := range opts.Reviewers {
if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil {
return err
}
}
for _, teamReviewer := range opts.TeamReviewers {
if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil {
return err
}
}
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content) mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
if err != nil { if err != nil {
return err return err
@@ -211,17 +195,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error {
} }
notify_service.IssueChangeAssignee(ctx, issue.Poster, issue, assignee, false, assigneeCommentMap[assigneeID]) notify_service.IssueChangeAssignee(ctx, issue.Poster, issue, assignee, false, assigneeCommentMap[assigneeID])
} }
permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster)
for _, reviewer := range opts.Reviewers {
if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil {
return err
}
}
for _, teamReviewer := range opts.TeamReviewers {
if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil {
return err
}
}
return nil return nil
} }
@@ -332,6 +306,14 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
pr.CommitsAhead = divergence.Ahead pr.CommitsAhead = divergence.Ahead
pr.CommitsBehind = divergence.Behind pr.CommitsBehind = divergence.Behind
// add first push codes comment
baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil {
return err
}
defer baseGitRepo.Close()
return db.WithTx(ctx, func(ctx context.Context) error {
if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil { if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil {
return err return err
} }
@@ -349,7 +331,17 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer
return fmt.Errorf("CreateChangeTargetBranchComment: %w", err) return fmt.Errorf("CreateChangeTargetBranchComment: %w", err)
} }
return nil // Delete all old push comments and insert new push comments
if _, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID).
And("type = ?", issues_model.CommentTypePullRequestPush).
NoAutoCondition().
Delete(new(issues_model.Comment)); err != nil {
return err
}
_, err = CreatePushPullComment(ctx, doer, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false)
return err
})
} }
func checkForInvalidation(ctx context.Context, requests issues_model.PullRequestList, repoID int64, doer *user_model.User, branch string) error { func checkForInvalidation(ctx context.Context, requests issues_model.PullRequestList, repoID int64, doer *user_model.User, branch string) error {
@@ -410,7 +402,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) {
} }
StartPullRequestCheckImmediately(ctx, pr) StartPullRequestCheckImmediately(ctx, pr)
comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID) comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID, opts.IsForcePush)
if err == nil && comment != nil { if err == nil && comment != nil {
notify_service.PullRequestPushCommits(ctx, opts.Doer, pr, comment) notify_service.PullRequestPushCommits(ctx, opts.Doer, pr, comment)
} }

View File

@@ -57,7 +57,7 @@ type (
DiscordPayload struct { DiscordPayload struct {
Wait bool `json:"wait"` Wait bool `json:"wait"`
Content string `json:"content"` Content string `json:"content"`
Username string `json:"username"` Username string `json:"username,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"` AvatarURL string `json:"avatar_url,omitempty"`
TTS bool `json:"tts"` TTS bool `json:"tts"`
Embeds []DiscordEmbed `json:"embeds"` Embeds []DiscordEmbed `json:"embeds"`

View File

@@ -445,6 +445,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us
log.Error("LoadPoster: %v", err) log.Error("LoadPoster: %v", err)
return return
} }
comment.Issue = nil // reload issue to ensure it has the latest data, especially the number of comments
if err = comment.LoadIssue(ctx); err != nil { if err = comment.LoadIssue(ctx); err != nil {
log.Error("LoadIssue: %v", err) log.Error("LoadIssue: %v", err)
return return

View File

@@ -35,7 +35,7 @@
{{template "repo/diff/whitespace_dropdown" .}} {{template "repo/diff/whitespace_dropdown" .}}
{{template "repo/diff/options_dropdown" .}} {{template "repo/diff/options_dropdown" .}}
{{if .PageIsPullFiles}} {{if .PageIsPullFiles}}
<div id="diff-commit-select" data-issuelink="{{$.Issue.Link}}" data-queryparams="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}" data-filter_changes_by_commit="{{ctx.Locale.Tr "repo.pulls.filter_changes_by_commit"}}"> <div id="diff-commit-select" data-merge-base="{{.MergeBase}}" data-issuelink="{{$.Issue.Link}}" data-queryparams="?style={{if $.IsSplitStyle}}split{{else}}unified{{end}}&whitespace={{$.WhitespaceBehavior}}&show-outdated={{$.ShowOutdatedComments}}" data-filter_changes_by_commit="{{ctx.Locale.Tr "repo.pulls.filter_changes_by_commit"}}">
{{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}} {{/* the following will be replaced by vue component, but this avoids any loading artifacts till the vue component is initialized */}}
<div class="ui jump dropdown tiny basic button custom"> <div class="ui jump dropdown tiny basic button custom">
{{svg "octicon-git-commit"}} {{svg "octicon-git-commit"}}

View File

@@ -15,7 +15,7 @@
</span> </span>
{{end}} {{end}}
</div> </div>
<div> <div class="tw-flex tw-items-center">
{{if or $invalid $resolved}} {{if or $invalid $resolved}}
<button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden{{end}} btn tiny show-outdated"> <button id="show-outdated-{{$comment.ID}}" data-comment="{{$comment.ID}}" class="{{if not $resolved}}tw-hidden{{end}} btn tiny show-outdated">
{{svg "octicon-unfold" 16 "tw-mr-2"}} {{svg "octicon-unfold" 16 "tw-mr-2"}}

View File

@@ -119,7 +119,7 @@
{{range $idx, $code := .FileContent}} {{range $idx, $code := .FileContent}}
{{$line := Eval $idx "+" 1}} {{$line := Eval $idx "+" 1}}
<tr> <tr>
<td id="L{{$line}}" class="lines-num"><span id="L{{$line}}" data-line-number="{{$line}}"></span></td> <td class="lines-num"><span id="L{{$line}}" data-line-number="{{$line}}"></span></td>
{{if $.EscapeStatus.Escaped}} {{if $.EscapeStatus.Escaped}}
<td class="lines-escape">{{if (index $.LineEscapeStatus $idx).Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{if (index $.LineEscapeStatus $idx).HasInvisible}}{{ctx.Locale.Tr "repo.invisible_runes_line"}} {{end}}{{if (index $.LineEscapeStatus $idx).HasAmbiguous}}{{ctx.Locale.Tr "repo.ambiguous_runes_line"}}{{end}}"></button>{{end}}</td> <td class="lines-escape">{{if (index $.LineEscapeStatus $idx).Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{if (index $.LineEscapeStatus $idx).HasInvisible}}{{ctx.Locale.Tr "repo.invisible_runes_line"}} {{end}}{{if (index $.LineEscapeStatus $idx).HasAmbiguous}}{{ctx.Locale.Tr "repo.ambiguous_runes_line"}}{{end}}"></button>{{end}}</td>
{{end}} {{end}}

View File

@@ -355,6 +355,7 @@ func TestPackageSwift(t *testing.T) {
assert.Equal(t, packageVersion, result.Metadata.Version) assert.Equal(t, packageVersion, result.Metadata.Version)
assert.Equal(t, packageDescription, result.Metadata.Description) assert.Equal(t, packageDescription, result.Metadata.Description)
assert.Equal(t, "Swift", result.Metadata.ProgrammingLanguage.Name) assert.Equal(t, "Swift", result.Metadata.ProgrammingLanguage.Name)
assert.Equal(t, packageAuthor, result.Metadata.Author.Name)
assert.Equal(t, packageAuthor, result.Metadata.Author.GivenName) assert.Equal(t, packageAuthor, result.Metadata.Author.GivenName)
req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion)). req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion)).

View File

@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/indexer/issues" "code.gitea.io/gitea/modules/indexer/issues"
@@ -471,19 +472,38 @@ func TestIssueRedirect(t *testing.T) {
session := loginUser(t, "user2") session := loginUser(t, "user2")
// Test external tracker where style not set (shall default numeric) // Test external tracker where style not set (shall default numeric)
req := NewRequest(t, "GET", path.Join("org26", "repo_external_tracker", "issues", "1")) req := NewRequest(t, "GET", "/org26/repo_external_tracker/issues/1")
resp := session.MakeRequest(t, req, http.StatusSeeOther) resp := session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "https://tracker.com/org26/repo_external_tracker/issues/1", test.RedirectURL(resp)) assert.Equal(t, "https://tracker.com/org26/repo_external_tracker/issues/1", test.RedirectURL(resp))
// Test external tracker with numeric style // Test external tracker with numeric style
req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_numeric", "issues", "1")) req = NewRequest(t, "GET", "/org26/repo_external_tracker_numeric/issues/1")
resp = session.MakeRequest(t, req, http.StatusSeeOther) resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "https://tracker.com/org26/repo_external_tracker_numeric/issues/1", test.RedirectURL(resp)) assert.Equal(t, "https://tracker.com/org26/repo_external_tracker_numeric/issues/1", test.RedirectURL(resp))
// Test external tracker with alphanumeric style (for a pull request) // Test external tracker with alphanumeric style (for a pull request)
req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_alpha", "issues", "1")) req = NewRequest(t, "GET", "/org26/repo_external_tracker_alpha/issues/1")
resp = session.MakeRequest(t, req, http.StatusSeeOther) resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/"+path.Join("org26", "repo_external_tracker_alpha", "pulls", "1"), test.RedirectURL(resp)) assert.Equal(t, "/org26/repo_external_tracker_alpha/pulls/1", test.RedirectURL(resp))
// test to check that the PR redirection works if the issue unit is disabled
// repo1 is a normal repository with issue unit enabled, visit issue 2(which is a pull request)
// will redirect to pulls
req = NewRequest(t, "GET", "/user2/repo1/issues/2")
resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user2/repo1/pulls/2", test.RedirectURL(resp))
repoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{RepoID: 1, Type: unit.TypeIssues})
// disable issue unit, it will be reset
_, err := db.DeleteByID[repo_model.RepoUnit](t.Context(), repoUnit.ID)
assert.NoError(t, err)
// even if the issue unit is disabled, visiting an issue which is a pull request
// will still redirect to pull request
req = NewRequest(t, "GET", "/user2/repo1/issues/2")
resp = session.MakeRequest(t, req, http.StatusSeeOther)
assert.Equal(t, "/user2/repo1/pulls/2", test.RedirectURL(resp))
} }
func TestSearchIssues(t *testing.T) { func TestSearchIssues(t *testing.T) {

View File

@@ -105,7 +105,15 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) {
// user4 creates a new branch and a PR // user4 creates a new branch and a PR
testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n") testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n")
resp := testPullCreateDirectly(t, user4Session, repo3.OwnerName, repo3.Name, "master", "user4", forkedRepoName, "user4/update-readme", "PR for user4 forked repo3") resp := testPullCreateDirectly(t, user4Session, createPullRequestOptions{
BaseRepoOwner: repo3.OwnerName,
BaseRepoName: repo3.Name,
BaseBranch: "master",
HeadRepoOwner: "user4",
HeadRepoName: forkedRepoName,
HeadBranch: "user4/update-readme",
Title: "PR for user4 forked repo3",
})
prURL := test.RedirectURL(resp) prURL := test.RedirectURL(resp)
// user2 (admin of repo3) goes to the PR files page // user2 (admin of repo3) goes to the PR files page

View File

@@ -60,26 +60,50 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSel
return resp return resp
} }
func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder { type createPullRequestOptions struct {
headCompare := headBranch BaseRepoOwner string
if headRepoOwner != "" { BaseRepoName string
if headRepoName != "" { BaseBranch string
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch) HeadRepoOwner string
HeadRepoName string
HeadBranch string
Title string
ReviewerIDs string // comma-separated list of user IDs
}
func (opts createPullRequestOptions) IsValid() bool {
return opts.BaseRepoOwner != "" && opts.BaseRepoName != "" && opts.BaseBranch != "" &&
opts.HeadBranch != "" && opts.Title != ""
}
func testPullCreateDirectly(t *testing.T, session *TestSession, opts createPullRequestOptions) *httptest.ResponseRecorder {
if !opts.IsValid() {
t.Fatal("Invalid pull request options")
}
headCompare := opts.HeadBranch
if opts.HeadRepoOwner != "" {
if opts.HeadRepoName != "" {
headCompare = fmt.Sprintf("%s/%s:%s", opts.HeadRepoOwner, opts.HeadRepoName, opts.HeadBranch)
} else { } else {
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch) headCompare = fmt.Sprintf("%s:%s", opts.HeadRepoOwner, opts.HeadBranch)
} }
} }
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare)) req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", opts.BaseRepoOwner, opts.BaseRepoName, opts.BaseBranch, headCompare))
resp := session.MakeRequest(t, req, http.StatusOK) resp := session.MakeRequest(t, req, http.StatusOK)
// Submit the form for creating the pull // Submit the form for creating the pull
htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action") link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
assert.True(t, exists, "The template has changed") assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{ params := map[string]string{
"_csrf": htmlDoc.GetCSRF(), "_csrf": htmlDoc.GetCSRF(),
"title": title, "title": opts.Title,
}) }
if opts.ReviewerIDs != "" {
params["reviewer_ids"] = opts.ReviewerIDs
}
req = NewRequestWithValues(t, "POST", link, params)
resp = session.MakeRequest(t, req, http.StatusOK) resp = session.MakeRequest(t, req, http.StatusOK)
return resp return resp
} }
@@ -246,7 +270,15 @@ func TestPullCreatePrFromBaseToFork(t *testing.T) {
testEditFile(t, sessionBase, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") testEditFile(t, sessionBase, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n")
// Create a PR // Create a PR
resp := testPullCreateDirectly(t, sessionFork, "user1", "repo1", "master", "user2", "repo1", "master", "This is a pull title") resp := testPullCreateDirectly(t, sessionFork, createPullRequestOptions{
BaseRepoOwner: "user1",
BaseRepoName: "repo1",
BaseBranch: "master",
HeadRepoOwner: "user2",
HeadRepoName: "repo1",
HeadBranch: "master",
Title: "This is a pull title",
})
// check the redirected URL // check the redirected URL
url := test.RedirectURL(resp) url := test.RedirectURL(resp)
assert.Regexp(t, "^/user1/repo1/pulls/[0-9]*$", url) assert.Regexp(t, "^/user1/repo1/pulls/[0-9]*$", url)

View File

@@ -25,10 +25,6 @@ func TestPullDiff_CommitRangePRDiff(t *testing.T) {
doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"}) doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/4ca8bcaf27e28504df7bf996819665986b01c847..23576dd018294e476c06e569b6b0f170d0558705", true, []string{"test2.txt", "test3.txt", "test4.txt"})
} }
func TestPullDiff_StartingFromBaseToCommitPRDiff(t *testing.T) {
doTestPRDiff(t, "/user2/commitsonpr/pulls/1/files/c5626fc9eff57eb1bb7b796b01d4d0f2f3f792a2", true, []string{"test1.txt", "test2.txt", "test3.txt"})
}
func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) { func doTestPRDiff(t *testing.T, prDiffURL string, reviewBtnDisabled bool, expectedFilenames []string) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()

View File

@@ -184,13 +184,29 @@ func TestPullView_CodeOwner(t *testing.T) {
session := loginUser(t, "user5") session := loginUser(t, "user5")
// create a pull request on the forked repository, code reviewers should not be mentioned // create a pull request on the forked repository, code reviewers should not be mentioned
testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository") testPullCreateDirectly(t, session, createPullRequestOptions{
BaseRepoOwner: "user5",
BaseRepoName: "test_codeowner",
BaseBranch: forkedRepo.DefaultBranch,
HeadRepoOwner: "",
HeadRepoName: "",
HeadBranch: "codeowner-basebranch-forked",
Title: "Test Pull Request on Forked Repository",
})
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertNotExistsBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) unittest.AssertNotExistsBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
// create a pull request to base repository, code reviewers should be mentioned // create a pull request to base repository, code reviewers should be mentioned
testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3") testPullCreateDirectly(t, session, createPullRequestOptions{
BaseRepoOwner: repo.OwnerName,
BaseRepoName: repo.Name,
BaseBranch: repo.DefaultBranch,
HeadRepoOwner: forkedRepo.OwnerName,
HeadRepoName: forkedRepo.Name,
HeadBranch: "codeowner-basebranch-forked",
Title: "Test Pull Request3",
})
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})

View File

@@ -14,6 +14,7 @@ import (
"time" "time"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@@ -529,15 +530,30 @@ func Test_WebhookPullRequest(t *testing.T) {
}, http.StatusOK) }, http.StatusOK)
defer provider.Close() defer provider.Close()
testCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeAll)
// add user4 as collabrator so that it can be a reviewer
doAPIAddCollaborator(testCtx, "user4", perm.AccessModeWrite)(t)
// 1. create a new webhook with special webhook for repo1 // 1. create a new webhook with special webhook for repo1
session := loginUser(t, "user2") sessionUser2 := loginUser(t, "user2")
sessionUser4 := loginUser(t, "user4")
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request") // ignore the possible review_requested event to keep the test deterministic
testAPICreateWebhookForRepo(t, sessionUser2, "user2", "repo1", provider.URL(), "pull_request_only")
testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated) testAPICreateBranch(t, sessionUser2, "user2", "repo1", "master", "master2", http.StatusCreated)
// 2. trigger the webhook // 2. trigger the webhook
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1}) repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request") testPullCreateDirectly(t, sessionUser4, createPullRequestOptions{
BaseRepoOwner: repo1.OwnerName,
BaseRepoName: repo1.Name,
BaseBranch: repo1.DefaultBranch,
HeadRepoOwner: "",
HeadRepoName: "",
HeadBranch: "master2",
Title: "first pull request",
ReviewerIDs: "2", // add user2 as reviewer
})
// 3. validate the webhook is triggered // 3. validate the webhook is triggered
assert.Equal(t, "pull_request", triggeredEvent) assert.Equal(t, "pull_request", triggeredEvent)
@@ -549,6 +565,8 @@ func Test_WebhookPullRequest(t *testing.T) {
assert.Equal(t, 0, *payloads[0].PullRequest.Additions) assert.Equal(t, 0, *payloads[0].PullRequest.Additions)
assert.Equal(t, 0, *payloads[0].PullRequest.ChangedFiles) assert.Equal(t, 0, *payloads[0].PullRequest.ChangedFiles)
assert.Equal(t, 0, *payloads[0].PullRequest.Deletions) assert.Equal(t, 0, *payloads[0].PullRequest.Deletions)
assert.Len(t, payloads[0].PullRequest.RequestedReviewers, 1)
assert.Equal(t, int64(2), payloads[0].PullRequest.RequestedReviewers[0].ID)
}) })
} }

View File

@@ -32,6 +32,7 @@ export default defineComponent({
locale: { locale: {
filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'), filter_changes_by_commit: el.getAttribute('data-filter_changes_by_commit'),
} as Record<string, string>, } as Record<string, string>,
mergeBase: el.getAttribute('data-merge-base'),
commits: [] as Array<Commit>, commits: [] as Array<Commit>,
hoverActivated: false, hoverActivated: false,
lastReviewCommitSha: '', lastReviewCommitSha: '',
@@ -176,32 +177,38 @@ export default defineComponent({
} }
}, },
/** /**
* When a commit is clicked with shift this enables the range * When a commit is clicked while holding Shift, it enables range selection.
* selection. Second click (with shift) defines the end of the * - The range selection is a half-open, half-closed range, meaning it excludes the start commit but includes the end commit.
* range. This opens the diff of this range * - The start of the commit range is always the previous commit of the first clicked commit.
* Exception: first commit is the first commit of this PR. Then * - If the first commit in the list is clicked, the mergeBase will be used as the start of the range instead.
* the diff from beginning of PR up to the second clicked commit is * - The second Shift-click defines the end of the range.
* opened * - Once both are selected, the diff view for the selected commit range will open.
*/ */
commitClickedShift(commit: Commit) { commitClickedShift(commit: Commit) {
this.hoverActivated = !this.hoverActivated; this.hoverActivated = !this.hoverActivated;
commit.selected = true; commit.selected = true;
// Second click -> determine our range and open links accordingly // Second click -> determine our range and open links accordingly
if (!this.hoverActivated) { if (!this.hoverActivated) {
// since at least one commit is selected, we can determine the range
// find all selected commits and generate a link // find all selected commits and generate a link
if (this.commits[0].selected) { const firstSelected = this.commits.findIndex((x) => x.selected);
// first commit is selected - generate a short url with only target sha const lastSelected = this.commits.findLastIndex((x) => x.selected);
const lastCommitIdx = this.commits.findLastIndex((x) => x.selected); let beforeCommitID: string;
if (lastCommitIdx === this.commits.length - 1) { if (firstSelected === 0) {
// user selected all commits - just show the normal diff page beforeCommitID = this.mergeBase;
} else {
beforeCommitID = this.commits[firstSelected - 1].id;
}
const afterCommitID = this.commits[lastSelected].id;
if (firstSelected === lastSelected) {
// if the start and end are the same, we show this single commit
window.location.assign(`${this.issueLink}/commits/${afterCommitID}${this.queryParams}`);
} else if (beforeCommitID === this.mergeBase && afterCommitID === this.commits.at(-1).id) {
// if the first commit is selected and the last commit is selected, we show all commits
window.location.assign(`${this.issueLink}/files${this.queryParams}`); window.location.assign(`${this.issueLink}/files${this.queryParams}`);
} else { } else {
window.location.assign(`${this.issueLink}/files/${this.commits[lastCommitIdx].id}${this.queryParams}`); window.location.assign(`${this.issueLink}/files/${beforeCommitID}..${afterCommitID}${this.queryParams}`);
}
} else {
const start = this.commits[this.commits.findIndex((x) => x.selected) - 1].id;
const end = this.commits.findLast((x) => x.selected).id;
window.location.assign(`${this.issueLink}/files/${start}..${end}${this.queryParams}`);
} }
} }
}, },