Compare commits

...

124 Commits

Author SHA1 Message Date
wxiaoguang
99596044d7 Don't use full-file highlight when there is a git diff textconv (#35114) (#35119)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Fix #35106
2025-07-18 13:52:41 +00:00
Giteabot
693d26914f Fix submodule parsing when the gitmodules is missing (#35109) (#35118)
Backport #35109

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-18 20:22:18 +08:00
Giteabot
315f197790 Increase gap on latest commit (#35104) (#35113)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35104 by @silverwind

Before:

<img width="964" height="101" alt="Screenshot 2025-07-17 at 02 31 05"
src="https://github.com/user-attachments/assets/e02181f3-f730-40bb-8cfa-ecfea4ed4aec"
/>

After:

<img width="967" height="104" alt="Screenshot 2025-07-17 at 02 42 13"
src="https://github.com/user-attachments/assets/7ca7b9a8-1f59-4dc0-9bb0-c72346fd792a"
/>

Co-authored-by: silverwind <me@silverwind.io>
2025-07-18 08:25:51 +03:00
Giteabot
76b8f0c3a7 Fix review comment/dimiss comment x reference can be refereced back (#35094) (#35099)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-07-17 00:04:05 +08:00
Giteabot
f99bbd7f3f Fix submodule nil check (#35096) (#35098)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35096 by wxiaoguang

Fix  #35095

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-16 13:02:18 +00:00
Giteabot
f7ef657b5a nix flake update (#35085) (#35090)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35085 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2025-07-15 14:37:44 -07:00
Lunny Xiao
486d274be6 Add missing changelog (#35079)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-15 12:08:13 +08:00
Giteabot
ab3d2a944c Fix form property assignment edge case (#35073) (#35078)
Backport #35073 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-15 09:40:50 +08:00
wxiaoguang
12bfa9e83d Improve submodule relative path handling (#35056) (#35075)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35056
2025-07-14 17:26:16 +00:00
Giteabot
dd661e92df Fix incorrect comment diff hunk parsing, fix github asset ID nil panic (#35046) (#35055)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35046 by lunny

* Fix missing the first char when parsing diff hunk header
* Fix #35040
* Fix #35049

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-12 15:12:02 +08:00
Giteabot
0b31272c7e Fix updating user visibility (#35036) (#35044)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35036 by @lunny

Fix #35030

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-11 02:44:06 +00:00
Giteabot
ec0c418719 Support base64-encoded agit push options (#35037) (#35041)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35037 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-10 18:56:43 +00:00
Giteabot
6dc19fc29a Make submodule link work with relative path (#35034) (#35038)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35034 by wxiaoguang

Fix #35033

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-10 18:20:14 +00:00
Giteabot
9f1baa7d18 Fix bug when displaying git user avatar in commits list (#35006)
A quick fix for #34991

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-10 08:46:44 -07:00
wxiaoguang
e13deb7a16 Fix API response for swagger spec (#35029)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Co-authored-by: Scion <scion@studiowhy.net>
2025-07-10 15:27:34 +08:00
Lunny Xiao
e5c1b8b632 Add changelog for 1.24.3 (#34975) 2025-07-10 03:21:01 +00:00
Lunny Xiao
e931b62f33 Start automerge check again after the conflict check and the schedule (#35002)
Fix #34988
Backport #34989 

Co-authored-by: posativ

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-10 02:37:15 +00:00
wxiaoguang
81ee93e5bc Fix repo settings and protocol log problems (#35012) (#35013)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #35012
2025-07-09 17:20:15 +00:00
ChristopherHX
053f9186bc Fix the response format for actions/workflows. (#35009) (#35016)
Backport #35009

This PR fixes the response format for the OpenAPI Spec of
`ActionsListRepositoryWorkflows`.
It was specified in the OpenAPI spec as returning a `[]*ActionWorkflow`,
but it actually should return a `api.ActionWorkflowResponse`.

The test already expects an `api.ActionWorkflowResponse` like expected.

Co-authored-by: Scion <Filiecs2@gmail.com>
2025-07-09 18:18:40 +02:00
wxiaoguang
68fcdb6122 Fix project images scroll (#34971) (#34972)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-07-07 00:30:43 +08:00
Giteabot
14ca309c39 Mark old reviews as stale on agit pr updates (#34933) (#34965)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34933 by @dcermak

Fixes: https://github.com/go-gitea/gitea/issues/34134

Co-authored-by: Dan Čermák <dan.cermak@posteo.net>
2025-07-05 20:33:26 -07:00
wxiaoguang
4aba42519d Fix git graph page (#34948) (#34949)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-07-04 15:33:17 +00:00
Giteabot
9adf175df0 Don't send trigger for a pending review's comment create/update/delete (#34928) (#34939)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34928 by lunny

Fix #18846 
Fix #34924

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-07-03 03:01:17 +00:00
wxiaoguang
c3fa2a8729 Fix issue filter (#34914) (#34915)
Backport #34914
2025-07-03 09:45:17 +08:00
Giteabot
89dfed32e0 support the open-icon of folder (#34168) (#34896)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34168 by @kerwin612

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-30 13:55:24 +02:00
Giteabot
d5062d0c27 docs: fix typo in pull request merge warning message text (#34899) (#34903)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34899 by @Pavanipogula

### Description

This PR fixes two typos in the pull request merge command warning
message.

- "can not" → "cannot"
- "was not enable" → "is not enabled."

### File Updated
- `options/locale/locale_en-US.ini` (line 1972)

### Related Discussion
https://github.com/go-gitea/gitea/issues/34893

Co-authored-by: Pavanipogula <51442511+Pavanipogula@users.noreply.github.com>
2025-06-29 18:26:15 -07:00
Giteabot
90e9e79232 Optimize flex layout of release attachment area (#34885) (#34886)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34885 by kerwin612

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2025-06-28 09:10:41 +08:00
Giteabot
c6467edcb1 Fix the issue of abnormal interface when there is no issue-item on the project page (#34791) (#34880)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34791 by @kerwin612

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2025-06-26 23:18:02 -07:00
wxiaoguang
5d5b695527 Skip updating timestamp when sync branch (#34875)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-26 17:59:06 -07:00
Giteabot
0af7a7b79f Fix some log and UI problems (#34863) (#34868)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34863 by @wxiaoguang

Remove the misleading error log, fix #34738

Make the "search" input auto-focused, fix #34807

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-25 22:08:30 +03:00
Giteabot
9339661078 Fix archive API (#34853) (#34857)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34853 by wxiaoguang

Fix #34852

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-25 07:20:42 +00:00
Giteabot
1e69f085d6 Ignore force pushes for changed files in a PR review (#34837) (#34843)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34837 by delvh

Fixes #34832

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-24 16:41:07 +00:00
Giteabot
0bfccd8ecf Fix SSH LFS timeout (#34838) (#34842)
Backport #34838 by wxiaoguang

Fix #34834

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-24 16:15:03 +00:00
Giteabot
534b9b35dd Fix job status aggregation logic (#34823) (#34835)
Backport #34823 by nienjiuntai

Co-authored-by: JIUN-TAI NIEN <44364165+nienjiuntai@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-24 23:23:16 +08:00
Giteabot
dbadc59b56 Fix team permissions (#34827) (#34836)
Backport #34827 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-24 14:17:14 +00:00
Zettat123
a57e2c4bc3 Fix required contexts and commit status matching bug (#34815) (#34829)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34815

Fix #34504

Since one required context can match more than one commit statuses, we
should not directly compare the lengths of `requiredCommitStatuses` and
`requiredContexts`
2025-06-24 07:15:25 +08:00
Lunny Xiao
acd4e10990 release of 1.24.2 (#34800)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-20 12:41:59 -07:00
Lunny Xiao
0a1df294c8 upgrade chi (#34799)
Backport #34798
2025-06-20 18:30:49 +00:00
wxiaoguang
52a964d1fc Fix container range bug (#34795) (#34796)
Backport #34795
2025-06-20 17:35:36 +00:00
Giteabot
d3dbe0d9ce Bump poetry feature to new url for dev container (#34787) (#34790)
Backport #34787 by @yp05327

Why:
I got this error when build dev container

![4be14223f83e4d19ff04b0b31e38b8fc](https://github.com/user-attachments/assets/6e14630f-e505-4a8c-819f-229efee67116)

It seems that the url of poetry feature was changed:
https://containers.dev/features

![image](https://github.com/user-attachments/assets/8d3d4beb-9344-494a-8176-80b6f52ad6af)

After change to the new one, it worked.

Co-authored-by: yp05327 <576951401@qq.com>
2025-06-20 10:08:38 -07:00
Lunny Xiao
cdbbdbef06 Changelog for 1.24.1 (#34774)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-19 18:56:55 +00:00
Giteabot
79f555d465 Fix tag target (#34781) (#34783)
Backport #34781 by wxiaoguang

Fix #34779

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-19 18:53:42 +00:00
wxiaoguang
ae2b795693 Fix incorrect cli default values (#34765) (#34766)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34765
2025-06-18 23:51:37 +08:00
Giteabot
d1fdbf46bd when using rules to delete packages, remove unclean bugs (#34632) (#34761)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34632 by @anthony-zh

By default, the code extracts 200 package versions. If too many packages
are generated every day or if rule cleaning is enabled later, which
means there are more than 200 versions corresponding to the library
package, it may not be cleaned up completely, resulting in residue

Fix #31961

Co-authored-by: anthony-zh <118415914+anthony-zh@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-18 06:18:55 +00:00
Giteabot
f27a75564a Fix readme path and markdown link paste (#34755) (#34760)
Backport #34755 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-18 05:38:45 +00:00
Giteabot
958d0db4f4 Improve alignment of commit status icon on commit page (#34750) (#34757)
Backport #34750 by @silverwind

Before, icon vertically misaligned:

<img width="243" alt="Screenshot 2025-06-17 at 18 14 26"
src="https://github.com/user-attachments/assets/ac515c6d-25bd-44da-88be-a1d93c137ed0"
/>

After, icon correctly vertically centered:

<img width="244" alt="Screenshot 2025-06-17 at 18 14 40"
src="https://github.com/user-attachments/assets/41556d52-aa15-4bfb-82e2-91ed774cf2b0"
/>

I think it's fine to single out this one case and not alter
`flex-text-inline` because that class seems to work well in other
places.

Co-authored-by: silverwind <me@silverwind.io>
2025-06-17 21:22:23 -07:00
Giteabot
4c2441ba5d Fix ghost user in feeds when pushing in an actions, it should be gitea-actions (#34703) (#34756)
Backport #34703 by @lunny

Fix #34688 

This PR will store the `publisher_id` of `release`(tag) table as
pusher's id. It could be a real userID or a system user id. If the user
is deleted, ghost will be replaced.

This PR will also correct the wrong user `Ghost` in the feeds and wrong
committer on tag list page if pushing a tag from an actions. Now the
behavior is the same as Github. Some codes are deleted because it tries
to get commit author as pusher which is not right.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-18 01:04:45 +00:00
Giteabot
6f5f0be9e3 Support title and body query parameters for new PRs (#34537) (#34752)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34537 by endo0911engineer

Co-authored-by: endo0911engineer <161911062+endo0911engineer@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-18 07:16:36 +08:00
Giteabot
23d2d224c2 fix: prevent double markdown link brackets when pasting URL (#34745) (#34748)
Backport #34745 by MaxWebZ

When adding a link using the "Add a link" button in comment editor,
pasting a URL resulted in incorrect Markdown formatting (double
brackets) instead of replacing the placeholder text.

This fix adds a context check to prevent creating a new markdown link
when we're already inside an existing one.

Fixes #34740

Co-authored-by: MaxWebZ <5054326@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-17 16:21:08 +00:00
Giteabot
a43d829de8 Fix JS error for "select" dropdown (#34743) (#34749)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34743 by wxiaoguang

Regression of recent dropdown filter change.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-17 22:34:14 +08:00
Giteabot
8ab1363fef Prevent duplicate form submissions when creating forks (#34714) (#34735)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34714 by @kerwin612

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-15 22:35:58 -07:00
Giteabot
178fd90852 Fix container range bug (#34725) (#34732)
Backport #34725 by wxiaoguang

Fix #34724

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-16 06:48:04 +08:00
wxiaoguang
b39f7a37d1 Fix dropdown filter (#34708) (#34711)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Partially backport #34708
2025-06-12 19:31:12 -07:00
Giteabot
b9ed8fceff Fix markdown wrap (#34697) (#34702)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34697 by wxiaoguang

Fix #34696

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-12 03:08:38 +00:00
Lunny Xiao
e6ce72b14a frontport changelog to v1.24 (#34690)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-11 09:58:11 -07:00
Giteabot
2eecd58bbe Fix pull requests API convert panic when head repository is deleted. (#34685) (#34687)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34685 by @lunny

Fix #34682

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-11 03:45:04 +00:00
Giteabot
64b9b21790 Hide href attribute of a tag if there is no target_url (#34556) (#34684)
Backport #34556 by @lunny

Relate #34450

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-11 00:55:40 +00:00
Giteabot
3290aff964 Fix commit message rendering and some UI problems (#34680) (#34683)
Backport #34680 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-10 19:57:25 +00:00
Lunny Xiao
7ed1e8987e Changelog for 1.24.0 (#34543)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-09 18:26:58 -07:00
Lunny Xiao
f10e909fce Fix: skip paths check on tag push events in workflows (#34602) (#34670) 2025-06-10 00:11:46 +00:00
Giteabot
a3b25436f2 Fix last admin check when syncing users (#34649) (#34673)
Backport #34649 by @wxiaoguang

Fix #34358

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-09 15:18:44 -07:00
Giteabot
b947bc4363 Fix footnote jump behavior on the issue page. (#34621) (#34669)
Backport #34621 by @charles7668

Close #34511 
Close #34590 

Add comment ID to the footnote item's id attribute to ensure uniqueness.

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-09 20:57:01 +00:00
Giteabot
18dc41d6f8 Fix "oras" OCI client compatibility (#34666) (#34671)
Backport #34666 by wxiaoguang

Fix #25846

1. the ImageConfig can be empty, fall back to default
2. the blob size can be empty, it still needs "Content-Length" header

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-09 15:20:34 -04:00
Giteabot
bf5d00074d Only activity tab needs heatmap data loading (#34652) (#34668)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34652 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-09 10:49:04 -07:00
Giteabot
fb4e9f92f9 Ignore "Close" error when uploading container blob (#34620) (#34665)
Backport #34620 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-09 21:35:17 +08:00
Giteabot
468d1919b5 Fix missed merge commit sha and time when migrating from codecommit (#34645) (#34650)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34645 by @lunny

Fix #34627

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-08 21:21:27 -07:00
Giteabot
1b788946a7 Fix GetUsersByEmails (#34643) (#34646)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34643 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-08 10:40:58 +08:00
Giteabot
e8646ad1d8 Misc CSS fixes (#34638) (#34644)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34638 by @silverwind

1. apply [`text-wrap:
balance`](https://developer.mozilla.org/en-US/docs/Web/CSS/text-wrap#balance)
to various places making the text wrapping nicer, moving
`empty-placeholder` CSS to base because it's not repo-specific.

<img width="537" alt="Screenshot 2025-06-07 at 15 09 00"
src="https://github.com/user-attachments/assets/8b37d031-269d-4ab3-ba59-2ac469c431e4"
/>
<img width="514" alt="Screenshot 2025-06-07 at 15 11 16"
src="https://github.com/user-attachments/assets/27a63117-be1d-4797-80f7-9ed14cca41dc"
/>
<img width="346" alt="Screenshot 2025-06-07 at 15 22 26"
src="https://github.com/user-attachments/assets/2f371384-0330-4a00-bb79-bc3c50ba5c91"
/>

2. fix overflow-related bug on actions run list, before:

<img width="302" alt="Screenshot 2025-06-07 at 15 26 26"
src="https://github.com/user-attachments/assets/d6607eeb-288b-4e81-a770-45a421c9c68c"
/>

After:
<img width="299" alt="Screenshot 2025-06-07 at 15 26 59"
src="https://github.com/user-attachments/assets/b0ddb66f-d4fe-4711-8ed9-eca08ce608f3"
/>

Co-authored-by: silverwind <me@silverwind.io>
2025-06-07 18:24:59 +00:00
Giteabot
29dc9c784e add codecommit to supported services in api docs (#34626) (#34633)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34626 by TheFox0x7

Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2025-06-07 17:19:53 +08:00
wxiaoguang
b1cc4bf77f Validate hex colors when creating/editing labels (#34623) (#34630)
Backport #34623

---------

Co-authored-by: Kemal Zebari <kemalzebra@gmail.com>
2025-06-07 11:24:28 +03:00
Giteabot
d35161ceb8 bump to alpine 3.22 (#34613) (#34615)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-06-05 21:45:30 -04:00
Giteabot
8defca6d39 Fix possible pull request broken when leave the page immediately after clicking the update button (#34509) (#34607)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34509 by @lunny

If user leaves the page, the context will become cancelled, so that the
update process maybe terminal in an unexpected status. This PR haven't
resolve the problem totally. It uses a background context to not cancel
the update process even if the user leaved the pull request view page.

Fix #31779

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-05 07:15:32 +00:00
Giteabot
fac434da0a Fix margin issue in markup paragraph rendering (#34599) (#34606)
Backport #34599 by @silverwind

The Fomantic-inherited `p:last-child` rule in base.css interferes with
this markdown rendering:

```
1. a

2. a
```

Before (unequal margin):
<img width="143" alt="Screenshot 2025-06-04 at 14 09 07"
src="https://github.com/user-attachments/assets/6d6ba5e0-0d84-42e0-a0e4-9e93a59c4d65"
/>

After (rendering matches GitHub):
<img width="181" alt="Screenshot 2025-06-04 at 14 09 14"
src="https://github.com/user-attachments/assets/bb81e14e-bc9f-4d52-92d0-f7a17c63070e"
/>

Co-authored-by: silverwind <me@silverwind.io>
2025-06-05 13:48:07 +08:00
Giteabot
e18eae7129 Fix migration pull request title too long (#34577) (#34604)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34577 by @lunny

Fix #34294

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-04 13:36:09 -04:00
Giteabot
c60bc26fd3 Fix issue label delete incorrect labels webhook payload (#34575) (#34603)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34575 by @badhezi

Fixes https://github.com/go-gitea/gitea/issues/34560
explanation of the bug in the issue

setting `issue.isLabelsLoaded = false` before calling `deleteIssueLabel`
guarantee we will load the new state of the labels into the issue object
before sending it in the webhook.

Co-authored-by: badhezi <zlilaharon@gmail.com>
2025-06-05 00:41:01 +08:00
Giteabot
bacc69db83 Make pull request and issue history more compact (#34588) (#34594)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34588 by @silverwind

Before, waste of space in multiple places:

<img width="962" alt="Screenshot 2025-06-02 at 23 11 08"
src="https://github.com/user-attachments/assets/ecddf247-d690-434c-a865-9d5e6c690b7d"
/>

After, reduced spacing around history entries and inside commits list,
also fixed unequal horizontal spacing inside commit badge.

<img width="946" alt="Screenshot 2025-06-02 at 23 10 45"
src="https://github.com/user-attachments/assets/fe92f8e6-28af-4647-ac3a-1aced23d3b27"
/>

Also, here's a rendering of issue history events, it's more compact now
at 8px padding:

<img width="565" alt="image"
src="https://github.com/user-attachments/assets/cdbf7c0a-a9a2-4942-85fc-c388abb36e15"
/>

Co-authored-by: silverwind <me@silverwind.io>
2025-06-04 00:04:07 +00:00
Giteabot
c5da032193 fixed incorrect page navigation with up and down arrow on last item of dashboard repos (#34570) (#34596)
Backport #34570 by metiftikci

Co-authored-by: metiftikci <metiftikci@hotmail.com>
2025-06-04 07:13:39 +08:00
Giteabot
3ace45c118 Fix doctor deleting orphaned issues attachments (#34142) (#34571)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34142 by @lunny

Fix the bug when deleting orphaned issues attachments. The attachments
maybe stored on other storages service rather than disk.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-06-03 07:01:09 +00:00
Giteabot
5d6c5ce71a Fix/improve avatar sync from LDAP (#34573) (#34587)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34573 by @raucao

This fixes 3 issues I encountered when debugging problems with our LDAP
sync:

1. The comparison of the hashed image data in `IsUploadAvatarChanged` is
wrong. It seems to be from before avatar hashing was changed and unified
in #22289. This results in the function always returning `true` for any
avatars, even if they weren't changed.
2. Even if there's no avatar to upload (i.e. no avatar available for the
LDAP entry), the upload function would still be called for every single
user, only to then fail, because the data isn't valid. This is
unnecessary.
3. Another small issue is that the comparison function (and thus hashing
of data) is called for every user, even if there is no avatar attribute
configured at all for the LDAP source. Thus, I switched the condition
nesting, so that no cycles are wasted when avatar sync isn't configured
in the first place.

I also added a trace log for when there is actually a new avatar being
uploaded for an existing user, which is now only shown when that is
actually the case.

Co-authored-by: Râu Cao <842+raucao@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-02 23:19:55 +02:00
Giteabot
7baa6fa47c Fix some trivial problems (#34579) (#34585)
Backport #34579 by wxiaoguang

See the comments

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-02 17:31:35 +00:00
Giteabot
f9a0b077a7 Retain issue sort type when a keyword search is introduced (#34559) (#34581)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34559 by badhezi

Fixes #34523

Co-authored-by: badhezi <zlilaharon@gmail.com>
2025-06-02 22:33:44 +08:00
Giteabot
d3317ebabe Always use an empty line to separate the commit message and trailer (#34512) (#34578)
Backport #34512 by tclin914

If the message from form.MergeMessageField is empty, we will miss a "\n"
between the title and the "Co-authored-by:" line. The title and message
should have a blank line between of them.

Co-authored-by: Jim Lin <jim@andestech.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-06-02 15:59:10 +08:00
wxiaoguang
e9481e1da3 Fix line-button issue after file selection in file tree (#34574) (#34576)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34574

---------

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2025-06-01 23:20:21 -07:00
Giteabot
8965c068e9 Fix possible nil description of pull request when migrating from CodeCommit (#34541) (#34550)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34541 by @lunny

Fix #34320

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-27 20:11:09 +00:00
Giteabot
eaaa158df3 Add webhook assigning test and fix possible bug (#34420) (#34551)
Backport #34420 by lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-28 03:45:25 +08:00
Giteabot
f5498421c4 Refactor commit reader (#34542) (#34549)
Backport #34542 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-27 17:14:33 +00:00
Giteabot
a6a14c9a92 Don't display error log when .git-blame-ignore-revs doesn't exist (#34457) (#34540)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34457 by @lunny

Fix #34454

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-26 21:54:03 +02:00
Giteabot
d0ec1788b8 Add sort option recentclose for issues and pulls (#34525) (#34539)
Backport #34525 by @MarkusAmshove

closes #34171 

Adds a new sort option `recentclose` for issues and pull requests which
will return items in a descending order of when they were closed

Co-authored-by: Markus Amshove <scm@amshove.org>
2025-05-27 02:16:50 +08:00
Giteabot
c1202f1b57 Run integration tests against postgres 14 (#34514) (#34536)
Backport #34514 by @silverwind

postgres 12 is end of life since 6 months. 13 and above are still
supported but I think it's overall better if we test a more recent
version of postgres because that's what new users will be running on.

Ref: https://endoflife.date/postgresql

Co-authored-by: silverwind <me@silverwind.io>
2025-05-26 12:19:19 -04:00
Giteabot
1162cbccc0 Performance optimization for tags synchronization (#34355) (#34522)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34355 by @lunny

The tags synchronization is very slow for a non-mirror repository with
many tags especially forking. This PR make all repositories' tags
synchronization use the same function and remove the low performance
synchronization function. The commit count of tag now will not be stored
into database when syncing. Since the commits count will always be read
from cache or git data, the `NumCommits` in the release table will be
updated for the first read from git data.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-22 19:28:25 -07:00
NorthRealm
038990e0ff Fix ephemeral runner deletion (#34447) (#34513)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-05-21 18:02:34 -04:00
Giteabot
03ff09870d Fix edithook api can not update package, status and workflow_job events (#34495) (#34499)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34495 by @ChristopherHX

* the origin of this problem is duplicated code

Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2025-05-18 21:18:19 -07:00
Giteabot
8bf4f2cc8f Fix url validation in webhook add/edit API (#34492) (#34496)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34492 by @lunny

Fix #34491

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-17 15:31:36 -07:00
Giteabot
21731c1370 Fix get / delete runner to use consistent http 404 and 500 status (#34480) (#34488)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34480 by @ChristopherHX

* previously deleting an already deleted runner returned http 500
* previously any database error for the get endpoint was http 404 and
never 500

Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2025-05-16 16:59:29 +00:00
Giteabot
a0e272d95a Add missing setting load in dump-repo command (#34479) (#34489)
Backport #34479 by @lunny

Fix #34465

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-16 16:33:04 +00:00
NorthRealm
47537a8361 Add a button editing action secret #34348 (#34462)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34348

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-16 15:26:21 +00:00
Giteabot
d018c1b4b1 nix flake update (#34476) (#34490)
Backport #34476 by techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2025-05-16 23:00:18 +08:00
Giteabot
d2cbe2fba0 Add migrations tests (#34456) (#34483)
Backport #34456 by @lunny

Fix #34455

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-16 10:37:06 -04:00
Giteabot
d6233c25b5 Fix project board view (#34470) (#34475)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34470 by wxiaoguang

Fix #34469

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-16 01:32:53 +08:00
Giteabot
2bf2d00c8a When updating comment, if the content is the same, just return and not update the databse (#34422) (#34464)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34422 by @lunny

Fix #34318

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-14 21:48:49 -07:00
Giteabot
9bd56a8ba0 Fix Workflow run Not Found page (#34459) (#34466)
Backport #34459 by NorthRealm

Co-authored-by: NorthRealm <155140859+NorthRealm@users.noreply.github.com>
2025-05-15 11:55:39 +08:00
Giteabot
a1dc3c9bd1 Fix remove org user failure on mssql (#34449) (#34453)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34449 by @ChristopherHX

* mssql does not support fetching 0 repositories
* remove paging by NumRepos that might be 0
* extend admin api test to purge user 2

Fixes #34448

Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-13 13:28:26 -07:00
Giteabot
47ee84d1f3 Fix repo broken check (#34444) (#34452)
Backport #34444 by wxiaoguang

Fix #34424

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-13 19:02:24 +00:00
Giteabot
89f1df033a Fix comment textarea scroll issue in Firefox (#34438) (#34446)
Backport #34438 by @silverwind

In the comment editor, there is a bug in Firefox where the scroll
position unexpectedly moves up, which is annoying. This is not
reproducible in Chrome and Safari. To reproduce here are some steps:

- Go into an editable issue
- Scroll page to bottom
- Focus the textarea and press Return many times, causing the textarea
to get a scrollbar
- Scroll page to bottom again
- Press Return once more
- Page should not scroll up.

This fixes the bug by adding a temporary margin, and I verified it works
in all browsers.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-13 18:39:02 +02:00
Giteabot
94b67f1967 Fix releases sidebar navigation link (#34436) (#34440)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34436 by @badhezi

Resolves https://github.com/go-gitea/gitea/issues/34435

Co-authored-by: badhezi <zlilaharon@gmail.com>
2025-05-12 14:41:24 -07:00
Giteabot
0a9a84df11 Fix bug webhook milestone is not right. (#34419) (#34428)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34419 by @lunny

Fix #34400

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2025-05-12 00:21:14 +00:00
Giteabot
cdac263bb8 Only git operations should update last changed of a repository (#34388) (#34427)
Backport #34388 by @lunny

Try to fix #32046

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-11 23:23:12 +02:00
Giteabot
a5c7df7a4c Fix GetUsersByEmails (#34423) (#34425)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34423 by wxiaoguang

Fix #34418, fix #34353

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-11 04:42:59 +08:00
Giteabot
6d738fecc4 Fix a bug when uploading file via lfs ssh command (#34408) (#34416)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34408 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-10 02:03:37 +00:00
Giteabot
38cc7453e2 Merge and tweak markup editor expander CSS (#34409) (#34415)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34409 by @silverwind

- Merge the CSS for the two expanders (text-expander-element and
tribute.js) into one file
- Fix overflow issues
- Remove min-width
- Various other tweaks like borders, colors, padding, gaps.

text-expander:

<img width="645" alt="Screenshot 2025-05-09 at 02 21 24"
src="https://github.com/user-attachments/assets/33276dc4-38e8-45e1-8216-2a4baa9bc039"
/>

tribute:

<img width="624" alt="Screenshot 2025-05-09 at 02 21 37"
src="https://github.com/user-attachments/assets/91fbcd1a-9bfc-40fd-93f0-a05b4bd4c98d"
/>

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-09 18:58:19 +02:00
Giteabot
b44175c071 Refactor commit message rendering and fix bugs (#34412) (#34414)
Backport #34412 by wxiaoguang

Fix #34398, fix #33308

Remove all `repo.ComposeCommentMetas` from templates,
only use `repo` to render commit message.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-09 13:08:23 +00:00
Giteabot
947358dffe Fix incorrect divergence cache after switching default branch (#34370) (#34406)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34370 by @GWDx

Issue: After switching the default branch, other branches are still
compared against the old default branch due to outdated divergence
cache.

Change: Clear the divergence cache in SetRepoDefaultBranch to ensure
correct comparisons against the new default branch.

Fixes #34369

Co-authored-by: GWDx <gwdx@mail.ustc.edu.cn>
2025-05-09 01:31:34 +02:00
Giteabot
be1090cb2d Grey out expired artifact on Artifacts list (#34314) (#34404)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34314 by @NorthRealm

Grey out expired artifact on Artifacts list.


![1](https://github.com/user-attachments/assets/79c00e39-29f5-4264-b7b2-7ed638ab71c1)

![2](https://github.com/user-attachments/assets/686b745f-d6d7-4921-8e1b-3472ac8b6c17)

Co-authored-by: NorthRealm <155140859+NorthRealm@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-08 10:56:51 -07:00
Giteabot
c8f3402841 Fix LFS file not stored in LFS when uploaded/edited via API or web UI (#34367) (#34396)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34367 by bytedream

Files that should be stored in LFS and are uploaded/edited from the API
or web UI aren't stored in LFS. This may be a regression from #34154.

Co-authored-by: bytedream <bytedream@protonmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-08 05:33:19 +00:00
Giteabot
a3a95a0b67 Upgrade go-github v61 -> v71 (#34385) (#34387)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34385 by @lunny

There will be a possible bug when migrating from Github
https://github.com/google/go-github/issues/3229
This PR upgrades go-github from v61 to v71 to resolve that problem.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-07 10:05:41 -07:00
Giteabot
ed527b664d Fix only text/* being viewable in web UI (#34374) (#34378)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34374 by @bytedream

Regression from #34356, files like SVGs should be editable too
(https://github.com/go-gitea/gitea/pull/34356#discussion_r2072766240).

Co-authored-by: bytedream <bytedream@protonmail.com>
2025-05-06 05:18:12 +00:00
Giteabot
e4717d426e Fix bug when visiting comparation page (#34334) (#34365)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34334 by @lunny

The `ci.HeadGitRepo` was opened and closed in the function
`ParseCompareInfo` but reused in the function `PrepareCompareDiff`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-04 20:43:42 +00:00
Giteabot
16f15d2f7b Fix bug when API get pull changed files for deleted head repository (#34333) (#34366)
Backport #34333 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-05-04 20:18:11 +00:00
Giteabot
b3f5196241 Fix LFS files being editable in web UI (#34356) (#34362)
Backport #34356 by @bytedream

It's possible to edit "raw" lfs files in the web UI when accessing the path manually.

![image](https://github.com/user-attachments/assets/62610e9e-24db-45ec-ad04-28062073164c)

Co-authored-by: bytedream <git@bytedream.dev>
2025-05-04 12:38:23 -07:00
Giteabot
6c5f0af45d feat: return time of last usage for public keys and access tokens in the api (#34323) (#34339)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
Backport #34323 by @tobiasbp

In the Gitea GUI, the user can see the time that _AccessTokens_ and
_PublicKeys_ were last used. This information is not returned by the
_/users/{username}/tokens_ and _/user/keys_ endpoints in the API. This
PR adds the missing data.

The time of last usage for for _tokens_ & _keys_ seem to be stored in
the _Updated_ field of the structs internally. For consistency, I have
used the name _updated_at_ for the new field returned by the _API_.
However, for the _API_ user, I don't think that name reflects the data
returned, as I believe it is the time of last usage. I propose that we
use the name _last_used_at_ instead. Let's hear reviewers opinion on
that.

* PublicKey
  1. _last_used_at_: string($date-time)
* AccessToken
  1. _created_at_: string($date-time) (for parity with public keys)
  2. _last_used_at_: string($date-time)

Fix #34313

Co-authored-by: Tobias Balle-Petersen <tobiasbp@gmail.com>
2025-05-01 14:45:08 -07:00
Giteabot
c95cb7c7e2 fix: do not return archive download URLs in API if downloads are disabled (#34324) (#34338)
Backport #34324 by @tobiasbp

If archive downloads are are disabled using
_DISABLE_DOWNLOAD_SOURCE_ARCHIVES_, archive links are still returned by
the API.

This PR changes the data returned, so the fields _zipball_url_ and
_tarball_url_ are omitted if archive downloads have been disabled.

Resolve #32159

Co-authored-by: Tobias Balle-Petersen <tobiasbp@gmail.com>
2025-05-01 12:18:43 -07:00
Giteabot
6747e3e0eb Fix some dropdown problems on the issue sidebar (#34308) (#34327)
Backport #34308 by wxiaoguang

Also fix #34300

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2025-05-01 09:24:18 -07:00
Lunny Xiao
a12b5b3640 Add release notes for 1.24.0rc0 (#34305)
Some checks failed
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-docker-rootful (push) Has been cancelled
release-nightly / nightly-docker-rootless (push) Has been cancelled
2025-04-29 21:45:47 +00:00
Giteabot
834dad8cef Fix the ci build (#34309) (#34310)
Backport #34309 by @lunny

Fix
https://github.com/go-gitea/gitea/actions/runs/14722306878/job/41318217870

A fork has been created under https://gitea.com/gitea/go-xsd-duration

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2025-04-28 22:48:55 -07:00
306 changed files with 4503 additions and 2216 deletions

View File

@@ -7,7 +7,7 @@
"version": "20"
},
"ghcr.io/devcontainers/features/git-lfs:1.2.2": {},
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
"ghcr.io/devcontainers-extra/features/poetry:2": {},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.12"
},

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
services:
pgsql:
image: postgres:12
image: postgres:14
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres

12
.gitignore vendored
View File

@@ -39,14 +39,10 @@ _testmain.go
coverage.all
cpu.out
/modules/migration/bindata.go
/modules/migration/bindata.go.hash
/modules/options/bindata.go
/modules/options/bindata.go.hash
/modules/public/bindata.go
/modules/public/bindata.go.hash
/modules/templates/bindata.go
/modules/templates/bindata.go.hash
/modules/migration/bindata.*
/modules/options/bindata.*
/modules/public/bindata.*
/modules/templates/bindata.*
*.db
*.log

View File

@@ -4,6 +4,489 @@ 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
been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.24.3](https://github.com/go-gitea/gitea/releases/tag/1.24.3) - 2025-07-15
* BUGFIXES
* Fix form property assignment edge case (#35073) (#35078)
* Improve submodule relative path handling (#35056) (#35075)
* Fix incorrect comment diff hunk parsing, fix github asset ID nil panic (#35046) (#35055)
* Fix updating user visibility (#35036) (#35044)
* Support base64-encoded agit push options (#35037) (#35041)
* Make submodule link work with relative path (#35034) (#35038)
* Fix bug when displaying git user avatar in commits list (#35006)
* Fix API response for swagger spec (#35029)
* Start automerge check again after the conflict check and the schedule (#34988) (#35002)
* Fix the response format for actions/workflows (#35009) (#35016)
* Fix repo settings and protocol log problems (#35012) (#35013)
* Fix project images scroll (#34971) (#34972)
* Mark old reviews as stale on agit pr updates (#34933) (#34965)
* Fix git graph page (#34948) (#34949)
* Don't send trigger for a pending review's comment create/update/delete (#34928) (#34939)
* Fix some log and UI problems (#34863) (#34868)
* Fix archive API (#34853) (#34857)
* Ignore force pushes for changed files in a PR review (#34837) (#34843)
* Fix SSH LFS timeout (#34838) (#34842)
* Fix team permissions (#34827) (#34836)
* Fix job status aggregation logic (#34823) (#34835)
* Fix issue filter (#34914) (#34915)
* Fix typo in pull request merge warning message text (#34899) (#34903)
* Support the open-icon of folder (#34168) (#34896)
* Optimize flex layout of release attachment area (#34885) (#34886)
* Fix the issue of abnormal interface when there is no issue-item on the project page (#34791) (#34880)
* Skip updating timestamp when sync branch (#34875)
* 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
* BUGFIXES
* Fix container range bug (#34795) (#34796)
* Upgrade chi to v5.2.2 (#34798) (#34799)
* BUILD
* 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
* ENHANCEMENTS
* Improve alignment of commit status icon on commit page (#34750) (#34757)
* Support title and body query parameters for new PRs (#34537) (#34752)
* BUGFIXES
* When using rules to delete packages, remove unclean bugs (#34632) (#34761)
* Fix ghost user in feeds when pushing in an actions, it should be gitea-actions (#34703) (#34756)
* Prevent double markdown link brackets when pasting URL (#34745) (#34748)
* Prevent duplicate form submissions when creating forks (#34714) (#34735)
* Fix markdown wrap (#34697) (#34702)
* Fix pull requests API convert panic when head repository is deleted. (#34685) (#34687)
* Fix commit message rendering and some UI problems (#34680) (#34683)
* Fix container range bug (#34725) (#34732)
* Fix incorrect cli default values (#34765) (#34766)
* Fix dropdown filter (#34708) (#34711)
* Hide href attribute of a tag if there is no target_url (#34556) (#34684)
* Fix tag target (#34781) #34783
## [1.24.0](https://github.com/go-gitea/gitea/releases/tag/1.24.0) - 2025-05-26
* BREAKING
* Make Gitea always use its internal config, ignore `/etc/gitconfig` (#33076)
* Improve log format (#33814)
* Fix markdown render behaviors (#34122)
* Add package version api endpoints (#34173)
* FEATURES
* Enforce two-factor auth (2FA: TOTP or WebAuthn) (#34187)
* Add fullscreen mode as a more efficient operation way to view projects (#34081)
* Add anonymous access support for private/unlisted repositories (#34051)
* Support public code/issue access for private repositories (#33127)
* Add middleware for request prioritization (#33951)
* Add cli flags LDAP group configuration (#33933)
* Add file tree to file view page (#32721)
* Add material icons for file list (#33837)
* Artifacts download api for artifact actions v4 (#33510)
* Support choose email when creating a commit via web UI (#33432)
* Add basic auth support to rss/atom feeds (#33371)
* Add sorting by exclusive labels (issue priority) (#33206)
* Add sub issue list support (#32940)
* Private README.md for organization (#32872)
* Email option to embed images as base64 instead of link (#32061)
* Option to delay conflict checking of old pull requests until page view (#27779)
* Worktime tracking for the organization level (#19808)
* PERFORMANCE
* Add cache for common package queries (#22491)
* Move issue pin to an standalone table for querying performance (#33452)
* Improve commits list performance to reduce unnecessary database queries (#33528)
* Optimize total count of feed when loading activities in user dashboard. (#33841)
* Optimize heatmap query (#33853)
* Only use prev and next buttons for pagination on user dashboard (#33981)
* Improve pull request list API performance (#34052)
* Cache GPG keys, emails and users when list commits (#34086)
* Refactor Git Attribute & performance optimization (#34154)
* Performance optimization for tags synchronization (#34355) #34522
* ENHANCEMENTS
* Code
* Display when a release attachment was uploaded (#34261)
* Support creating relative link to raw path in markdown (#34105)
* Improve code block readability and isolate copy button (#34009)
* Improve repository commit view (#33877)
* Full-file syntax highlighting for diff pages (#33766)
* Clone repository with Tea CLI (#33725)
* Improve sync fork behavior (#33319)
* Make git clone URL could use current signed-in user (#33091)
* Add submodule diff links (#33097)
* Link to tree views of submodules if possible (#33424)
* Only keep popular licenses (#33832)
* De-emphasize signed commits (#31160)
* Actions
* Add flat-square action badge style (#34062)
* Update action status badge layout (#34018)
* Download actions job logs from API (#33858)
* Always show the "rerun" button for action jobs (#33692)
* Add auto-expanding running actions step (#30058)
* Update status check for all supported on.pull_request.types in Gitea (#33117)
* Workflow_dispatch use workflow from trigger branch (#33098)
* Add action auto-scroll (#30057)
* Add workflow_job webhook (#33694)
* Add a button editing action secret (#34462)
* Pull Request
* Auto expand "New PR" form (#33971)
* Mark parent directory as viewed when all files are viewed (#33958)
* Show info about maintainers are allowed to edit a PR (#33738)
* Automerge supports deleting branch automatically after merging (#32343)
* Add additional command hints for PowerShell & CMD (#33548)
* Issues
* Allow filtering issues by any assignee (#33343)
* Show warning on navigation if currently editing comment or title (#32920)
* Make tracked time representation display as hours (#33315)
* Add No Results Prompt Message on Issue List Page (#33699)
* Add sort option recentclose for issues and pulls (#34525) #34539
* Packages
* Link to nuget dependencies (#26554)
* Add composor source field (#33502)
* Administration
* Improve navbar: add "admin" tip, add "active" style (#32927)
* Add a option "--user-type bot" to admin user create, improve role display (#27885)
* Improve admin user view page (#33735)
* Support performance trace (#32973)
* Change pprof labels to be prometheus compatible (#32865)
* Allow admins and org owners to change org member public status (#28294)
* Optimize the installation page (#32994)
* Make public URL generation configurable (#34250)
* Add a --fullname arg to gitea admin user create. (#34241)
* Others
* Improve oauth2 error handling (#33969)
* Fail mirroring more gracefully (#34002)
* Align User Details Page Header Layout with Design Specifications (#34192)
* Webhook add X-Gitea-Hook-Installation-Target-Type Header (#33752)
* Optimize the dashboard (#32990)
* Improve button layout on small screens (#33633)
* Add cropping support when modifying the user/org/repo avatar (#33498)
* Make ROOT_URL support using request Host header (#32564)
* Add `show more` organizations icon in user's profile (#32986)
* Introduce `--page-space-bottom` at 64px (#30692)
* Improve theme display (#30671)
* Add alphabetical project sorting (#33504)
* Add global lock for migrations to make upgrade more safe with multiple replications (#33706)
* Add descriptions for private repo public access settings and improve the UI (#34057)
* API
* Actions Runner rest api (#33873)
* Inclusion of rename organization api (#33303)
* Add API to support link package to repository and unlink it (#33481)
* Add API endpoint to request contents of multiple files simultaniously (#34139)
* Actions artifacts API list/download check status upload confirmed (#34273)
* Add API routes to lock and unlock issues (#34165)
* Fix some user name usages (#33689)
* Allow filtering /repos/{owner}/{repo}/pulls by target base branch queryparam (#33684)
* Improve swagger generation (#33664)
* Support Ephemeral action runners (#33570)
* Support workflow event dispatch via API (#33545)
* Support workflow event dispatch via API (#32059)
* Added Description Field for Secrets and Variables (#33526)
* Reject star-related requests if stars are disabled (#33208)
* Let API create and edit system webhooks, attempt 2 (#33180)
* Use `Project-URL` metadata field to get a PyPI package's homepage URL (#33089)
* Add `last_committer_date` and `last_author_date` for file contents API (#32921)
* REFACTORS
* Remove context from git struct (#33793)
* Refactor admin/common.ts (#33788)
* Refactor repo-settings.ts (#33785)
* Refactor repo-issue.ts (#33784)
* Small refactor to reduce unnecessary database queries and remove duplicated functions (#33779)
* Refactor initRepoBranchTagSelector to use new init framework (#33776)
* Refactor buttons to use new init framework (#33774)
* Refactor markup and pdf-viewer to use new init framework (#33772)
* Refactor error system (#33771)
* Refactor mail code (#33768)
* Update TypeScript types (#33799)
* Refactor older tests to use testify (#33140)
* Move notifywatch to service layer (#33825)
* Decouple context from repository related structs (#33823)
* Remove context from mail struct (#33811)
* Refactor dropdown ellipsis (#34123)
* Refactor functions to reduce repopath expose (#33892)
* Refactor repo-diff.ts (#33746)
* Refactor web route handler (#33488)
* Refactor user & avatar (#33433)
* Refactor user package (#33423)
* Refactor decouple context from migration structs (#33399)
* Refactor context flash msg and global variables (#33375)
* Refactor response writer & access logger (#33323)
* Refactor ref type (#33242)
* Refactor context repository (#33202)
* Refactor legacy JS (#33115)
* Refactor legacy line-number and scroll code (#33094)
* Refactor env var related code (#33075)
* Move SetMerged to service layer (#33045)
* Merge updatecommentattachment functions (#33044)
* Refactor pull-request compare&create page (#33071)
* Refactor repo-new.ts (#33070)
* Refactor pagination (#33037)
* Refactor tests (#33021)
* Refactor markup render to fix various path problems (#34114)
* Refactor Branch struct in package modules/git (#33980)
* Don't create duplicated functions for code repositories and wiki repositories (#33924)
* Move git references checking to gitrepo packages to reduce expose of repository path (#33891)
* Refactor cache-control (#33861)
* Decouple diff stats query from actual diffing (#33810)
* Move part of updating protected branch logic to service layer (#33742)
* Decouple Batch from git.Repository to simplify usage without requiring the creation of a Repository struct. (#34001)
* Refactor tmpl and blob_excerpt (#32967)
* Refactor template & test related code (#32938)
* Refactor db package and remove unnecessary `DumpTables` (#32930)
* Refactor pprof labels and process desc (#32909)
* Refactor repo-projects.ts (#32892)
* Refactor getpatch/getdiff functions and remove unnecessary fallback (#32817)
* Uniform all temporary directories and allow customizing temp path (#32352)
* Remove context from retry downloader (#33871)
* Refactor global init code and add more comments (#33755)
* Remove some unnecessary template helpers (#33069)
* Move and rename UpdateRepository (#34136)
* Move hooks function to gitrepo and reduce expose repopath (#33890)
* Add abstraction layer to delete repository from disk (#33879)
* Add abstraction layer to check if the repository exists on disk (#33874)
* Move ParseCommitWithSSHSignature to service layer (#34087)
* Move duplicated functions (#33977)
* Extract code to their own functions for push update (#33944)
* Move gitgraph from modules to services layer (#33527)
* Move commits signature and verify functions to service layers (#33605)
* Use `CloseIssue` and `ReopenIssue` instead of `ChangeStatus` (#32467)
* Refactor arch route handlers (#32993)
* Refactor "string truncate" (#32984)
* Refactor arch route handlers (#32972)
* Clarify path param naming (#32969)
* Refactor request context (#32956)
* Move some errors to their own sub packages (#32880)
* Move RepoTransfer from models to models/repo sub package (#32506)
* Move delete deploy keys into service layer (#32201)
* Refactor webhook events (#33337)
* Move some Actions related functions from `routers` to `services` (#33280)
* Refactor RefName (#33234)
* Refactor context RefName and RepoAssignment (#33226)
* Refactor repository transfer (#33211)
* Refactor error system (#33626)
* Refactor error system (#33610)
* Refactor package (routes and error handling, npm peer dependency) (#33111)
* Use test context in tests and new loop system in benchmarks (#33648)
* Some small refactors (#33144)
* Simplify context ref name (#33267)
* BUGFIXES
* Fix some dropdown problems on the issue sidebar (#34308) #34327
* Do not return archive download URLs in API if downloads are disabled (#34324) #34338
* Fix LFS files being editable in web UI (#34356) #34362
* Fix only text/* being viewable in web UI (#34374) #34378
* Fix LFS file not stored in LFS when uploaded/edited via API or web UI (#34367)
* Grey out expired artifact on Artifacts list (#34314) #34404
* Fix incorrect divergence cache after switching default branch (#34370) #34406
* Refactor commit message rendering and fix bugs (#34412) #34414
* Merge and tweak markup editor expander CSS (#34409) #34415
* Fix GetUsersByEmails (#34423) #34425
* Only git operations should update last changed of a repository (#34388) #34427
* Fix comment textarea scroll issue in Firefox (#34438) #34446
* Fix repo broken check (#34444) #34452
* Fix remove org user failure on mssql (#34449) #34453
* Fix Workflow run Not Found page (#34459) #34466
* When updating comment, if the content is the same, just return and not update the database (#34422) #34464
* Fix project board view (#34470) #34475
* Fix get / delete runner to use consistent http 404 and 500 status (#34480) #34488
* Fix url validation in webhook add/edit API (#34492) #34496
* Fix edithook api can not update package, status and workflow_job events (#34495) #34499
* Fix ephemeral runner deletion (#34447) #34513
* Don't display error log when .git-blame-ignore-revs doesn't exist (#34457)
* Only allow admins to rename default/protected branches (#33276)
* Improve "lock conversation" UI (#34207)
* Fix incorrect file links (#34189)
* Optimize Overflow Menu (#34183)
* Check user/org repo limit instead of doer (#34147)
* Make markdown render match GitHub's behavior (#34129)
* Fix team permission (#34128)
* Correctly handle submodule view and avoid throwing 500 error (#34121)
* Fix users being able bypass limits with repo transfers (#34031)
* Avoid creating unnecessary temporary cat file sub process (#33942)
* Refactor organization menu (#33928)
* Fix various Fomantic UI and htmx problems (#33851)
* Fix 500 error when error occurred in migration page (#33256)
* Validate that the tag doesn't exist when creating a tag via the web (#33241)
* Add missed transaction on setmerged (#33079)
* Rework create/fork/adopt/generate repository to make sure resources will be cleanup once failed (#31035)
* Valid email address should only start with alphanumeric (#28174)
* Fix webhook url (#34186)
* Fix "toAbsoluteLocaleDate" test when system locale is not en-US (#33939)
* Fix file name could not be searched if the file was not a text file when using the Bleve indexer (#33959)
* Fix cannot delete runners via the modal dialog (#33895)
* Fix unpin hint on the pinned pull requests (#33207)
* Fix parentCommit invalid memory address or nil pointer dereference. (#33204)
* Fix comment header padding (#33377)
* Fix some migration and repo name problems (#33986)
* Fix various trivial frontend problems (#34263)
* Fix Set Email Preference dropdown and button placement (#34255)
* Fix quoted replies incorrectly render user input as part of the quote (#34216)
* Fix button alignments and remove unnecessary styles (#34206)
* Restore form inputs on organization create error (#34201)
* Try to fix ACME (3rd) (#33807)
* Fix incorrect ref "blob" (#33240)
* Fix dynamic content loading init problem (#33748)
* Fix git empty check and HEAD request (#33690)
* Fix Untranslated Text on Actions Page (#33635)
* Fix issue label delete incorrect labels webhook payload (#34575)
* Fix incorrect page navigation with up and down arrow on last item of dashboard repos (#34570)
* Fix/improve avatar sync from LDAP (#34573)
* Fix some trivial problems (#34579)
* Retain issue sort type when a keyword search is introduced (#34559)
* Always use an empty line to separate the commit message and trailer (#34512)
* Fix line-button issue after file selection in file tree (#34574)
* Fix doctor deleting orphaned issues attachments (#34142)
* Add webhook assigning test and fix possible bug (#34420)
* Fix possible nil description of pull request when migrating from CodeCommit (#34541)
* Refactor commit reader (#34542)
* Fix possible pull request broken when leave the page immediately after clicking the update button #34509
* Ignore "Close" error when uploading container blob (#34620)
* Fix missed merge commit sha and time when migrating from codecommit (#34645)
* Fix GetUsersByEmails (#34643)
* Misc CSS fixes (#34638)
* Add codecommit to supported services in api docs (#34626)
* Validate hex colors when creating/editing labels (#34623)
* Fix possible pull request broken when leave the page immediately after clicking the update button (#34509)
* Fix margin issue in markup paragraph rendering (#34599)
* Fix migration pull request title too long (#34577)
* Fix footnote jump behavior on the issue page. (#34621)
* Fix "oras" OCI client compatibility (#34666)
* Fix last admin check when syncing users (#34649)
* Fix skip paths check on tag push events in workflows (#34602) #34670
* MISC
* Bump to alpine 3.22 (#34613)
* Make pull request and issue history more compact (#34588)
* Run integration tests against postgres 14 (#34514) #34536
* Enable addtional linters (#34085)
* Enable testifylint rules (#34075)
* Enable staticcheck QFxxxx rules (#34064)
* Improve Actions test (#32883)
* Drop fomantic build (#33845)
* Go1.24 (#33562)
* Run yamllint with strict mode, fix issue (#33551)
* Disable cron task to update license (#33486)
* Optimize makefile help information generation (#33390)
* Convert github.com/xanzy/go-gitlab into gitlab.com/gitlab-org/api/client-go (#33126)
* Add missed changelogs (#33649)
* Update .changelog file to add performance label group (#33472)
* Add missing POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES in app.example.ini (#33363)
* Update README screenshots (#33347)
* Update unrs-resolver (#34279)
* Update go&js dependencies (#34262)
* Optimize the calling code of queryElems (#34235)
* Update protected_branch.tmpl (#34193)
* Feat/optimize span svg layout (#34185)
* Set MERMAID_MAX_SOURCE_CHARACTERS to 50000 (#34152)
* Update JS and PY deps (#34143)
* Add Chinese translations for README files (#34132)
* Use `overflow-wrap: anywhere` to replace `word-break: break-all` (#34126)
* Clarify ownership in password change error messages (#34092)
* Add toggleClass function in dom.ts (#34063)
* Update to golangci-lint v2 (#34054)
* Update Makefile test comments (#34013)
* Update go mod dependencies (#33988)
* Use filepath.Join instead of path.Join for file system file operations (#33978)
* Prepare common tmpl functions in a middleware (#33957)
* Remove unused or abused styles (#33918)
* Update JS and PY deps, misc tweaks (#33903)
* Try to figure out attribute checker problem (#33901)
* Add lock for a repository pull mirror (#33876)
* Fine tune push mirror UI (#33866)
* Improve issue & code search (#33860)
* Use pullrequestlist instead of []*pullrequest (#33765)
* Upgrade act to 0.261.4 and actions-proto-go to v0.4.1 (#33760)
* Align sidebar gears to the right (#33721)
* Update Go dependencies (skip blevesearch, meilisearch) (#33655)
* Add migrations and doctor fixes (#33556)
* Remove "class-name" from svg icon (#33540)
* Update MAINTAINERS (#33529)
* Add "No data available" display when list is empty (#33517)
* Use `git diff-tree` for `DiffFileTree` on diff pages (#33514)
* Give organisation members access to organisation feeds (#33508)
* Update feishu icon (#33470)
* Hide/disable unusable UI elements when a repository is archived (#33459)
* Update `@github/text-expander-element` to 2.9.0 (#33435)
* Do not access GitRepo when a repo is being created (#33380)
* Fix incorrect ref usages (#33301)
* Prepare for support performance trace (#33286)
* Enable Typescript `noImplicitThis` (#33250)
* Remove unused CSS styles and move some styles to proper files (#33217)
* Add .run to gitignore (#33175)
* Fix typo in gitea downloader test and add missing codebase in `ToGitServiceType` (#33146)
* Remove extended glob pattern from branch protection UI (#33125)
* Clean up legacy form CSS styles (#33081)
* Unset XDG_HOME_CONFIG as gitea manages configuration locations (#33067)
* Add IntelliJ Gateway's .uuid to gitignore (#33052)
* User facing messages for AGit errors (#33012)
* Always show assignees on right (#33006)
* Fix eslint (#33002)
* Update JS dependencies (#32914)
* Bump x/net (#32896) (#32900)
* 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
* SECURITY
* Fix a bug when uploading file via lfs ssh command (#34408) (#34411)
* Update net package (#34228) (#34232)
* BUGFIXES
* Fix releases sidebar navigation link (#34436) #34439
* Fix bug webhook milestone is not right. (#34419) #34429
* Fix two missed null value checks on the wiki page. (#34205) (#34215)
* Swift files can be passed either as file or as form value (#34068) (#34236)
* Fix bug when API get pull changed files for deleted head repository (#34333) (#34368)
* Upgrade github v61 -> v71 to fix migrating bug (#34389)
* Fix bug when visiting comparation page (#34334) (#34364)
* Fix wrong review requests when updating the pull request (#34286) (#34304)
* Fix github migration error when using multiple tokens (#34144) (#34302)
* Explicitly not update indexes when sync database schemas (#34281) (#34295)
* Fix panic when comment is nil (#34257) (#34277)
* Fix project board links to related Pull Requests (#34213) (#34222)
* Don't assume the default wiki branch is master in the wiki API (#34244) (#34245)
* DOCUMENTATION
* Update token creation API swagger documentation (#34288) (#34296)
* MISC
* Fix CI Build (#34315)
* Add riscv64 support (#34199) (#34204)
* Bump go version in go.mod (#34160)
* 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
* Enhancements
* Add a config option to block "expensive" pages for anonymous users (#34024) (#34071)
* Also check default ssh-cert location for host (#34099) (#34100) (#34116)
* BUGFIXES
* Fix discord webhook 400 status code when description limit is exceeded (#34084) (#34124)
* Get changed files based on merge base when checking `pull_request` actions trigger (#34106) (#34120)
* Fix invalid version in RPM package path (#34112) (#34115)
* Return default avatar url when user id is zero rather than updating database (#34094) (#34095)
* Add additional ReplaceAll in pathsep to cater for different pathsep (#34061) (#34070)
* Try to fix check-attr bug (#34029) (#34033)
* Git client will follow 301 but 307 (#34005) (#34010)
* Fix block expensive for 1.23 (#34127)
* Fix markdown frontmatter rendering (#34102) (#34107)
* Add new CLI flags to set name and scopes when creating a user with access token (#34080) (#34103)
* Do not show 500 error when default branch doesn't exist (#34096) (#34097)
* Hide activity contributors, recent commits and code frequrency left tabs if there is no code permission (#34053) (#34065)
* Simplify emoji rendering (#34048) (#34049)
* Adjust the layout of the toolbar on the Issues/Projects page (#33667) (#34047)
* Pull request updates will also trigger code owners review requests (#33744) (#34045)
* Fix org repo creation being limited by user limits (#34030) (#34044)
* Fix git client accessing renamed repo (#34034) (#34043)
* Fix the issue with error message logging for the `check-attr` command on Windows OS. (#34035) (#34036)
* Polyfill WeakRef (#34025) (#34028)
## [1.23.6](https://github.com/go-gitea/gitea/releases/tag/v1.23.6) - 2025-03-24
* SECURITY

View File

@@ -1,5 +1,5 @@
# Build stage
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -41,7 +41,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
FROM docker.io/library/alpine:3.21
FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000

View File

@@ -1,5 +1,5 @@
# Build stage
FROM docker.io/library/golang:1.24-alpine3.21 AS build-env
FROM docker.io/library/golang:1.24-alpine3.22 AS build-env
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct}
@@ -39,7 +39,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/go/src/code.gitea.io/gitea/environment-to-ini
RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete
FROM docker.io/library/alpine:3.21
FROM docker.io/library/alpine:3.22
LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000

File diff suppressed because one or more lines are too long

View File

@@ -38,12 +38,10 @@ var (
&cli.BoolFlag{
Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
Value: true,
},
&cli.BoolFlag{
Name: "skip-verify",
Usage: "Skip TLS verify.",
Value: true,
},
&cli.StringFlag{
Name: "helo-hostname",
@@ -53,7 +51,6 @@ var (
&cli.BoolFlag{
Name: "disable-helo",
Usage: "Disable SMTP helo.",
Value: true,
},
&cli.StringFlag{
Name: "allowed-domains",
@@ -63,7 +60,6 @@ var (
&cli.BoolFlag{
Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.",
Value: true,
},
&cli.BoolFlag{
Name: "active",

View File

@@ -80,6 +80,11 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}
func runDumpRepository(ctx *cli.Context) error {
setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr)
setting.DisableLoggerInit()
setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
stdCtx, cancel := installSignals()
defer cancel()

View File

@@ -24,7 +24,7 @@ import (
)
const (
hookBatchSize = 30
hookBatchSize = 500
)
var (

View File

@@ -118,7 +118,6 @@ var (
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
Value: true,
},
&cli.Int64Flag{
Name: "max-size",
@@ -129,7 +128,6 @@ var (
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
Value: true,
},
&cli.IntFlag{
Name: "max-days",
@@ -140,7 +138,6 @@ var (
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
Value: true,
},
&cli.IntFlag{
Name: "compression-level",

View File

@@ -11,7 +11,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@@ -20,7 +19,7 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
@@ -37,14 +36,6 @@ import (
"github.com/urfave/cli/v2"
)
const (
verbUploadPack = "git-upload-pack"
verbUploadArchive = "git-upload-archive"
verbReceivePack = "git-receive-pack"
verbLfsAuthenticate = "git-lfs-authenticate"
verbLfsTransfer = "git-lfs-transfer"
)
// CmdServ represents the available serv sub-command.
var CmdServ = &cli.Command{
Name: "serv",
@@ -78,22 +69,6 @@ func setup(ctx context.Context, debug bool) {
}
}
var (
// keep getAccessMode() in sync
allowedCommands = container.SetOf(
verbUploadPack,
verbUploadArchive,
verbReceivePack,
verbLfsAuthenticate,
verbLfsTransfer,
)
allowedCommandsLfs = container.SetOf(
verbLfsAuthenticate,
verbLfsTransfer,
)
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
)
// fail prints message to stdout, it's mainly used for git serv and git hook commands.
// The output will be passed to git client and shown to user.
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
@@ -139,19 +114,20 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
func getAccessMode(verb, lfsVerb string) perm.AccessMode {
switch verb {
case verbUploadPack, verbUploadArchive:
case git.CmdVerbUploadPack, git.CmdVerbUploadArchive:
return perm.AccessModeRead
case verbReceivePack:
case git.CmdVerbReceivePack:
return perm.AccessModeWrite
case verbLfsAuthenticate, verbLfsTransfer:
case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
switch lfsVerb {
case "upload":
case git.CmdSubVerbLfsUpload:
return perm.AccessModeWrite
case "download":
case git.CmdSubVerbLfsDownload:
return perm.AccessModeRead
}
}
// should be unreachable
setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
@@ -230,12 +206,12 @@ func runServ(c *cli.Context) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
}
words, err := shellquote.Split(cmd)
sshCmdArgs, err := shellquote.Split(cmd)
if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
}
if len(words) < 2 {
if len(sshCmdArgs) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
@@ -246,25 +222,21 @@ func runServ(c *cli.Context) error {
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
verb := words[0]
repoPath := strings.TrimPrefix(words[1], "/")
var lfsVerb string
rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 {
repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
repoPathFields := strings.SplitN(repoPath, "/", 2)
if len(repoPathFields) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
}
username := rr[0]
reponame := strings.TrimSuffix(rr[1], ".git")
username := repoPathFields[0]
reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
// LowerCase and trim the repoPath as that's how they are stored.
// This should be done after splitting the repoPath into username and reponame
// so that username and reponame are not affected.
repoPath = strings.ToLower(strings.TrimSpace(repoPath))
if alphaDashDotPattern.MatchString(reponame) {
if !repo.IsValidSSHAccessRepoName(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
}
@@ -286,22 +258,23 @@ func runServ(c *cli.Context) error {
}()
}
if allowedCommands.Contains(verb) {
if allowedCommandsLfs.Contains(verb) {
if !setting.LFS.StartServer {
return fail(ctx, "LFS Server is not enabled", "")
}
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
return fail(ctx, "LFS SSH transfer is not enabled", "")
}
if len(words) > 2 {
lfsVerb = words[2]
}
}
} else {
verb, lfsVerb := sshCmdArgs[0], ""
if !git.IsAllowedVerbForServe(verb) {
return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
}
if git.IsAllowedVerbForServeLfs(verb) {
if !setting.LFS.StartServer {
return fail(ctx, "LFS Server is not enabled", "")
}
if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH {
return fail(ctx, "LFS SSH transfer is not enabled", "")
}
if len(sshCmdArgs) > 2 {
lfsVerb = sshCmdArgs[2]
}
}
requestedMode := getAccessMode(verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
@@ -310,7 +283,7 @@ func runServ(c *cli.Context) error {
}
// LFS SSH protocol
if verb == verbLfsTransfer {
if verb == git.CmdVerbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
@@ -319,7 +292,7 @@ func runServ(c *cli.Context) error {
}
// LFS token authentication
if verb == verbLfsAuthenticate {
if verb == git.CmdVerbLfsAuthenticate {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
token, err := getLFSAuthToken(ctx, lfsVerb, results)

View File

@@ -18,7 +18,7 @@ import (
"strings"
"syscall"
"github.com/google/go-github/v61/github"
"github.com/google/go-github/v71/github"
"github.com/urfave/cli/v2"
"gopkg.in/yaml.v3"
)

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1739214665,
"narHash": "sha256-26L8VAu3/1YRxS8MHgBOyOM8xALdo6N0I04PgorE7UM=",
"lastModified": 1752480373,
"narHash": "sha256-JHQbm+OcGp32wAsXTE/FLYGNpb+4GLi5oTvCxwSoBOA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "64e75cd44acf21c7933d61d7721e812eac1b5a0a",
"rev": "62e0f05ede1da0d54515d4ea8ce9c733f12d9f08",
"type": "github"
},
"original": {

6
go.mod
View File

@@ -51,7 +51,7 @@ require (
github.com/gliderlabs/ssh v0.3.8
github.com/go-ap/activitypub v0.0.0-20250409143848-7113328b1f3d
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.2
@@ -66,7 +66,7 @@ require (
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/google/go-github/v61 v61.0.0
github.com/google/go-github/v71 v71.0.0
github.com/google/licenseclassifier/v2 v2.0.0
github.com/google/pprof v0.0.0-20250422154841-e1f9c1950416
github.com/google/uuid v1.6.0
@@ -325,6 +325,8 @@ replace github.com/charmbracelet/git-lfs-transfer => gitea.com/gitea/git-lfs-tra
// TODO: This could be removed after https://github.com/mholt/archiver/pull/396 merged
replace github.com/mholt/archiver/v3 => github.com/anchore/archiver/v3 v3.5.2
replace git.sr.ht/~mariusor/go-xsd-duration => gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078
exclude github.com/gofrs/uuid v3.2.0+incompatible
exclude github.com/gofrs/uuid v4.0.0+incompatible

12
go.sum
View File

@@ -14,12 +14,12 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/gitea/act v0.261.4 h1:Tf9eLlvsYFtKcpuxlMvf9yT3g4Hshb2Beqw6C1STuH8=
gitea.com/gitea/act v0.261.4/go.mod h1:Pg5C9kQY1CEA3QjthjhlrqOC/QOT5NyWNjOjRHw23Ok=
gitea.com/gitea/git-lfs-transfer v0.2.0 h1:baHaNoBSRaeq/xKayEXwiDQtlIjps4Ac/Ll4KqLMB40=
gitea.com/gitea/git-lfs-transfer v0.2.0/go.mod h1:UrXUCm3xLQkq15fu7qlXHUMlrhdlXHoi13KH2Dfiits=
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:BAFmdZpRW7zMQZQDClaCWobRj9uL1MR3MzpCVJvc5s4=
gitea.com/gitea/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed h1:EZZBtilMLSZNWtHHcgq2mt6NSGhJSZBuduAlinMEmso=
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed/go.mod h1:E3i3cgB04dDx0v3CytCgRTTn9Z/9x891aet3r456RVw=
gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
@@ -301,8 +301,8 @@ github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5La
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/chi/v5 v5.2.2 h1:CMwsvRVTbXVytCk1Wd72Zy1LAsAh9GxMmSNWLHCG618=
github.com/go-chi/chi/v5 v5.2.2/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
@@ -420,8 +420,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5pNlu1go=
github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
github.com/google/go-github/v71 v71.0.0 h1:Zi16OymGKZZMm8ZliffVVJ/Q9YZreDKONCr+WUd0Z30=
github.com/google/go-github/v71 v71.0.0/go.mod h1:URZXObp2BLlMjwu0O8g4y6VBneUj2bCHgnI8FfgZ51M=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=

View File

@@ -171,6 +171,7 @@ func (run *ActionRun) IsSchedule() bool {
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID).
NoAutoTime().
SetExpr("num_action_runs",
builder.Select("count(*)").From("action_run").
Where(builder.Eq{"repo_id": repo.ID}),

View File

@@ -185,10 +185,10 @@ func AggregateJobStatus(jobs []*ActionRunJob) Status {
return StatusSuccess
case hasCancelled:
return StatusCancelled
case hasFailure:
return StatusFailure
case hasRunning:
return StatusRunning
case hasFailure:
return StatusFailure
case hasWaiting:
return StatusWaiting
case hasBlocked:

View File

@@ -58,14 +58,14 @@ func TestAggregateJobStatus(t *testing.T) {
{[]Status{StatusCancelled, StatusRunning}, StatusCancelled},
{[]Status{StatusCancelled, StatusBlocked}, StatusCancelled},
// failure with other status, fail fast
// Should "running" win? Maybe no: old code does make "running" win, but GitHub does fail fast.
// failure with other status, usually fail fast, but "running" wins to match GitHub's behavior
// another reason that we can't make "failure" wins over "running": it would cause a weird behavior that user cannot cancel a workflow or get current running workflows correctly by filter after a job fail.
{[]Status{StatusFailure}, StatusFailure},
{[]Status{StatusFailure, StatusSuccess}, StatusFailure},
{[]Status{StatusFailure, StatusSkipped}, StatusFailure},
{[]Status{StatusFailure, StatusCancelled}, StatusCancelled},
{[]Status{StatusFailure, StatusWaiting}, StatusFailure},
{[]Status{StatusFailure, StatusRunning}, StatusFailure},
{[]Status{StatusFailure, StatusRunning}, StatusRunning},
{[]Status{StatusFailure, StatusBlocked}, StatusFailure},
// skipped with other status

View File

@@ -5,6 +5,7 @@ package actions
import (
"context"
"errors"
"fmt"
"strings"
"time"
@@ -298,6 +299,23 @@ func DeleteRunner(ctx context.Context, id int64) error {
return err
}
// DeleteEphemeralRunner deletes a ephemeral runner by given ID.
func DeleteEphemeralRunner(ctx context.Context, id int64) error {
runner, err := GetRunnerByID(ctx, id)
if err != nil {
if errors.Is(err, util.ErrNotExist) {
return nil
}
return err
}
if !runner.Ephemeral {
return nil
}
_, err = db.DeleteByID[ActionRunner](ctx, id)
return err
}
// CreateRunner creates new runner.
func CreateRunner(ctx context.Context, t *ActionRunner) error {
if t.OwnerID != 0 && t.RepoID != 0 {

View File

@@ -336,6 +336,11 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
sess.Cols(cols...)
}
_, err := sess.Update(task)
// Automatically delete the ephemeral runner if the task is done
if err == nil && task.Status.IsDone() && util.SliceContainsString(cols, "status") {
return DeleteEphemeralRunner(ctx, task.RunnerID)
}
return err
}

View File

@@ -191,7 +191,7 @@ func (a *Action) LoadActUser(ctx context.Context) {
return
}
var err error
a.ActUser, err = user_model.GetUserByID(ctx, a.ActUserID)
a.ActUser, err = user_model.GetPossibleUserByID(ctx, a.ActUserID)
if err == nil {
return
} else if user_model.IsErrUserNotExist(err) {
@@ -530,7 +530,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
if opts.RequestedTeam != nil {
env := repo_model.AccessibleTeamReposEnv(organization.OrgFromUser(opts.RequestedUser), opts.RequestedTeam)
teamRepoIDs, err := env.RepoIDs(ctx, 1, opts.RequestedUser.NumRepos)
teamRepoIDs, err := env.RepoIDs(ctx)
if err != nil {
return nil, fmt.Errorf("GetTeamRepositories: %w", err)
}

View File

@@ -66,7 +66,7 @@ func getUserHeatmapData(ctx context.Context, user *user_model.User, team *organi
Select(groupBy+" AS timestamp, count(user_id) as contributions").
Table("action").
Where(cond).
And("created_unix > ?", timeutil.TimeStampNow()-31536000).
And("created_unix > ?", timeutil.TimeStampNow()-(366+7)*86400). // (366+7) days to include the first week for the heatmap
GroupBy(groupByName).
OrderBy("timestamp").
Find(&hdata)

View File

@@ -91,7 +91,7 @@ func AddGPGKey(ctx context.Context, ownerID int64, content, token, signature str
signer, err = openpgp.CheckArmoredDetachedSignature(ekeys, strings.NewReader(token+"\r\n"), strings.NewReader(signature), nil)
}
if err != nil {
log.Error("Unable to validate token signature. Error: %v", err)
log.Debug("AddGPGKey CheckArmoredDetachedSignature failed: %v", err)
return nil, ErrGPGInvalidTokenSignature{
ID: ekeys[0].PrimaryKey.KeyIdString(),
Wrapped: err,

View File

@@ -85,7 +85,7 @@ func VerifyGPGKey(ctx context.Context, ownerID int64, keyID, token, signature st
}
if signer == nil {
log.Error("Unable to validate token signature. Error: %v", err)
log.Debug("VerifyGPGKey failed: no signer")
return "", ErrGPGInvalidTokenSignature{
ID: key.KeyID,
}

View File

@@ -35,7 +35,7 @@ func VerifySSHKey(ctx context.Context, ownerID int64, fingerprint, token, signat
// edge case for Windows based shells that will add CR LF if piped to ssh-keygen command
// see https://github.com/PowerShell/PowerShell/issues/5974
if sshsig.Verify(strings.NewReader(token+"\r\n"), []byte(signature), []byte(key.Content), "gitea") != nil {
log.Error("Unable to validate token signature. Error: %v", err)
log.Debug("VerifySSHKey sshsig.Verify failed: %v", err)
return "", ErrSSHInvalidTokenSignature{
Fingerprint: key.Fingerprint,
}

View File

@@ -38,3 +38,14 @@
repo_id: 0
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'
-
id: 34350
name: runner_to_be_deleted-org-ephemeral
uuid: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
token_hash: 3FF231BD-FBB7-4E4B-9602-E6F28363EF20
ephemeral: true
version: "1.0.0"
owner_id: 3
repo_id: 0
description: "This runner is going to be deleted"
agent_labels: '["runner_to_be_deleted","linux"]'

View File

@@ -117,3 +117,23 @@
log_length: 707
log_size: 90179
log_expired: 0
-
id: 52
job_id: 196
attempt: 1
runner_id: 34350
status: 6 # running
started: 1683636528
stopped: 1683636626
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
is_fork_pull_request: 0
token_hash: f8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
token_salt: ffffffffff
token_last_eight: ffffffff
log_filename: artifact-test2/2f/47.log
log_in_storage: 1
log_length: 707
log_size: 90179
log_expired: 0

View File

@@ -81,7 +81,7 @@
-
id: 11
uid: 4
email: user4@example.com
email: User4@Example.Com
lower_email: user4@example.com
is_activated: true
is_primary: true

View File

@@ -518,7 +518,7 @@ func updateTeamWhitelist(ctx context.Context, repo *repo_model.Repository, curre
return currentWhitelist, nil
}
teams, err := organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead)
teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead, unit.TypeCode, unit.TypePullRequests)
if err != nil {
return nil, fmt.Errorf("GetTeamsWithAccessToRepo [org_id: %d, repo_id: %d]: %v", repo.OwnerID, repo.ID, err)
}

View File

@@ -719,7 +719,8 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository
return nil
}
func (c *Comment) loadReview(ctx context.Context) (err error) {
// LoadReview loads the associated review
func (c *Comment) LoadReview(ctx context.Context) (err error) {
if c.ReviewID == 0 {
return nil
}
@@ -736,11 +737,6 @@ func (c *Comment) loadReview(ctx context.Context) (err error) {
return nil
}
// LoadReview loads the associated review
func (c *Comment) LoadReview(ctx context.Context) error {
return c.loadReview(ctx)
}
// DiffSide returns "previous" if Comment.Line is a LOC of the previous changes and "proposed" if it is a LOC of the proposed changes.
func (c *Comment) DiffSide() string {
if c.Line < 0 {
@@ -860,7 +856,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment
}
if comment.ReviewID != 0 {
if comment.Review == nil {
if err := comment.loadReview(ctx); err != nil {
if err := comment.LoadReview(ctx); err != nil {
return err
}
}

View File

@@ -5,6 +5,7 @@ package issues
import (
"context"
"strconv"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/renderhelper"
@@ -114,7 +115,9 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
}
var err error
rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo)
rctx := renderhelper.NewRenderContextRepoComment(ctx, issue.Repo, renderhelper.RepoCommentOptions{
FootnoteContextID: strconv.FormatInt(comment.ID, 10),
})
if comment.RenderedContent, err = markdown.RenderString(rctx, comment.Content); err != nil {
return nil, err
}

View File

@@ -206,6 +206,7 @@ func DeleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use
}
issue.Labels = nil
issue.isLabelsLoaded = false
return issue.LoadLabels(ctx)
}

View File

@@ -88,6 +88,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
case "recentclose":
sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":

View File

@@ -12,9 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -715,138 +713,13 @@ func UpdateReactionsMigrationsByType(ctx context.Context, gitServiceType api.Git
return err
}
// DeleteIssuesByRepoID deletes issues by repositories id
func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) {
// MariaDB has a performance bug: https://jira.mariadb.org/browse/MDEV-16289
// so here it uses "DELETE ... WHERE IN" with pre-queried IDs.
sess := db.GetEngine(ctx)
for {
issueIDs := make([]int64, 0, db.DefaultMaxInSize)
err := sess.Table(&Issue{}).Where("repo_id = ?", repoID).OrderBy("id").Limit(db.DefaultMaxInSize).Cols("id").Find(&issueIDs)
if err != nil {
return nil, err
}
if len(issueIDs) == 0 {
break
}
// Delete content histories
_, err = sess.In("issue_id", issueIDs).Delete(&ContentHistory{})
if err != nil {
return nil, err
}
// Delete comments and attachments
_, err = sess.In("issue_id", issueIDs).Delete(&Comment{})
if err != nil {
return nil, err
}
// Dependencies for issues in this repository
_, err = sess.In("issue_id", issueIDs).Delete(&IssueDependency{})
if err != nil {
return nil, err
}
// Delete dependencies for issues in other repositories
_, err = sess.In("dependency_id", issueIDs).Delete(&IssueDependency{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&IssueUser{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&Reaction{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&IssueWatch{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&Stopwatch{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&TrackedTime{})
if err != nil {
return nil, err
}
_, err = sess.In("issue_id", issueIDs).Delete(&project_model.ProjectIssue{})
if err != nil {
return nil, err
}
_, err = sess.In("dependent_issue_id", issueIDs).Delete(&Comment{})
if err != nil {
return nil, err
}
var attachments []*repo_model.Attachment
err = sess.In("issue_id", issueIDs).Find(&attachments)
if err != nil {
return nil, err
}
for j := range attachments {
attachmentPaths = append(attachmentPaths, attachments[j].RelativePath())
}
_, err = sess.In("issue_id", issueIDs).Delete(&repo_model.Attachment{})
if err != nil {
return nil, err
}
_, err = sess.In("id", issueIDs).Delete(&Issue{})
if err != nil {
return nil, err
}
func GetOrphanedIssueRepoIDs(ctx context.Context) ([]int64, error) {
var repoIDs []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).
Find(&repoIDs); err != nil {
return nil, err
}
return attachmentPaths, err
}
// DeleteOrphanedIssues delete issues without a repo
func DeleteOrphanedIssues(ctx context.Context) error {
var attachmentPaths []string
err := db.WithTx(ctx, func(ctx context.Context) error {
var ids []int64
if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id").
Join("LEFT", "repository", "issue.repo_id=repository.id").
Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id").
Find(&ids); err != nil {
return err
}
for i := range ids {
paths, err := DeleteIssuesByRepoID(ctx, ids[i])
if err != nil {
return err
}
attachmentPaths = append(attachmentPaths, paths...)
}
return nil
})
if err != nil {
return err
}
// Remove issue attachment files.
for i := range attachmentPaths {
// FIXME: it's not right, because the attachment might not be on local filesystem
system_model.RemoveAllWithNotice(ctx, "Delete issue attachment", attachmentPaths[i])
}
return nil
return repoIDs, nil
}

View File

@@ -235,7 +235,7 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe
// AddCrossReferences add cross references
func (c *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error {
if c.Type != CommentTypeCode && c.Type != CommentTypeComment {
if !c.Type.HasContentSupport() {
return nil
}
if err := c.LoadIssue(stdCtx); err != nil {

View File

@@ -152,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
return prs, maxResults, findSession.Find(&prs)
found := findSession.Find(&prs)
return prs, maxResults, found
}
// PullRequestList defines a list of pull requests

View File

@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPullRequest_LoadAttributes(t *testing.T) {
@@ -76,6 +77,47 @@ func TestPullRequestsNewest(t *testing.T) {
}
}
func TestPullRequests_Closed_RecentSortType(t *testing.T) {
// Issue ID | Closed At. | Updated At
// 2 | 1707270001 | 1707270001
// 3 | 1707271000 | 1707279999
// 11 | 1707279999 | 1707275555
tests := []struct {
sortType string
expectedIssueIDOrder []int64
}{
{"recentupdate", []int64{3, 11, 2}},
{"recentclose", []int64{11, 3, 2}},
}
assert.NoError(t, unittest.PrepareTestDatabase())
_, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11")
require.NoError(t, err)
for _, test := range tests {
t.Run(test.sortType, func(t *testing.T) {
prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{
Page: 1,
},
State: "closed",
SortType: test.sortType,
})
require.NoError(t, err)
if assert.Len(t, prs, len(test.expectedIssueIDOrder)) {
for i := range test.expectedIssueIDOrder {
assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID)
}
}
})
}
}
func TestLoadRequestedReviewers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

View File

@@ -14,5 +14,8 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error {
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
LogExpired bool `xorm:"index(stopped_log_expired)"`
}
return x.Sync(new(ActionTask))
_, err := x.SyncWithOptions(xorm.SyncOptions{
IgnoreDropIndices: true,
}, new(ActionTask))
return err
}

View File

@@ -0,0 +1,51 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import (
"testing"
"code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
func Test_AddIndexToActionTaskStoppedLogExpired(t *testing.T) {
type ActionTask struct {
ID int64
JobID int64
Attempt int64
RunnerID int64 `xorm:"index"`
Status int `xorm:"index"`
Started timeutil.TimeStamp `xorm:"index"`
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
RepoID int64 `xorm:"index"`
OwnerID int64 `xorm:"index"`
CommitSHA string `xorm:"index"`
IsForkPullRequest bool
Token string `xorm:"-"`
TokenHash string `xorm:"UNIQUE"` // sha256 of token
TokenSalt string
TokenLastEight string `xorm:"index token_last_eight"`
LogFilename string // file name of log
LogInStorage bool // read log from database or from storage
LogLength int64 // lines count
LogSize int64 // blob size
LogIndexes []int64 `xorm:"LONGBLOB"` // line number to offset
LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted
Created timeutil.TimeStamp `xorm:"created"`
Updated timeutil.TimeStamp `xorm:"updated index"`
}
// Prepare and load the testing database
x, deferable := base.PrepareTestEnv(t, 0, new(ActionTask))
defer deferable()
assert.NoError(t, AddIndexToActionTaskStoppedLogExpired(x))
}

View File

@@ -9,5 +9,8 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error {
type Release struct {
Sha1 string `xorm:"INDEX VARCHAR(64)"`
}
return x.Sync(new(Release))
_, err := x.SyncWithOptions(xorm.SyncOptions{
IgnoreDropIndices: true,
}, new(Release))
return err
}

View File

@@ -0,0 +1,40 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import (
"testing"
"code.gitea.io/gitea/models/migrations/base"
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
)
func Test_AddIndexForReleaseSha1(t *testing.T) {
type Release struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX UNIQUE(n)"`
PublisherID int64 `xorm:"INDEX"`
TagName string `xorm:"INDEX UNIQUE(n)"`
OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"`
LowerTagName string
Target string
Title string
Sha1 string `xorm:"VARCHAR(64)"`
NumCommits int64
Note string `xorm:"TEXT"`
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
}
// Prepare and load the testing database
x, deferable := base.PrepareTestEnv(t, 0, new(Release))
defer deferable()
assert.NoError(t, AddIndexForReleaseSha1(x))
}

View File

@@ -602,8 +602,3 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
"team_user.uid": userID,
})
}
// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)
}

View File

@@ -334,7 +334,7 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
repoIDs, err := env.RepoIDs(db.DefaultContext, 1, 100)
repoIDs, err := env.RepoIDs(db.DefaultContext)
assert.NoError(t, err)
assert.Equal(t, expectedRepoIDs, repoIDs)
}
@@ -342,25 +342,6 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
testSuccess(4, []int64{3, 32})
}
func TestAccessibleReposEnv_Repos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := repo_model.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
repos, err := env.Repos(db.DefaultContext, 1, 100)
assert.NoError(t, err)
expectedRepos := make(repo_model.RepositoryList, len(expectedRepoIDs))
for i, repoID := range expectedRepoIDs {
expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
&repo_model.Repository{ID: repoID})
}
assert.Equal(t, expectedRepos, repos)
}
testSuccess(2, []int64{3, 5, 32})
testSuccess(4, []int64{3, 32})
}
func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})

View File

@@ -9,6 +9,8 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
"xorm.io/builder"
)
// TeamRepo represents an team-repository relation.
@@ -48,26 +50,27 @@ func RemoveTeamRepo(ctx context.Context, teamID, repoID int64) error {
return err
}
// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
func GetTeamsWithAccessToRepo(ctx context.Context, orgID, repoID int64, mode perm.AccessMode) ([]*Team, error) {
// GetTeamsWithAccessToAnyRepoUnit returns all teams in an organization that have given access level to the repository special unit.
// This function is only used for finding some teams that can be used as branch protection allowlist or reviewers, it isn't really used for access control.
// FIXME: TEAM-UNIT-PERMISSION this logic is not complete, search the fixme keyword to see more details
func GetTeamsWithAccessToAnyRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type, unitTypesMore ...unit.Type) ([]*Team, error) {
teams := make([]*Team, 0, 5)
return teams, db.GetEngine(ctx).Where("team.authorize >= ?", mode).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
And("team_repo.org_id = ?", orgID).
And("team_repo.repo_id = ?", repoID).
OrderBy("name").
Find(&teams)
}
// GetTeamsWithAccessToRepoUnit returns all teams in an organization that have given access level to the repository special unit.
func GetTeamsWithAccessToRepoUnit(ctx context.Context, orgID, repoID int64, mode perm.AccessMode, unitType unit.Type) ([]*Team, error) {
teams := make([]*Team, 0, 5)
return teams, db.GetEngine(ctx).Where("team_unit.access_mode >= ?", mode).
sub := builder.Select("team_id").From("team_unit").
Where(builder.Expr("team_unit.team_id = team.id")).
And(builder.In("team_unit.type", append([]unit.Type{unitType}, unitTypesMore...))).
And(builder.Expr("team_unit.access_mode >= ?", mode))
err := db.GetEngine(ctx).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
Join("INNER", "team_unit", "team_unit.team_id = team.id").
And("team_repo.org_id = ?", orgID).
And("team_repo.repo_id = ?", repoID).
And("team_unit.type = ?", unitType).
And(builder.Or(
builder.Expr("team.authorize >= ?", mode),
builder.In("team.id", sub),
)).
OrderBy("name").
Find(&teams)
return teams, err
}

View File

@@ -22,7 +22,7 @@ func TestGetTeamsWithAccessToRepoUnit(t *testing.T) {
org41 := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 41})
repo61 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 61})
teams, err := organization.GetTeamsWithAccessToRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
teams, err := organization.GetTeamsWithAccessToAnyRepoUnit(db.DefaultContext, org41.ID, repo61.ID, perm.AccessModeRead, unit.TypePullRequests)
assert.NoError(t, err)
if assert.Len(t, teams, 2) {
assert.EqualValues(t, 21, teams[0].ID)

View File

@@ -33,7 +33,7 @@ func SearchVersions(ctx context.Context, opts *packages_model.PackageSearchOptio
Where(cond).
OrderBy("package.name ASC")
if opts.Paginator != nil {
skip, take := opts.GetSkipTake()
skip, take := opts.Paginator.GetSkipTake()
inner = inner.Limit(take, skip)
}

View File

@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
)
// ErrDuplicatePackageVersion indicates a duplicated package version error
@@ -187,7 +188,7 @@ type PackageSearchOptions struct {
HasFileWithName string // only results are found which are associated with a file with the specific name
HasFiles optional.Option[bool] // only results are found which have associated files
Sort VersionSort
db.Paginator
Paginator db.Paginator
}
func (opts *PackageSearchOptions) ToConds() builder.Cond {
@@ -282,6 +283,18 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
e.Desc("package_version.id") // Sort by id for stable order with duplicates in the other field
}
func searchVersionsBySession(sess *xorm.Session, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
opts.configureOrderBy(sess)
pvs := make([]*PackageVersion, 0, 10)
if opts.Paginator != nil {
sess = db.SetSessionPagination(sess, opts.Paginator)
count, err := sess.FindAndCount(&pvs)
return pvs, count, err
}
err := sess.Find(&pvs)
return pvs, int64(len(pvs)), err
}
// SearchVersions gets all versions of packages matching the search options
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
sess := db.GetEngine(ctx).
@@ -289,16 +302,7 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
opts.configureOrderBy(sess)
if opts.Paginator != nil {
sess = db.SetSessionPagination(sess, opts)
}
pvs := make([]*PackageVersion, 0, 10)
count, err := sess.FindAndCount(&pvs)
return pvs, count, err
return searchVersionsBySession(sess, opts)
}
// SearchLatestVersions gets the latest version of every package matching the search options
@@ -316,15 +320,7 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P
Join("INNER", "package", "package.id = package_version.package_id").
Where(builder.In("package_version.id", in))
opts.configureOrderBy(sess)
if opts.Paginator != nil {
sess = db.SetSessionPagination(sess, opts)
}
pvs := make([]*PackageVersion, 0, 10)
count, err := sess.FindAndCount(&pvs)
return pvs, count, err
return searchVersionsBySession(sess, opts)
}
// ExistVersion checks if a version matching the search options exist

View File

@@ -42,6 +42,7 @@ func (p *Permission) IsAdmin() bool {
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
// It doesn't count the "public(anonymous/everyone) access mode".
// TODO: most calls to this function should be replaced with `HasAnyUnitAccessOrPublicAccess`
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
@@ -267,7 +268,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
perm.units = repo.Units
// anonymous user visit private repo.
// TODO: anonymous user visit public unit of private repo???
if user == nil && repo.IsPrivate {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -286,7 +286,8 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
// Prevent strangers from checking out public repo of private organization/users
// Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself
// Allow user if they are a collaborator of a repo within a private user or a private organization but not a member of the organization itself
// TODO: rename it to "IsOwnerVisibleToDoer"
if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator {
perm.AccessMode = perm_model.AccessModeNone
return perm, nil
@@ -304,7 +305,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
// plain user
// plain user TODO: this check should be replaced, only need to check collaborator access mode
perm.AccessMode, err = accessLevel(ctx, user, repo)
if err != nil {
return perm, err
@@ -314,6 +315,19 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
return perm, nil
}
// now: the owner is visible to doer, if the repo is public, then the min access mode is read
minAccessMode := util.Iif(!repo.IsPrivate && !user.IsRestricted, perm_model.AccessModeRead, perm_model.AccessModeNone)
perm.AccessMode = max(perm.AccessMode, minAccessMode)
// get units mode from teams
teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
if err != nil {
return perm, err
}
if len(teams) == 0 {
return perm, nil
}
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
// Collaborators on organization
@@ -323,12 +337,6 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
}
// get units mode from teams
teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID)
if err != nil {
return perm, err
}
// if user in an owner team
for _, team := range teams {
if team.HasAdminAccess() {
@@ -339,19 +347,12 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use
}
for _, u := range repo.Units {
var found bool
for _, team := range teams {
unitAccessMode := minAccessMode
if teamMode, exist := team.UnitAccessModeEx(ctx, u.Type); exist {
perm.unitsMode[u.Type] = max(perm.unitsMode[u.Type], teamMode)
found = true
}
}
// for a public repo on an organization, a non-restricted user has read permission on non-team defined units.
if !found && !repo.IsPrivate && !user.IsRestricted {
if _, ok := perm.unitsMode[u.Type]; !ok {
perm.unitsMode[u.Type] = perm_model.AccessModeRead
unitAccessMode = max(perm.unitsMode[u.Type], unitAccessMode, teamMode)
}
perm.unitsMode[u.Type] = unitAccessMode
}
}

View File

@@ -6,12 +6,16 @@ package access
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
perm_model "code.gitea.io/gitea/models/perm"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestHasAnyUnitAccess(t *testing.T) {
@@ -152,3 +156,45 @@ func TestUnitAccessMode(t *testing.T) {
}
assert.Equal(t, perm_model.AccessModeRead, perm.UnitAccessMode(unit.TypeWiki), "has unit, and map, use map")
}
func TestGetUserRepoPermission(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
ctx := t.Context()
repo32 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32}) // org public repo
require.NoError(t, repo32.LoadOwner(ctx))
require.True(t, repo32.Owner.IsOrganization())
require.NoError(t, db.TruncateBeans(ctx, &organization.Team{}, &organization.TeamUser{}, &organization.TeamRepo{}, &organization.TeamUnit{}))
org := repo32.Owner
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
team := &organization.Team{OrgID: org.ID, LowerName: "test_team"}
require.NoError(t, db.Insert(ctx, team))
t.Run("DoerInTeamWithNoRepo", func(t *testing.T) {
require.NoError(t, db.Insert(ctx, &organization.TeamUser{OrgID: org.ID, TeamID: team.ID, UID: user.ID}))
perm, err := GetUserRepoPermission(ctx, repo32, user)
require.NoError(t, err)
assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
assert.Nil(t, perm.unitsMode) // doer in the team, but has no access to the repo
})
require.NoError(t, db.Insert(ctx, &organization.TeamRepo{OrgID: org.ID, TeamID: team.ID, RepoID: repo32.ID}))
require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeNone}))
t.Run("DoerWithTeamUnitAccessNone", func(t *testing.T) {
perm, err := GetUserRepoPermission(ctx, repo32, user)
require.NoError(t, err)
assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeCode])
assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
})
require.NoError(t, db.TruncateBeans(ctx, &organization.TeamUnit{}))
require.NoError(t, db.Insert(ctx, &organization.TeamUnit{OrgID: org.ID, TeamID: team.ID, Type: unit.TypeCode, AccessMode: perm_model.AccessModeWrite}))
t.Run("DoerWithTeamUnitAccessWrite", func(t *testing.T) {
perm, err := GetUserRepoPermission(ctx, repo32, user)
require.NoError(t, err)
assert.Equal(t, perm_model.AccessModeRead, perm.AccessMode)
assert.Equal(t, perm_model.AccessModeWrite, perm.unitsMode[unit.TypeCode])
assert.Equal(t, perm_model.AccessModeRead, perm.unitsMode[unit.TypeIssues])
})
}

View File

@@ -5,12 +5,14 @@ package pull
import (
"context"
"errors"
"fmt"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
)
// AutoMerge represents a pull request scheduled for merging when checks succeed
@@ -76,7 +78,10 @@ func GetScheduledMergeByPullID(ctx context.Context, pullID int64) (bool, *AutoMe
return false, nil, err
}
doer, err := user_model.GetUserByID(ctx, scheduledPRM.DoerID)
doer, err := user_model.GetPossibleUserByID(ctx, scheduledPRM.DoerID)
if errors.Is(err, util.ErrNotExist) {
doer, err = user_model.NewGhostUser(), nil
}
if err != nil {
return false, nil, err
}

View File

@@ -47,7 +47,7 @@ func (c *commitChecker) IsCommitIDExisting(commitID string) bool {
c.gitRepo, c.gitRepoCloser = r, closer
}
exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
exist = c.gitRepo.IsReferenceExist(commitID) // Don't use IsObjectExist since it doesn't support short hashes with gogit edition.
c.commitCache[commitID] = exist
return exist
}

View File

@@ -44,30 +44,31 @@ type RepoCommentOptions struct {
DeprecatedRepoName string // it is only a patch for the non-standard "markup" api
DeprecatedOwnerName string // it is only a patch for the non-standard "markup" api
CurrentRefPath string // eg: "branch/main" or "commit/11223344"
FootnoteContextID string // the extra context ID for footnotes, used to avoid conflicts with other footnotes in the same page
}
func NewRenderContextRepoComment(ctx context.Context, repo *repo_model.Repository, opts ...RepoCommentOptions) *markup.RenderContext {
helper := &RepoComment{
repoLink: repo.Link(),
opts: util.OptionalArg(opts),
}
helper := &RepoComment{opts: util.OptionalArg(opts)}
rctx := markup.NewRenderContext(ctx)
helper.ctx = rctx
var metas map[string]string
if repo != nil {
helper.repoLink = repo.Link()
helper.commitChecker = newCommitChecker(ctx, repo)
rctx = rctx.WithMetas(repo.ComposeCommentMetas(ctx))
metas = repo.ComposeCommentMetas(ctx)
} else {
// this is almost dead code, only to pass the incorrect tests
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
rctx = rctx.WithMetas(map[string]string{
"user": helper.opts.DeprecatedOwnerName,
"repo": helper.opts.DeprecatedRepoName,
"markdownNewLineHardBreak": "true",
"markupAllowShortIssuePattern": "true",
})
// repo can be nil when rendering a commit message in user's dashboard feedback whose repository has been deleted
metas = map[string]string{}
if helper.opts.DeprecatedOwnerName != "" {
// this is almost dead code, only to pass the incorrect tests
helper.repoLink = fmt.Sprintf("%s/%s", helper.opts.DeprecatedOwnerName, helper.opts.DeprecatedRepoName)
metas["user"] = helper.opts.DeprecatedOwnerName
metas["repo"] = helper.opts.DeprecatedRepoName
}
metas["markdownNewLineHardBreak"] = "true"
metas["markupAllowShortIssuePattern"] = "true"
}
rctx = rctx.WithHelper(helper)
metas["footnoteContextId"] = helper.opts.FootnoteContextID
rctx = rctx.WithMetas(metas).WithHelper(helper)
return rctx
}

View File

@@ -72,4 +72,11 @@ func TestRepoComment(t *testing.T) {
<a href="/user2/repo1/commit/1234/image" target="_blank" rel="nofollow noopener"><img src="/user2/repo1/commit/1234/image" alt="./image"/></a></p>
`, rendered)
})
t.Run("NoRepo", func(t *testing.T) {
rctx := NewRenderContextRepoComment(t.Context(), nil).WithMarkupType(markdown.MarkupName)
rendered, err := markup.RenderString(rctx, "any")
assert.NoError(t, err)
assert.Equal(t, "<p>any</p>\n", rendered)
})
}

View File

@@ -48,8 +48,7 @@ func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (Repo
// accessible to a particular user
type AccessibleReposEnvironment interface {
CountRepos(ctx context.Context) (int64, error)
RepoIDs(ctx context.Context, page, pageSize int) ([]int64, error)
Repos(ctx context.Context, page, pageSize int) (RepositoryList, error)
RepoIDs(ctx context.Context) ([]int64, error)
MirrorRepos(ctx context.Context) (RepositoryList, error)
AddKeyword(keyword string)
SetSort(db.SearchOrderBy)
@@ -132,40 +131,18 @@ func (env *accessibleReposEnv) CountRepos(ctx context.Context) (int64, error) {
return repoCount, nil
}
func (env *accessibleReposEnv) RepoIDs(ctx context.Context, page, pageSize int) ([]int64, error) {
if page <= 0 {
page = 1
}
repoIDs := make([]int64, 0, pageSize)
func (env *accessibleReposEnv) RepoIDs(ctx context.Context) ([]int64, error) {
var repoIDs []int64
return repoIDs, db.GetEngine(ctx).
Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()).
GroupBy("`repository`.id,`repository`."+strings.Fields(string(env.orderBy))[0]).
GroupBy("`repository`.id,`repository`." + strings.Fields(string(env.orderBy))[0]).
OrderBy(string(env.orderBy)).
Limit(pageSize, (page-1)*pageSize).
Cols("`repository`.id").
Find(&repoIDs)
}
func (env *accessibleReposEnv) Repos(ctx context.Context, page, pageSize int) (RepositoryList, error) {
repoIDs, err := env.RepoIDs(ctx, page, pageSize)
if err != nil {
return nil, fmt.Errorf("GetUserRepositoryIDs: %w", err)
}
repos := make([]*Repository, 0, len(repoIDs))
if len(repoIDs) == 0 {
return repos, nil
}
return repos, db.GetEngine(ctx).
In("`repository`.id", repoIDs).
OrderBy(string(env.orderBy)).
Find(&repos)
}
func (env *accessibleReposEnv) MirrorRepoIDs(ctx context.Context) ([]int64, error) {
repoIDs := make([]int64, 0, 10)
return repoIDs, db.GetEngine(ctx).

View File

@@ -161,6 +161,11 @@ func UpdateRelease(ctx context.Context, rel *Release) error {
return err
}
func UpdateReleaseNumCommits(ctx context.Context, rel *Release) error {
_, err := db.GetEngine(ctx).ID(rel.ID).Cols("num_commits").Update(rel)
return err
}
// AddReleaseAttachments adds a release attachments
func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs []string) (err error) {
// Check attachments
@@ -418,8 +423,8 @@ func UpdateReleasesMigrationsByType(ctx context.Context, gitServiceType structs.
return err
}
// PushUpdateDeleteTagsContext updates a number of delete tags with context
func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []string) error {
// PushUpdateDeleteTags updates a number of delete tags with context
func PushUpdateDeleteTags(ctx context.Context, repo *Repository, tags []string) error {
if len(tags) == 0 {
return nil
}
@@ -448,58 +453,6 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []s
return nil
}
// PushUpdateDeleteTag must be called for any push actions to delete tag
func PushUpdateDeleteTag(ctx context.Context, repo *Repository, tagName string) error {
rel, err := GetRelease(ctx, repo.ID, tagName)
if err != nil {
if IsErrReleaseNotExist(err) {
return nil
}
return fmt.Errorf("GetRelease: %w", err)
}
if rel.IsTag {
if _, err = db.DeleteByID[Release](ctx, rel.ID); err != nil {
return fmt.Errorf("Delete: %w", err)
}
} else {
rel.IsDraft = true
rel.NumCommits = 0
rel.Sha1 = ""
if _, err = db.GetEngine(ctx).ID(rel.ID).AllCols().Update(rel); err != nil {
return fmt.Errorf("Update: %w", err)
}
}
return nil
}
// SaveOrUpdateTag must be called for any push actions to add tag
func SaveOrUpdateTag(ctx context.Context, repo *Repository, newRel *Release) error {
rel, err := GetRelease(ctx, repo.ID, newRel.TagName)
if err != nil && !IsErrReleaseNotExist(err) {
return fmt.Errorf("GetRelease: %w", err)
}
if rel == nil {
rel = newRel
if _, err = db.GetEngine(ctx).Insert(rel); err != nil {
return fmt.Errorf("InsertOne: %w", err)
}
} else {
rel.Sha1 = newRel.Sha1
rel.CreatedUnix = newRel.CreatedUnix
rel.NumCommits = newRel.NumCommits
rel.IsDraft = false
if rel.IsTag && newRel.PublisherID > 0 {
rel.PublisherID = newRel.PublisherID
}
if _, err = db.GetEngine(ctx).ID(rel.ID).AllCols().Update(rel); err != nil {
return fmt.Errorf("Update: %w", err)
}
}
return nil
}
// RemapExternalUser ExternalUserRemappable interface
func (r *Release) RemapExternalUser(externalName string, externalID, userID int64) error {
r.OriginalAuthor = externalName

View File

@@ -64,18 +64,18 @@ func (err ErrRepoIsArchived) Error() string {
}
type globalVarsStruct struct {
validRepoNamePattern *regexp.Regexp
invalidRepoNamePattern *regexp.Regexp
reservedRepoNames []string
reservedRepoPatterns []string
validRepoNamePattern *regexp.Regexp
invalidRepoNamePattern *regexp.Regexp
reservedRepoNames []string
reservedRepoNamePatterns []string
}
var globalVars = sync.OnceValue(func() *globalVarsStruct {
return &globalVarsStruct{
validRepoNamePattern: regexp.MustCompile(`[-.\w]+`),
invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
reservedRepoNames: []string{".", "..", "-"},
reservedRepoPatterns: []string{"*.git", "*.wiki", "*.rss", "*.atom"},
validRepoNamePattern: regexp.MustCompile(`^[-.\w]+$`),
invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
reservedRepoNames: []string{".", "..", "-"},
reservedRepoNamePatterns: []string{"*.wiki", "*.git", "*.rss", "*.atom"},
}
})
@@ -86,7 +86,16 @@ func IsUsableRepoName(name string) error {
// Note: usually this error is normally caught up earlier in the UI
return db.ErrNameCharsNotAllowed{Name: name}
}
return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoPatterns, name)
return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns, name)
}
// IsValidSSHAccessRepoName is like IsUsableRepoName, but it allows "*.wiki" because wiki repo needs to be accessed in SSH code
func IsValidSSHAccessRepoName(name string) bool {
vars := globalVars()
if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
return false
}
return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoNamePatterns[1:], name) == nil
}
// TrustModelType defines the types of trust model for this repository

View File

@@ -216,8 +216,23 @@ func TestIsUsableRepoName(t *testing.T) {
assert.Error(t, IsUsableRepoName("-"))
assert.Error(t, IsUsableRepoName("🌞"))
assert.Error(t, IsUsableRepoName("the/repo"))
assert.Error(t, IsUsableRepoName("the..repo"))
assert.Error(t, IsUsableRepoName("foo.wiki"))
assert.Error(t, IsUsableRepoName("foo.git"))
assert.Error(t, IsUsableRepoName("foo.RSS"))
}
func TestIsValidSSHAccessRepoName(t *testing.T) {
assert.True(t, IsValidSSHAccessRepoName("a"))
assert.True(t, IsValidSSHAccessRepoName("-1_."))
assert.True(t, IsValidSSHAccessRepoName(".profile"))
assert.True(t, IsValidSSHAccessRepoName("foo.wiki"))
assert.False(t, IsValidSSHAccessRepoName("-"))
assert.False(t, IsValidSSHAccessRepoName("🌞"))
assert.False(t, IsValidSSHAccessRepoName("the/repo"))
assert.False(t, IsValidSSHAccessRepoName("the..repo"))
assert.False(t, IsValidSSHAccessRepoName("foo.git"))
assert.False(t, IsValidSSHAccessRepoName("foo.RSS"))
}

View File

@@ -249,7 +249,7 @@ func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_m
}
repo.Status = RepositoryPendingTransfer
if err := UpdateRepositoryCols(ctx, repo, "status"); err != nil {
if err := UpdateRepositoryColsNoAutoTime(ctx, repo, "status"); err != nil {
return err
}

View File

@@ -25,7 +25,7 @@ func UpdateRepositoryOwnerNames(ctx context.Context, ownerID int64, ownerName st
}
defer committer.Close()
if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").Update(&Repository{
if _, err := db.GetEngine(ctx).Where("owner_id = ?", ownerID).Cols("owner_name").NoAutoTime().Update(&Repository{
OwnerName: ownerName,
}); err != nil {
return err
@@ -40,8 +40,8 @@ func UpdateRepositoryUpdatedTime(ctx context.Context, repoID int64, updateTime t
return err
}
// UpdateRepositoryCols updates repository's columns
func UpdateRepositoryCols(ctx context.Context, repo *Repository, cols ...string) error {
// UpdateRepositoryColsWithAutoTime updates repository's columns
func UpdateRepositoryColsWithAutoTime(ctx context.Context, repo *Repository, cols ...string) error {
_, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).Update(repo)
return err
}

View File

@@ -5,7 +5,6 @@ package user
import (
"context"
"crypto/md5"
"fmt"
"image/png"
"io"
@@ -106,7 +105,7 @@ func (u *User) IsUploadAvatarChanged(data []byte) bool {
if !u.UseCustomAvatar || len(u.Avatar) == 0 {
return true
}
avatarID := fmt.Sprintf("%x", md5.Sum([]byte(fmt.Sprintf("%d-%x", u.ID, md5.Sum(data)))))
avatarID := avatar.HashAvatar(u.ID, data)
return u.Avatar != avatarID
}

View File

@@ -1146,8 +1146,8 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([
}
for _, c := range oldCommits {
user, ok := emailUserMap[c.Author.Email]
if !ok {
user := emailUserMap.GetByEmail(c.Author.Email) // FIXME: why ValidateCommitsWithEmails uses "Author", but ParseCommitsWithSignature uses "Committer"?
if user == nil {
user = &User{
Name: c.Author.Name,
Email: c.Author.Email,
@@ -1161,19 +1161,29 @@ func ValidateCommitsWithEmails(ctx context.Context, oldCommits []*git.Commit) ([
return newCommits, nil
}
func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, error) {
type EmailUserMap struct {
m map[string]*User
}
func (eum *EmailUserMap) GetByEmail(email string) *User {
return eum.m[strings.ToLower(email)]
}
func GetUsersByEmails(ctx context.Context, emails []string) (*EmailUserMap, error) {
if len(emails) == 0 {
return nil, nil
}
needCheckEmails := make(container.Set[string])
needCheckUserNames := make(container.Set[string])
noReplyAddressSuffix := "@" + strings.ToLower(setting.Service.NoReplyAddress)
for _, email := range emails {
if strings.HasSuffix(email, "@"+setting.Service.NoReplyAddress) {
username := strings.TrimSuffix(email, "@"+setting.Service.NoReplyAddress)
needCheckUserNames.Add(username)
emailLower := strings.ToLower(email)
if noReplyUserNameLower, ok := strings.CutSuffix(emailLower, noReplyAddressSuffix); ok {
needCheckUserNames.Add(noReplyUserNameLower)
needCheckEmails.Add(emailLower)
} else {
needCheckEmails.Add(strings.ToLower(email))
needCheckEmails.Add(emailLower)
}
}
@@ -1198,7 +1208,7 @@ func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, e
for _, email := range emailAddresses {
user := users[email.UID]
if user != nil {
results[user.GetEmail()] = user
results[email.LowerEmail] = user
}
}
}
@@ -1208,9 +1218,9 @@ func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, e
return nil, err
}
for _, user := range users {
results[user.GetPlaceholderEmail()] = user
results[strings.ToLower(user.GetPlaceholderEmail())] = user
}
return results, nil
return &EmailUserMap{results}, nil
}
// GetUserByEmail returns the user object by given e-mail if exists.

View File

@@ -23,6 +23,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestIsUsableUsername(t *testing.T) {
@@ -48,14 +49,47 @@ func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NotNil(t, user)
}
func TestGetUserEmailsByNames(t *testing.T) {
func TestUserEmails(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// ignore none active user email
assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
t.Run("GetUserEmailsByNames", func(t *testing.T) {
// ignore none active user email
assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user9"}))
assert.ElementsMatch(t, []string{"user8@example.com", "user5@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "user5"}))
assert.ElementsMatch(t, []string{"user8@example.com"}, user_model.GetUserEmailsByNames(db.DefaultContext, []string{"user8", "org7"}))
})
t.Run("GetUsersByEmails", func(t *testing.T) {
defer test.MockVariableValue(&setting.Service.NoReplyAddress, "NoReply.gitea.internal")()
testGetUserByEmail := func(t *testing.T, email string, uid int64) {
m, err := user_model.GetUsersByEmails(db.DefaultContext, []string{email})
require.NoError(t, err)
user := m.GetByEmail(email)
if uid == 0 {
require.Nil(t, user)
return
}
require.NotNil(t, user)
assert.Equal(t, uid, user.ID)
}
cases := []struct {
Email string
UID int64
}{
{"UseR1@example.com", 1},
{"user1-2@example.COM", 1},
{"USER2@" + setting.Service.NoReplyAddress, 2},
{"user4@example.com", 4},
{"no-such", 0},
}
for _, c := range cases {
t.Run(c.Email, func(t *testing.T) {
testGetUserByEmail(t, c.Email, c.UID)
})
}
t.Run("NoReplyConflict", func(t *testing.T) {
setting.Service.NoReplyAddress = "example.com"
testGetUserByEmail(t, "user1-2@example.COM", 1)
})
})
}
func TestCanCreateOrganization(t *testing.T) {

View File

@@ -311,6 +311,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
matchTimes++
}
case "paths":
if refName.IsTag() {
matchTimes++
break
}
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)
@@ -324,6 +328,10 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa
}
}
case "paths-ignore":
if refName.IsTag() {
matchTimes++
break
}
filesChanged, err := commit.GetFilesChangedSinceCommit(pushPayload.Before)
if err != nil {
log.Error("GetFilesChangedSinceCommit [commit_sha1: %s]: %v", commit.ID.String(), err)

View File

@@ -125,6 +125,24 @@ func TestDetectMatched(t *testing.T) {
yamlOn: "on: schedule",
expected: true,
},
{
desc: "push to tag matches workflow with paths condition (should skip paths check)",
triggedEvent: webhook_module.HookEventPush,
payload: &api.PushPayload{
Ref: "refs/tags/v1.0.0",
Before: "0000000",
Commits: []*api.PayloadCommit{
{
ID: "abcdef123456",
Added: []string{"src/main.go"},
Message: "Release v1.0.0",
},
},
},
commit: nil,
yamlOn: "on:\n push:\n paths:\n - src/**",
expected: true,
},
}
for _, tc := range testCases {

View File

@@ -6,22 +6,26 @@ package fileicon
import (
"html/template"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/util"
)
func BasicThemeIcon(entry *git.TreeEntry) template.HTML {
func BasicEntryIconName(entry *EntryInfo) string {
svgName := "octicon-file"
switch {
case entry.IsLink():
case entry.EntryMode.IsLink():
svgName = "octicon-file-symlink-file"
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.SymlinkToMode.IsDir() {
svgName = "octicon-file-directory-symlink"
}
case entry.IsDir():
svgName = "octicon-file-directory-fill"
case entry.IsSubModule():
case entry.EntryMode.IsDir():
svgName = util.Iif(entry.IsOpen, "octicon-file-directory-open-fill", "octicon-file-directory-fill")
case entry.EntryMode.IsSubModule():
svgName = "octicon-file-submodule"
}
return svg.RenderHTML(svgName)
return svgName
}
func BasicEntryIconHTML(entry *EntryInfo) template.HTML {
return svg.RenderHTML(BasicEntryIconName(entry))
}

31
modules/fileicon/entry.go Normal file
View File

@@ -0,0 +1,31 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package fileicon
import "code.gitea.io/gitea/modules/git"
type EntryInfo struct {
FullName string
EntryMode git.EntryMode
SymlinkToMode git.EntryMode
IsOpen bool
}
func EntryInfoFromGitTreeEntry(gitEntry *git.TreeEntry) *EntryInfo {
ret := &EntryInfo{FullName: gitEntry.Name(), EntryMode: gitEntry.Mode()}
if gitEntry.IsLink() {
if te, err := gitEntry.FollowLink(); err == nil && te.IsDir() {
ret.SymlinkToMode = te.Mode()
}
}
return ret
}
func EntryInfoFolder() *EntryInfo {
return &EntryInfo{EntryMode: git.EntryModeTree}
}
func EntryInfoFolderOpen() *EntryInfo {
return &EntryInfo{EntryMode: git.EntryModeTree, IsOpen: true}
}

View File

@@ -9,11 +9,12 @@ import (
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/util"
)
type materialIconRulesData struct {
@@ -69,41 +70,51 @@ func (m *MaterialIconProvider) renderFileIconSVG(p *RenderedIconPool, name, svg,
}
svgID := "svg-mfi-" + name
svgCommonAttrs := `class="svg git-entry-icon ` + extraClass + `" width="16" height="16" aria-hidden="true"`
svgHTML := template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
if p == nil {
return svgHTML
}
if p.IconSVGs[svgID] == "" {
p.IconSVGs[svgID] = template.HTML(`<svg id="` + svgID + `" ` + svgCommonAttrs + svg[4:])
p.IconSVGs[svgID] = svgHTML
}
return template.HTML(`<svg ` + svgCommonAttrs + `><use xlink:href="#` + svgID + `"></use></svg>`)
}
func (m *MaterialIconProvider) FileIcon(p *RenderedIconPool, entry *git.TreeEntry) template.HTML {
func (m *MaterialIconProvider) EntryIconHTML(p *RenderedIconPool, entry *EntryInfo) template.HTML {
if m.rules == nil {
return BasicThemeIcon(entry)
return BasicEntryIconHTML(entry)
}
if entry.IsLink() {
if te, err := entry.FollowLink(); err == nil && te.IsDir() {
if entry.EntryMode.IsLink() {
if entry.SymlinkToMode.IsDir() {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
return svg.RenderHTML("material-folder-symlink", 16, "octicon-file-directory-symlink")
}
return svg.RenderHTML("octicon-file-symlink-file") // TODO: find some better icons for them
}
name := m.findIconNameByGit(entry)
// the material icon pack's "folder" icon doesn't look good, so use our built-in one
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
if iconSVG, ok := m.svgs[name]; ok && name != "folder" && iconSVG != "" {
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
extraClass := "octicon-file"
switch {
case entry.IsDir():
extraClass = "octicon-file-directory-fill"
case entry.IsSubModule():
extraClass = "octicon-file-submodule"
name := m.FindIconName(entry)
iconSVG := m.svgs[name]
if iconSVG == "" {
name = "file"
if entry.EntryMode.IsDir() {
name = util.Iif(entry.IsOpen, "folder-open", "folder")
}
iconSVG = m.svgs[name]
if iconSVG == "" {
setting.PanicInDevOrTesting("missing file icon for %s", name)
}
return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
// TODO: use an interface or wrapper for git.Entry to make the code testable.
return BasicThemeIcon(entry)
// keep the old "octicon-xxx" class name to make some "theme plugin selector" could still work
extraClass := "octicon-file"
switch {
case entry.EntryMode.IsDir():
extraClass = BasicEntryIconName(entry)
case entry.EntryMode.IsSubModule():
extraClass = "octicon-file-submodule"
}
return m.renderFileIconSVG(p, name, iconSVG, extraClass)
}
func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
@@ -118,13 +129,17 @@ func (m *MaterialIconProvider) findIconNameWithLangID(s string) string {
return ""
}
func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
fileNameLower := strings.ToLower(path.Base(name))
if isDir {
func (m *MaterialIconProvider) FindIconName(entry *EntryInfo) string {
if entry.EntryMode.IsSubModule() {
return "folder-git"
}
fileNameLower := strings.ToLower(path.Base(entry.FullName))
if entry.EntryMode.IsDir() {
if s, ok := m.rules.FolderNames[fileNameLower]; ok {
return s
}
return "folder"
return util.Iif(entry.IsOpen, "folder-open", "folder")
}
if s, ok := m.rules.FileNames[fileNameLower]; ok {
@@ -146,10 +161,3 @@ func (m *MaterialIconProvider) FindIconName(name string, isDir bool) string {
return "file"
}
func (m *MaterialIconProvider) findIconNameByGit(entry *git.TreeEntry) string {
if entry.IsSubModule() {
return "folder-git"
}
return m.FindIconName(entry.Name(), entry.IsDir())
}

View File

@@ -8,6 +8,7 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/fileicon"
"code.gitea.io/gitea/modules/git"
"github.com/stretchr/testify/assert"
)
@@ -19,8 +20,8 @@ func TestMain(m *testing.M) {
func TestFindIconName(t *testing.T) {
unittest.PrepareTestEnv(t)
p := fileicon.DefaultMaterialIconProvider()
assert.Equal(t, "php", p.FindIconName("foo.php", false))
assert.Equal(t, "php", p.FindIconName("foo.PHP", false))
assert.Equal(t, "javascript", p.FindIconName("foo.js", false))
assert.Equal(t, "visualstudio", p.FindIconName("foo.vba", false))
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.php", EntryMode: git.EntryModeBlob}))
assert.Equal(t, "php", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.PHP", EntryMode: git.EntryModeBlob}))
assert.Equal(t, "javascript", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.js", EntryMode: git.EntryModeBlob}))
assert.Equal(t, "visualstudio", p.FindIconName(&fileicon.EntryInfo{FullName: "foo.vba", EntryMode: git.EntryModeBlob}))
}

View File

@@ -7,7 +7,6 @@ import (
"html/template"
"strings"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
)
@@ -34,19 +33,9 @@ func (p *RenderedIconPool) RenderToHTML() template.HTML {
return template.HTML(sb.String())
}
// TODO: use an interface or struct to replace "*git.TreeEntry", to decouple the fileicon module from git module
func RenderEntryIcon(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
func RenderEntryIconHTML(renderedIconPool *RenderedIconPool, entry *EntryInfo) template.HTML {
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
return DefaultMaterialIconProvider().EntryIconHTML(renderedIconPool, entry)
}
return BasicThemeIcon(entry)
}
func RenderEntryIconOpen(renderedIconPool *RenderedIconPool, entry *git.TreeEntry) template.HTML {
// TODO: add "open icon" support
if setting.UI.FileIconTheme == "material" {
return DefaultMaterialIconProvider().FileIcon(renderedIconPool, entry)
}
return BasicThemeIcon(entry)
return BasicEntryIconHTML(entry)
}

View File

@@ -20,6 +20,7 @@ const (
GitlabLanguage = "gitlab-language"
Lockable = "lockable"
Filter = "filter"
Diff = "diff"
)
var LinguistAttributes = []string{

View File

@@ -39,7 +39,12 @@ func checkAttrCommand(gitRepo *git.Repository, treeish string, filenames, attrib
)
cancel = deleteTemporaryFile
}
} // else: no treeish, assume it is a not a bare repo, read from working directory
} else {
// Read from existing index, in cases where the repo is bare and has an index,
// or the work tree contains unstaged changes that shouldn't affect the attribute check.
// It is caller's responsibility to add changed ".gitattributes" into the index if they want to respect the new changes.
cmd.AddArguments("--cached")
}
cmd.AddDynamicArguments(attributes...)
if len(filenames) > 0 {

View File

@@ -57,8 +57,18 @@ func Test_Checker(t *testing.T) {
assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
})
t.Run("Run git check-attr in bare repository using index", func(t *testing.T) {
attrs, err := CheckAttributes(t.Context(), gitRepo, "", CheckAttributeOpts{
Filenames: []string{"i-am-a-python.p"},
Attributes: LinguistAttributes,
})
assert.NoError(t, err)
assert.Len(t, attrs, 1)
assert.Equal(t, expectedAttrs(), attrs["i-am-a-python.p"])
})
if !git.DefaultFeatures().SupportCheckAttrOnBare {
t.Skip("git version 2.40 is required to support run check-attr on bare repo")
t.Skip("git version 2.40 is required to support run check-attr on bare repo without using index")
return
}

View File

@@ -132,18 +132,22 @@ func (r *BlameReader) Close() error {
}
// CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
reader, stdout, err := os.Pipe()
if err != nil {
return nil, err
}
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) {
var ignoreRevsFileName string
var ignoreRevsFileCleanup func()
defer func() {
if err != nil && ignoreRevsFileCleanup != nil {
ignoreRevsFileCleanup()
}
}()
cmd := NewCommandNoGlobals("blame", "--porcelain")
var ignoreRevsFileName string
var ignoreRevsFileCleanup func() // TODO: maybe it should check the returned err in a defer func to make sure the cleanup could always be executed correctly
if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
ignoreRevsFileName, ignoreRevsFileCleanup = tryCreateBlameIgnoreRevsFile(commit)
ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit)
if err != nil && !IsErrNotExist(err) {
return nil, err
}
if ignoreRevsFileName != "" {
// Possible improvement: use --ignore-revs-file /dev/stdin on unix
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
@@ -154,6 +158,10 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
done := make(chan error, 1)
reader, stdout, err := os.Pipe()
if err != nil {
return nil, err
}
go func() {
stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
@@ -182,33 +190,29 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
}, nil
}
func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func()) {
func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file: GetTreeEntryByPath: %v", err)
return "", nil
return "", nil, err
}
r, err := entry.Blob().DataAsync()
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file data: DataAsync: %v", err)
return "", nil
return "", nil, err
}
defer r.Close()
f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file data: CreateTempFileRandom: %v", err)
return "", nil
return "", nil, err
}
filename := f.Name()
_, err = io.Copy(f, r)
_ = f.Close()
if err != nil {
cleanup()
log.Error("Unable to get .git-blame-ignore-revs file data: Copy: %v", err)
return "", nil
return "", nil, err
}
return filename, cleanup
return filename, cleanup, nil
}

36
modules/git/cmdverb.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
const (
CmdVerbUploadPack = "git-upload-pack"
CmdVerbUploadArchive = "git-upload-archive"
CmdVerbReceivePack = "git-receive-pack"
CmdVerbLfsAuthenticate = "git-lfs-authenticate"
CmdVerbLfsTransfer = "git-lfs-transfer"
CmdSubVerbLfsUpload = "upload"
CmdSubVerbLfsDownload = "download"
)
func IsAllowedVerbForServe(verb string) bool {
switch verb {
case CmdVerbUploadPack,
CmdVerbUploadArchive,
CmdVerbReceivePack,
CmdVerbLfsAuthenticate,
CmdVerbLfsTransfer:
return true
}
return false
}
func IsAllowedVerbForServeLfs(verb string) bool {
switch verb {
case CmdVerbLfsAuthenticate,
CmdVerbLfsTransfer:
return true
}
return false
}

View File

@@ -34,7 +34,7 @@ type Commit struct {
// CommitSignature represents a git commit signature part.
type CommitSignature struct {
Signature string
Payload string // TODO check if can be reconstruct from the rest of commit information to not have duplicate data
Payload string
}
// Message returns the commit message. Same as retrieving CommitMessage directly.

View File

@@ -3,9 +3,24 @@
package git
import "path"
// CommitInfo describes the first commit with the provided entry
type CommitInfo struct {
Entry *TreeEntry
Commit *Commit
SubmoduleFile *CommitSubmoduleFile
}
func getCommitInfoSubmoduleFile(repoLink string, entry *TreeEntry, commit *Commit, treePathDir string) (*CommitSubmoduleFile, error) {
fullPath := path.Join(treePathDir, entry.Name())
submodule, err := commit.GetSubModule(fullPath)
if err != nil {
return nil, err
}
if submodule == nil {
// unable to find submodule from ".gitmodules" file
return NewCommitSubmoduleFile(repoLink, fullPath, "", entry.ID.String()), nil
}
return NewCommitSubmoduleFile(repoLink, fullPath, submodule.URL, entry.ID.String()), nil
}

View File

@@ -16,7 +16,7 @@ import (
)
// GetCommitsInfo gets information of all commits that are corresponding to these entries
func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
entryPaths := make([]string, len(tes)+1)
// Get the commit for the treePath itself
entryPaths[0] = ""
@@ -71,22 +71,12 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
commitsInfo[i].Commit = entryCommit
}
// If the entry is a submodule add a submodule file for this
// If the entry is a submodule, add a submodule file for this
if entry.IsSubModule() {
subModuleURL := ""
var fullPath string
if len(treePath) > 0 {
fullPath = treePath + "/" + entry.Name()
} else {
fullPath = entry.Name()
}
if subModule, err := commit.GetSubModule(fullPath); err != nil {
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath)
if err != nil {
return nil, nil, err
} else if subModule != nil {
subModuleURL = subModule.URL
}
subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
commitsInfo[i].SubmoduleFile = subModuleFile
}
}

View File

@@ -16,7 +16,7 @@ import (
)
// GetCommitsInfo gets information of all commits that are corresponding to these entries
func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
func (tes Entries) GetCommitsInfo(ctx context.Context, repoLink string, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
entryPaths := make([]string, len(tes)+1)
// Get the commit for the treePath itself
entryPaths[0] = ""
@@ -65,22 +65,12 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
log.Debug("missing commit for %s", entry.Name())
}
// If the entry is a submodule add a submodule file for this
// If the entry is a submodule, add a submodule file for this
if entry.IsSubModule() {
subModuleURL := ""
var fullPath string
if len(treePath) > 0 {
fullPath = treePath + "/" + entry.Name()
} else {
fullPath = entry.Name()
}
if subModule, err := commit.GetSubModule(fullPath); err != nil {
commitsInfo[i].SubmoduleFile, err = getCommitInfoSubmoduleFile(repoLink, entry, commit, treePath)
if err != nil {
return nil, nil, err
} else if subModule != nil {
subModuleURL = subModule.URL
}
subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
commitsInfo[i].SubmoduleFile = subModuleFile
}
}

View File

@@ -9,6 +9,7 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
@@ -82,7 +83,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) {
}
// FIXME: Context.TODO() - if graceful has started we should use its Shutdown context otherwise use install signals in TestMain.
commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), commit, testCase.Path)
commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), "/any/repo-link", commit, testCase.Path)
assert.NoError(t, err, "Unable to get commit information for entries of subtree: %s in commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err)
if err != nil {
t.FailNow()
@@ -120,6 +121,23 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
defer clonedRepo1.Close()
testGetCommitsInfo(t, clonedRepo1)
t.Run("NonExistingSubmoduleAsNil", func(t *testing.T) {
commit, err := bareRepo1.GetCommit("HEAD")
require.NoError(t, err)
tree, err := commit.GetTreeEntryByPath("file1.txt")
require.NoError(t, err)
cisf, err := getCommitInfoSubmoduleFile("/any/repo-link", tree, commit, "")
require.NoError(t, err)
assert.Equal(t, &CommitSubmoduleFile{
repoLink: "/any/repo-link",
fullPath: "file1.txt",
refURL: "",
refID: "e2129701f1a4d54dc44f03c93bca0a2aec7c5449",
}, cisf)
// since there is no refURL, it means that the submodule info doesn't exist, so it won't have a web link
assert.Nil(t, cisf.SubmoduleWebLinkTree(t.Context()))
})
}
func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
@@ -159,7 +177,7 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
b.ResetTimer()
b.Run(benchmark.name, func(b *testing.B) {
for b.Loop() {
_, _, err := entries.GetCommitsInfo(b.Context(), commit, "")
_, _, err := entries.GetCommitsInfo(b.Context(), "/any/repo-link", commit, "")
if err != nil {
b.Fatal(err)
}

View File

@@ -6,10 +6,44 @@ package git
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
const (
commitHeaderGpgsig = "gpgsig"
commitHeaderGpgsigSha256 = "gpgsig-sha256"
)
func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error {
if len(headerValue) > 0 && headerValue[len(headerValue)-1] == '\n' {
headerValue = headerValue[:len(headerValue)-1] // remove trailing newline
}
switch headerKey {
case "tree":
objID, err := NewIDFromString(string(headerValue))
if err != nil {
return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err)
}
commit.Tree = *NewTree(gitRepo, objID)
case "parent":
objID, err := NewIDFromString(string(headerValue))
if err != nil {
return fmt.Errorf("invalid parent ID %q: %w", string(headerValue), err)
}
commit.Parents = append(commit.Parents, objID)
case "author":
commit.Author.Decode(headerValue)
case "committer":
commit.Committer.Decode(headerValue)
case commitHeaderGpgsig, commitHeaderGpgsigSha256:
// if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
// so we don't need to handle duplicate headers here
commit.Signature = &CommitSignature{Signature: string(headerValue)}
}
return nil
}
// CommitFromReader will generate a Commit from a provided reader
// We need this to interpret commits from cat-file or cat-file --batch
//
@@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
Committer: &Signature{},
}
payloadSB := new(strings.Builder)
signatureSB := new(strings.Builder)
messageSB := new(strings.Builder)
message := false
pgpsig := false
bufReader, ok := reader.(*bufio.Reader)
if !ok {
bufReader = bufio.NewReader(reader)
}
readLoop:
bufReader := bufio.NewReader(reader)
inHeader := true
var payloadSB, messageSB bytes.Buffer
var headerKey string
var headerValue []byte
for {
line, err := bufReader.ReadBytes('\n')
if err != nil {
if err == io.EOF {
if message {
_, _ = messageSB.Write(line)
if err != nil && err != io.EOF {
return nil, fmt.Errorf("unable to read commit %q: %w", objectID.String(), err)
}
if len(line) == 0 {
break
}
if inHeader {
inHeader = !(len(line) == 1 && line[0] == '\n') // still in header if line is not just a newline
k, v, _ := bytes.Cut(line, []byte{' '})
if len(k) != 0 || !inHeader {
if headerKey != "" {
if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil {
return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err)
}
}
_, _ = payloadSB.Write(line)
break readLoop
headerKey = string(k) // it also resets the headerValue to empty string if not inHeader
headerValue = v
} else {
headerValue = append(headerValue, v...)
}
return nil, err
}
if pgpsig {
if len(line) > 0 && line[0] == ' ' {
_, _ = signatureSB.Write(line[1:])
continue
}
pgpsig = false
}
if !message {
// This is probably not correct but is copied from go-gits interpretation...
trimmed := bytes.TrimSpace(line)
if len(trimmed) == 0 {
message = true
if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
_, _ = payloadSB.Write(line)
continue
}
split := bytes.SplitN(trimmed, []byte{' '}, 2)
var data []byte
if len(split) > 1 {
data = split[1]
}
switch string(split[0]) {
case "tree":
commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
_, _ = payloadSB.Write(line)
case "parent":
commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
_, _ = payloadSB.Write(line)
case "author":
commit.Author = &Signature{}
commit.Author.Decode(data)
_, _ = payloadSB.Write(line)
case "committer":
commit.Committer = &Signature{}
commit.Committer.Decode(data)
_, _ = payloadSB.Write(line)
case "encoding":
_, _ = payloadSB.Write(line)
case "gpgsig":
fallthrough
case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
_, _ = signatureSB.Write(data)
_ = signatureSB.WriteByte('\n')
pgpsig = true
}
} else {
_, _ = messageSB.Write(line)
_, _ = payloadSB.Write(line)
}
}
commit.CommitMessage = messageSB.String()
commit.Signature = &CommitSignature{
Signature: signatureSB.String(),
Payload: payloadSB.String(),
}
if len(commit.Signature.Signature) == 0 {
commit.Signature = nil
}
commit.CommitMessage = messageSB.String()
if commit.Signature != nil {
commit.Signature.Payload = payloadSB.String()
}
return commit, nil
}

View File

@@ -60,8 +60,7 @@ func TestGetFullCommitIDErrorSha256(t *testing.T) {
}
func TestCommitFromReaderSha256(t *testing.T) {
commitString := `9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 commit 1114
tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
commitString := `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100
committer Adam Majer <amajer@suse.de> 1698676906 +0100
@@ -112,8 +111,7 @@ VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X
HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR
8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6
=xybZ
-----END PGP SIGNATURE-----
`, commitFromReader.Signature.Signature)
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
author Adam Majer <amajer@suse.de> 1698676906 +0100

View File

@@ -35,7 +35,8 @@ func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) {
return c.submoduleCache, nil
}
// GetSubModule get the submodule according entry name
// GetSubModule gets the submodule by the entry name.
// It returns "nil, nil" if the submodule does not exist, caller should always remember to check the "nil"
func (c *Commit) GetSubModule(entryName string) (*SubModule, error) {
modules, err := c.GetSubModules()
if err != nil {

View File

@@ -6,49 +6,64 @@ package git
import (
"context"
"path"
"strings"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/util"
)
// CommitSubmoduleFile represents a file with submodule type.
type CommitSubmoduleFile struct {
refURL string
parsedURL *giturl.RepositoryURL
parsed bool
refID string
repoLink string
repoLink string
fullPath string
refURL string
refID string
parsed bool
parsedTargetLink string
}
// NewCommitSubmoduleFile create a new submodule file
func NewCommitSubmoduleFile(refURL, refID string) *CommitSubmoduleFile {
return &CommitSubmoduleFile{refURL: refURL, refID: refID}
func NewCommitSubmoduleFile(repoLink, fullPath, refURL, refID string) *CommitSubmoduleFile {
return &CommitSubmoduleFile{repoLink: repoLink, fullPath: fullPath, refURL: refURL, refID: refID}
}
// RefID returns the commit ID of the submodule, it returns empty string for nil receiver
func (sf *CommitSubmoduleFile) RefID() string {
return sf.refID // this function is only used in templates
if sf == nil {
return ""
}
return sf.refID
}
// SubmoduleWebLink tries to make some web links for a submodule, it also works on "nil" receiver
func (sf *CommitSubmoduleFile) SubmoduleWebLink(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
if sf == nil {
func (sf *CommitSubmoduleFile) getWebLinkInTargetRepo(ctx context.Context, moreLinkPath string) *SubmoduleWebLink {
if sf == nil || sf.refURL == "" {
return nil
}
if strings.HasPrefix(sf.refURL, "../") {
targetLink := path.Join(sf.repoLink, path.Dir(sf.fullPath), sf.refURL)
return &SubmoduleWebLink{RepoWebLink: targetLink, CommitWebLink: targetLink + moreLinkPath}
}
if !sf.parsed {
sf.parsed = true
parsedURL, err := giturl.ParseRepositoryURL(ctx, sf.refURL)
if err != nil {
return nil
}
sf.parsedURL = parsedURL
sf.repoLink = giturl.MakeRepositoryWebLink(sf.parsedURL)
sf.parsedTargetLink = giturl.MakeRepositoryWebLink(parsedURL)
}
var commitLink string
if len(optCommitID) == 2 {
commitLink = sf.repoLink + "/compare/" + optCommitID[0] + "..." + optCommitID[1]
} else if len(optCommitID) == 1 {
commitLink = sf.repoLink + "/tree/" + optCommitID[0]
} else {
commitLink = sf.repoLink + "/tree/" + sf.refID
}
return &SubmoduleWebLink{RepoWebLink: sf.repoLink, CommitWebLink: commitLink}
return &SubmoduleWebLink{RepoWebLink: sf.parsedTargetLink, CommitWebLink: sf.parsedTargetLink + moreLinkPath}
}
// SubmoduleWebLinkTree tries to make the submodule's tree link in its own repo, it also works on "nil" receiver
// It returns nil if the submodule does not have a valid URL or is nil
func (sf *CommitSubmoduleFile) SubmoduleWebLinkTree(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
return sf.getWebLinkInTargetRepo(ctx, "/tree/"+util.OptionalArg(optCommitID, sf.RefID()))
}
// SubmoduleWebLinkCompare tries to make the submodule's compare link in its own repo, it also works on "nil" receiver
// It returns nil if the submodule does not have a valid URL or is nil
func (sf *CommitSubmoduleFile) SubmoduleWebLinkCompare(ctx context.Context, commitID1, commitID2 string) *SubmoduleWebLink {
return sf.getWebLinkInTargetRepo(ctx, "/compare/"+commitID1+"..."+commitID2)
}

View File

@@ -10,20 +10,31 @@ import (
)
func TestCommitSubmoduleLink(t *testing.T) {
sf := NewCommitSubmoduleFile("git@github.com:user/repo.git", "aaaa")
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkTree(t.Context()))
assert.Nil(t, (*CommitSubmoduleFile)(nil).SubmoduleWebLinkCompare(t.Context(), "", ""))
assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkTree(t.Context()))
assert.Nil(t, (&CommitSubmoduleFile{}).SubmoduleWebLinkCompare(t.Context(), "", ""))
wl := sf.SubmoduleWebLink(t.Context())
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
assert.Equal(t, "https://github.com/user/repo/tree/aaaa", wl.CommitWebLink)
t.Run("GitHubRepo", func(t *testing.T) {
sf := NewCommitSubmoduleFile("/any/repo-link", "full-path", "git@github.com:user/repo.git", "aaaa")
wl := sf.SubmoduleWebLinkTree(t.Context())
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
assert.Equal(t, "https://github.com/user/repo/tree/aaaa", wl.CommitWebLink)
wl = sf.SubmoduleWebLink(t.Context(), "1111")
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
assert.Equal(t, "https://github.com/user/repo/tree/1111", wl.CommitWebLink)
wl = sf.SubmoduleWebLinkCompare(t.Context(), "1111", "2222")
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
assert.Equal(t, "https://github.com/user/repo/compare/1111...2222", wl.CommitWebLink)
})
wl = sf.SubmoduleWebLink(t.Context(), "1111", "2222")
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
assert.Equal(t, "https://github.com/user/repo/compare/1111...2222", wl.CommitWebLink)
t.Run("RelativePath", func(t *testing.T) {
sf := NewCommitSubmoduleFile("/subpath/any/repo-home-link", "full-path", "../../user/repo", "aaaa")
wl := sf.SubmoduleWebLinkTree(t.Context())
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
assert.Equal(t, "/subpath/user/repo/tree/aaaa", wl.CommitWebLink)
wl = (*CommitSubmoduleFile)(nil).SubmoduleWebLink(t.Context())
assert.Nil(t, wl)
sf = NewCommitSubmoduleFile("/subpath/any/repo-home-link", "dir/submodule", "../../../user/repo", "aaaa")
wl = sf.SubmoduleWebLinkCompare(t.Context(), "1111", "2222")
assert.Equal(t, "/subpath/user/repo", wl.RepoWebLink)
assert.Equal(t, "/subpath/user/repo/compare/1111...2222", wl.CommitWebLink)
})
}

View File

@@ -59,8 +59,7 @@ func TestGetFullCommitIDError(t *testing.T) {
}
func TestCommitFromReader(t *testing.T) {
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
commitString := `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200
committer silverwind <me@silverwind.io> 1563741793 +0200
@@ -108,8 +107,7 @@ sD53z/f0J+We4VZjY+pidvA9BGZPFVdR3wd3xGs8/oH6UWaLJAMGkLG6dDb3qDLm
mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i
1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs=
=FRsO
-----END PGP SIGNATURE-----
`, commitFromReader.Signature.Signature)
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
parent 37991dec2c8e592043f47155ce4808d4580f9123
author silverwind <me@silverwind.io> 1563741793 +0200
@@ -126,8 +124,7 @@ empty commit`, commitFromReader.Signature.Payload)
}
func TestCommitWithEncodingFromReader(t *testing.T) {
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
commitString := `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
@@ -172,8 +169,7 @@ SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
jw4YcO5u
=r3UU
-----END PGP SIGNATURE-----
`, commitFromReader.Signature.Signature)
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100

View File

@@ -99,9 +99,9 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff
return nil
}
// ParseDiffHunkString parse the diffhunk content and return
func ParseDiffHunkString(diffhunk string) (leftLine, leftHunk, rightLine, righHunk int) {
ss := strings.Split(diffhunk, "@@")
// ParseDiffHunkString parse the diff hunk content and return
func ParseDiffHunkString(diffHunk string) (leftLine, leftHunk, rightLine, rightHunk int) {
ss := strings.Split(diffHunk, "@@")
ranges := strings.Split(ss[1][1:], " ")
leftRange := strings.Split(ranges[0], ",")
leftLine, _ = strconv.Atoi(leftRange[0][1:])
@@ -112,14 +112,19 @@ func ParseDiffHunkString(diffhunk string) (leftLine, leftHunk, rightLine, righHu
rightRange := strings.Split(ranges[1], ",")
rightLine, _ = strconv.Atoi(rightRange[0])
if len(rightRange) > 1 {
righHunk, _ = strconv.Atoi(rightRange[1])
rightHunk, _ = strconv.Atoi(rightRange[1])
}
} else {
log.Debug("Parse line number failed: %v", diffhunk)
log.Debug("Parse line number failed: %v", diffHunk)
rightLine = leftLine
righHunk = leftHunk
rightHunk = leftHunk
}
return leftLine, leftHunk, rightLine, righHunk
if rightLine == 0 {
// FIXME: GIT-DIFF-CUT-BUG search this tag to see details
// this is only a hacky patch, the rightLine&rightHunk might still be incorrect in some cases.
rightLine++
}
return leftLine, leftHunk, rightLine, rightHunk
}
// Example: @@ -1,8 +1,9 @@ => [..., 1, 8, 1, 9]
@@ -270,6 +275,12 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
oldNumOfLines++
}
}
// "git diff" outputs "@@ -1 +1,3 @@" for "OLD" => "A\nB\nC"
// FIXME: GIT-DIFF-CUT-BUG But there is a bug in CutDiffAroundLine, then the "Patch" stored in the comment model becomes "@@ -1,1 +0,4 @@"
// It may generate incorrect results for difference cases, for example: delete 2 line add 1 line, delete 2 line add 2 line etc, need to double check.
// For example: "L1\nL2" => "A\nB", then the patch shows "L2" as line 1 on the left (deleted part)
// construct the new hunk header
newHunk[headerLines] = fmt.Sprintf("@@ -%d,%d +%d,%d @@",
oldBegin, oldNumOfLines, newBegin, newNumOfLines)

View File

@@ -97,17 +97,17 @@ func GetLanguageStats(repo *git.Repository, commitID string) (map[string]int64,
}
isVendored := optional.None[bool]()
isGenerated := optional.None[bool]()
isDocumentation := optional.None[bool]()
isDetectable := optional.None[bool]()
attrs, err := checker.CheckPath(f.Name())
attrLinguistGenerated := optional.None[bool]()
if err == nil {
if isVendored = attrs.GetVendored(); isVendored.ValueOrDefault(false) {
continue
}
if isGenerated = attrs.GetGenerated(); isGenerated.ValueOrDefault(false) {
if attrLinguistGenerated = attrs.GetGenerated(); attrLinguistGenerated.ValueOrDefault(false) {
continue
}
@@ -169,7 +169,15 @@ func GetLanguageStats(repo *git.Repository, commitID string) (map[string]int64,
return nil, err
}
}
if !isGenerated.Has() && enry.IsGenerated(f.Name(), content) {
// if "generated" attribute is set, use it, otherwise use enry.IsGenerated to guess
var isGenerated bool
if attrLinguistGenerated.Has() {
isGenerated = attrLinguistGenerated.Value()
} else {
isGenerated = enry.IsGenerated(f.Name(), content)
}
if isGenerated {
continue
}

View File

@@ -30,6 +30,31 @@ func (e EntryMode) String() string {
return strconv.FormatInt(int64(e), 8)
}
// IsSubModule if the entry is a sub module
func (e EntryMode) IsSubModule() bool {
return e == EntryModeCommit
}
// IsDir if the entry is a sub dir
func (e EntryMode) IsDir() bool {
return e == EntryModeTree
}
// IsLink if the entry is a symlink
func (e EntryMode) IsLink() bool {
return e == EntryModeSymlink
}
// IsRegular if the entry is a regular file
func (e EntryMode) IsRegular() bool {
return e == EntryModeBlob
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (e EntryMode) IsExecutable() bool {
return e == EntryModeExec
}
func ParseEntryMode(mode string) (EntryMode, error) {
switch mode {
case "000000":

View File

@@ -59,27 +59,27 @@ func (te *TreeEntry) Size() int64 {
// IsSubModule if the entry is a sub module
func (te *TreeEntry) IsSubModule() bool {
return te.entryMode == EntryModeCommit
return te.entryMode.IsSubModule()
}
// IsDir if the entry is a sub dir
func (te *TreeEntry) IsDir() bool {
return te.entryMode == EntryModeTree
return te.entryMode.IsDir()
}
// IsLink if the entry is a symlink
func (te *TreeEntry) IsLink() bool {
return te.entryMode == EntryModeSymlink
return te.entryMode.IsLink()
}
// IsRegular if the entry is a regular file
func (te *TreeEntry) IsRegular() bool {
return te.entryMode == EntryModeBlob
return te.entryMode.IsRegular()
}
// IsExecutable if the entry is an executable file (not necessarily binary)
func (te *TreeEntry) IsExecutable() bool {
return te.entryMode == EntryModeExec
return te.entryMode.IsExecutable()
}
// Blob returns the blob object the entry

View File

@@ -132,6 +132,7 @@ func newInternalRequestLFS(ctx context.Context, internalURL, method string, head
return nil
}
req := private.NewInternalRequest(ctx, internalURL, method)
req.SetReadWriteTimeout(0)
for k, v := range headers {
req.Header(k, v)
}

View File

@@ -409,9 +409,9 @@ func (r *FootnoteHTMLRenderer) renderFootnoteLink(w util.BufWriter, source []byt
_, _ = w.Write(n.Name)
_, _ = w.WriteString(`"><a href="#fn:`)
_, _ = w.Write(n.Name)
_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`)
_, _ = w.WriteString(`" class="footnote-ref" role="doc-noteref">`) // FIXME: here and below, need to keep the classes
_, _ = w.WriteString(is)
_, _ = w.WriteString(`</a></sup>`)
_, _ = w.WriteString(` </a></sup>`) // the style doesn't work at the moment, so add a space to separate the names
}
return ast.WalkContinue, nil
}

View File

@@ -86,8 +86,8 @@ var globalVars = sync.OnceValue(func() *globalVarsType {
// codePreviewPattern matches "http://domain/.../{owner}/{repo}/src/commit/{commit}/{filepath}#L10-L20"
v.codePreviewPattern = regexp.MustCompile(`https?://\S+/([^\s/]+)/([^\s/]+)/src/commit/([0-9a-f]{7,64})(/\S+)#(L\d+(-L\d+)?)`)
// cleans: "<foo/bar", "<any words/", ("<html", "<head", "<script", "<style")
v.tagCleaner = regexp.MustCompile(`(?i)<(/?\w+/\w+|/[\w ]+/|/?(html|head|script|style\b))`)
// cleans: "<foo/bar", "<any words/", ("<html", "<head", "<script", "<style", "<?", "<%")
v.tagCleaner = regexp.MustCompile(`(?i)<(/?\w+/\w+|/[\w ]+/|/?(html|head|script|style|%|\?)\b)`)
v.nulCleaner = strings.NewReplacer("\000", "")
return v
})
@@ -253,7 +253,7 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output
node, err := html.Parse(io.MultiReader(
// prepend "<html><body>"
strings.NewReader("<html><body>"),
// Strip out nuls - they're always invalid
// strip out NULLs (they're always invalid), and escape known tags
bytes.NewReader(globalVars().tagCleaner.ReplaceAll([]byte(globalVars().nulCleaner.Replace(string(rawHTML))), []byte("&lt;$1"))),
// close the tags
strings.NewReader("</body></html>"),
@@ -320,6 +320,7 @@ func visitNode(ctx *RenderContext, procs []processor, node *html.Node) *html.Nod
}
processNodeAttrID(node)
processFootnoteNode(ctx, node) // FIXME: the footnote processing should be done in the "footnote.go" renderer directly
if isEmojiNode(node) {
// TextNode emoji will be converted to `<span class="emoji">`, then the next iteration will visit the "span"

View File

@@ -30,6 +30,7 @@ func TestRender_IssueList(t *testing.T) {
rctx := markup.NewTestRenderContext(markup.TestAppURL, map[string]string{
"user": "test-user", "repo": "test-repo",
"markupAllowShortIssuePattern": "true",
"footnoteContextId": "12345",
})
out, err := markdown.RenderString(rctx, input)
require.NoError(t, err)
@@ -69,4 +70,22 @@ func TestRender_IssueList(t *testing.T) {
</ul>`,
)
})
t.Run("IssueFootnote", func(t *testing.T) {
test(
"foo[^1][^2]\n\n[^1]: bar\n[^2]: baz",
`<p>foo<sup id="fnref:user-content-1-12345"><a href="#fn:user-content-1-12345" rel="nofollow">1 </a></sup><sup id="fnref:user-content-2-12345"><a href="#fn:user-content-2-12345" rel="nofollow">2 </a></sup></p>
<div>
<hr/>
<ol>
<li id="fn:user-content-1-12345">
<p>bar <a href="#fnref:user-content-1-12345" rel="nofollow">↩︎</a></p>
</li>
<li id="fn:user-content-2-12345">
<p>baz <a href="#fnref:user-content-2-12345" rel="nofollow">↩︎</a></p>
</li>
</ol>
</div>`,
)
})
}

View File

@@ -15,6 +15,14 @@ func isAnchorIDUserContent(s string) bool {
return strings.HasPrefix(s, "user-content-") || strings.Contains(s, ":user-content-")
}
func isAnchorIDFootnote(s string) bool {
return strings.HasPrefix(s, "fnref:user-content-") || strings.HasPrefix(s, "fn:user-content-")
}
func isAnchorHrefFootnote(s string) bool {
return strings.HasPrefix(s, "#fnref:user-content-") || strings.HasPrefix(s, "#fn:user-content-")
}
func processNodeAttrID(node *html.Node) {
// Add user-content- to IDs and "#" links if they don't already have them,
// and convert the link href to a relative link to the host root
@@ -27,6 +35,18 @@ func processNodeAttrID(node *html.Node) {
}
}
func processFootnoteNode(ctx *RenderContext, node *html.Node) {
for idx, attr := range node.Attr {
if (attr.Key == "id" && isAnchorIDFootnote(attr.Val)) ||
(attr.Key == "href" && isAnchorHrefFootnote(attr.Val)) {
if footnoteContextID := ctx.RenderOptions.Metas["footnoteContextId"]; footnoteContextID != "" {
node.Attr[idx].Val = attr.Val + "-" + footnoteContextID
}
continue
}
}
}
func processNodeA(ctx *RenderContext, node *html.Node) {
for idx, attr := range node.Attr {
if attr.Key == "href" {

View File

@@ -525,6 +525,10 @@ func TestPostProcess(t *testing.T) {
test("<script>a</script>", `&lt;script&gt;a&lt;/script&gt;`)
test("<STYLE>a", `&lt;STYLE&gt;a`)
test("<style>a</STYLE>", `&lt;style&gt;a&lt;/STYLE&gt;`)
// other special tags, our special behavior
test("<?php\nfoo", "&lt;?php\nfoo")
test("<%asp\nfoo", "&lt;%asp\nfoo")
}
func TestIssue16020(t *testing.T) {

View File

@@ -223,7 +223,7 @@ This PR has been generated by [Renovate Bot](https://github.com/renovatebot/reno
<dd>This is another definition of the second term.</dd>
</dl>
<h3 id="user-content-footnotes">Footnotes</h3>
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1</a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2</a></sup></p>
<p>Here is a simple footnote,<sup id="fnref:user-content-1"><a href="#fn:user-content-1" rel="nofollow">1 </a></sup> and here is a longer one.<sup id="fnref:user-content-bignote"><a href="#fn:user-content-bignote" rel="nofollow">2 </a></sup></p>
<div>
<hr/>
<ol>

View File

@@ -22,6 +22,13 @@ func FromPtr[T any](v *T) Option[T] {
return Some(*v)
}
func FromMapLookup[K comparable, V any](m map[K]V, k K) Option[V] {
if v, ok := m[k]; ok {
return Some(v)
}
return None[V]()
}
func FromNonDefault[T comparable](v T) Option[T] {
var zero T
if v == zero {

View File

@@ -56,6 +56,12 @@ func TestOption(t *testing.T) {
opt3 := optional.FromNonDefault(1)
assert.True(t, opt3.Has())
assert.Equal(t, int(1), opt3.Value())
opt4 := optional.FromMapLookup(map[string]int{"a": 1}, "a")
assert.True(t, opt4.Has())
assert.Equal(t, 1, opt4.Value())
opt4 = optional.FromMapLookup(map[string]int{"a": 1}, "b")
assert.False(t, opt4.Has())
}
func Test_ParseBool(t *testing.T) {

View File

@@ -4,6 +4,7 @@
package container
import (
"errors"
"fmt"
"io"
"strings"
@@ -83,7 +84,8 @@ func ParseImageConfig(mt string, r io.Reader) (*Metadata, error) {
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
var image oci.Image
if err := json.NewDecoder(r).Decode(&image); err != nil {
// EOF means empty input, still use the default data
if err := json.NewDecoder(r).Decode(&image); err != nil && !errors.Is(err, io.EOF) {
return nil, err
}

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