Compare commits

..

336 Commits

Author SHA1 Message Date
Lunny Xiao
8eefa1f6de Add missing two sync feed for refs/pull (#32815) (#32822)
Fowllow #32659
Backport #32815
2024-12-13 08:25:22 +00:00
Lunny Xiao
d172c6d2b0 Add changelog for 1.22.6 (#32825) 2024-12-13 15:55:25 +08:00
Lunny Xiao
c630b88f35 Fix misuse of PublicKeyCallback(#32810) (#32824)
Backport #32810
2024-12-13 07:34:52 +00:00
Giteabot
e7de2fc136 Fix lfs migration (#32812) (#32818)
Backport #32812 by @hiifong

Fix: #32803

![image](https://github.com/user-attachments/assets/3ea1f4e0-e26f-4a15-957e-dd6caf91deb1)

![image](https://github.com/user-attachments/assets/44b99624-c347-4f2d-a11c-13ec1276eea2)

Co-authored-by: hiifong <i@hiif.ong>
2024-12-13 11:41:12 +08:00
Giteabot
4fe19fc722 Avoid MacOS keychain dialog in integration tests (#32813) (#32816)
Backport #32813 by @bohde

Mac's git installation ships with a system wide config that configures
the credential helper `osxkeychain`, which will prompt the user with a
dialog.

```
$ git config list --system 
credential.helper=osxkeychain
```
By setting the environment variable
[`GIT_CONFIG_NOSYSTEM=true`](https://git-scm.com/docs/git-config#ENVIRONMENT),
Git will not load the system wide config, preventing the dialog from
populating.

Closes #26717

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
2024-12-13 05:41:02 +08:00
techknowlogick
b54b6d103f use specific namespace labels 2024-12-12 15:45:55 -05:00
Giteabot
84ce417312 use dedicated runners for release artifacts (#32811) (#32814)
Backport #32811 by @techknowlogick

GH runners are having trouble, so switch the remaining release jobs to
use dedicated runners.

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-12-12 15:44:58 -05:00
Lunny Xiao
c0092af2e0 Add changelog for 1.22.5 (#32794) 2024-12-12 04:56:35 +08:00
Lunny Xiao
6092bbac4d 🐛 Fixe a keystring misuse and refactor duplicates keystrings (#32668) (#32792)
Backport #32668 

- Fixes a translation keystring misuse where the string 'open
milestones' is used in place of 'closed milestones'.
- De-duplicates the use of 'open milesones' and 'closed milestones'
keystrings on the sidebar of an issue, reusing the ones on the issues
filter and action bars.
- Closes #32667

Co-authored-by: Simon Pistache <105607989+SimonPistache@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-12-11 13:32:10 -05:00
Lunny Xiao
e4ca557fd0 Upgrade crypto library (#32791)
backport #32750
2024-12-11 13:18:58 -05:00
Giteabot
ef9d1e9002 Add standard-compliant route to serve outdated R packages (#32783) (#32789)
Backport #32783 by Sebastian-T-T

The R package repository currently does not have support for older
versions of packages which should be stored in a separate /Archive
router. This PR remedies that by adding a new path router.

Fixes #32782

Co-authored-by: Sebastian T. T. <109338575+Sebastian-T-T@users.noreply.github.com>
2024-12-11 16:49:06 +00:00
Giteabot
0c7e44fcf7 Fix internal server error when updating labels without write permission (#32776) (#32785) 2024-12-10 17:22:03 -08:00
Giteabot
3a9039bc95 Make wiki pages visit fast (#32732) (#32745)
Backport #32732 by @lunny

Fix #20156

We reuse the code from the repository code view instead of the current
code.
Previously it took `5653ms` for
https://gitea.com/henri/wiki/wiki/?action=_pages in my local machine,
now it's about `300ms` .

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-07 02:54:29 +08:00
silverwind
063655c391 Bump relative-time-element to v4.4.4 (#32739)
Backport https://github.com/go-gitea/gitea/pull/32730 to v1.22
2024-12-06 15:39:45 +01:00
Giteabot
eee16e433c Fix fork page branch selection (#32711) (#32725)
Backport #32711 by @lunny

Fix #32709

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-05 18:25:14 +00:00
KN4CK3R
0c4c28bc29 Add Swift login endpoint (#32693) (#32701)
Backport of #32693

Fix #32683

This PR adds the login endpoint and fixes the documentation links.
2024-12-06 01:53:55 +08:00
Giteabot
d8ad9228ca Fix gogit GetRefCommitID (#32705) (#32712)
Backport #32705 by @Zettat123

Fix #32335

When we call `GetRefCommitID` and the reference is already a commit ID,
the `GetRefCommitID` with go-git will return a `NotExist` error. This PR
improves the `GetRefCommitID` for go-git. If the input is already a
commit ID, it will be returned directly.

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-12-04 07:59:48 +00:00
Giteabot
0d1fc2b2e9 Fix delete branch perm checking (#32654) (#32707)
Backport #32654 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-12-04 13:25:35 +08:00
Giteabot
a332805f6e Fix word overflow in file search page (#32695) (#32699)
Backport #32695 by yp05327

Co-authored-by: yp05327 <576951401@qq.com>
2024-12-04 08:19:43 +08:00
Giteabot
4b73e92264 Fix race condition in mermaid observer (#32599) (#32673)
Backport #32599 by william-allspice
2024-11-29 19:44:41 +08:00
Giteabot
27489f2e0b Don't create action when syncing mirror pull refs (#32659) (#32664)
Backport #32659 by @lunny

Fix #27961

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-29 02:13:16 +08:00
Lunny Xiao
293355777f Add release note for v1.22.4 (#32513)
Add release note for v1.22.4

---------

Co-authored-by: Kyle D. <kdumontnu@gmail.com>
2024-11-26 03:01:54 +08:00
Lunny Xiao
cf1a38b03d Fix get reviewers' bug (#32415) (#32616)
This PR rewrites `GetReviewer` function and move it to service layer.

Reviewers should not be watchers, so that this PR removed all watchers
from reviewers. When the repository is under an organization, the pull
request unit read permission will be checked to resolve the bug of

Fix #32394
Backport #32415
2024-11-23 12:42:58 +08:00
Lunny Xiao
073ba977fc Fix clean tmp dir (#32360) (#32593)
Backport #32360 

Try to fix #31792 

Credit to @jeroenlaylo
Copied from
https://github.com/go-gitea/gitea/issues/31792#issuecomment-2311920520

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-11-22 08:50:35 +00:00
Lunny Xiao
2b8b2772fd Fix PR creation on forked repositories (#31863) (#32591)
Resolves #20475
Backport #31863

Co-authored-by: Job <LordChunk@users.noreply.github.com>
2024-11-22 08:12:40 +00:00
Lunny Xiao
87ceecfb3a Fix the missing menu in organization project view page (#32313) (#32592)
Backport #32313 

#29248 didn't modify the view page.
The class name is not good enough, so this is a quick fix.

Before:
org:

![image](https://github.com/user-attachments/assets/3e26502d-66b4-4043-ab03-003ba7391487)
user:

![image](https://github.com/user-attachments/assets/9b22b90c-d63c-4228-acad-4d9fb20590ac)

After:
org:

![image](https://github.com/user-attachments/assets/21bf98a7-8a5b-4dc6-950a-88f529e36450)
user: (no change)

![image](https://github.com/user-attachments/assets/fea0dcae-3625-44e8-bb9e-4c3733da8764)

Co-authored-by: yp05327 <576951401@qq.com>
2024-11-22 01:50:34 +00:00
Lunny Xiao
c2598b4642 Support HTTP POST requests to /userinfo, aligning to OpenID Core specification (#32578) (#32594) 2024-11-21 07:22:18 -08:00
wxiaoguang
a290aab0e8 Fix debian package clean up (#32351) (#32590)
Partially backport #32351
2024-11-21 06:27:02 +00:00
Giteabot
8f6cc95734 Fix GetInactiveUsers (#32540) (#32588)
Backport #32540 by @lunny

Fix #31480

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-21 13:25:36 +08:00
Rowan Bohde
0b5da27570 allow the actions user to login via the jwt token (#32527) (#32580)
Backport #32527

We have some actions that leverage the Gitea API that began receiving
401 errors, with a message that the user was not found. These actions
use the `ACTIONS_RUNTIME_TOKEN` env var in the actions job to
authenticate with the Gitea API. The format of this env var in actions
jobs changed with go-gitea/gitea/pull/28885 to be a JWT (with a
corresponding update to `act_runner`) Since it was a JWT, the OAuth
parsing logic attempted to parse it as an OAuth token, and would return
user not found, instead of falling back to look up the running task and
assigning it to the actions user.

Make ACTIONS_RUNTIME_TOKEN in action runners could be used, attempting
to parse Oauth JWTs. The code to parse potential old
`ACTION_RUNTIME_TOKEN` was kept in case someone is running an older
version of act_runner that doesn't support the Actions JWT.
2024-11-21 03:18:00 +00:00
wxiaoguang
81ec66c257 Fix submodule parsing (#32571) (#32577)
A quick fix for #32568
Partially backport from #32571
2024-11-21 10:32:19 +08:00
Giteabot
3661b14d97 Remove unnecessary code (#32560) (#32567)
Backport #32560 by @lunny

PushMirrors only be used in the repository setting page. So it should
not be loaded on every repository page.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-20 02:55:59 +08:00
Lunny Xiao
cf2d332443 Refactor find forks and fix possible bugs that weak permissions check (#32528) (#32547)
Backport #32528

- Move models/GetForks to services/FindForks
- Add doer as a parameter of FindForks to check permissions
- Slight performance optimization for get forks API with batch loading
of repository units
- Add tests for forking repository to organizations

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-11-19 04:08:32 +00:00
Giteabot
1b7031c5c2 Fix some places which doesn't repsect org full name setting (#32243) (#32550)
Backport #32243 by @lunny

Partially fix #31345

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-19 02:49:29 +00:00
Lunny Xiao
673fee427e Refactor push mirror find and add check for updating push mirror (#32539) (#32549)
backport #32539

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-11-18 23:55:27 +08:00
wxiaoguang
578c02d652 Improve some sanitizer rules (#32534)
This is a backport-only fix for 1.22

1.23 has a proper fix #32533
2024-11-18 03:42:30 +00:00
Giteabot
6555cfcac3 Fix basic auth with webauthn (#32531) (#32536)
Backport #32531 by @lunny

WebAuthn should behave the same way as TOTP. When enabled, basic auth
with username/password should need to WebAuthn auth, otherwise returned
401.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-16 18:21:00 +00:00
Giteabot
b6eef34874 Fix artifact v4 upload above 8MB (#31664) (#32523) 2024-11-16 09:15:33 -08:00
Giteabot
d03dd04d65 Remove transaction for archive download (#32186) (#32520)
Backport #32186 by @lunny

Since there is a status column in the database, the transaction is
unnecessary when downloading an archive. The transaction is blocking
database operations, especially with SQLite.

Replace #27563

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-15 10:27:38 +01:00
Giteabot
257ce61023 Fix oauth2 error handle not return immediately (#32514) (#32516)
Backport #32514 by lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-15 11:27:04 +08:00
Lunny Xiao
781310df77 Trim title before insert/update to database to match the size requirements of database (#32498) (#32507) 2024-11-14 18:06:31 -08:00
Giteabot
f79f8e13e3 Fix nil panic if repo doesn't exist (#32501) (#32502)
Backport #32501 by wxiaoguang

fix  #32496

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-11-14 04:47:04 +00:00
Giteabot
a4263d341c Add a doctor check to disable the "Actions" unit for mirrors (#32424) (#32497)
Backport #32424 by @Zettat123

Resolve #32232

Users can disable the "Actions" unit for all mirror repos by running 
```
gitea doctor check --run  disable-mirror-actions-unit --fix
```

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-11-13 18:47:56 +00:00
6543
52a66d78d4 Update nix development environment vor v1.22.x (#32495)
just bump:

 * golang:  v1.22.2 ->  v1.22.9
 * nodejs: v20.12.2 -> v20.18.0
 * python: v3.12.3 -> v3.12.7
2024-11-13 12:40:52 -05:00
wxiaoguang
ef339713c2 Refactor internal routers (partial backport, auth token const time comparing) (#32473) (#32479)
Partially backport #32473. LFS related changes are not in 1.22, so skip
them.

1. Ignore non-existing repos during migrations
2. Improve ReadBatchLine's comment
3. Use `X-Gitea-Internal-Auth` header for internal API calls and make
the comparing constant time (it wasn't a serous problem because in a
real world it's nearly impossible to timing-attack the token, but indeed
security related and good to fix and backport)
4. Fix route mock nil check
2024-11-13 10:26:37 +08:00
wxiaoguang
26437a03b0 Disable Oauth check if oauth disabled (#32368) (#32480)
Partially backport Disable Oauth check if oauth disabled #32368
2024-11-12 06:09:47 +00:00
Giteabot
b48df1082e cargo registry - respect renamed dependencies (#32430) (#32478)
Backport #32430 by usbalbin

Co-authored-by: Albin Hedman <albin9604@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-11-12 03:26:26 +00:00
Giteabot
eb5733636b Fix broken releases when re-pushing tags (#32435) (#32449)
Backport #32435 by @Zettat123

Fix #32427

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-11-10 23:49:59 +00:00
Giteabot
62d8433194 Fix mermaid diagram height when initially hidden (#32457) (#32464)
Backport #32457 by @silverwind

In a hidden iframe, `document.body.clientHeight` is not reliable. Use
`IntersectionObserver` to detect the visibility change and update the
height there.

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

<img width="885" alt="image"
src="https://github.com/user-attachments/assets/a95ef6aa-27e7-443f-9d06-400ef27919ae">

Co-authored-by: silverwind <me@silverwind.io>
2024-11-11 04:05:42 +08:00
Giteabot
22a93c1cdc Only provide the commit summary for Discord webhook push events (#32432) (#32447)
Backport #32432 by @kemzeb

Resolves #32371.

#31970 should have just showed the commit summary, but
`strings.SplitN()` was misused such that we did not perform any
splitting at all and just used the message. This was not caught in the
unit test made in that PR since the test commit summary was > 50 (which
truncated away the commit description).

This snapshot resolves this and adds another unit test to ensure that we
only show the commit summary.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-11-08 09:13:49 +08:00
Lunny Xiao
16e51e91a1 Only query team tables if repository is under org when getting assignees (#32414) (#32426)
backport #32414 

It's unnecessary to query the team table if the repository is not under
organization when getting assignees.
2024-11-06 11:22:11 +08:00
wxiaoguang
936847b3da Quick fix milestone deadline 9999 for 1.22 (#32423) 2024-11-05 14:13:19 +08:00
Lunny Xiao
7430d069b3 Fix created_unix for mirroring (#32342) (#32406)
Fix #32233
Backport #32342
2024-11-05 11:43:30 +08:00
Lunny Xiao
a3b7b98336 Fix broken image when editing comment with non-image attachments (#32319) (#32345)
Backport #32319 

Fix #32316

---------

Co-authored-by: yp05327 <576951401@qq.com>
2024-11-02 13:34:09 +08:00
Zettat123
898f852d03 Fix missing signature key error when pulling Docker images with SERVE_DIRECT enabled (#32365) (#32397)
Backport #32365

Fix #28121

I did some tests and found that the `missing signature key` error is
caused by an incorrect `Content-Type` header. Gitea correctly sets the
`Content-Type` header when serving files.


348d1d0f32/routers/api/packages/container/container.go (L712-L717)
However, when `SERVE_DIRECT` is enabled, the `Content-Type` header may
be set to an incorrect value by the storage service. To fix this issue,
we can use query parameters to override response header values.

https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html <img
width="600px"

src="https://github.com/user-attachments/assets/f2ff90f0-f1df-46f9-9680-b8120222c555"
/>

In this PR, I introduced a new parameter to the `URL` method to support
additional parameters.

```
URL(path, name string, reqParams url.Values) (*url.URL, error)
```
2024-11-01 03:53:59 +00:00
6543
9d62d7a443 Respect UI.ExploreDefaultSort setting again (#32357) (#32385)
Backport #32357

fix regression of https://github.com/go-gitea/gitea/pull/29430

---
*Sponsored by Kithara Software GmbH*
2024-10-31 13:49:09 +08:00
Lunny Xiao
bf53ab26fa Fix disable 2fa bug (#32320) (#32330)
Backport #32320
2024-10-25 17:54:56 +08:00
Zettat123
0d11ba93dd Fix the permission check for user search API and limit the number of returned users for /user/search (#32310)
Partially backport #32288

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-10-23 04:56:13 +00:00
Lunny Xiao
b7d12347f3 Add warn log when deleting inactive users (#32318) (#32321)
Backport #32318 

Add log for the problem #31480
2024-10-23 10:48:42 +08:00
6543
b6f8372d7d API: enhance SearchIssues swagger docs (#32208) (#32298)
Backport  #32208

This will result in better api clients generated out of the openapi docs for SearchIssues

---
*Sponsored by Kithara Software GmbH*
2024-10-21 08:32:34 +08:00
YR Chen
0c12252c23 Update github.com/go-enry/go-enry to v2.9.1 (#32295) (#32296)
Backport #32295

`go-enry` v2.9.1 includes latest file patterns from Linguist, which can
identify more generated file type, eg. `pdm.lock`.
2024-10-21 02:12:51 +08:00
Zettat123
99cac1f50c Always update expiration time when creating an artifact (#32281) (#32285)
Backport #32281

Fix #32256
2024-10-18 10:36:23 +08:00
a1012112796
2a99607add make show stats work when only one file changed (#32244) (#32268)
Backport #32244

fix https://github.com/go-gitea/gitea/issues/32226

in https://github.com/go-gitea/gitea/pull/27775 , it do some changes to
only show diff file tree when more than one file changed. But looks it
also break the `diff-file-list` logic, which looks not expected change.
so try fix it.

/cc @silverwind

example view:

![image](https://github.com/user-attachments/assets/281e9c4f-a269-4d36-94eb-a132058aea87)

Signed-off-by: a1012112796 <1012112796@qq.com>
2024-10-17 08:03:21 +00:00
cloudchamb3r
c1023b97aa [v1.22 backport] Fix null errors on conversation holder (#32258) (#32266) (#32282)
Backport #32266

fix #32258

Errors in the issue was due to unhandled null check. so i fixed it.

### Detailed description for Issue & Fix
To reproduce that issue, the comment must be deleted on Conversation
tab.
#### Before Delete
<img width="1032" alt="image"

src="https://github.com/user-attachments/assets/72df61ba-7db6-44c9-bebc-ca1178dd27f1">

#### After Delete (AS-IS)
<img width="1010" alt="image"

src="https://github.com/user-attachments/assets/36fa537e-4f8e-4535-8d02-e538c50f0dd8">

gitea already have remove logic for `timeline-item-group`, but because
of null ref exception the later logic that removes `timeline-item-group`
could be not be called correctly.
2024-10-17 13:34:39 +08:00
wxiaoguang
7e0fd4c208 Warn users when they try to use a non-root-url to sign in/up (#32272) (#32273) 2024-10-17 09:01:44 +08:00
wxiaoguang
db7349bc0d Make owner/repo/pulls handlers use "PR reader" permission (#32254) (#32265)
Backport #32254 (no conflict)
2024-10-15 22:32:54 +08:00
Zettat123
55562f9c79 Update scheduled tasks even if changes are pushed by "ActionsUser" (#32246) (#32252)
Backport #32246

Fix #32219

Co-authored-by: delvh <dev.lh@web.de>
2024-10-14 16:55:16 +08:00
Giteabot
24b65f122a Only rename a user when they should receive a different name (#32247) (#32249)
Backport #32247 by @lunny

Fix #31996

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-10-13 19:27:37 +00:00
Giteabot
bcfe1f91d2 Fix dropdown content overflow (#31610) (#32250)
Backport #31610 by charles7668

close #31602 

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-10-13 03:46:55 +00:00
Giteabot
f15d5f0c4a Fix checkbox bug on private/archive filter (#32236) (#32240)
Backport #32236 by cloudchamb3r

fix #32235

Co-authored-by: cloudchamb3r <jizon0123@protonmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-10-11 21:13:09 +08:00
Lunny Xiao
494017e478 Add release note for v1.22.3 (#32205)
Co-authored-by: sebastian-sauer <sauer.sebastian@gmail.com>
2024-10-09 11:23:48 +08:00
Lunny Xiao
56051d9b3b Fix bug when a token is given public only (#32204) (#32218)
Backport #32204
2024-10-09 02:16:37 +00:00
Giteabot
4815c4aeae Add null check for responseData.invalidTopics (#32212) (#32217)
Backport #32212 by @cloudchamb3r

<img width="553" alt="Screenshot 2024-10-08 at 10 49 10 AM"
src="https://github.com/user-attachments/assets/faeef64d-684a-4aba-b7fc-c7c6a0301abe">

`responseData.invalidTopics` can be null but it wasn't handled.

Co-authored-by: cloudchamb3r <jizon0123@protonmail.com>
2024-10-09 09:18:29 +08:00
Lunny Xiao
2e3a191097 Fix javascript error when an anonymous user visiting migration page (#32144) (#32179)
backport #32144

This PR fixes javascript errors when an anonymous user visits the
migration page.
It also makes task view checking more restrictive.

The router moved from `/user/task/{id}/status` to
`/username/reponame/-/migrate/status` because it's a migrate status.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-10-04 17:58:04 +00:00
Giteabot
361221c531 Fixed race condition when deleting documents by repoId in ElasticSearch (#32185) (#32188)
Backport #32185 by @bsofiato

Resolves #32184

Signed-off-by: Bruno Sofiato <bruno.sofiato@gmail.com>
Co-authored-by: Bruno Sofiato <bruno.sofiato@gmail.com>
2024-10-03 16:33:26 +00:00
Lunny Xiao
d86433cce2 Don't init singing keys if oauth2 provider disabled (#32177)
Backport #32148
2024-10-03 11:34:56 -04:00
Lunny Xiao
85897f9b28 Upgrade some dependencies include minio-go (#32166)
backport #32156

Co-authored-by: Manuel Valls Fernández <manuel@valls.dev>
2024-10-03 02:08:06 +00:00
Giteabot
4703e5270f Ensure GetCSRF doesn't return an empty token (#32130) (#32157)
Backport #32130 by @wolfogre

Since page templates keep changing, some pages that contained forms with
CSRF token no longer have them.

It leads to some calls of `GetCSRF` returning an empty string, which
fails the tests. Like


3269b04d61/tests/integration/attachment_test.go (L62-L63)

The test did try to get the CSRF token and provided it, but it was
empty.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-10-01 05:27:37 +00:00
Giteabot
9fc3915e04 Fix the logic of finding the latest pull review commit ID (#32139) (#32165)
Backport #32139 by @Zettat123

Fix #31423

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-10-01 13:10:03 +09:00
Lunny Xiao
a4a6c785b4 Don't join repository when loading action table data (#32127) (#32143)
backport #32127
2024-09-30 11:04:08 +08:00
Giteabot
634454c48c Fix wrong status of Set up Job when first step is skipped (#32120) (#32125)
Backport #32120 by @yp05327

Fix #32089

Co-authored-by: yp05327 <576951401@qq.com>
2024-09-25 10:19:35 +08:00
Zettat123
737c947287 Fix bug in getting merged pull request by commit (#32079) (#32117)
Backport #32079

Fix #32027
2024-09-25 00:12:02 +08:00
Giteabot
1ef74004a2 Fix bug when deleting a migrated branch (#32075) (#32123)
Backport #32075 by @lunny

After migrating a repository with pull request, the branch is missed and
after the pull request merged, the branch cannot be deleted.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 17:01:05 +08:00
Giteabot
5c73da7c54 Include collaboration repositories on dashboard source/forks/mirrors list (#31946) (#32122)
Backport #31946 by @lunny

Fix #13489

In the original implementation, only `All` will display your owned and
collaborated repositories. For other filters like `Source`, `Mirrors`
and etc. will only display your owned repositories.

This PR removed the limitations. Now except `collbrations`, other
filters will always display your owned and collaborated repositories.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 15:17:30 +08:00
Giteabot
a32aaf4d43 Truncate commit message during Discord webhook push events (#31970) (#32121)
Backport #31970 by @kemzeb

Resolves #31668.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-09-24 13:28:01 +08:00
Giteabot
0f834f052b Allow set branch protection in an empty repository (#32095) (#32119)
Backport #32095 by @lunny

Resolve #32093

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 11:42:52 +09:00
Giteabot
a3c660f89a Fix panic when cloning with wrong ssh format. (#32076) (#32118)
Backport #32076 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 01:58:58 +00:00
Giteabot
d5d5fb1925 Fix Bug in Issue/pulls list (#32081) (#32115) 2024-09-24 01:26:10 +00:00
Giteabot
ae37f31df6 use rebuilt mssql-2017 image (#32109) (#32114)
Backport #32109 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-09-23 21:23:04 +00:00
Giteabot
1f8cbbab3d Fix rename branch permission bug (#32066) (#32108)
Backport #32066 by @lunny

The previous implementation requires admin permission to rename branches
which should be write permission.

Fix #31993

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-22 19:43:13 +00:00
Giteabot
af0cab23ea Fix wrong last modify time (#32102) (#32104)
Backport #32102 by @lunny

Fix #31930 and more places which use `http.TimeFormat` wrongly.
`http.TimeFormat` requires a UTC time. refer to
https://pkg.go.dev/net/http#TimeFormat

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-22 19:12:57 +00:00
Giteabot
73066e3f97 Add bin to Composer Metadata (#32099) (#32106)
Backport #32099 by @maantje

This PR addresses the missing `bin` field in Composer metadata, which
currently causes vendor-provided binaries to not be symlinked to
`vendor/bin` during installation.

In the current implementation, running `composer install` does not
publish the binaries, leading to issues where expected binaries are not
available.

By properly declaring the `bin` field, this PR ensures that binaries are
correctly symlinked upon installation, as described in the [Composer
documentation](https://getcomposer.org/doc/articles/vendor-binaries.md).

Co-authored-by: Jamie Schouten <j4mie@hey.com>
2024-09-22 18:42:02 +00:00
Giteabot
919b82461a Fix incorrect /tokens api (#32085) (#32092)
Backport #32085 by @KN4CK3R

Fixes #32078

- Add missing scopes output.
- Disallow empty scope.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-22 18:02:09 +00:00
Lunny Xiao
69ba37e9fd Fix mssql ci with a new mssql version on ci (#32094)
backport from https://github.com/go-gitea/gitea/pull/32060
2024-09-23 01:32:26 +08:00
Giteabot
ea9e09abe5 Fix: database not update release when using git push --tags --force (#32040) (#32074)
Backport #32040 by @ExplodingDragon

link: https://codeberg.org/forgejo/forgejo/issues/4274

Co-authored-by: Exploding Dragon <explodingfkl@gmail.com>
2024-09-19 07:57:28 +08:00
wxiaoguang
2891edbbcb Refactor CSRF protector (#32057) (#32069)
#32057 improves the CSRF handling and is worth to backport
2024-09-18 17:02:45 +00:00
Giteabot
8dbe83d205 Add missing comment reply handling (#32050) (#32065)
Backport #32050 by @KN4CK3R

Fixes #31937

- Add missing comment reply handling
- Use `onGiteaRun` in the test because the fixtures are not present
otherwise (did this behaviour change?)

Compare without whitespaces.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-18 09:23:28 +00:00
Giteabot
2831ae369e Lazy load avatar images (#32051) (#32063)
Backport #32051 by @hiifong

Before:

https://github.com/user-attachments/assets/7b1681ba-4781-432a-ae20-c07e94c1dbb6

After:

https://github.com/user-attachments/assets/5154e160-e22c-460e-b0d9-28768486c178

Co-authored-by: hiifong <i@hiif.ong>
2024-09-18 16:52:44 +08:00
Giteabot
e6395e1e81 Handle invalid target when creating releases using API (#31841) (#32043)
Backport #31841 by @kemzeb

A 500 status code was thrown when passing a non-existent target to the
create release API. This snapshot handles this error and instead throws
a 404 status code.

Discovered while working on #31840.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-09-17 02:23:40 +00:00
Giteabot
8a39a4812f Do not escape relative path in RPM primary index (#32038) (#32054)
Backport #32038 by @KN4CK3R

Fixes #32021

Do not escape the relative path.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-09-17 08:19:35 +08:00
Giteabot
3d7d0c36e7 Check if the due_date is nil when editing issues (#32035) (#32042)
Backport #32035 by @Zettat123

Fix #32030

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-09-15 01:31:34 +08:00
Lunny Xiao
30d989d411 Fix container parallel upload bugs (#32022)
This PR should be replaced by #31860 in v1.23. The aim of creating this
PR is to fix it in 1.22 because globallock hasn't been introduced.

Fix #27640
Fix #29563
Fix #31215
2024-09-12 03:11:03 +00:00
Giteabot
b3af359cc6 Fix /repos/{owner}/{repo}/pulls/{index}/files endpoint not populating previous_filename (#32017) (#32028)
Backport #32017 by @charles-plutohealth

---
`status == "rename"` should have read `status == "renamed"`. The typo
means that file.PreviousFilename would never be populated, which e.g.
breaks usage of the Github Action at
https://github.com/dorny/paths-filter.

Co-authored-by: charles-plutohealth <143208583+charles-plutohealth@users.noreply.github.com>
2024-09-12 08:58:43 +09:00
Giteabot
0629c08a6d Support allowed hosts for migrations to work with proxy (#32025) (#32026)
Backport #32025 by @wolfogre

Fix #32024. Follow #27655.

After this PR, all usage of "new dial context" needs to provide a proxy,
so I dropped the old `NewDialContext` and renamed
`NewDialContextWithProxy` to `NewDialContext`.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-09-11 14:54:19 +08:00
Giteabot
54d828f8ec Increase cacheContextLifetime to reduce false reports (#32011) (#32023)
Backport #32011 by @wolfogre

Replace #32001.

To prevent the context cache from being misused for long-term work
(which would result in using invalid cache without awareness), the
context cache is designed to exist for a maximum of 10 seconds. This
leads to many false reports, especially in the case of slow SQL.

This PR increases it to 5 minutes to reduce false reports.

5 minutes is not a very safe value, as a lot of changes may have
occurred within that time frame. However, as far as I know, there has
not been a case of misuse of context cache discovered so far, so I think
5 minutes should be OK.

Please note that after this PR, if warning logs are found again, it
should get attention, at that time it can be almost 100% certain that it
is a misuse.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-09-11 11:14:40 +08:00
Lunny Xiao
6d4dfcd187 Upgrade cache to v0.2.1 (#32003) (#32009)
Fix #31757
Backport #32003
2024-09-07 21:44:28 +00:00
Giteabot
f7f4256c82 Use forum.gitea.com instead of old URL (#31989) (#31992)
Backport #31989 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-07 03:47:23 +08:00
Lunny Xiao
5e36e9f5a7 Add changelog for 1.22.2 (#31935) 2024-09-06 00:16:54 +08:00
Giteabot
b39aa8528b Fix nuget/conan/container packages upload bugs (#31967) (#31982)
Backport #31967 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-05 07:34:41 +00:00
Lunny Xiao
244fb11c6b Replace v-html with v-text in search inputbox (#31966) (#31973) (#31975)
Backport #31966, #31973
Cherry-pick 30da734f37,
74b1c589c6
Replace #31972

---------

Co-authored-by: techknowlogick <techknowlogick@noreply.gitea.com>
2024-09-05 01:59:57 +00:00
Lunny Xiao
9c990ac043 Add lock for parallel maven upload (#31954)
Backport #31851 
Fix #30171
2024-09-03 14:33:28 +08:00
Lunny Xiao
d3b0bc22c0 Fix index too many file names bug (#31903) (#31953)
Try to fix #31884
Fix #28584 
Backport #31903
2024-09-03 01:15:30 +00:00
Giteabot
6f5748c507 Prevent update pull refs manually and will not affect other refs update (#31931) (#31955)
Backport #31931 by @lunny

All refs under `refs/pull` should only be changed from Gitea inside but
not by pushing from outside of Gitea.
This PR will prevent the pull refs update but allow other refs to be
updated on the same pushing with `--mirror` operations.

The main changes are to add checks on `update` hook but not
`pre-receive` because `update` will be invoked by every ref but
`pre-receive` will revert all changes once one ref update fails.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-02 18:28:00 +08:00
yp05327
cc1520221a Fix sort order for organization home and user profile page (#31921) (#31922)
Backport #31921
2024-09-02 07:58:18 +00:00
Giteabot
b5500cded1 Fix 500 error when state params is set when editing issue/PR by API (#31880) (#31952)
Backport #31880 by @yp05327

A quick fix for #31871

Co-authored-by: yp05327 <576951401@qq.com>
2024-09-01 18:38:10 +00:00
Lunny Xiao
0de69c26ec Upgrade micromatch to 4.0.8 (#31944)
backport #31939
2024-08-30 10:36:49 +08:00
silverwind
24e8825f1f Update webpack to 5.94.0 (#31941)
Update webpack on v1.22 branch because of
https://github.com/go-gitea/gitea/security/dependabot/70.
2024-08-29 16:10:25 +00:00
Giteabot
1d98d4e69a Fix search team (#31923) (#31942)
Backport #31923 by @lunny

Fix #20658

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-30 00:05:21 +08:00
Giteabot
b140f647fc Remove "dsa-1024" testcases from Test_SSHParsePublicKey and Test_calcFingerprint (#31905) (#31914)
Backport #31905 by @s4uliu5

DSA is considered inherently insecure and is already disabled/removed in
OpenSSH 9.8.

Therefore "dsa-1024" tescases are failing.

```
--- FAIL: Test_calcFingerprint (0.02s)
    --- FAIL: Test_calcFingerprint/dsa-1024 (0.00s)
        --- FAIL: Test_calcFingerprint/dsa-1024/SSHKeygen (0.00s)
            ssh_key_test.go:196:
                        Error Trace:    /src/gitea/models/asymkey/ssh_key_test.go:196
                        Error:          Received unexpected error:
                                        Unable to verify key content [result: /tmp/gitea_keytest1239408114 is not a public key file.
                                        ]
                        Test:           Test_calcFingerprint/dsa-1024/SSHKeygen
            ssh_key_test.go:197:
                        Error Trace:    /src/gitea/models/asymkey/ssh_key_test.go:197
                        Error:          Not equal:
                                        expected: "SHA256:fSIHQlpKMDsGPVAXI8BPYfRp+e2sfvSt1sMrPsFiXrc"
                                        actual  : ""

                                        Diff:
                                        --- Expected
                                        +++ Actual
                                        @@ -1 +1 @@
                                        -SHA256:fSIHQlpKMDsGPVAXI8BPYfRp+e2sfvSt1sMrPsFiXrc
                                        +
                        Test:           Test_calcFingerprint/dsa-1024/SSHKeygen
FAIL
```

Fix #31624

Co-authored-by: Saulius Gurklys <s4uliu5@gmail.com>
2024-08-25 20:39:00 +08:00
Lunny Xiao
e060ae88e5 Don't return 500 if mirror url contains special chars (#31859) (#31895)
Backport #31859
2024-08-22 00:10:50 +08:00
Lunny Xiao
d9c65c9a52 Upgrade bleve to 2.4.2 (#31894)
backport #31762
2024-08-21 05:13:59 +00:00
Lunny Xiao
96de5c2a9f bug fix for translation in ru (#31892)
Fix #31891
2024-08-21 10:01:36 +08:00
Giteabot
e536d18fe5 Refactor the usage of batch catfile (#31754) (#31889)
Backport #31754 by @lunny

When opening a repository, it will call `ensureValidRepository` and also
`CatFileBatch`. But sometimes these will not be used until repository
closed. So it's a waste of CPU to invoke 3 times git command for every
open repository.

This PR removed all of these from `OpenRepository` but only kept
checking whether the folder exists. When a batch is necessary, the
necessary functions will be invoked.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-21 01:55:14 +08:00
Giteabot
a0d1630700 Fix agit automerge (#31207) (#31881)
Backport #31207 by @lunny

Fix #31134

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-20 16:20:58 +00:00
Giteabot
0affb5c775 add CfTurnstileSitekey context data to all captcha templates (#31874) (#31876)
Backport #31874 by @bohde

In the OpenID flows, the "CfTurnstileSitekey" wasn't populated, which
caused those flows to fail if using Turnstile as the Captcha
implementation.

This adds the missing context variables, allowing Turnstile to be used
in the OpenID flows.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
2024-08-20 14:45:08 +00:00
Giteabot
3913ef69d5 Fix actions notify bug (#31866) (#31875)
Backport #31866 by @lunny

Try to fix
https://github.com/go-gitea/gitea/issues/31757#issuecomment-2295131062

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-20 02:14:29 +08:00
sillyguodong
5d2afc6e4f fix the component of access token list not mounted (#31824) (#31868) 2024-08-19 12:56:17 -04:00
Giteabot
fe9a631939 Fix overflowing content in action run log (#31842) (#31853)
Backport #31842 by @Adrian-Hirt

When a long line with characters such as dots is returned by a step in
an action (e.g. by the output of the Ruby on Rails test runner), it
overflows the log container, causing the page to scroll sideways (see
first screenshot):


![before](https://github.com/user-attachments/assets/d71a8446-2c81-42d7-ad20-92514884365a)

This PR adds the CSS `overflow-wrap: anywhere;` to the
`.job-step-section .job-step-logs .job-log-line .log-msg` selector,
which causes such lines to wrap as well (see second screenshot in which
the line wraps nicely):


![after](https://github.com/user-attachments/assets/ba9abaec-dc0b-4fab-8129-b9341d4bf784)

Co-authored-by: Adrian Hirt <13788379+Adrian-Hirt@users.noreply.github.com>
2024-08-18 08:34:05 +08:00
Giteabot
3fe1f73268 Fix raw wiki links (#31825) (#31845)
Backport #31825 by @Zettat123

Fix #31395

This regression is introduced by #30273. To find out how GitHub handles
this case, I did [some
tests](https://github.com/go-gitea/gitea/issues/31395#issuecomment-2278929115).

I use redirect in this PR instead of checking if the corresponding `.md`
file exists when rendering the link because GitHub also uses redirect.
With this PR, there is no need to resolve the raw wiki link when
rendering a wiki page. If a wiki link points to a raw file, access will
be redirected to the raw link.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: yp05327 <576951401@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-17 03:19:26 +00:00
Giteabot
1cf8f69b38 Avoid returning without written ctx when posting PR (#31843) (#31848)
Backport #31843 by @wolfogre

Fix #31625.

If `pull_service.NewPullRequest` return an error which misses each `if`
check, `CompareAndPullRequestPost` will return immediately, since it
doesn't write the HTTP response, a 200 response with empty body will be
sent to clients.

```go
	if err := pull_service.NewPullRequest(ctx, repo, pullIssue, labelIDs, attachments, pullRequest, assigneeIDs); err != nil {
		if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) {
			ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error())
		} else if git.IsErrPushRejected(err) {
			// ...
			ctx.JSONError(flashError)
		} else if errors.Is(err, user_model.ErrBlockedUser) {
			// ...
			ctx.JSONError(flashError)
		} else if errors.Is(err, issues_model.ErrMustCollaborator) {
			// ...
			ctx.JSONError(flashError)
		}
		return
	}
```

Not sure what kind of error can cause it to happen, so this PR just
expose it. And we can fix it when users report that creating PRs failed
with error responses.

It's all my guess since I cannot reproduce the problem, but even if it's
not related, the code here needs to be improved.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-08-16 13:50:12 -04:00
Giteabot
771fb453a1 Add missing repository type filter parameters to pager (#31832) (#31837)
Backport #31832 by @yp05327

Fix #31807

ps: the newly added params's value will be changed.
When the first time you selected the filter, the values of params will
be `0` or `1`
But in pager it will be `true` or `false`.
So do we have `boolToInt` function?

Co-authored-by: yp05327 <576951401@qq.com>
2024-08-16 20:41:45 +08:00
Giteabot
5fa90ad9bc Fix panic of ssh public key page after deletion of auth source (#31829) (#31836)
Backport #31829 by @lunny

Fix #31730 

This PR rewrote the function `PublicKeysAreExternallyManaged` with a
simple test. The new function removed the loop to make it more readable.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-16 01:50:57 +08:00
Giteabot
b6ede69a1b Fixes for unreachable project issues when transfer repository from organization (#31770) (#31828)
Backport #31770 by @emrebdr

When transferring repositories that have issues linked to a project
board to another organization, the issues remain associated with the
original project board. This causes the columns in the project board to
become bugged, making it difficult to move other issues in or out of the
affected columns. As a solution, I removed the issue relations since the
other organization does not have this project table.

Fix for #31538

Co-authored-by: Edip Emre Bodur <emrebdr29@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
2024-08-14 09:57:23 +08:00
Giteabot
a3633b53d4 Scroll images in project issues separately from the remaining issue (#31683) (#31823)
Backport #31683 by @SimonPistache

As discussed in #31667 & #26561, when a card on a Project contains
images, they can overflow the card on its containing column. This aims
to fix this issue via snapping scrollbars.

---
Issue #31667 is open to discussion as there should be room for
improvement.

Co-authored-by: Simon Priet <105607989+SimonPistache@users.noreply.github.com>
2024-08-13 10:43:48 +08:00
Giteabot
f6f2349f8c Add :focus-visible style to buttons (#31799) (#31819)
Backport #31799 by @silverwind

Buttons now show a focus style via
[`:focus-visible`](https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible)
when the browser deems the focus to be important, like for example when
the button is focused via keyboard navigation.

<img width="492" alt="Screenshot 2024-08-07 at 22 12 51"
src="https://github.com/user-attachments/assets/060568b1-1599-4c56-bafb-b36ebb1bec35">
<img width="479" alt="image"
src="https://github.com/user-attachments/assets/885f4e10-f496-47f0-8ae5-45827ded09f8">

Co-authored-by: silverwind <me@silverwind.io>
2024-08-12 12:41:13 +08:00
Giteabot
a39fe53252 Show lock owner instead of repo owner on LFS setting page (#31788) (#31817)
Backport #31788 by @wolfogre

Fix #31784.

Before:

<img width="1648" alt="image"
src="https://github.com/user-attachments/assets/03f32545-4a85-42ed-bafc-2b193a5d8023">

After:

<img width="1653" alt="image"
src="https://github.com/user-attachments/assets/e5bcaf93-49cb-421f-aac1-5122bc488b02">

Co-authored-by: Jason Song <i@wolfogre.com>
2024-08-11 15:17:34 +00:00
Bo-Yi Wu
e563297c34 fix(api): owner ID should be zero when created repo secret (#31715) (#31811)
- Change condition to include `RepoID` equal to 0 for organization
secrets

Backport https://github.com/go-gitea/gitea/pull/31715 by @appleboy

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2024-08-10 00:21:51 +08:00
Giteabot
144648a4af Fix IsObjectExist with gogit (#31790) (#31806)
Backport #31790 by @wolfogre

Fix #31271.

When gogit is enabled, `IsObjectExist` calls
`repo.gogitRepo.ResolveRevision`, which is not correct. It's for
checking references not objects, it could work with commit hash since
it's both a valid reference and a commit object, but it doesn't work
with blob objects.

So it causes #31271 because it reports that all blob objects do not
exist.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-08-09 15:43:23 +08:00
Giteabot
8d11946d67 Fix protected branch files detection on pre_receive hook (#31778) (#31796)
Backport #31778 by @lunny

Fix #31738

When pushing a new branch, the old commit is zero. Most git commands
cannot recognize the zero commit id. To get the changed files in the
push, we need to get the first diverge commit of this branch. In most
situations, we could check commits one by one until one commit is
contained by another branch. Then we will think that commit is the
diverge point.

And in a pre-receive hook, this will be more difficult because all
commits haven't been merged and they actually stored in a temporary
place by git. So we need to bring some envs to let git know the commit
exist.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-08 03:08:30 +00:00
Giteabot
27e4b316f1 Add TAGS to TEST_TAGS and fix bugs found with gogit (#31791) (#31795)
Backport #31791 by @wolfogre

Found at
https://github.com/go-gitea/gitea/pull/31790#issuecomment-2272898915

`unit-tests-gogit` never work since the workflow set `TAGS` with
`gogit`, but the Makefile use `TEST_TAGS`.

<img width="690" alt="image"
src="https://github.com/user-attachments/assets/fb68df49-952b-42b9-8438-44200cefff43">


![image](https://github.com/user-attachments/assets/78ff88c7-3b5f-4d50-9c58-e607bf7b1a71)

This PR adds the values of `TAGS` to `TEST_TAGS`, ensuring that setting
`TAGS` is always acceptable and avoiding confusion about which one
should be set.

After this PR:

<img width="714" alt="image"
src="https://github.com/user-attachments/assets/54cc7f38-d95b-4dbc-a87c-daba63462b86">

Co-authored-by: Jason Song <i@wolfogre.com>
2024-08-07 15:58:09 +00:00
Giteabot
b1266ed182 Rename head branch of pull requests when renaming a branch (#31759) (#31774) 2024-08-04 14:37:02 +08:00
Giteabot
82003a3b47 Fix wiki revision pagination (#31760) (#31772)
Backport #31760 by @lunny

Fix #31755

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-08-04 10:45:41 +08:00
aceArt-GmbH
9d362ca8d9 Backport: Fix dates displaying in a wrong manner when we're close to the end of… (#31750)
… the month (31331)

Backport #31331

Fix #31197 on v1.22

Co-authored-by: lukas <lukas.walter@aceart.de>
2024-08-01 18:54:30 +00:00
techknowlogick
edf96fcf6a bump vue-bar-graph (#31705) (#31753)
backport vue-bar-graph bump to remove gsap dep
2024-08-01 21:34:32 +03:00
Giteabot
6203ae764a Distinguish LFS object errors to ignore missing objects during migration (#31702) (#31745)
Backport #31702 by @wolfogre

Fix #31137.

Replace #31623 #31697.

When migrating LFS objects, if there's any object that failed (like some
objects are losted, which is not really critical), Gitea will stop
migrating LFS immediately but treat the migration as successful.

This PR checks the error according to the [LFS api
doc](https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md#successful-responses).

> LFS object error codes should match HTTP status codes where possible:
> 
> - 404 - The object does not exist on the server.
> - 409 - The specified hash algorithm disagrees with the server's
acceptable options.
> - 410 - The object was removed by the owner.
> - 422 - Validation error.

If the error is `404`, it's safe to ignore it and continue migration.
Otherwise, stop the migration and mark it as failed to ensure data
integrity of LFS objects.

And maybe we should also ignore others errors (maybe `410`? I'm not sure
what's the difference between "does not exist" and "removed by the
owner".), we can add it later when some users report that they have
failed to migrate LFS because of an error which should be ignored.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-07-31 23:06:37 +08:00
Giteabot
8591c918f6 Fix the display of project type for deleted projects (#31732) (#31734)
Backport #31732 by @yp05327

Fix: #31727
After:

![image](https://github.com/user-attachments/assets/1dfb4b31-3bd6-47f7-b126-650f33f453e2)

Co-authored-by: yp05327 <576951401@qq.com>
2024-07-30 14:05:14 +08:00
Giteabot
ec467c344f Set owner id to zero when GetRegistrationToken for repo (#31725) (#31729)
Backport #31725 by @wolfogre

Fix #31707.

It's split from #31724.

Although #31724 could also fix #31707, it has change a lot so it's not a
good idea to backport it.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-07-30 09:57:43 +08:00
Giteabot
7b37f77f1a Fix API endpoint for registration-token (#31722) (#31728)
Backport #31722 by @wolfogre

Partially fix #31707. Related to #30656.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-07-29 21:15:07 +03:00
yp05327
d3f0867204 Add permission check when creating PR (#31033) (#31720)
Backport #31033

user should be a collaborator of the base repo to create a PR
2024-07-29 14:11:29 +08:00
Giteabot
7e9a895007 Make GetRepositoryByName more safer (#31712) (#31718)
Backport #31712 by @lunny

Fix #31708

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-07-29 11:52:34 +08:00
wxiaoguang
0fb1c1fbfd Fix "Filter by commit" Dropdown (#31695) (#31696)
A separate backport of #31695 for 1.22
Fix #31673
2024-07-25 09:44:34 +08:00
Giteabot
60267859fc Properly filter issue list given no assignees filter (#31522) (#31685)
Backport #31522 by @kemzeb

Quick fix #31520. This issue is related to #31337.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-07-24 15:55:21 +08:00
Giteabot
17b04644ed Enable direnv (#31672) (#31679)
Backport #31672 by @techknowlogick

This lets developers who have direnv enabled to load our nix flake
automatically when entering it

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-07-23 11:35:28 -04:00
techknowlogick
c71c95d5e5 fix redis deps (#31662) (#31663)
fix https://github.com/go-gitea/gitea/issues/31658

backports #31662
2024-07-19 20:14:58 +00:00
Giteabot
00aade2cab Fix a branch divergence cache bug (#31659) (#31661)
Backport #31659 by @Zettat123

Fix #31599
Fix #31472

A branch divergence is counted based on the default branch. If the
default branch is updated, all divergence caches of the repo need to be
deleted.

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-07-19 13:12:23 -04:00
Giteabot
c2b1b94c5a Remove unneccessary uses of word-break: break-all (#31637) (#31652)
Backport #31637 by @silverwind

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

1. Issue sidebar topic is disussed in
https://github.com/go-gitea/gitea/issues/31636
2. Org description already has `overflow-wrap: anywhere` to ensure no
overflow.

Co-authored-by: silverwind <me@silverwind.io>
2024-07-18 12:58:11 +02:00
Giteabot
c2445ae3d4 Fix: Allow org team names of length 255 in create team form (#31564) (#31603)
Backport #31564 by @tobiasbp

Gitea 1.22.1 was supposed to allow for team names of length 255 (up from
30) after the following PR was merged in:
https://github.com/go-gitea/gitea/pull/31410. However, the length of
team names was still limited to 30 as described in this issue:
https://github.com/go-gitea/gitea/issues/31554.

One more change to _gitea_ needs to be made to allow for the longer team
names, as there is a 30 character limit here:
2c92c7c522/services/forms/org.go (L65)

This PR changes that value to 255.

Co-authored-by: Tobias Balle-Petersen <tobias.petersen@unity3d.com>
2024-07-10 12:28:27 -04:00
wxiaoguang
43c63c33ae Use old behavior for telegram webhook (#31588)
Fix #31182
2024-07-09 11:23:33 +08:00
Jimmy Praet
2dc6993467 Return an empty string when a repo has no avatar in the repo API (#31187) (#31567)
Backport #31187

Resolves #31167.

https://github.com/go-gitea/gitea/pull/30885 changed the behavior of
`repo.AvatarLink()` where it can now take the empty string and append it
to the app data URL. This does not point to a valid avatar image URL,
and, as the issue mentions, previous Gitea versions returned the empty
string.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-07-05 14:40:45 +02:00
Giteabot
6486c8b7b3 Fix slow patch checking with commits that add or remove many files (#31548) (#31560)
Backport #31548 by @brechtvl

Running git update-index for every individual file is slow, so add and
remove everything with a single git command.

When such a big commit lands in the default branch, it could cause PR
creation and patch checking for all open PRs to be slow, or time out
entirely. For example, a commit that removes 1383 files was measured to
take more than 60 seconds and timed out. With this change checking took
about a second.

This is related to #27967, though this will not help with commits that
change many lines in few files.

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-07-05 11:24:01 +02:00
Lunny Xiao
35c5192b25 Add changelog for 1.22.1 (#31517)
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2024-07-04 10:17:07 +08:00
silverwind
e2905761c3 Update golang.org/x/image to v0.18.0 (#31542) 2024-07-02 19:42:16 -04:00
rayden84
50ecae7357 remove typo (#31524) 2024-06-29 15:54:15 +00:00
Giteabot
dceadd2ebe Fix avatar radius problem on the new issue page (#31506) (#31508)
Backport #31506 by charles7668

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
2024-06-27 23:34:40 +08:00
Giteabot
3cecfa3df4 use correct l10n string (#31487) (#31490)
Backport #31487 by CyberFlameGO

Co-authored-by: CyberFlame <cyberflameu@gmail.com>
2024-06-26 13:45:48 +00:00
silverwind
d9e6657879 Upgrade github.com/hashicorp/go-retryablehttp (#31499) 2024-06-26 13:15:42 +00:00
wxiaoguang
151b786837 Fix dropzone JS error when attachment is disabled (#31486)
Fix #31485
2024-06-26 18:09:25 +08:00
Giteabot
58b1ee5ebd Fix overflow menu flickering on mobile (#31484) (#31488)
Backport #31484 by brechtvl

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-26 01:13:04 +08:00
Giteabot
1825c316a6 Fix poor table column width due to breaking words (#31473) (#31477)
Backport #31473 by brechtvl

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
Co-authored-by: silverwind <me@silverwind.io>
2024-06-25 08:00:17 +08:00
Giteabot
d1dd3fa49c Fix deprecated Dockerfile ENV format (#31450) (#31452)
Backport #31450 by @silverwind

See
https://docs.docker.com/reference/build-checks/legacy-key-value-format/.
Fixes these warnings seen during the docker build:

```
 4 warnings found (use --debug to expand):
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 5)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 9)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 75)
 - LegacyKeyValueFormat: "ENV key=value" should be used instead of legacy "ENV key value" format (line 76)
 ```

Introduced in: https://github.com/moby/buildkit/pull/4923

Co-authored-by: silverwind <me@silverwind.io>
2024-06-24 00:53:37 +00:00
Giteabot
eaeb4d1b96 Fix web notification icon not updated once you read all notifications (#31447) (#31466)
Backport #31447 by kiatt210

Fix #29065
Remove status filtering from GetUIDsAndNotificationCounts sql.

Co-authored-by: kiatt210 <40639725+kiatt210@users.noreply.github.com>
Co-authored-by: kiatt210 <kiatt210@github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-24 00:08:37 +08:00
techknowlogick
688085c15e Switch to upstream of gorilla/feeds (#31400) (#31464)
backport #31400
2024-06-23 16:47:43 +08:00
Giteabot
8d60c7d568 Switch to "Write" tab when edit comment again (#31445) (#31461)
Backport #31445 by wxiaoguang

Fix #19031

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-22 23:47:36 +08:00
Giteabot
be302f3025 Support relative paths to videos from Wiki pages (#31061) (#31453)
Backport #31061 by @sergeyvfx

This change fixes cases when a Wiki page refers to a video stored in the
Wiki repository using relative path. It follows the similar case which
has been already implemented for images.

Test plan:
- Create repository and Wiki page
- Clone the Wiki repository
- Add video to it, say `video.mp4`
- Modify the markdown file to refer to the video using `<video
src="video.mp4">`
- Commit the Wiki page
- Observe that the video is properly displayed

Co-authored-by: Sergey Sharybin <sergey.vfx@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-22 00:03:31 +03:00
Giteabot
a3529d662f Fix the link for .git-blame-ignore-revs bypass (#31432) (#31442)
Backport #31432 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-21 14:47:11 +08:00
Giteabot
a4f977e00b Fix the wrong line number in the diff view page when expanded twice. (#31431) (#31440)
Backport #31431 by @charles7668

close #31149

The problem is caused by the `data-query=` string affecting the parsing
of the `last_left` parameter. When the page is first rendered,
`data-query=` does not exist. However, after clicking expand, the
returned HTML contains this string, causing the line numbers to start
from 1 upon expanding a second time.

before fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/36842bde-cc30-43ba-91b0-c60a50a6dc0f)

after fix

![圖片](https://github.com/go-gitea/gitea/assets/30816317/b73d7e40-ec01-48ae-9992-09c19ee477ad)

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
2024-06-20 20:33:51 +00:00
Giteabot
b83482b4ba Fix labels and projects menu overflow on issue page (#31435) (#31439)
Backport #31435 by brechtvl

It was correct only on the new issue page.

Resolves #31415

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-20 16:59:49 +00:00
Giteabot
9ecaeda66e [Fix] Account Linking UpdateMigrationsByType (#31428) (#31434)
Backport #31428 by Sumit189

Co-authored-by: Sumit <sumit.18.paul@gmail.com>
2024-06-20 13:53:15 +00:00
Giteabot
7fbcc58062 Fix markdown math brackets render problem (#31420) (#31430)
Backport #31420 by charles7668

Co-authored-by: charles <30816317+charles7668@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-20 17:45:08 +08:00
Giteabot
05f32114d1 Increase max length of org team names from 30 to 255 characters (#31410) (#31421)
Backport #31410 by tobiasbp

This PR modifies the structs for editing and creating org teams to allow
team names to be up to 255 characters. The previous maximum length was
30 characters.

Co-authored-by: Tobias Balle-Petersen <tobias.petersen@unity3d.com>
2024-06-20 01:27:13 +00:00
Giteabot
5c79c2b431 Fix new issue/pr avatar (#31419) (#31424)
Backport #31419 by @silverwind

The avatar on "New Issue" and "New Pull Request" pages was inconsistent.
Removed the extra margin and the new CSS rules now use common parent
`<form id="#new-issue">` because `.repository.new.issue` is not present
on pull request page.

Before:

<img width="181" alt="Screenshot 2024-06-19 at 13 56 17"
src="https://github.com/go-gitea/gitea/assets/115237/5270d352-db5b-45b3-9d06-4790c17ae9b4">
<img width="213" alt="Screenshot 2024-06-19 at 13 54 02"
src="https://github.com/go-gitea/gitea/assets/115237/012f5607-aef0-4f48-90e3-8d4022480203">


After:

<img width="195" alt="Screenshot 2024-06-19 at 13 54 16"
src="https://github.com/go-gitea/gitea/assets/115237/e7590c66-3b28-4790-9970-33bd567eeb31">
<img width="212" alt="Screenshot 2024-06-19 at 13 54 22"
src="https://github.com/go-gitea/gitea/assets/115237/8e1cfede-614c-4cea-9af2-ada6da7a7361">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-19 21:18:10 +00:00
Giteabot
3813b27862 Reduce air verbosity (#31417) (#31425)
Backport #31417 by @silverwind

Make `air` log less. Uses the option added in
https://github.com/air-verse/air/pull/367.

Co-authored-by: silverwind <me@silverwind.io>
2024-06-20 04:57:28 +08:00
wxiaoguang
042e9fcd81 Fix rendered wiki page link (#31398) (#31407)
Backport #31398

Fix #31395
2024-06-19 11:23:24 +08:00
Giteabot
e8e43a7ee4 Fix double border in system status table (#31363) (#31401)
Backport #31363 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-18 06:29:43 +08:00
Lunny Xiao
a9d1e4311e Fix bug filtering issues which have no project (#31337) (#31367) 2024-06-17 10:23:15 +00:00
Giteabot
ed0fc0ec46 Fix natural sort (#31384) (#31394)
Backport #31384 by wxiaoguang

Fix #31374

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-17 07:41:47 +00:00
wxiaoguang
fa307167f9 Fix missing images in editor preview due to wrong links (#31299) (#31393)
Backport #31299

Parse base path and tree path so that media links can be correctly
created with /media/.

Resolves #31294

---------

Co-authored-by: Brecht Van Lommel <brecht@blender.org>
2024-06-17 15:07:21 +08:00
Giteabot
3f44844244 Allow downloading attachments of draft releases (#31369) (#31380)
Backport #31369 by Zettat123

Fix #31362

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-06-16 20:55:14 +08:00
wxiaoguang
52925e9c7c Fix duplicate sub-path for avatars (#31365) (#31368)
Backport #31365, only backport necessary changes.
2024-06-15 03:44:44 +00:00
wxiaoguang
188e515efc Fix repo graph JS (#31377)
Fix #31376
Regression of #30395
2024-06-14 18:21:40 +02:00
wxiaoguang
cdd057c7a3 Backport Iif (#31353) 2024-06-13 14:52:34 +08:00
Giteabot
6d0d4640f6 Fixed incorrect localization explorer.go (#31348) (#31350)
Backport #31348 by @kerwin612

see: https://github.com/go-gitea/gitea/pull/29701/files#r1637325139

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-13 02:16:17 +00:00
Giteabot
6ca70c5bf2 Fix hash render end with colon (#31319) (#31346)
Backport #31319 by @lunny

Fix a hash render problem like `<hash>: xxxxx` which is usually used in
release notes.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-13 01:14:49 +00:00
Giteabot
95dfd945bc Fix line number widths (#31341) (#31343)
Backport #31341 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-13 00:13:35 +00:00
Giteabot
568ff1015b Fix adopt repository has empty object name in database (#31333) (#31335)
Backport #31333 by @lunny

Fix #31330
Fix #31311

A workaround to fix the old database is to update object_format_name to
`sha1` if it's empty or null.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-12 15:59:54 +00:00
Giteabot
4b6ef9265b Fix navbar + menu flashing on page load (#31281) (#31342)
Backport #31281 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-12 15:26:04 +00:00
Giteabot
b1ad8ccb73 Reduce memory usage for chunked artifact uploads to MinIO (#31325) (#31338)
Backport #31325 by @bohde

When using the MinIO storage driver for Actions Artifacts, we found that
the chunked artifact required significantly more memory usage to both
upload and merge than the local storage driver. This seems to be related
to hardcoding a value of `-1` for the size to the MinIO client [which
has a warning about memory usage in the respective
docs](https://pkg.go.dev/github.com/minio/minio-go/v7#Client.PutObject).
Specifying the size in both the upload and merge case reduces memory
usage of the MinIO client.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
Co-authored-by: Kyle D <kdumontnu@gmail.com>
2024-06-12 16:25:46 +02:00
Giteabot
758f84f33e Fix #31185 try fix lfs download from bitbucket failed (#31201) (#31329)
Backport #31201 by @Zoupers

Fix #31185

Co-authored-by: Zoupers Zou <1171443643@qq.com>
2024-06-12 02:34:37 +03:00
Giteabot
3fcf865a4b Delete legacy cookie before setting new cookie (#31306) (#31317)
Backport #31306 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-11 05:57:51 +00:00
Giteabot
c1c11aaf60 Fix line number width in code preview (#31307) (#31316)
Backport #31307 by silverwind

Line numbers were using some hacky CSS `width: 1%` that did nothing to
the code rendering as far as I can tell but broken the inline preview in
markup when line numbers are greater than 2 digits. Also I removed one
duplicate `font-family` rule (it is set below in the `.lines-num,
.lines-code` selector.

Co-authored-by: silverwind <me@silverwind.io>
2024-06-11 13:30:02 +08:00
6543
abc92df701 Add nix flake for dev shell (#30967) (#31310)
Backport #30967
2024-06-11 09:50:13 +08:00
Giteabot
1dc8a66074 Remove sub-path from container registry realm (#31293) (#31300)
Backport #31293 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-10 12:04:49 +08:00
Giteabot
bbe98a3254 Fix some URLs whose sub-path is missing (#31289) (#31292)
Backport #31289 by wxiaoguang

Fix #31285

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-07 15:43:47 +00:00
Giteabot
5ca4c6d066 Fix and clean up ConfirmModal (#31283) (#31291)
Backport #31283 by @silverwind

Bug: orange button color was removed in
https://github.com/go-gitea/gitea/pull/30475, replaced with red
Bug: translation text was not html-escaped
Refactor: Replaced as much jQuery as possible, added useful
`createElementFromHTML`
Refactor: Remove colors checks that don't exist on `.link-action`

<img width="381" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/5900bf6a-8a86-4a86-b368-0559cbfea66e">

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
2024-06-07 15:51:54 +02:00
Giteabot
75e0bdcec5 Optimize repo-list layout to enhance visual experience (#31272) (#31276)
Backport #31272 by @kerwin612

before: 

![1717655078227](https://github.com/go-gitea/gitea/assets/3371163/4d564f96-c2f8-46b1-996f-6cc7abb940ef)
***The problem was that the icon and text were not on a horizontal line,
and the horizontal was not centered;***

after: 

![1717655094071](https://github.com/go-gitea/gitea/assets/3371163/b11797f6-05f8-486c-b5fd-df89d0cbdcfd)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-07 04:37:47 +00:00
Giteabot
a918757105 fixed the dropdown menu for the top New button to expand to the left (#31273) (#31275)
Backport #31273 by @kerwin612

before: 

![1717660314025](https://github.com/go-gitea/gitea/assets/3371163/17ae7a48-31c5-4c71-b285-f65d9106bf86)

after: 

![1717660674763](https://github.com/go-gitea/gitea/assets/3371163/85f847ac-a044-4695-9004-26e6485288c6)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-07 04:19:00 +02:00
Giteabot
c07416b3d0 Fix Activity Page Contributors dropdown (#31264) (#31269)
Backport #31264 by wxiaoguang

Fix #31261

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-06 08:14:00 +00:00
Giteabot
875579cc65 Add replacement module for mholt/archiver (#31267) (#31270)
Backport #31267 by @silverwind

Switch to this fork tag:
https://github.com/anchore/archiver/releases/tag/v3.5.2 which includes
82ca88a2eb.

Ref: https://pkg.go.dev/vuln/GO-2024-2698
Ref: https://github.com/advisories/GHSA-rhh4-rh7c-7r5v

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-06 04:04:47 +00:00
Giteabot
83cf348e07 Optimize runner-tags layout to enhance visual experience (#31258) (#31263)
Backport #31258 by @kerwin612


![image](https://github.com/go-gitea/gitea/assets/3371163/b8199005-94f2-45be-8ca9-4fa1b3f221b2)

Co-authored-by: Kerwin Bryant <kerwin612@qq.com>
2024-06-06 04:35:36 +08:00
Giteabot
7cb67cfd7f fix: allow actions artifacts storage migration to complete succesfully (#31251) (#31257)
Backport #31251 by @bohde

Change the copy to use `ActionsArtifact.StoragePath` instead of the
`ArtifactPath`. Skip artifacts that are expired, and don't error if the
file to copy does not exist.

---

When trying to migrate actions artifact storage from local to MinIO, we
encountered errors that prevented the process from completing
successfully:

* The migration tries to copy the files using the per-run
`ArtifactPath`, instead of the unique `StoragePath`.
* Artifacts that have been marked expired and had their files deleted
would throw an error
* Artifacts that are pending, but don't have a file uploaded yet will
throw an error.

This PR addresses these cases, and allow the process to complete
successfully.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
2024-06-05 11:25:48 +03:00
Giteabot
1c1c2d36e8 Make blockquote attention recognize more syntaxes (#31240) (#31250)
Backport #31240 by wxiaoguang

Fix #31214

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-04 16:03:01 +00:00
Giteabot
082600a50e Fix admin oauth2 custom URL settings (#31246) (#31247)
Backport #31246 by wxiaoguang

Fix #31244

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-06-04 15:11:07 +00:00
Giteabot
5136c879c2 Make pasted "img" tag has the same behavior as markdown image (#31235) (#31243)
Backport #31235 by wxiaoguang

Fix #31230

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-04 13:26:55 +00:00
Thomas Desveaux
ca414a7ccf Fix NuGet Package API for $filter with Id equality (#31188) (#31242)
Backport #31188

Fixes issue when running `choco info pkgname` where `pkgname` is also a
substring of another package Id.

Relates to #31168

---

This might fix the issue linked, but I'd like to test it with more choco
commands before closing the issue in case I find other problems if
that's ok.
I'm pretty inexperienced with Go, so feel free to nitpick things.

Not sure I handled
[this](70f87e11b5/routers/api/packages/nuget/nuget.go (L135-L137))
in the best way, so looking for feedback on if I should fix the
underlying issue (`nil` might be a better default for `Value`?).

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-06-04 14:56:59 +03:00
Giteabot
331c32f9b6 Fix overflow on push notification (#31179) (#31238)
Backport #31179 by @silverwind

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

<img width="1301" alt="Screenshot 2024-05-30 at 14 43 24"
src="https://github.com/go-gitea/gitea/assets/115237/00443af0-088d-49a5-be9e-8c9adcc2c01d">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 10:15:25 +00:00
Giteabot
298d05df3b Remove .segment from .project-column (#31204) (#31239)
Backport #31204 by @silverwind

Using `.segment` on the project columns is a major abuse of that class,
so remove it and instead set the border-radius directly on it.

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

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 09:45:50 +00:00
Giteabot
85a8176708 Fix overflow on notifications (#31178) (#31237)
Backport #31178 by @silverwind

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

<img width="1312" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/627711ed-93ca-4be6-b958-10d673ae9517">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-04 11:17:00 +02:00
Giteabot
0b5012c6fc Update air package path (#31233) (#31236) 2024-06-04 05:28:21 +00:00
Giteabot
0328f31fdc Fix overflow in issue card (#31203) (#31225)
Backport #31203 by @silverwind

Before:

<img width="373" alt="Screenshot 2024-06-01 at 01 31 26"
src="https://github.com/go-gitea/gitea/assets/115237/82a210f2-c82e-4b7e-ac43-e70e46fa1186">

After:
<img width="376" alt="Screenshot 2024-06-01 at 01 31 32"
src="https://github.com/go-gitea/gitea/assets/115237/82d1b9f7-4fad-47bd-948a-04e1e7e006e6">

Co-authored-by: silverwind <me@silverwind.io>
2024-06-03 02:14:15 +00:00
Giteabot
33fa93a952 Fix agit checkout command line hint & fix ShowMergeInstructions checking (#31219) (#31222)
Backport #31219 by @lunny

Fix #31135 

Before
<img width="527" alt="图片"
src="https://github.com/go-gitea/gitea/assets/81045/60c07b55-3db2-4a9a-98f1-0eb72d149df2">

After
<img width="664" alt="图片"
src="https://github.com/go-gitea/gitea/assets/81045/0670ff5d-9e0b-47ac-aba4-9db9131de3b3">

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-03 09:45:57 +08:00
Giteabot
68e405cf0b Fix the possible migration failure on 286 with postgres 16 (#31209) (#31218)
Backport #31209 by @lunny

Try to fix #31205

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-06-02 10:19:30 +03:00
Giteabot
b6280f4d21 Split sanitizer functions and fine-tune some tests (#31192) (#31200)
Backport #31192 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-31 13:54:14 +00:00
Giteabot
1987c86f3c Fix URL In Gitea Actions Badge Docs (#31191) (#31198)
Backport #31191 by @TheBrokenRail

The example URL given in the documentation leads to a 404.

For instance,
`https://your-gitea-instance.com/{owner}/{repo}/actions/workflows/{workflow_file}?branch={branch}&event={event}`
translates to
`https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions/workflows/build.yml`,
which is a 404.

I had to check the [linked GitHub
docs](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge)
to learn that you have to add `/badge.svg` to the URL.

Example:
https://gitea.thebrokenrail.com/minecraft-pi-reborn/minecraft-pi-reborn/actions/workflows/build.yml/badge.svg

Co-authored-by: TheBrokenRail <17478432+TheBrokenRail@users.noreply.github.com>
2024-05-31 21:08:04 +08:00
Giteabot
c6176ee59f Fix branch order (#31174) (#31193)
Backport #31174 by @lunny

Fix #31172

The original order or the default order should not be ignored even if we
have an is_deleted order.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-31 11:34:05 +08:00
Giteabot
e8c776c793 Fix push multiple branches error with tests (#31151) (#31153)
Backport #31151 by @lunny

Fix #31140 

The previous logic is wrong when pushing multiple branches. After first
branch updated, it will ignore left other branches sync operations.

As a workaround for the repositories, just push a new commit after the
patch applied will fix the repositories status.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-30 07:04:42 +00:00
wxiaoguang
cc64d4d2b2 Ignore FindRecentlyPushedNewBranches err (#31164) (#31171)
Backport #31164
2024-05-30 14:24:14 +08:00
Giteabot
f0d55e4819 Swap word order in Comment and Close (#31148) (#31165)
Backport #31148 by @techknowlogick

Reduce accident closing of tickets only to re-open them right away. This
aligns the text on these buttons with what GitHub has.

Commit is authored by @LazyDodo, and was committed to the Blender fork
by @brechtvl

Background details:
https://projects.blender.org/infrastructure/gitea-custom/pulls/7

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-authored-by: Ray Molenkamp <github@lazydodo.com>
2024-05-30 01:07:27 +00:00
Giteabot
fb14ca30eb Fix markup preview (#31158) (#31166)
Backport #31158 by @wxiaoguang

Fix #31157

After:


![image](https://github.com/go-gitea/gitea/assets/2114189/4d918cce-cd0d-4601-9c81-4b32df1b0b38)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-30 02:38:30 +02:00
Giteabot
a672f066f7 Use vertical layout for multiple code expander buttons (#31122) (#31152)
Backport #31122 by @silverwind

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

- Now it only does a single call to `GetExpandDirection` per line
instead of multiples.
- Exposed `data-expand-direction` to frontend so it can correctly size
the buttons (it's a pain to do in tables).

<img width="142" alt="Screenshot 2024-05-27 at 20 44 56"
src="https://github.com/go-gitea/gitea/assets/115237/8b0b45a6-8e50-4081-8822-5e0775d8d941">
<img width="142" alt="Screenshot 2024-05-27 at 20 44 51"
src="https://github.com/go-gitea/gitea/assets/115237/b7ba2c57-8f55-4e9f-9606-c96d16b77892">
<img width="132" alt="Screenshot 2024-05-27 at 20 44 46"
src="https://github.com/go-gitea/gitea/assets/115237/0e838fb8-5e8c-4250-9843-a68b88d5418b">
<img width="80" alt="Screenshot 2024-05-27 at 20 44 33"
src="https://github.com/go-gitea/gitea/assets/115237/da6c7f83-c160-4389-8ab2-889d0568cbe8">
<img width="80" alt="Screenshot 2024-05-27 at 20 44 26"
src="https://github.com/go-gitea/gitea/assets/115237/cdb490b2-5040-484a-92e5-46fc5e37c199">
<img width="78" alt="Screenshot 2024-05-27 at 20 44 20"
src="https://github.com/go-gitea/gitea/assets/115237/d2978ab0-764e-41ff-922c-25f8fe749f28">

Would backport as trivial enhancement.

Co-authored-by: silverwind <me@silverwind.io>
2024-05-29 09:06:34 +02:00
Giteabot
c6189cfcb9 Add an immutable tarball link to archive download headers for Nix (#31139) (#31145)
Backport #31139 by @Mic92

This allows `nix flake metadata` and nix in general to lock a *branch*
tarball link in a manner that causes it to fetch the correct commit even
if the branch is updated with a newer version.

For further context, Nix flakes are a feature that, among other things,
allows for "inputs" that are "github:someuser/somerepo",
"https://some-tarball-service/some-tarball.tar.gz",
"sourcehut:~meow/nya" or similar. This feature allows our users to fetch
tarballs of git-based inputs to their builds rather than using git to
fetch them, saving significant download time.

There is presently no gitea or forgejo specific fetcher in Nix, and we
don't particularly wish to have one. Ideally (as a developer on a Nix
implementation myself) we could just use the generic tarball fetcher and
not add specific forgejo support, but to do so, we need additional
metadata to know which commit a given *branch* tarball represents, which
is the purpose of the Link header added here.

The result of this patch is that a Nix user can specify
`inputs.something.url =
"https://forgejo-host/some/project/archive/main.tar.gz"` in flake.nix
and get a link to some concrete tarball for the actual commit in the
lock file, then when they run `nix flake update` in the future, they
will get the latest commit in that branch.

Example of it working locally:

» nix flake metadata --refresh
'http://localhost:3000/api/v1/repos/jade/cats/archive/main.tar.gz?dir=configs/nix'
Resolved URL:
http://localhost:3000/api/v1/repos/jade/cats/archive/main.tar.gz?dir=configs/nix
Locked URL:
804ede182b.tar.gz?dir=configs
/nix&narHash=sha256-yP7KkDVfuixZzs0fsqhSETXFC0y8m6nmPLw2GrAMxKQ%3D
Description:   Computers with the nixos
Path:          /nix/store/s856c6yqghyan4v0zy6jj19ksv0q22nx-source
Revision:      804ede182b6b66469b23ea4d21eece52766b7a06
Last modified: 2024-05-02 00:48:32

For details on the header value, see:

56763ff918/doc/manual/src/protocols/tarball-fetcher.md

Co-authored-by: Jörg Thalheim <Mic92@users.noreply.github.com>
Co-authored-by: Jade Lovelace <software@lfcode.ca>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-29 01:34:14 +00:00
Giteabot
42b2541cb5 Update demo site location from try.gitea.io -> demo.gitea.com (#31054) (#31146)
Backport #31054 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-29 01:05:07 +00:00
Giteabot
9c93c6249c Remove duplicate ProxyPreserveHost in Apache httpd doc (#31143) (#31147)
Backport #31143 by @HorlogeSkynet

---

(fix up for #31003)

Co-authored-by: Samuel FORESTIER <HorlogeSkynet@users.noreply.github.com>
2024-05-29 08:54:14 +08:00
Giteabot
b615a59db8 Improve mobile review ui (#31091) (#31136)
Backport #31091 by @silverwind

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

Not perfect but much better than before.

Before: Overflows, sticky not working, filename unreadable:

<img width="506" alt="Screenshot 2024-05-27 at 02 02 40"
src="https://github.com/go-gitea/gitea/assets/115237/a06b1edf-dece-4402-98c2-68670fca265f">

After:
<img width="457" alt="Screenshot 2024-05-27 at 01 59 06"
src="https://github.com/go-gitea/gitea/assets/115237/2a282c96-e719-4554-b418-81963ae6269c">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-28 14:10:01 +00:00
Giteabot
e6bacf1fed Add missed return after ctx.ServerError (#31130) (#31133)
Backport #31130 by @lunny

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-28 12:47:11 +02:00
Giteabot
d6ae2b3c4e Fix API repository object format missed (#31118) (#31132)
Backport #31118 by @lunny

Fix #31117

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-28 09:59:56 +00:00
Giteabot
447b3e2475 Fix DashboardRepoList margin (#31121) (#31128)
Backport #31121 by @silverwind

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

<img width="476" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/ba508ba9-b02d-47c6-ad9f-495101c81330">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-28 17:12:15 +08:00
Giteabot
7ecb1d63bb Move documents under actions (#31110) (#31116)
Backport #31110 by @lunny

Move secrets and badge under actions

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-27 23:05:49 +08:00
Giteabot
b0981f6509 Fix missing memcache import (#31105) (#31109)
Backport #31105 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-27 14:23:47 +00:00
Giteabot
7f706bd171 Update pip related commands for docker (#31106) (#31111)
Backport #31106 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-27 21:54:19 +08:00
Lunny Xiao
803b0c9ab4 Add v1.22.0 changelog (#31060) 2024-05-27 07:51:53 +00:00
Giteabot
b903e2b753 Prevent tab shifting, remove extra margin on fluid pages (#31090) (#31099)
Backport #31090 by @silverwind

1. Extend concept of https://github.com/go-gitea/gitea/pull/29831 to all
tabular menus, there were only three left that weren't already
`<overflow-menu>`.

<img width="634" alt="Screenshot 2024-05-27 at 00 42 16"
src="https://github.com/go-gitea/gitea/assets/115237/d9a7e219-d05e-40a1-9e93-777f9a8a90dd">
<img width="965" alt="Screenshot 2024-05-27 at 00 29 32"
src="https://github.com/go-gitea/gitea/assets/115237/e6ed71b1-11fb-4a74-9adb-af4524286cff">

2. Remove extra padding on `fluid padded` container like for example PR
diff view. The page margin is already correctly sized via
`.ui.container`, so this was just extraneous padding that looked ugly.

Before:
<img width="1351" alt="Screenshot 2024-05-27 at 00 45 11"
src="https://github.com/go-gitea/gitea/assets/115237/4b45fd11-b1b2-4fbb-a618-26eb22be9472">

After:
<img width="1344" alt="Screenshot 2024-05-27 at 00 45 22"
src="https://github.com/go-gitea/gitea/assets/115237/d09593eb-6c7f-45e7-85b6-f0050047004b">

3. Replace `gt-word-break` with `tw-break-anywhere` in issue-title,
fixing overflow.

Before:
<img width="1333" alt="Screenshot 2024-05-27 at 00 50 14"
src="https://github.com/go-gitea/gitea/assets/115237/64d15d04-b456-401e-a972-df636965f0eb">

After:
<img width="1316" alt="Screenshot 2024-05-27 at 00 50 26"
src="https://github.com/go-gitea/gitea/assets/115237/ed1ce830-1408-414b-8263-eeaf773f52c8">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-27 07:41:10 +00:00
Giteabot
0e70f73055 Fix border radius on hovered secondary menu (#31089) (#31097)
Backport #31089 by @silverwind

Presumably a regression from
https://github.com/go-gitea/gitea/pull/30325, these menus were showing a
border radius on hover, which is fixed with this change.

<img width="154" alt="image"
src="https://github.com/go-gitea/gitea/assets/115237/eafdc1c5-3cf5-48d1-86c4-21c58f92cfaf">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-27 07:07:16 +00:00
Giteabot
e0b7938d74 Fix possible ui 500 if workflow's job is nil (#31092) (#31098)
Backport #31092 by @lunny

Fix #31087

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-27 14:13:15 +08:00
Giteabot
02b947a15f Fix View File button link if branch deleted on pull request files pages (#31063) (#31077) 2024-05-26 14:07:17 +00:00
Giteabot
7b7318255e Change --border-radius-circle to --border-radius-full (#30936) (#31078) 2024-05-26 13:16:13 +00:00
Giteabot
1171b24d52 Make gitea webhooks openproject compatible (#28435) (#31081)
Backport #28435 by Chief-Detektor

Co-authored-by: André Rosenhammer <andre.rosenhammer@gmail.com>
2024-05-26 12:53:42 +08:00
Giteabot
7d56ee3c0f Support setting the default attribute of the issue template dropdown field (#31045) (#31059)
Backport #31045 by @Zettat123

Fix #31044

According to [GitHub issue template
documentation](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema#attributes-for-dropdown),
the `default` attribute can be used to specify the preselected option
for a dropdown field.

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-05-24 11:01:09 +08:00
Giteabot
2e90b80d64 Alpine 3.20 has been released (#31047) (#31057)
Backport #31047 by @techknowlogick

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2024-05-23 08:36:34 -04:00
yp05327
564fef1e20 Fix wrong display of recently pushed notification (#25812) (#31043)
Backport #25812

~~ps: removed some new codes in `tests/integration/pull_merge_test.go`~~

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-23 04:14:26 +00:00
Giteabot
14ed06d675 align s3 files with docker naming (#31050) (#31052) 2024-05-22 20:06:52 -04:00
Giteabot
cc51f70d86 Update Actions documentation missing feature (#31034) (#31048)
Backport #31034 by @lunny

Fix
https://github.com/go-gitea/gitea/issues/25897#issuecomment-2117145391

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: yp05327 <576951401@qq.com>
2024-05-22 15:04:40 +00:00
Giteabot
7d5ac68bc4 Sync up deleted branches & action assets related cleanup documentation (#31022) (#31049)
Backport #31022 by @kemzeb

Syncs up docs associated to actions and deleted branch cleanup i.e. in
custom/app.example.ini and the config cheat sheet.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-05-22 11:02:34 -04:00
Giteabot
2648962ae0 Fix automerge will not work because of some events haven't been triggered (#30780) (#31039)
Backport #30780 by @lunny

Replace #25741
Close #24445
Close #30658
Close #20646
~Depends on #30805~

Since #25741 has been rewritten totally, to make the contribution
easier, I will continue the work in this PR. Thanks @6543

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-22 02:08:05 +00:00
Giteabot
e3390e2441 use existing oauth grant for public client (#31015) (#31042)
Backport #31015 by @denyskon

Do not try to create a new authorization grant when one exists already,
thus preventing a DB-related authorization issue.

Fix https://github.com/go-gitea/gitea/pull/30790#issuecomment-2118812426

Co-authored-by: Denys Konovalov <kontakt@denyskon.de>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-21 17:32:31 +00:00
Giteabot
27a4c67992 Don't include link of deleted branch when listing branches (#31028) (#31032)
Backport #31028 by @kemzeb

From
https://github.com/go-gitea/gitea/issues/31018#issuecomment-2119622680.

This commit removes the link to a deleted branch name because it returns
a 404 while it is in this deleted state. GitHub also throws a 404 when
navigating to a branch link that was just deleted, but this deleted
branch is removed from the branch list after a page refresh. Since with
Gitea this deleted branch would be kept around for quite some time
(well, until the "cleanup deleted branches" cron job begins), it makes
sense to not have this as a link that users can navigate to.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-05-21 15:51:36 +08:00
Giteabot
55cb356b84 Refactor sha1 and time-limited code (#31023) (#31030)
Backport #31023 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-21 00:58:21 +08:00
Giteabot
8a259e54c5 Return access_denied error when an OAuth2 request is denied (#30974) (#31029)
Backport #30974 by Zettat123

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-05-20 22:49:04 +08:00
Giteabot
8663a351d1 Fix incorrect "blob excerpt" link when comparing files (#31013) (#31026)
Backport #31013 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-20 07:40:14 +00:00
Giteabot
3d3123d553 Avoid 500 panic error when uploading invalid maven package file (#31014) (#31027)
Backport #31014 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-20 07:10:27 +00:00
Giteabot
a798a1eb17 Fix project column title overflow (#31011) (#31025)
Backport #31011 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-20 06:17:21 +00:00
Giteabot
d17bfad940 Fix data-race during testing (#30999) (#31024)
Backport #30999 by wxiaoguang

Fix #30992

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-20 13:49:24 +08:00
Giteabot
8446caa813 Fix bug on avatar (#31008) (#31019)
Backport #31008 by @lunny

Extract from #30995

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-20 02:54:53 +00:00
Giteabot
ab33b7849f Fix "force private" logic (#31012) (#31021)
Backport #31012 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-20 10:27:52 +08:00
Giteabot
a58498cc43 Improve reverse proxy documents and clarify the AppURL guessing behavior (#31003) (#31020)
Backport #31003 by wxiaoguang

Fix #31002

1. Mention Make sure `Host` and `X-Fowarded-Proto` headers are correctly passed to Gitea
2. Clarify the basic requirements and move the "general configuration" to the top
3. Add a comment for the "container registry"
4. Use 1.21 behavior if the reverse proxy is not correctly configured

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-05-19 15:22:54 +00:00
Giteabot
8eac16de21 Simplify mirror repository API logic (#30963) (#31009)
Backport #30963 by wxiaoguang

Fix #30921

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-18 08:26:20 +00:00
Giteabot
bd3787c774 Fix JS error when editing a merged PR's title (#30990) (#31001)
Backport #30990 by @wxiaoguang

Regression of Fix issue/PR title edit (#30858)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-17 01:07:09 +00:00
Giteabot
f0e74da719 Upgrade tqdm dependency (#30996) (#31000)
Backport #30996 by @silverwind

Result of `make update-py`

Fixes: https://github.com/go-gitea/gitea/security/dependabot/65

Co-authored-by: silverwind <me@silverwind.io>
2024-05-17 02:39:39 +02:00
Giteabot
835411b978 template: label fix correct input id (#30987) (#30997)
Just a small commit to fix a wrong label for id.
Thanks and cheers!

Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
Co-authored-by: Frank Villaro-Dixon <frank@vi-di.fr>
2024-05-16 08:45:52 -05:00
Giteabot
3cdf9ed202 Put web editor into a segment (#30966) (#30993)
Backport #30966 by silverwind

Co-authored-by: silverwind <me@silverwind.io>
2024-05-16 07:03:45 +00:00
Giteabot
0c9dcda10d Check if the release is converted from the tag when updating the release (#30984) (#30986)
We should call `notify_service.NewRelease` when a release is created
from an existing tag.

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-05-15 16:34:38 -05:00
Giteabot
cb52eb639e Remove unnecessary double quotes on language file (#30977) (#30979)
Backport #30977 by @lunny

The double quotes and the prefix/suffix space are unnecessary.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-05-15 11:00:58 +02:00
Giteabot
5b7e54f72f Always load or generate oauth2 jwt secret (#30942) (#30978)
Backport #30942 by @wxiaoguang

 Fix #30923

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-14 15:00:38 +00:00
Giteabot
042777abd7 Filter out duplicate action(activity) items for a repository (#30957) (#30976)
Backport #30957

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-14 14:14:39 +00:00
Giteabot
8393ff78fc Protected tag is no internal server error (#30962) (#30970)
Backport #30962 by @KN4CK3R

Fixes #30959

Adds an API test for protected tags.
Fix existing tag in combination with fixtures.

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-05-14 07:15:36 +00:00
Giteabot
c1337e7316 Restyle release list, fix branch dropdown (#30837) (#30968)
Backport #30837 by @silverwind

Fixes https://github.com/go-gitea/gitea/issues/30821 and restyles the
release list.

Desktop:

<img width="1199" alt="Screenshot 2024-05-02 at 20 46 10"
src="https://github.com/go-gitea/gitea/assets/115237/bee92423-d4a9-4b26-8301-3a1e09eef4cd">


Mobile:

<img width="443" alt="Screenshot 2024-05-02 at 20 46 21"
src="https://github.com/go-gitea/gitea/assets/115237/42ecbae5-bdb6-4b16-a0ee-9c64daede68d">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-13 22:00:52 +00:00
Giteabot
70eaa99ac0 fix: change npm scope registry (#30964) (#30965)
Backport #30964 by @llxlr


https://docs.npmjs.com/cli/v10/using-npm/scope#associating-a-scope-with-a-registry

Co-authored-by: james yang <yanghongday369@gmail.com>
2024-05-13 18:14:31 +02:00
Giteabot
f806bbb815 Support using label names when changing issue labels (#30943) (#30958)
Backport #30943 by @Zettat123

Resolve #30917

Make the APIs for adding labels and replacing labels support both label
IDs and label names so the
[`actions/labeler`](https://github.com/actions/labeler) action can work
in Gitea.

<img width="600px"
src="https://github.com/go-gitea/gitea/assets/15528715/7835c771-f637-4c57-9ce5-e4fbf56fa0d3"
/>

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-05-13 14:28:11 +08:00
Giteabot
ebe8e63dfd Fix file path width in repo non-homepage view (#30951) (#30952)
Backport #30951 by @silverwind

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

<img width="1310" alt="Screenshot 2024-05-11 at 20 48 41"
src="https://github.com/go-gitea/gitea/assets/115237/f163dfd4-1299-421f-a99e-cd0c793e0e3d">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-12 14:07:16 +02:00
Giteabot
bc455883fc Remove If Exist check on migration for mssql because that syntax required SQL server 2016 (#30894) (#30946)
Backport #30894 by @lunny

Fix #30872

We will assume the database is consistent before executing the
migration. So the indexes should exist. Removing `IF EXIST` then is safe
enough.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-12 03:15:01 +00:00
Giteabot
14dc00ae01 Move reverproxyauth before session so the header will not be ignored even if user has login (#27821) (#30948)
Backport #27821 by @lunny

When a user logout and then login another user, the reverseproxy auth
should be checked before session otherwise the old user is still login.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-12 10:25:55 +08:00
Giteabot
94c5a30c8b Fix some UI regressions for commit list (#30920) (#30937)
Backport #30920 by wxiaoguang

Close #30919

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-10 22:36:06 +08:00
Giteabot
b99473f4ec Check if reverse proxy is correctly configured (#30890) (#30935)
Backport #30890 by wxiaoguang

Follow #27011
Follow #30885

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-10 12:34:04 +00:00
yp05327
2200c41ffd Fix incorrect default branch when adopt a repository (#30912) (#30928)
Backport #30912

ps: removed useless `u *user_model.User` for `adoptRepository`
2024-05-10 12:00:01 +00:00
Giteabot
45475250bd Remove deprecated stuff for runners (#30930) (#30931)
Backport #30930 by @wolfogre

It's time (maybe somewhat late) to remove some deprecated stuff for the
runner.

- `x-runner-version`: runners needn't to report version in every
request, they will call `Declare`.
- `AgentLabels`: runners will report them as `Labels`.

Co-authored-by: Jason Song <i@wolfogre.com>
2024-05-10 16:57:30 +08:00
Giteabot
df5513978a Update issue indexer after merging a PR (#30715) (#30903)
Backport #30715 by @Zettat123

Fix #30684

Co-authored-by: Zettat123 <zettat123@gmail.com>
2024-05-09 13:51:57 +08:00
Giteabot
08d697ae70 Add missing menu active item background back (#30897) (#30907)
Backport #30897 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-08 23:53:02 +00:00
Giteabot
5f1af1f37d Fix incorrect issue form (#30881) (#30904)
Backport #30881 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-08 23:26:53 +00:00
Giteabot
2f91a461f7 Fix misspelling of mergable (#30896) (#30905)
Backport #30896 by @yp05327

https://github.com/go-gitea/gitea/pull/25812#issuecomment-2099833692
Follow #30573

Co-authored-by: yp05327 <576951401@qq.com>
2024-05-08 16:38:46 +00:00
Giteabot
084bec89ed Fix various problems around projects board view (#30696) (#30902)
Backport #30696 by @lunny

# The problem
The previous implementation will start multiple POST requests from the
frontend when moving a column and another bug is moving the default
column will never be remembered in fact.

# What's changed

- [x] This PR will allow the default column to move to a non-first
position
- [x] And it also uses one request instead of multiple requests when
moving the columns
- [x] Use a star instead of a pin as the icon for setting the default
column action
- [x] Inserted new column will be append to the end
- [x] Fix #30701 the newly added issue will be append to the end of the
default column
- [x] Fix when deleting a column, all issues in it will be displayed
from UI but database records exist.
- [x] Add a limitation for columns in a project to 20. So the sorting
will not be overflow because it's int8.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-08 15:46:21 +00:00
Giteabot
271e8748a2 Fix wrong transfer hint (#30889) (#30900)
Backport #30889 by @lunny

Fix #30187

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-08 15:12:37 +00:00
Giteabot
ec3f5f9992 Move database operations of merging a pull request to post receive hook and add a transaction (#30805) (#30888)
Backport #30805 by @lunny

Merging PR may fail because of various problems. The pull request may
have a dirty state because there is no transaction when merging a pull
request. ref
https://github.com/go-gitea/gitea/pull/25741#issuecomment-2074126393

This PR moves all database update operations to post-receive handler for
merging a pull request and having a database transaction. That means if
database operations fail, then the git merging will fail, the git client
will get a fail result.

There are already many tests for pull request merging, so we don't need
to add a new one.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-08 14:17:18 +00:00
wxiaoguang
d4c2db39bf Refactor AppURL usage (#30885) (#30891)
Backport #30885
Fix #30883
Fix #29591

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-05-08 13:34:43 +00:00
6543
d410e2acce Repository explore alphabetically order respect owner name (#30882)
similar to #30784 but only for the repo explore page

is covered by #30876 for the main branch
2024-05-07 16:35:02 +00:00
Giteabot
216c8eada3 Fix missing migrate actions artifacts (#30874) (#30886)
Backport #30874 by @lunny

The actions artifacts should be able to be migrate to the new storage
place.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-07 21:59:00 +08:00
wxiaoguang
d5563be0ee Make sure git version&feature are always prepared (#30877) (#30879)
Backport #30877
2024-05-07 02:07:33 +00:00
Giteabot
ad5a8d043c Make "sync branch" also sync object format and add tests (#30878) (#30880)
Backport #30878 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-07 08:33:28 +08:00
Giteabot
cfe6779d4e Get repo list with OrderBy alpha should respect owner too (#30784) (#30875)
Backport #30784 by @6543

instead of:
- zowner/gcode
- awesome/nul
- zowner/nul
- zowner/zzz

we will get:
- awesome/nul
- zowner/gcode
- zowner/nul
- zowner/zzz

Co-authored-by: 6543 <6543@obermui.de>
2024-05-06 15:06:45 +00:00
Giteabot
b22d7fd8cd Fix some UI problems (dropdown/container) (#30849) (#30871)
Backport #30849 by wxiaoguang

Follow #30345
Follow #30547

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-06 07:45:04 +00:00
Giteabot
e2e326f154 Fix some UI problems (install/checkbox) (#30854) (#30870)
Backport #30854 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-06 06:58:42 +00:00
Giteabot
2252a7bf84 Have time.js use UTC-related getters/setters (#30857) (#30869)
Backport #30857 by kemzeb

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
Co-authored-by: Sam Fisher <fisher@3echelon.local>
2024-05-06 10:01:22 +08:00
Giteabot
bb7150c30d Do not show monaco JS errors (#30862) (#30866)
Backport #30862 by wxiaoguang

Fix #30861

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-06 00:53:09 +08:00
Giteabot
60fa2a5960 Fix issue/PR title edit (#30858) (#30865)
Backport #30858 by wxiaoguang

1. "enter" doesn't work (I think it is the last enter support for #14843)
2. if a branch name contains something like `&`, then the branch selector doesn't update

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-05 13:53:12 +00:00
Giteabot
054602977a Add result check in TestAPIEditUser (#29674) (#30860)
Backport #29674 by @yp05327

Fix #29514
there are too many usage of `NewRequestWithValues`, so there's no need
to check all of them.
Just one is enough I think.

Co-authored-by: yp05327 <576951401@qq.com>
2024-05-05 21:17:03 +08:00
Giteabot
471b411873 Fix markdown URL parsing for commit ID (#30812) (#30855)
Backport #30812 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-04 12:04:05 +08:00
Giteabot
a82e6301f7 Fix no edit history after editing issue's title and content (#30814) (#30845)
Backport #30814 by @yp05327

Fix #30807

reuse functions in services

Co-authored-by: yp05327 <576951401@qq.com>
2024-05-03 14:43:16 +00:00
Giteabot
1f9a9fab5f Improve grep search (#30843) (#30850)
Backport #30843 by wxiaoguang

Reduce the context line number to 1, make "git grep" search respect the
include/exclude patter, and fix #30785

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-03 17:59:33 +08:00
Giteabot
7ad2d039fd Don't only list code-enabled repositories when using repository API (#30817) (#30848)
Backport #30817 by kemzeb

We should be listing all repositories by default.

Fixes #28483.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-05-03 16:52:38 +08:00
Giteabot
ab2ef1ae49 Ignore useless error message "broken pipe" (#30801) (#30842)
Backport #30801 by wxiaoguang

Fix #30792

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-03 04:20:34 +00:00
Giteabot
7db434bfa9 Fix JS error on pull request page (#30838) (#30841)
Backport #30838 by silverwind

Fix this error seen on PR page, regression from
https://github.com/go-gitea/gitea/pull/30803:

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-03 02:17:11 +00:00
Giteabot
41f8ef8af5 Improve repo button row layout (#30668) (#30839)
Backport #30668 by @silverwind

Since there is now a second `<input>` in the repo buttons, we can make a
better-looking layout with no empty space, except on mobile.

Also I fixed one bug with focus border on clone panel.

## Large

<img width="1163" alt="Screenshot 2024-04-23 at 22 25 22"
src="https://github.com/go-gitea/gitea/assets/115237/8135a572-aa67-4672-ad49-b76b06890b52">

## Medium
<img width="870" alt="Screenshot 2024-04-23 at 22 25 34"
src="https://github.com/go-gitea/gitea/assets/115237/9e93f61c-3315-4a78-8328-8cefad5b50fa">

## Mobile
<img width="416" alt="Screenshot 2024-04-23 at 22 25 52"
src="https://github.com/go-gitea/gitea/assets/115237/859e341f-807a-48e6-8bcf-31715963216c">

Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 19:38:37 +00:00
Giteabot
6d83f5eddc Prevent automatic OAuth grants for public clients (#30790) (#30836)
Backport #30790 by archer-321

This commit forces the resource owner (user) to always approve OAuth 2.0
authorization requests if the client is public (e.g. native
applications).

As detailed in [RFC 6749 Section
10.2](https://www.rfc-editor.org/rfc/rfc6749.html#section-10.2),

> The authorization server SHOULD NOT process repeated authorization
requests automatically (without active resource owner interaction)
without authenticating the client or relying on other measures to ensure
that the repeated request comes from the original client and not an
impersonator.

With the implementation prior to this patch, attackers with access to
the redirect URI (e.g., the loopback interface for
`git-credential-oauth`) can get access to the user account without any
user interaction if they can redirect the user to the
`/login/oauth/authorize` endpoint somehow (e.g., with `xdg-open` on
Linux).

Fixes #25061.

Co-authored-by: Archer <archer@beezig.eu>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-02 19:06:32 +00:00
Giteabot
665a06c41f Catch and handle unallowed file type errors in issue attachment API (#30791) (#30834)
Backport #30791 by kemzeb

Before, we would just throw 500 if a user passes an attachment that is
not an allowed type. This commit catches this error and throws a 422
instead since this should be considered a validation error.

Co-authored-by: Kemal Zebari <60799661+kemzeb@users.noreply.github.com>
2024-05-03 01:45:45 +08:00
Giteabot
f62f04c6bf Fix incorrect message id for release email (#30825) (#30833)
Backport #30825 by wxiaoguang

Make generateMessageIDForRelease outputs the same format as
generateMessageIDForIssue (old `createReference`)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-03 00:00:29 +08:00
Giteabot
253c97b922 Add hover outline to heatmap squares (#30828) (#30832)
Backport #30828 by @silverwind

Makes it easier to use because you see which square is currently
hovered:

<img width="314" alt="Screenshot 2024-05-02 at 15 38 20"
src="https://github.com/go-gitea/gitea/assets/115237/3a15dad1-2259-4f28-9fae-5cf6ad3d8798">

I did try a `scoped` style for this, but that did not work for some
reason.

Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 15:01:07 +00:00
Giteabot
c34a03d504 Upgrade chi-binding (#30826) (#30830)
Backport #30826 by @lunny

Front port #30742

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-05-02 16:50:10 +02:00
Giteabot
3f0dc69400 Improve context popup rendering (#30824) (#30829)
Backport #30824 by @silverwind

Before, lot of empty space when no labels or body:

<img width="281" alt="Screenshot 2024-05-02 at 13 51 29"
src="https://github.com/go-gitea/gitea/assets/115237/8a980ccd-d53c-43a3-a059-dc8c614621e1">

After, empty space collapsed:

<img width="306" alt="Screenshot 2024-05-02 at 13 51 16"
src="https://github.com/go-gitea/gitea/assets/115237/8d9c154d-5de1-43d0-8536-afd9194d99b3">

All `<p>` (unsuitable) and `<small>` (discouraged in favor of css) tags
are removed.

Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 13:47:32 +00:00
Giteabot
606b12caf0 Fix activity heat map padding & locale (#30823) (#30827)
Backport #30823 by wxiaoguang

Fix #30808

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 13:27:45 +00:00
Giteabot
41efddafde Fix rounded border for segment followed by pagination (#30809) (#30818)
Backport #30809 by @silverwind

Fixes https://github.com/go-gitea/gitea/issues/30673, specifically
https://github.com/go-gitea/gitea/issues/30673#issuecomment-2085329812.

Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 13:06:02 +00:00
Giteabot
ee17289eeb Fix issue card layout (#30800) (#30820)
Backport #30800 by wxiaoguang

Fix #30788

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-02 20:10:32 +08:00
Giteabot
d793f256c8 Fix branch selector UI (#30803) (#30819)
Backport #30803 by wxiaoguang

Fix  #30802

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-02 11:12:43 +00:00
Giteabot
e1a79ae0bf Skip gzip for some well-known compressed file types (#30796) (#30813)
Backport #30796 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-05-02 14:50:24 +08:00
Giteabot
ac91bb27ff Fix markdown rendering when mentioning users (#30795) (#30810)
Backport #30795 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-02 09:48:24 +08:00
Giteabot
97a7c04a8f Fix bleve fuzziness (#30799) (#30804)
Backport #30799 by wxiaoguang

Fix #30797
Fix #30317

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-01 12:59:59 +00:00
Zettat123
99e89e57bc Fix duplicate status check contexts (#30660) (#30779)
Backport #30660.

Caused by #30076. 

There may be some duplicate status check contexts when setting status
checks for a branch protection rule. The duplicate contexts should be
removed.

Before:
<img

src="https://github.com/go-gitea/gitea/assets/15528715/97f4de2d-4868-47a3-8a99-5a180f9ac0a3"
width="600px" />

After:
<img

src="https://github.com/go-gitea/gitea/assets/15528715/ff7289c5-9793-4090-ba31-e8cb3c85f8a3"
width="600px" />
2024-05-01 02:10:37 +00:00
Giteabot
ab344a36e3 Rework and fix stopwatch (#30732) (#30787)
Backport #30732 by @silverwind

Fixes https://github.com/go-gitea/gitea/issues/30721 and overhauls the
stopwatch. Time is now shown inside the "dot" icon and on both mobile
and desktop. All rendering is now done by `<relative-time>`, the
`pretty-ms` dependency is dropped.

Desktop:
<img width="557" alt="Screenshot 2024-04-29 at 22 33 27"
src="https://github.com/go-gitea/gitea/assets/115237/3a46cdbf-6af2-4bf9-b07f-021348badaac">

Mobile:
<img width="640" alt="Screenshot 2024-04-29 at 22 34 19"
src="https://github.com/go-gitea/gitea/assets/115237/8a2beea7-bd5d-473f-8fff-66f63fd50877">

Note for tippy:
Previously, tippy instances defaulted to "menu" theme, but that theme is
really only meant for `.ui.menu`, so it was not optimal for the
stopwatch popover.

This introduces a unopinionated `default` theme that has no padding and
should be suitable for all content. I reviewed all existing uses and
explicitely set the desired `theme` on all of them.

Co-authored-by: silverwind <me@silverwind.io>
2024-04-30 21:46:45 +00:00
Giteabot
2bedd16c14 Improve logout from worker (#30775) (#30789)
Backport #30775 by wxiaoguang

A quick fix for #30756

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-05-01 05:19:13 +08:00
6543
022eac4ac8 Get repo assignees and reviewers should ignore deactivated users (#30770) (#30782)
Backport  #30770

If an user is deactivated, it should not be in the list of users who are
suggested to be assigned or review-requested.

old assignees or reviewers are not affected.

---
*Sponsored by Kithara Software GmbH*
2024-04-30 15:36:28 +00:00
Giteabot
a75b0d2813 Fix dashboard commit status null access (#30771) (#30786)
Backport #30771 by wxiaoguang

Fix #30768

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-30 20:53:20 +08:00
Giteabot
e8279fcefd Fix cross-compilation errors when CGO_CFLAGS/CGO_LDFLAGS is set (#30749) (#30774)
Backport #30749 by @sryze

When you cross-compile Gitea and you specify one of the envrionment
variables related to C flags, cgo will fail to build the generator
programs (e.g. generate-bindata) because GOOS and GOARCH are unset, but
those additional flags variables are not unset together with those.

To solve this issue, the simplest way that I've found is to disable cgo
in the `go generate` command as it's not really used there.

For example, I've had this problem with cross-compiling Gitea on FreeBSD
x86_64 to ARMv7 where it's necessary to pass `--target` to `clang` via
`CGO_CFLAGS`:
```
GOOS=freebsd \
GOARCH=arm \
GGOARM=7 \
CGO_ENABLED=1 \
SYSROOT=/usr/local/freebsd-sysroot/armv7 \
CC=clang \
CGO_CFLAGS="--target=armv7-unknown-freebsd13.2-gnueabihf" \
TAGS="bindata sqlite sqlite_unlock_notify" \
make SHELL='sh -x' build
```

```
Running go generate...
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/migration/schemas_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/options/options_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/public/public_bindata.go:8: running "go": exit status 1
# runtime/cgo
In file included from gcc_freebsd_amd64.c:9:
In file included from /usr/include/signal.h:42:
/usr/include/sys/_ucontext.h:44:2: error: unknown type name 'mcontext_t'
modules/templates/templates_bindata.go:8: running "go": exit status 1
gmake[1]: *** [Makefile:781: generate-go] Error 1
*** Error code 2

Stop.
```

But with this fix Gitea compiles successfully.

Co-authored-by: Sergey Zolotarev <4525736+sryze@users.noreply.github.com>
2024-04-30 10:51:38 +00:00
Giteabot
dc9e795ce2 Fix issue label rendering in the issue popup (#30763) (#30773)
Backport #30763 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-30 09:40:47 +00:00
Giteabot
6ee3a8a039 Right align the "Settings" menu item in overflow-menu (#30764) (#30777)
Backport #30764 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-30 16:40:09 +08:00
Giteabot
6edee2eca4 Rename CodeIndexerEnabled to IsRepoIndexerEnabled (#30762) (#30767)
Backport #30762 by @wxiaoguang

Fix  #30761

Most places use `IsRepoIndexerEnabled` but not `CodeIndexerEnabled`, so
it should always use `IsRepoIndexerEnabled` for consistency.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-29 21:57:48 +00:00
Giteabot
6c86034e0f Fix all rounded borders, change affected tab menus to pills (#30707) (#30769)
Backport #30707 by @silverwind

Fixes https://github.com/go-gitea/gitea/issues/30673, all 23 issues.
Notes:

- Tab bar menus had to change to pills because of unsolvable issue with
the border-radius as tab bar renders a overlapping border onto the box
below. And I think pills look better.
- Added padding to code editor empty preview message
- Hide monaco's built-in blue focus border, we don't need it and it
never showed before either.
- Label add menu is simplified, removing the nested segment.

<img width="1322" alt="Screenshot 2024-04-25 at 22 26 19"
src="https://github.com/go-gitea/gitea/assets/115237/7e394e0c-b7ad-417d-8e9f-12f1dea93ed1">
<img width="1326" alt="Screenshot 2024-04-25 at 22 28 00"
src="https://github.com/go-gitea/gitea/assets/115237/66c8499f-aa9f-4d95-8cca-ef13dfa82c65">
<img width="997" alt="Screenshot 2024-04-25 at 22 36 53"
src="https://github.com/go-gitea/gitea/assets/115237/07896102-c71d-4246-8173-c2bc2e1d3cae">
<img width="832" alt="Screenshot 2024-04-25 at 22 56 09"
src="https://github.com/go-gitea/gitea/assets/115237/d83afc96-08ca-4adc-baf4-3d02804be57c">
<img width="361" alt="Screenshot 2024-04-25 at 22 57 12"
src="https://github.com/go-gitea/gitea/assets/115237/c7371a68-00b5-47d8-84d0-ddc5268b2b2c">

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-29 21:19:44 +00:00
wxiaoguang
d07019f539 Fix nil dereference on error (#30740) (#30746)
Backport #30740 manually

Co-authored-by: Chongyi Zheng <git@zcy.dev>
2024-04-29 00:23:35 +00:00
Giteabot
bb7d36d751 Gitea with first upper case + typos (#30739) (#30747)
Backport #30739 by @mainboarder

* Corrected gitea to Gitea
* fixed some typos

Co-authored-by: mainboarder <git@mainboarder.de>
2024-04-28 23:51:42 +00:00
Giteabot
0fd8cba639 Fix documentation build problems because of MDX syntax conflicts (#30744) (#30745)
Backport #30744 by @lunny

Documentation building has encountered a problem like below. This is
because MDX syntax doesn't allow `{customPath}`, we have to use
\`{customPath}\`

```
Error: Can't render static file for pathname "/next/administration/config-cheat-sheet"
            at generateStaticFile (/workspace/gitea/gitea-docusaurus/node_modules/@docusaurus/core/lib/ssg.js:119:15)
            at runNextTicks (node:internal/process/task_queues:60:5)
            at process.processImmediate (node:internal/timers:449:9)
            at async /workspace/gitea/gitea-docusaurus/node_modules/p-map/index.js:57:22 {
          [cause]: ReferenceError: CustomPath is not defined
              at _createMdxContent (server.bundle.js:4406:106)
              at MDXContent (server.bundle.js:10745:8)
              at Uc (server.bundle.js:264171:44)
              at Xc (server.bundle.js:264173:253)
              at Z (server.bundle.js:264179:89)
              at Yc (server.bundle.js:264182:98)
              at $c (server.bundle.js:264181:140)
              at Z (server.bundle.js:264179:345)
              at Xc (server.bundle.js:264177:231)
              at Z (server.bundle.js:264179:89)
```

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-28 21:02:38 +08:00
4327 changed files with 188509 additions and 176120 deletions

View File

@@ -22,25 +22,20 @@ groups:
name: FEATURES name: FEATURES
labels: labels:
- type/feature - type/feature
-
name: API
labels:
- modifies/api
- -
name: ENHANCEMENTS name: ENHANCEMENTS
labels: labels:
- type/enhancement - type/enhancement
- - type/refactoring
name: PERFORMANCE - topic/ui
labels:
- performance/memory
- performance/speed
- performance/bigrepo
- performance/cpu
- -
name: BUGFIXES name: BUGFIXES
labels: labels:
- type/bug - type/bug
-
name: API
labels:
- modifies/api
- -
name: TESTING name: TESTING
labels: labels:

View File

@@ -1,21 +1,16 @@
{ {
"name": "Gitea DevContainer", "name": "Gitea DevContainer",
"image": "mcr.microsoft.com/devcontainers/go:1.25-trixie", "image": "mcr.microsoft.com/devcontainers/go:1.22-bullseye",
"containerEnv": {
// override "local" from packaged version
"GOTOOLCHAIN": "auto"
},
"features": { "features": {
// installs nodejs into container // installs nodejs into container
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": "latest" "version": "20"
}, },
"ghcr.io/devcontainers/features/git-lfs:1.2.5": {}, "ghcr.io/devcontainers/features/git-lfs:1.1.0": {},
"ghcr.io/jsburckhardt/devcontainer-features/uv:1": {}, "ghcr.io/devcontainers-contrib/features/poetry:2": {},
"ghcr.io/devcontainers/features/python:1": { "ghcr.io/devcontainers/features/python:1": {
"version": "3.13" "version": "3.12"
}, }
"ghcr.io/warrenbuckley/codespace-features/sqlite:1": {}
}, },
"customizations": { "customizations": {
"vscode": { "vscode": {
@@ -30,9 +25,8 @@
"Vue.volar", "Vue.volar",
"ms-azuretools.vscode-docker", "ms-azuretools.vscode-docker",
"vitest.explorer", "vitest.explorer",
"cweijan.vscode-database-client2", "qwtel.sqlite-viewer",
"GitHub.vscode-pull-request-github", "GitHub.vscode-pull-request-github"
"Azurite.azurite"
] ]
} }
}, },

View File

@@ -36,6 +36,15 @@ _testmain.go
coverage.all coverage.all
cpu.out 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
*.db *.db
*.log *.log
@@ -65,12 +74,23 @@ cpu.out
/yarn.lock /yarn.lock
/yarn-error.log /yarn-error.log
/npm-debug.log* /npm-debug.log*
/pnpm-debug.log*
/public/assets/js /public/assets/js
/public/assets/css /public/assets/css
/public/assets/fonts /public/assets/fonts
/public/assets/img/avatar /public/assets/img/avatar
/vendor /vendor
/web_src/fomantic/node_modules
/web_src/fomantic/build/*
!/web_src/fomantic/build/semantic.js
!/web_src/fomantic/build/semantic.css
!/web_src/fomantic/build/themes
/web_src/fomantic/build/themes/*
!/web_src/fomantic/build/themes/default
/web_src/fomantic/build/themes/default/assets/*
!/web_src/fomantic/build/themes/default/assets/fonts
/web_src/fomantic/build/themes/default/assets/fonts/*
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION /VERSION
/.air /.air
/.go-licenses /.go-licenses

View File

@@ -12,15 +12,11 @@ insert_final_newline = true
[*.{go,tmpl,html}] [*.{go,tmpl,html}]
indent_style = tab indent_style = tab
[go.*]
indent_style = tab
[templates/custom/*.tmpl] [templates/custom/*.tmpl]
insert_final_newline = false insert_final_newline = false
[templates/swagger/v1_json.tmpl] [templates/swagger/v1_json.tmpl]
indent_style = space indent_style = space
insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl] [templates/user/auth/oidc_wellknown.tmpl]
indent_style = space indent_style = space

845
.eslintrc.yaml Normal file
View File

@@ -0,0 +1,845 @@
root: true
reportUnusedDisableDirectives: true
ignorePatterns:
- /web_src/js/vendor
- /web_src/fomantic
parserOptions:
sourceType: module
ecmaVersion: latest
plugins:
- "@eslint-community/eslint-plugin-eslint-comments"
- "@stylistic/eslint-plugin-js"
- eslint-plugin-array-func
- eslint-plugin-github
- eslint-plugin-i
- eslint-plugin-jquery
- eslint-plugin-no-jquery
- eslint-plugin-no-use-extend-native
- eslint-plugin-regexp
- eslint-plugin-sonarjs
- eslint-plugin-unicorn
- eslint-plugin-vitest
- eslint-plugin-vitest-globals
- eslint-plugin-wc
env:
es2024: true
node: true
overrides:
- files: ["web_src/**/*"]
globals:
__webpack_public_path__: true
process: false # https://github.com/webpack/webpack/issues/15833
- files: ["web_src/**/*", "docs/**/*"]
env:
browser: true
node: false
- files: ["web_src/**/*worker.*"]
env:
worker: true
rules:
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
- files: ["*.config.*"]
rules:
i/no-unused-modules: [0]
- files: ["**/*.test.*", "web_src/js/test/setup.js"]
env:
vitest-globals/env: true
rules:
vitest/consistent-test-filename: [0]
vitest/consistent-test-it: [0]
vitest/expect-expect: [0]
vitest/max-expects: [0]
vitest/max-nested-describe: [0]
vitest/no-alias-methods: [0]
vitest/no-commented-out-tests: [0]
vitest/no-conditional-expect: [0]
vitest/no-conditional-in-test: [0]
vitest/no-conditional-tests: [0]
vitest/no-disabled-tests: [0]
vitest/no-done-callback: [0]
vitest/no-duplicate-hooks: [0]
vitest/no-focused-tests: [0]
vitest/no-hooks: [0]
vitest/no-identical-title: [2]
vitest/no-interpolation-in-snapshots: [0]
vitest/no-large-snapshots: [0]
vitest/no-mocks-import: [0]
vitest/no-restricted-matchers: [0]
vitest/no-restricted-vi-methods: [0]
vitest/no-standalone-expect: [0]
vitest/no-test-prefixes: [0]
vitest/no-test-return-statement: [0]
vitest/prefer-called-with: [0]
vitest/prefer-comparison-matcher: [0]
vitest/prefer-each: [0]
vitest/prefer-equality-matcher: [0]
vitest/prefer-expect-resolves: [0]
vitest/prefer-hooks-in-order: [0]
vitest/prefer-hooks-on-top: [2]
vitest/prefer-lowercase-title: [0]
vitest/prefer-mock-promise-shorthand: [0]
vitest/prefer-snapshot-hint: [0]
vitest/prefer-spy-on: [0]
vitest/prefer-strict-equal: [0]
vitest/prefer-to-be: [0]
vitest/prefer-to-be-falsy: [0]
vitest/prefer-to-be-object: [0]
vitest/prefer-to-be-truthy: [0]
vitest/prefer-to-contain: [0]
vitest/prefer-to-have-length: [0]
vitest/prefer-todo: [0]
vitest/require-hook: [0]
vitest/require-to-throw-message: [0]
vitest/require-top-level-describe: [0]
vitest/valid-describe-callback: [2]
vitest/valid-expect: [2]
vitest/valid-title: [2]
- files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"]
rules:
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression]
rules:
"@eslint-community/eslint-comments/disable-enable-pair": [2]
"@eslint-community/eslint-comments/no-aggregating-enable": [2]
"@eslint-community/eslint-comments/no-duplicate-disable": [2]
"@eslint-community/eslint-comments/no-restricted-disable": [0]
"@eslint-community/eslint-comments/no-unlimited-disable": [2]
"@eslint-community/eslint-comments/no-unused-disable": [2]
"@eslint-community/eslint-comments/no-unused-enable": [2]
"@eslint-community/eslint-comments/no-use": [0]
"@eslint-community/eslint-comments/require-description": [0]
"@stylistic/js/array-bracket-newline": [0]
"@stylistic/js/array-bracket-spacing": [2, never]
"@stylistic/js/array-element-newline": [0]
"@stylistic/js/arrow-parens": [2, always]
"@stylistic/js/arrow-spacing": [2, {before: true, after: true}]
"@stylistic/js/block-spacing": [0]
"@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}]
"@stylistic/js/comma-dangle": [2, always-multiline]
"@stylistic/js/comma-spacing": [2, {before: false, after: true}]
"@stylistic/js/comma-style": [2, last]
"@stylistic/js/computed-property-spacing": [2, never]
"@stylistic/js/dot-location": [2, property]
"@stylistic/js/eol-last": [2]
"@stylistic/js/function-call-spacing": [2, never]
"@stylistic/js/function-call-argument-newline": [0]
"@stylistic/js/function-paren-newline": [0]
"@stylistic/js/generator-star-spacing": [0]
"@stylistic/js/implicit-arrow-linebreak": [0]
"@stylistic/js/indent": [2, 2, {ignoreComments: true, SwitchCase: 1}]
"@stylistic/js/key-spacing": [2]
"@stylistic/js/keyword-spacing": [2]
"@stylistic/js/linebreak-style": [2, unix]
"@stylistic/js/lines-around-comment": [0]
"@stylistic/js/lines-between-class-members": [0]
"@stylistic/js/max-len": [0]
"@stylistic/js/max-statements-per-line": [0]
"@stylistic/js/multiline-ternary": [0]
"@stylistic/js/new-parens": [2]
"@stylistic/js/newline-per-chained-call": [0]
"@stylistic/js/no-confusing-arrow": [0]
"@stylistic/js/no-extra-parens": [0]
"@stylistic/js/no-extra-semi": [2]
"@stylistic/js/no-floating-decimal": [0]
"@stylistic/js/no-mixed-operators": [0]
"@stylistic/js/no-mixed-spaces-and-tabs": [2]
"@stylistic/js/no-multi-spaces": [2, {ignoreEOLComments: true, exceptions: {Property: true}}]
"@stylistic/js/no-multiple-empty-lines": [2, {max: 1, maxEOF: 0, maxBOF: 0}]
"@stylistic/js/no-tabs": [2]
"@stylistic/js/no-trailing-spaces": [2]
"@stylistic/js/no-whitespace-before-property": [2]
"@stylistic/js/nonblock-statement-body-position": [2]
"@stylistic/js/object-curly-newline": [0]
"@stylistic/js/object-curly-spacing": [2, never]
"@stylistic/js/object-property-newline": [0]
"@stylistic/js/one-var-declaration-per-line": [0]
"@stylistic/js/operator-linebreak": [2, after]
"@stylistic/js/padded-blocks": [2, never]
"@stylistic/js/padding-line-between-statements": [0]
"@stylistic/js/quote-props": [0]
"@stylistic/js/quotes": [2, single, {avoidEscape: true, allowTemplateLiterals: true}]
"@stylistic/js/rest-spread-spacing": [2, never]
"@stylistic/js/semi": [2, always, {omitLastInOneLineBlock: true}]
"@stylistic/js/semi-spacing": [2, {before: false, after: true}]
"@stylistic/js/semi-style": [2, last]
"@stylistic/js/space-before-blocks": [2, always]
"@stylistic/js/space-before-function-paren": [2, {anonymous: ignore, named: never, asyncArrow: always}]
"@stylistic/js/space-in-parens": [2, never]
"@stylistic/js/space-infix-ops": [2]
"@stylistic/js/space-unary-ops": [2]
"@stylistic/js/spaced-comment": [2, always]
"@stylistic/js/switch-colon-spacing": [2]
"@stylistic/js/template-curly-spacing": [2, never]
"@stylistic/js/template-tag-spacing": [2, never]
"@stylistic/js/wrap-iife": [2, inside]
"@stylistic/js/wrap-regex": [0]
"@stylistic/js/yield-star-spacing": [2, after]
accessor-pairs: [2]
array-callback-return: [2, {checkForEach: true}]
array-func/avoid-reverse: [2]
array-func/from-map: [2]
array-func/no-unnecessary-this-arg: [2]
array-func/prefer-array-from: [2]
array-func/prefer-flat-map: [0] # handled by unicorn/prefer-array-flat-map
array-func/prefer-flat: [0] # handled by unicorn/prefer-array-flat
arrow-body-style: [0]
block-scoped-var: [2]
camelcase: [0]
capitalized-comments: [0]
class-methods-use-this: [0]
complexity: [0]
consistent-return: [0]
consistent-this: [0]
constructor-super: [2]
curly: [0]
default-case-last: [2]
default-case: [0]
default-param-last: [0]
dot-notation: [0]
eqeqeq: [2]
for-direction: [2]
func-name-matching: [2]
func-names: [0]
func-style: [0]
getter-return: [2]
github/a11y-aria-label-is-well-formatted: [0]
github/a11y-no-title-attribute: [0]
github/a11y-no-visually-hidden-interactive-element: [0]
github/a11y-role-supports-aria-props: [0]
github/a11y-svg-has-accessible-name: [0]
github/array-foreach: [0]
github/async-currenttarget: [2]
github/async-preventdefault: [2]
github/authenticity-token: [0]
github/get-attribute: [0]
github/js-class-name: [0]
github/no-blur: [0]
github/no-d-none: [0]
github/no-dataset: [2]
github/no-dynamic-script-tag: [2]
github/no-implicit-buggy-globals: [2]
github/no-inner-html: [0]
github/no-innerText: [2]
github/no-then: [2]
github/no-useless-passive: [2]
github/prefer-observers: [2]
github/require-passive-events: [2]
github/unescaped-html-literal: [0]
grouped-accessor-pairs: [2]
guard-for-in: [0]
id-blacklist: [0]
id-length: [0]
id-match: [0]
i/consistent-type-specifier-style: [0]
i/default: [0]
i/dynamic-import-chunkname: [0]
i/export: [2]
i/exports-last: [0]
i/extensions: [2, always, {ignorePackages: true}]
i/first: [2]
i/group-exports: [0]
i/max-dependencies: [0]
i/named: [2]
i/namespace: [0]
i/newline-after-import: [0]
i/no-absolute-path: [0]
i/no-amd: [2]
i/no-anonymous-default-export: [0]
i/no-commonjs: [2]
i/no-cycle: [2, {ignoreExternal: true, maxDepth: 1}]
i/no-default-export: [0]
i/no-deprecated: [0]
i/no-dynamic-require: [0]
i/no-empty-named-blocks: [2]
i/no-extraneous-dependencies: [2]
i/no-import-module-exports: [0]
i/no-internal-modules: [0]
i/no-mutable-exports: [0]
i/no-named-as-default-member: [0]
i/no-named-as-default: [2]
i/no-named-default: [0]
i/no-named-export: [0]
i/no-namespace: [0]
i/no-nodejs-modules: [0]
i/no-relative-packages: [0]
i/no-relative-parent-imports: [0]
i/no-restricted-paths: [0]
i/no-self-import: [2]
i/no-unassigned-import: [0]
i/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$", ^vitest/]}]
i/no-unused-modules: [2, {unusedExports: true}]
i/no-useless-path-segments: [2, {commonjs: true}]
i/no-webpack-loader-syntax: [2]
i/order: [0]
i/prefer-default-export: [0]
i/unambiguous: [0]
init-declarations: [0]
jquery/no-ajax-events: [2]
jquery/no-ajax: [2]
jquery/no-animate: [2]
jquery/no-attr: [2]
jquery/no-bind: [2]
jquery/no-class: [0]
jquery/no-clone: [2]
jquery/no-closest: [0]
jquery/no-css: [2]
jquery/no-data: [0]
jquery/no-deferred: [2]
jquery/no-delegate: [2]
jquery/no-each: [0]
jquery/no-extend: [2]
jquery/no-fade: [2]
jquery/no-filter: [0]
jquery/no-find: [0]
jquery/no-global-eval: [2]
jquery/no-grep: [2]
jquery/no-has: [2]
jquery/no-hide: [2]
jquery/no-html: [0]
jquery/no-in-array: [2]
jquery/no-is-array: [2]
jquery/no-is-function: [2]
jquery/no-is: [2]
jquery/no-load: [2]
jquery/no-map: [2]
jquery/no-merge: [2]
jquery/no-param: [2]
jquery/no-parent: [0]
jquery/no-parents: [2]
jquery/no-parse-html: [2]
jquery/no-prop: [2]
jquery/no-proxy: [2]
jquery/no-ready: [2]
jquery/no-serialize: [2]
jquery/no-show: [2]
jquery/no-size: [2]
jquery/no-sizzle: [2]
jquery/no-slide: [2]
jquery/no-submit: [2]
jquery/no-text: [0]
jquery/no-toggle: [2]
jquery/no-trigger: [0]
jquery/no-trim: [2]
jquery/no-val: [0]
jquery/no-when: [2]
jquery/no-wrap: [2]
line-comment-position: [0]
logical-assignment-operators: [0]
max-classes-per-file: [0]
max-depth: [0]
max-lines-per-function: [0]
max-lines: [0]
max-nested-callbacks: [0]
max-params: [0]
max-statements: [0]
multiline-comment-style: [2, separate-lines]
new-cap: [0]
no-alert: [0]
no-array-constructor: [2]
no-async-promise-executor: [0]
no-await-in-loop: [0]
no-bitwise: [0]
no-buffer-constructor: [0]
no-caller: [2]
no-case-declarations: [2]
no-class-assign: [2]
no-compare-neg-zero: [2]
no-cond-assign: [2, except-parens]
no-console: [1, {allow: [debug, info, warn, error]}]
no-const-assign: [2]
no-constant-binary-expression: [2]
no-constant-condition: [0]
no-constructor-return: [2]
no-continue: [0]
no-control-regex: [0]
no-debugger: [1]
no-delete-var: [2]
no-div-regex: [0]
no-dupe-args: [2]
no-dupe-class-members: [2]
no-dupe-else-if: [2]
no-dupe-keys: [2]
no-duplicate-case: [2]
no-duplicate-imports: [2]
no-else-return: [2]
no-empty-character-class: [2]
no-empty-function: [0]
no-empty-pattern: [2]
no-empty-static-block: [2]
no-empty: [2, {allowEmptyCatch: true}]
no-eq-null: [2]
no-eval: [2]
no-ex-assign: [2]
no-extend-native: [2]
no-extra-bind: [2]
no-extra-boolean-cast: [2]
no-extra-label: [0]
no-fallthrough: [2]
no-func-assign: [2]
no-global-assign: [2]
no-implicit-coercion: [2]
no-implicit-globals: [0]
no-implied-eval: [2]
no-import-assign: [2]
no-inline-comments: [0]
no-inner-declarations: [2]
no-invalid-regexp: [2]
no-invalid-this: [0]
no-irregular-whitespace: [2]
no-iterator: [2]
no-jquery/no-ajax-events: [2]
no-jquery/no-ajax: [2]
no-jquery/no-and-self: [2]
no-jquery/no-animate-toggle: [2]
no-jquery/no-animate: [2]
no-jquery/no-append-html: [2]
no-jquery/no-attr: [2]
no-jquery/no-bind: [2]
no-jquery/no-box-model: [2]
no-jquery/no-browser: [2]
no-jquery/no-camel-case: [2]
no-jquery/no-class-state: [0]
no-jquery/no-class: [0]
no-jquery/no-clone: [2]
no-jquery/no-closest: [0]
no-jquery/no-constructor-attributes: [2]
no-jquery/no-contains: [2]
no-jquery/no-context-prop: [2]
no-jquery/no-css: [2]
no-jquery/no-data: [0]
no-jquery/no-deferred: [2]
no-jquery/no-delegate: [2]
no-jquery/no-each-collection: [0]
no-jquery/no-each-util: [0]
no-jquery/no-each: [0]
no-jquery/no-error-shorthand: [2]
no-jquery/no-error: [2]
no-jquery/no-escape-selector: [2]
no-jquery/no-event-shorthand: [2]
no-jquery/no-extend: [2]
no-jquery/no-fade: [2]
no-jquery/no-filter: [0]
no-jquery/no-find-collection: [0]
no-jquery/no-find-util: [2]
no-jquery/no-find: [0]
no-jquery/no-fx-interval: [2]
no-jquery/no-global-eval: [2]
no-jquery/no-global-selector: [0]
no-jquery/no-grep: [2]
no-jquery/no-has: [2]
no-jquery/no-hold-ready: [2]
no-jquery/no-html: [0]
no-jquery/no-in-array: [2]
no-jquery/no-is-array: [2]
no-jquery/no-is-empty-object: [2]
no-jquery/no-is-function: [2]
no-jquery/no-is-numeric: [2]
no-jquery/no-is-plain-object: [2]
no-jquery/no-is-window: [2]
no-jquery/no-is: [2]
no-jquery/no-jquery-constructor: [0]
no-jquery/no-live: [2]
no-jquery/no-load-shorthand: [2]
no-jquery/no-load: [2]
no-jquery/no-map-collection: [0]
no-jquery/no-map-util: [2]
no-jquery/no-map: [2]
no-jquery/no-merge: [2]
no-jquery/no-node-name: [2]
no-jquery/no-noop: [2]
no-jquery/no-now: [2]
no-jquery/no-on-ready: [2]
no-jquery/no-other-methods: [0]
no-jquery/no-other-utils: [2]
no-jquery/no-param: [2]
no-jquery/no-parent: [0]
no-jquery/no-parents: [2]
no-jquery/no-parse-html-literal: [0]
no-jquery/no-parse-html: [2]
no-jquery/no-parse-json: [2]
no-jquery/no-parse-xml: [2]
no-jquery/no-prop: [2]
no-jquery/no-proxy: [2]
no-jquery/no-ready-shorthand: [2]
no-jquery/no-ready: [2]
no-jquery/no-selector-prop: [2]
no-jquery/no-serialize: [2]
no-jquery/no-size: [2]
no-jquery/no-sizzle: [2]
no-jquery/no-slide: [2]
no-jquery/no-sub: [2]
no-jquery/no-support: [2]
no-jquery/no-text: [0]
no-jquery/no-trigger: [0]
no-jquery/no-trim: [2]
no-jquery/no-type: [2]
no-jquery/no-unique: [2]
no-jquery/no-unload-shorthand: [2]
no-jquery/no-val: [0]
no-jquery/no-visibility: [2]
no-jquery/no-when: [2]
no-jquery/no-wrap: [2]
no-jquery/variable-pattern: [2]
no-label-var: [2]
no-labels: [0] # handled by no-restricted-syntax
no-lone-blocks: [2]
no-lonely-if: [0]
no-loop-func: [0]
no-loss-of-precision: [2]
no-magic-numbers: [0]
no-misleading-character-class: [2]
no-multi-assign: [0]
no-multi-str: [2]
no-negated-condition: [0]
no-nested-ternary: [0]
no-new-func: [2]
no-new-native-nonconstructor: [2]
no-new-object: [2]
no-new-symbol: [2]
no-new-wrappers: [2]
no-new: [0]
no-nonoctal-decimal-escape: [2]
no-obj-calls: [2]
no-octal-escape: [2]
no-octal: [2]
no-param-reassign: [0]
no-plusplus: [0]
no-promise-executor-return: [0]
no-proto: [2]
no-prototype-builtins: [2]
no-redeclare: [2]
no-regex-spaces: [2]
no-restricted-exports: [0]
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename]
no-restricted-imports: [0]
no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.js instead"}]
no-return-assign: [0]
no-script-url: [2]
no-self-assign: [2, {props: true}]
no-self-compare: [2]
no-sequences: [2]
no-setter-return: [2]
no-shadow-restricted-names: [2]
no-shadow: [0]
no-sparse-arrays: [2]
no-template-curly-in-string: [2]
no-ternary: [0]
no-this-before-super: [2]
no-throw-literal: [2]
no-undef-init: [2]
no-undef: [2, {typeof: true}]
no-undefined: [0]
no-underscore-dangle: [0]
no-unexpected-multiline: [2]
no-unmodified-loop-condition: [2]
no-unneeded-ternary: [2]
no-unreachable-loop: [2]
no-unreachable: [2]
no-unsafe-finally: [2]
no-unsafe-negation: [2]
no-unused-expressions: [2]
no-unused-labels: [2]
no-unused-private-class-members: [2]
no-unused-vars: [2, {args: all, argsIgnorePattern: ^_, varsIgnorePattern: ^_, caughtErrorsIgnorePattern: ^_, destructuredArrayIgnorePattern: ^_, ignoreRestSiblings: false}]
no-use-before-define: [2, {functions: false, classes: true, variables: true, allowNamedExports: true}]
no-use-extend-native/no-use-extend-native: [2]
no-useless-backreference: [2]
no-useless-call: [2]
no-useless-catch: [2]
no-useless-computed-key: [2]
no-useless-concat: [2]
no-useless-constructor: [2]
no-useless-escape: [2]
no-useless-rename: [2]
no-useless-return: [2]
no-var: [2]
no-void: [2]
no-warning-comments: [0]
no-with: [0] # handled by no-restricted-syntax
object-shorthand: [2, always]
one-var-declaration-per-line: [0]
one-var: [0]
operator-assignment: [2, always]
operator-linebreak: [2, after]
prefer-arrow-callback: [2, {allowNamedFunctions: true, allowUnboundThis: true}]
prefer-const: [2, {destructuring: all, ignoreReadBeforeAssign: true}]
prefer-destructuring: [0]
prefer-exponentiation-operator: [2]
prefer-named-capture-group: [0]
prefer-numeric-literals: [2]
prefer-object-has-own: [2]
prefer-object-spread: [2]
prefer-promise-reject-errors: [2, {allowEmptyReject: false}]
prefer-regex-literals: [2]
prefer-rest-params: [2]
prefer-spread: [2]
prefer-template: [2]
radix: [2, as-needed]
regexp/confusing-quantifier: [2]
regexp/control-character-escape: [2]
regexp/hexadecimal-escape: [0]
regexp/letter-case: [0]
regexp/match-any: [2]
regexp/negation: [2]
regexp/no-contradiction-with-assertion: [0]
regexp/no-control-character: [0]
regexp/no-dupe-characters-character-class: [2]
regexp/no-dupe-disjunctions: [2]
regexp/no-empty-alternative: [2]
regexp/no-empty-capturing-group: [2]
regexp/no-empty-character-class: [0]
regexp/no-empty-group: [2]
regexp/no-empty-lookarounds-assertion: [2]
regexp/no-empty-string-literal: [2]
regexp/no-escape-backspace: [2]
regexp/no-extra-lookaround-assertions: [0]
regexp/no-invalid-regexp: [2]
regexp/no-invisible-character: [2]
regexp/no-lazy-ends: [2]
regexp/no-legacy-features: [2]
regexp/no-misleading-capturing-group: [0]
regexp/no-misleading-unicode-character: [0]
regexp/no-missing-g-flag: [2]
regexp/no-non-standard-flag: [2]
regexp/no-obscure-range: [2]
regexp/no-octal: [2]
regexp/no-optional-assertion: [2]
regexp/no-potentially-useless-backreference: [2]
regexp/no-standalone-backslash: [2]
regexp/no-super-linear-backtracking: [0]
regexp/no-super-linear-move: [0]
regexp/no-trivially-nested-assertion: [2]
regexp/no-trivially-nested-quantifier: [2]
regexp/no-unused-capturing-group: [0]
regexp/no-useless-assertions: [2]
regexp/no-useless-backreference: [2]
regexp/no-useless-character-class: [2]
regexp/no-useless-dollar-replacements: [2]
regexp/no-useless-escape: [2]
regexp/no-useless-flag: [2]
regexp/no-useless-lazy: [2]
regexp/no-useless-non-capturing-group: [2]
regexp/no-useless-quantifier: [2]
regexp/no-useless-range: [2]
regexp/no-useless-set-operand: [2]
regexp/no-useless-string-literal: [2]
regexp/no-useless-two-nums-quantifier: [2]
regexp/no-zero-quantifier: [2]
regexp/optimal-lookaround-quantifier: [2]
regexp/optimal-quantifier-concatenation: [0]
regexp/prefer-character-class: [0]
regexp/prefer-d: [0]
regexp/prefer-escape-replacement-dollar-char: [0]
regexp/prefer-lookaround: [0]
regexp/prefer-named-backreference: [0]
regexp/prefer-named-capture-group: [0]
regexp/prefer-named-replacement: [0]
regexp/prefer-plus-quantifier: [2]
regexp/prefer-predefined-assertion: [2]
regexp/prefer-quantifier: [0]
regexp/prefer-question-quantifier: [2]
regexp/prefer-range: [2]
regexp/prefer-regexp-exec: [2]
regexp/prefer-regexp-test: [2]
regexp/prefer-result-array-groups: [0]
regexp/prefer-set-operation: [2]
regexp/prefer-star-quantifier: [2]
regexp/prefer-unicode-codepoint-escapes: [2]
regexp/prefer-w: [0]
regexp/require-unicode-regexp: [0]
regexp/simplify-set-operations: [2]
regexp/sort-alternatives: [0]
regexp/sort-character-class-elements: [0]
regexp/sort-flags: [0]
regexp/strict: [2]
regexp/unicode-escape: [0]
regexp/use-ignore-case: [0]
require-atomic-updates: [0]
require-await: [0]
require-unicode-regexp: [0]
require-yield: [2]
sonarjs/cognitive-complexity: [0]
sonarjs/elseif-without-else: [0]
sonarjs/max-switch-cases: [0]
sonarjs/no-all-duplicated-branches: [2]
sonarjs/no-collapsible-if: [0]
sonarjs/no-collection-size-mischeck: [2]
sonarjs/no-duplicate-string: [0]
sonarjs/no-duplicated-branches: [0]
sonarjs/no-element-overwrite: [2]
sonarjs/no-empty-collection: [2]
sonarjs/no-extra-arguments: [2]
sonarjs/no-gratuitous-expressions: [2]
sonarjs/no-identical-conditions: [2]
sonarjs/no-identical-expressions: [2]
sonarjs/no-identical-functions: [2, 5]
sonarjs/no-ignored-return: [2]
sonarjs/no-inverted-boolean-check: [2]
sonarjs/no-nested-switch: [0]
sonarjs/no-nested-template-literals: [0]
sonarjs/no-one-iteration-loop: [2]
sonarjs/no-redundant-boolean: [2]
sonarjs/no-redundant-jump: [2]
sonarjs/no-same-line-conditional: [2]
sonarjs/no-small-switch: [0]
sonarjs/no-unused-collection: [2]
sonarjs/no-use-of-empty-return-value: [2]
sonarjs/no-useless-catch: [2]
sonarjs/non-existent-operator: [2]
sonarjs/prefer-immediate-return: [0]
sonarjs/prefer-object-literal: [0]
sonarjs/prefer-single-boolean-return: [0]
sonarjs/prefer-while: [2]
sort-imports: [0]
sort-keys: [0]
sort-vars: [0]
strict: [0]
symbol-description: [2]
unicode-bom: [2, never]
unicorn/better-regex: [0]
unicorn/catch-error-name: [0]
unicorn/consistent-destructuring: [2]
unicorn/consistent-function-scoping: [2]
unicorn/custom-error-definition: [0]
unicorn/empty-brace-spaces: [2]
unicorn/error-message: [0]
unicorn/escape-case: [0]
unicorn/expiring-todo-comments: [0]
unicorn/explicit-length-check: [0]
unicorn/filename-case: [0]
unicorn/import-index: [0]
unicorn/import-style: [0]
unicorn/new-for-builtins: [2]
unicorn/no-abusive-eslint-disable: [0]
unicorn/no-anonymous-default-export: [0]
unicorn/no-array-callback-reference: [0]
unicorn/no-array-for-each: [2]
unicorn/no-array-method-this-argument: [2]
unicorn/no-array-push-push: [2]
unicorn/no-array-reduce: [2]
unicorn/no-await-expression-member: [0]
unicorn/no-await-in-promise-methods: [2]
unicorn/no-console-spaces: [0]
unicorn/no-document-cookie: [2]
unicorn/no-empty-file: [2]
unicorn/no-for-loop: [0]
unicorn/no-hex-escape: [0]
unicorn/no-instanceof-array: [0]
unicorn/no-invalid-remove-event-listener: [2]
unicorn/no-keyword-prefix: [0]
unicorn/no-lonely-if: [2]
unicorn/no-negated-condition: [0]
unicorn/no-nested-ternary: [0]
unicorn/no-new-array: [0]
unicorn/no-new-buffer: [0]
unicorn/no-null: [0]
unicorn/no-object-as-default-parameter: [0]
unicorn/no-process-exit: [0]
unicorn/no-single-promise-in-promise-methods: [2]
unicorn/no-static-only-class: [2]
unicorn/no-thenable: [2]
unicorn/no-this-assignment: [2]
unicorn/no-typeof-undefined: [2]
unicorn/no-unnecessary-await: [2]
unicorn/no-unnecessary-polyfills: [2]
unicorn/no-unreadable-array-destructuring: [0]
unicorn/no-unreadable-iife: [2]
unicorn/no-unused-properties: [2]
unicorn/no-useless-fallback-in-spread: [2]
unicorn/no-useless-length-check: [2]
unicorn/no-useless-promise-resolve-reject: [2]
unicorn/no-useless-spread: [2]
unicorn/no-useless-switch-case: [2]
unicorn/no-useless-undefined: [0]
unicorn/no-zero-fractions: [2]
unicorn/number-literal-case: [0]
unicorn/numeric-separators-style: [0]
unicorn/prefer-add-event-listener: [2]
unicorn/prefer-array-find: [2]
unicorn/prefer-array-flat-map: [2]
unicorn/prefer-array-flat: [2]
unicorn/prefer-array-index-of: [2]
unicorn/prefer-array-some: [2]
unicorn/prefer-at: [0]
unicorn/prefer-blob-reading-methods: [2]
unicorn/prefer-code-point: [0]
unicorn/prefer-date-now: [2]
unicorn/prefer-default-parameters: [0]
unicorn/prefer-dom-node-append: [2]
unicorn/prefer-dom-node-dataset: [0]
unicorn/prefer-dom-node-remove: [2]
unicorn/prefer-dom-node-text-content: [2]
unicorn/prefer-event-target: [2]
unicorn/prefer-export-from: [0]
unicorn/prefer-includes: [2]
unicorn/prefer-json-parse-buffer: [0]
unicorn/prefer-keyboard-event-key: [2]
unicorn/prefer-logical-operator-over-ternary: [2]
unicorn/prefer-math-trunc: [2]
unicorn/prefer-modern-dom-apis: [0]
unicorn/prefer-modern-math-apis: [2]
unicorn/prefer-module: [2]
unicorn/prefer-native-coercion-functions: [2]
unicorn/prefer-negative-index: [2]
unicorn/prefer-node-protocol: [2]
unicorn/prefer-number-properties: [0]
unicorn/prefer-object-from-entries: [2]
unicorn/prefer-object-has-own: [0]
unicorn/prefer-optional-catch-binding: [2]
unicorn/prefer-prototype-methods: [0]
unicorn/prefer-query-selector: [0]
unicorn/prefer-reflect-apply: [0]
unicorn/prefer-regexp-test: [2]
unicorn/prefer-set-has: [0]
unicorn/prefer-set-size: [2]
unicorn/prefer-spread: [0]
unicorn/prefer-string-replace-all: [0]
unicorn/prefer-string-slice: [0]
unicorn/prefer-string-starts-ends-with: [2]
unicorn/prefer-string-trim-start-end: [2]
unicorn/prefer-switch: [0]
unicorn/prefer-ternary: [0]
unicorn/prefer-text-content: [2]
unicorn/prefer-top-level-await: [0]
unicorn/prefer-type-error: [0]
unicorn/prevent-abbreviations: [0]
unicorn/relative-url-style: [2]
unicorn/require-array-join-separator: [2]
unicorn/require-number-to-fixed-digits-argument: [2]
unicorn/require-post-message-target-origin: [0]
unicorn/string-content: [0]
unicorn/switch-case-braces: [0]
unicorn/template-indent: [2]
unicorn/text-encoding-identifier-case: [0]
unicorn/throw-new-error: [2]
use-isnan: [2]
valid-typeof: [2, {requireStringLiterals: true}]
vars-on-top: [0]
wc/attach-shadow-constructor: [2]
wc/define-tag-after-class-definition: [0]
wc/expose-class-on-global: [0]
wc/file-name-matches-element: [2]
wc/guard-define-call: [0]
wc/guard-super-call: [2]
wc/max-elements-per-file: [0]
wc/no-child-traversal-in-attributechangedcallback: [2]
wc/no-child-traversal-in-connectedcallback: [2]
wc/no-closed-shadow-root: [2]
wc/no-constructor-attributes: [2]
wc/no-constructor-params: [2]
wc/no-constructor: [2]
wc/no-customized-built-in-elements: [2]
wc/no-exports-with-element: [0]
wc/no-invalid-element-name: [2]
wc/no-invalid-extends: [2]
wc/no-method-prefixed-with-on: [2]
wc/no-self-class: [2]
wc/no-typos: [2]
wc/require-listener-teardown: [2]
wc/tag-name-matches-class: [2]
yoda: [2, never]

3
.gitattributes vendored
View File

@@ -4,7 +4,8 @@
/assets/*.json linguist-generated /assets/*.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated /public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated /templates/swagger/v1_json.tmpl linguist-generated
/options/fileicon/** linguist-generated
/vendor/** -text -eol linguist-vendored /vendor/** -text -eol linguist-vendored
/web_src/fomantic/build/** linguist-generated
/web_src/fomantic/_site/globals/site.variables linguist-language=Less
/web_src/js/vendor/** -text -eol linguist-vendored /web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile Dockerfile.* linguist-language=Dockerfile

View File

@@ -13,5 +13,5 @@ contact_links:
url: https://docs.gitea.com/help/faq url: https://docs.gitea.com/help/faq
about: Please check if your question isn't mentioned here. about: Please check if your question isn't mentioned here.
- name: Crowdin Translations - name: Crowdin Translations
url: https://translate.gitea.com url: https://crowdin.com/project/gitea
about: Translations are managed here. about: Translations are managed here.

30
.github/labeler.yml vendored
View File

@@ -41,7 +41,7 @@ modifies/internal:
- ".dockerignore" - ".dockerignore"
- "docker/**" - "docker/**"
- ".editorconfig" - ".editorconfig"
- ".eslintrc.cjs" - ".eslintrc.yaml"
- ".golangci.yml" - ".golangci.yml"
- ".gitpod.yml" - ".gitpod.yml"
- ".markdownlint.yaml" - ".markdownlint.yaml"
@@ -49,7 +49,7 @@ modifies/internal:
- "stylelint.config.js" - "stylelint.config.js"
- ".yamllint.yaml" - ".yamllint.yaml"
- ".github/**" - ".github/**"
- ".gitea/**" - ".gitea/"
- ".devcontainer/**" - ".devcontainer/**"
- "build.go" - "build.go"
- "build/**" - "build/**"
@@ -59,9 +59,9 @@ modifies/dependencies:
- changed-files: - changed-files:
- any-glob-to-any-file: - any-glob-to-any-file:
- "package.json" - "package.json"
- "pnpm-lock.yaml" - "package-lock.json"
- "pyproject.toml" - "pyproject.toml"
- "uv.lock" - "poetry.lock"
- "go.mod" - "go.mod"
- "go.sum" - "go.sum"
@@ -70,24 +70,8 @@ modifies/go:
- any-glob-to-any-file: - any-glob-to-any-file:
- "**/*.go" - "**/*.go"
modifies/frontend: modifies/js:
- changed-files: - changed-files:
- any-glob-to-any-file: - any-glob-to-any-file:
- "*.js" - "**/*.js"
- "*.ts" - "**/*.vue"
- "web_src/**"
docs-update-needed:
- changed-files:
- any-glob-to-any-file:
- "custom/conf/app.example.ini"
topic/code-linting:
- changed-files:
- any-glob-to-any-file:
- ".eslintrc.cjs"
- ".golangci.yml"
- ".markdownlint.yaml"
- ".spectral.yaml"
- ".yamllint.yaml"
- "stylelint.config.js"

View File

@@ -1,10 +1,9 @@
<!-- start tips --> <!-- start tips -->
Please check the following: Please check the following:
1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports. 1. Make sure you are targeting the `main` branch, pull requests on release branches are only allowed for backports.
2. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md . 2. Make sure you have read contributing guidelines: https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md .
3. For documentations contribution, please go to https://gitea.com/gitea/docs 3. Describe what your pull request does and which issue you're targeting (if any).
4. Describe what your pull request does and which issue you're targeting (if any). 4. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily.
5. It is recommended to enable "Allow edits by maintainers", so maintainers can help more easily. 5. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`.
6. Your input here will be included in the commit message when this PR has been merged. If you don't want some content to be included, please separate them with a line like `---`. 6. Delete all these tips before posting.
7. Delete all these tips before posting.
<!-- end tips --> <!-- end tips -->

View File

@@ -1,8 +1,8 @@
name: cron-licenses name: cron-licenses
on: on:
# schedule: schedule:
# - cron: "7 0 * * 1" # every Monday at 00:07 UTC - cron: "7 0 * * 1" # every Monday at 00:07 UTC
workflow_dispatch: workflow_dispatch:
jobs: jobs:
@@ -15,7 +15,7 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- run: make generate-gitignore - run: make generate-license generate-gitignore
timeout-minutes: 40 timeout-minutes: 40
- name: push translations to repo - name: push translations to repo
uses: appleboy/git-push-action@v0.0.3 uses: appleboy/git-push-action@v0.0.3

25
.github/workflows/disk-clean.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: disk-clean
on:
workflow_call:
jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main
with:
# this might remove tools that are actually needed,
# if set to "true" but frees about 6 GB
tool-cache: false
# all of these default to true, but feel free to set to
# "false" if necessary for your workflow
android: true
dotnet: true
haskell: true
large-packages: false
docker-images: false
swap-storage: true

View File

@@ -51,23 +51,22 @@ jobs:
- "options/locale/locale_en-US.ini" - "options/locale/locale_en-US.ini"
frontend: frontend:
- "*.js" - "**/*.js"
- "*.ts"
- "web_src/**" - "web_src/**"
- "tools/*.js"
- "tools/*.ts"
- "assets/emoji.json" - "assets/emoji.json"
- "package.json" - "package.json"
- "pnpm-lock.yaml" - "package-lock.json"
- "Makefile" - "Makefile"
- ".eslintrc.cjs" - ".eslintrc.yaml"
- "stylelint.config.js"
- ".npmrc" - ".npmrc"
docs: docs:
- "**/*.md" - "**/*.md"
- "docs/**"
- ".markdownlint.yaml" - ".markdownlint.yaml"
- "package.json" - "package.json"
- "pnpm-lock.yaml" - "package-lock.json"
actions: actions:
- ".github/workflows/*" - ".github/workflows/*"
@@ -77,7 +76,7 @@ jobs:
- "tools/lint-templates-*.js" - "tools/lint-templates-*.js"
- "templates/**/*.tmpl" - "templates/**/*.tmpl"
- "pyproject.toml" - "pyproject.toml"
- "uv.lock" - "poetry.lock"
docker: docker:
- "Dockerfile" - "Dockerfile"
@@ -87,10 +86,9 @@ jobs:
swagger: swagger:
- "templates/swagger/v1_json.tmpl" - "templates/swagger/v1_json.tmpl"
- "templates/swagger/v1_input.json"
- "Makefile" - "Makefile"
- "package.json" - "package.json"
- "pnpm-lock.yaml" - "package-lock.json"
- ".spectral.yaml" - ".spectral.yaml"
yaml: yaml:
@@ -98,3 +96,4 @@ jobs:
- "**/*.yaml" - "**/*.yaml"
- ".yamllint.yaml" - ".yamllint.yaml"
- "pyproject.toml" - "pyproject.toml"
- "poetry.lock"

View File

@@ -32,12 +32,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6 - uses: actions/setup-python@v5
- run: uv python install 3.12
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 python-version: "3.12"
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: pip install poetry
- run: make deps-py - run: make deps-py
- run: make deps-frontend - run: make deps-frontend
- run: make lint-templates - run: make lint-templates
@@ -48,8 +51,10 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v6 - uses: actions/setup-python@v5
- run: uv python install 3.12 with:
python-version: "3.12"
- run: pip install poetry
- run: make deps-py - run: make deps-py
- run: make lint-yaml - run: make lint-yaml
@@ -59,10 +64,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend - run: make deps-frontend
- run: make lint-swagger - run: make lint-swagger
@@ -89,7 +95,7 @@ jobs:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- run: make deps-backend deps-tools - run: make deps-backend deps-tools
- run: make lint-go-windows lint-go-gitea-vet - run: make lint-go-windows lint-go-vet
env: env:
TAGS: bindata sqlite sqlite_unlock_notify TAGS: bindata sqlite sqlite_unlock_notify
GOOS: windows GOOS: windows
@@ -129,10 +135,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend - run: make deps-frontend
- run: make lint-frontend - run: make lint-frontend
- run: make checks-frontend - run: make checks-frontend
@@ -177,12 +184,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend - run: make deps-frontend
- run: make lint-md - run: make lint-md
- run: make docs
actions: actions:
if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.actions == 'true'

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
services: services:
pgsql: pgsql:
image: postgres:14 image: postgres:12
env: env:
POSTGRES_DB: test POSTGRES_DB: test
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
@@ -31,7 +31,7 @@ jobs:
minio: minio:
# as github actions doesn't support "entrypoint", we need to use a non-official image # as github actions doesn't support "entrypoint", we need to use a non-official image
# that has a custom entrypoint set to "minio server /data" # that has a custom entrypoint set to "minio server /data"
image: bitnamilegacy/minio:2023.8.31 image: bitnami/minio:2023.8.31
env: env:
MINIO_ROOT_USER: 123456 MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678 MINIO_ROOT_PASSWORD: 12345678
@@ -98,7 +98,7 @@ jobs:
ports: ports:
- "9200:9200" - "9200:9200"
meilisearch: meilisearch:
image: getmeili/meilisearch:v1 image: getmeili/meilisearch:v1.2.0
env: env:
MEILI_ENV: development # disable auth MEILI_ENV: development # disable auth
ports: ports:
@@ -113,16 +113,12 @@ jobs:
ports: ports:
- 6379:6379 - 6379:6379
minio: minio:
image: bitnamilegacy/minio:2021.3.17 image: bitnami/minio:2021.3.17
env: env:
MINIO_ACCESS_KEY: 123456 MINIO_ACCESS_KEY: 123456
MINIO_SECRET_KEY: 12345678 MINIO_SECRET_KEY: 12345678
ports: ports:
- "9000:9000" - "9000:9000"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- 10000:10000
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
@@ -130,7 +126,7 @@ jobs:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- name: Add hosts to /etc/hosts - name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 minio devstoreaccount1.azurite.local mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts' run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts'
- run: make deps-backend - run: make deps-backend
- run: make backend - run: make backend
env: env:
@@ -154,15 +150,12 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
services: services:
mysql: mysql:
# the bitnami mysql image has more options than the official one, it's easier to customize image: mysql:8.0
image: bitnamilegacy/mysql:8.0
env: env:
ALLOW_EMPTY_PASSWORD: true MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: testgitea MYSQL_DATABASE: testgitea
ports: ports:
- "3306:3306" - "3306:3306"
options: >-
--mount type=tmpfs,destination=/bitnami/mysql/data
elasticsearch: elasticsearch:
image: elasticsearch:7.5.0 image: elasticsearch:7.5.0
env: env:
@@ -191,8 +184,7 @@ jobs:
- name: run migration tests - name: run migration tests
run: make test-mysql-migration run: make test-mysql-migration
- name: run tests - name: run tests
# run: make integration-test-coverage (at the moment, no coverage is really handled) run: make integration-test-coverage
run: make test-mysql
env: env:
TAGS: bindata TAGS: bindata
RACE_ENABLED: true RACE_ENABLED: true
@@ -202,20 +194,18 @@ jobs:
test-mssql: test-mssql:
if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true' if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.actions == 'true'
needs: files-changed needs: files-changed
runs-on: ubuntu-latest # specifying the version of ubuntu in use as mssql fails on newer kernels
# pending resolution from vendor
runs-on: ubuntu-20.04
services: services:
mssql: mssql:
image: mcr.microsoft.com/mssql/server:2019-latest image: mcr.microsoft.com/mssql/server:2017-latest
env: env:
ACCEPT_EULA: Y ACCEPT_EULA: Y
MSSQL_PID: Standard MSSQL_PID: Standard
SA_PASSWORD: MwantsaSecurePassword1 SA_PASSWORD: MwantsaSecurePassword1
ports: ports:
- "1433:1433" - "1433:1433"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- 10000:10000
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
@@ -223,7 +213,7 @@ jobs:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- name: Add hosts to /etc/hosts - name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql devstoreaccount1.azurite.local" | sudo tee -a /etc/hosts' run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql" | sudo tee -a /etc/hosts'
- run: make deps-backend - run: make deps-backend
- run: make backend - run: make backend
env: env:

View File

@@ -12,9 +12,7 @@ jobs:
uses: ./.github/workflows/files-changed.yml uses: ./.github/workflows/files-changed.yml
test-e2e: test-e2e:
# the "test-e2e" won't pass, and it seems that there is no useful test, so skip if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
# if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.frontend == 'true' || needs.files-changed.outputs.actions == 'true'
if: false
needs: files-changed needs: files-changed
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -23,12 +21,13 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend frontend deps-backend - run: make deps-frontend frontend deps-backend
- run: pnpm exec playwright install --with-deps - run: npx playwright install --with-deps
- run: make test-e2e-sqlite - run: make test-e2e-sqlite
timeout-minutes: 40 timeout-minutes: 40
env: env:

View File

@@ -9,6 +9,8 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
disk-clean:
uses: ./.github/workflows/disk-clean.yml
nightly-binary: nightly-binary:
runs-on: namespace-profile-gitea-release-binary runs-on: namespace-profile-gitea-release-binary
steps: steps:
@@ -20,10 +22,11 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend - run: make deps-frontend deps-backend
# xgo build # xgo build
- run: make release - run: make release
@@ -58,8 +61,6 @@ jobs:
aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress aws s3 sync dist/release s3://${{ secrets.AWS_S3_BUCKET }}/gitea/${{ steps.clean_name.outputs.branch }} --no-progress
nightly-docker-rootful: nightly-docker-rootful:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -74,6 +75,11 @@ jobs:
- name: Get cleaned branch name - name: Get cleaned branch name
id: clean_name id: clean_name
run: | run: |
# if main then say nightly otherwise cleanup name
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "branch=nightly" >> "$GITHUB_OUTPUT"
exit 0
fi
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//') REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- name: Login to Docker Hub - name: Login to Docker Hub
@@ -81,27 +87,17 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules - name: fetch go modules
run: make vendor run: make vendor
- name: build rootful docker image - name: build rootful docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64,linux/riscv64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: |- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}
gitea/gitea:${{ steps.clean_name.outputs.branch }}
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}
nightly-docker-rootless: nightly-docker-rootless:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -116,6 +112,11 @@ jobs:
- name: Get cleaned branch name - name: Get cleaned branch name
id: clean_name id: clean_name
run: | run: |
# if main then say nightly otherwise cleanup name
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "branch=nightly" >> "$GITHUB_OUTPUT"
exit 0
fi
REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//') REF_NAME=$(echo "${{ github.ref }}" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT" echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- name: Login to Docker Hub - name: Login to Docker Hub
@@ -123,12 +124,6 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: fetch go modules - name: fetch go modules
run: make vendor run: make vendor
- name: build rootless docker image - name: build rootless docker image
@@ -138,6 +133,4 @@ jobs:
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
push: true push: true
file: Dockerfile.rootless file: Dockerfile.rootless
tags: |- tags: gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless
ghcr.io/go-gitea/gitea:${{ steps.clean_name.outputs.branch }}-rootless

View File

@@ -21,10 +21,11 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend - run: make deps-frontend deps-backend
# xgo build # xgo build
- run: make release - run: make release
@@ -68,8 +69,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful: docker-rootful:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -80,9 +79,7 @@ jobs:
- uses: docker/metadata-action@v5 - uses: docker/metadata-action@v5
id: meta id: meta
with: with:
images: |- images: gitea/gitea
gitea/gitea
ghcr.io/go-gitea/gitea
flavor: | flavor: |
latest=false latest=false
# 1.2.3-rc0 # 1.2.3-rc0
@@ -93,24 +90,16 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image - name: build rootful docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64,linux/riscv64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
docker-rootless: docker-rootless:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -121,9 +110,7 @@ jobs:
- uses: docker/metadata-action@v5 - uses: docker/metadata-action@v5
id: meta id: meta
with: with:
images: |- images: gitea/gitea
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless # each tag below will have the suffix of -rootless
flavor: | flavor: |
latest=false latest=false
@@ -136,17 +123,11 @@ jobs:
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image - name: build rootless docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64,linux/riscv64 platforms: linux/amd64,linux/arm64
push: true push: true
file: Dockerfile.rootless file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}

View File

@@ -14,8 +14,6 @@ concurrency:
jobs: jobs:
binary: binary:
runs-on: namespace-profile-gitea-release-binary runs-on: namespace-profile-gitea-release-binary
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -25,10 +23,11 @@ jobs:
with: with:
go-version-file: go.mod go-version-file: go.mod
check-latest: true check-latest: true
- uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4
- uses: actions/setup-node@v5
with: with:
node-version: 24 node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend - run: make deps-frontend deps-backend
# xgo build # xgo build
- run: make release - run: make release
@@ -72,8 +71,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
docker-rootful: docker-rootful:
runs-on: namespace-profile-gitea-release-docker runs-on: namespace-profile-gitea-release-docker
permissions:
packages: write # to publish to ghcr.io
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
# fetch all commits instead of only the last as some branches are long lived and could have many between versions # fetch all commits instead of only the last as some branches are long lived and could have many between versions
@@ -84,34 +81,26 @@ jobs:
- uses: docker/metadata-action@v5 - uses: docker/metadata-action@v5
id: meta id: meta
with: with:
images: |- images: gitea/gitea
gitea/gitea
ghcr.io/go-gitea/gitea
# this will generate tags in the following format: # this will generate tags in the following format:
# latest # latest
# 1 # 1
# 1.2 # 1.2
# 1.2.3 # 1.2.3
tags: | tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootful docker image - name: build rootful docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64,linux/riscv64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
@@ -127,9 +116,7 @@ jobs:
- uses: docker/metadata-action@v5 - uses: docker/metadata-action@v5
id: meta id: meta
with: with:
images: |- images: gitea/gitea
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless # each tag below will have the suffix of -rootless
flavor: | flavor: |
suffix=-rootless,onlatest=true suffix=-rootless,onlatest=true
@@ -139,25 +126,19 @@ jobs:
# 1.2 # 1.2
# 1.2.3 # 1.2.3
tags: | tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}} type=semver,pattern={{major}}
type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{version}}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build rootless docker image - name: build rootless docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
platforms: linux/amd64,linux/arm64,linux/riscv64 platforms: linux/amd64,linux/arm64
push: true push: true
file: Dockerfile.rootless file: Dockerfile.rootless
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}

46
.gitignore vendored
View File

@@ -9,11 +9,6 @@ _test
# IntelliJ # IntelliJ
.idea .idea
.run
# IntelliJ Gateway
.uuid
# Goland's output filename can not be set manually # Goland's output filename can not be set manually
/go_build_* /go_build_*
/gitea_* /gitea_*
@@ -22,9 +17,6 @@ _test
.vscode .vscode
__debug_bin* __debug_bin*
# Visual Studio
/.vs/
*.cgo1.go *.cgo1.go
*.cgo2.c *.cgo2.c
_cgo_defun.c _cgo_defun.c
@@ -36,16 +28,19 @@ _testmain.go
*.exe *.exe
*.test *.test
*.prof *.prof
*.tsbuildinfo
*coverage.out *coverage.out
coverage.all coverage.all
cpu.out cpu.out
/modules/migration/bindata.* /modules/migration/bindata.go
/modules/options/bindata.* /modules/migration/bindata.go.hash
/modules/public/bindata.* /modules/options/bindata.go
/modules/templates/bindata.* /modules/options/bindata.go.hash
/modules/public/bindata.go
/modules/public/bindata.go.hash
/modules/templates/bindata.go
/modules/templates/bindata.go.hash
*.db *.db
*.log *.log
@@ -78,12 +73,23 @@ cpu.out
/yarn.lock /yarn.lock
/yarn-error.log /yarn-error.log
/npm-debug.log* /npm-debug.log*
/.pnpm-store
/public/assets/js /public/assets/js
/public/assets/css /public/assets/css
/public/assets/fonts /public/assets/fonts
/public/assets/licenses.txt /public/assets/licenses.txt
/vendor /vendor
/web_src/fomantic/node_modules
/web_src/fomantic/build/*
!/web_src/fomantic/build/semantic.js
!/web_src/fomantic/build/semantic.css
!/web_src/fomantic/build/themes
/web_src/fomantic/build/themes/*
!/web_src/fomantic/build/themes/default
/web_src/fomantic/build/themes/default/assets/*
!/web_src/fomantic/build/themes/default/assets/fonts
/web_src/fomantic/build/themes/default/assets/fonts/*
!/web_src/fomantic/build/themes/default/assets/fonts/icons.woff2
!/web_src/fomantic/build/themes/default/assets/fonts/outline-icons.woff2
/VERSION /VERSION
/.air /.air
/.go-licenses /.go-licenses
@@ -110,15 +116,3 @@ prime/
# Manpage # Manpage
/man /man
# Ignore AI/LLM instruction files
/.claude/
/.cursorrules
/.cursor/
/.goosehints
/.windsurfrules
/.github/copilot-instructions.md
/AGENT.md
/CLAUDE.md
/llms.txt

View File

@@ -43,7 +43,7 @@ vscode:
- Vue.volar - Vue.volar
- ms-azuretools.vscode-docker - ms-azuretools.vscode-docker
- vitest.explorer - vitest.explorer
- cweijan.vscode-database-client2 - qwtel.sqlite-viewer
- GitHub.vscode-pull-request-github - GitHub.vscode-pull-request-github
ports: ports:

View File

@@ -1,9 +1,7 @@
version: "2"
output:
sort-order:
- file
linters: linters:
default: none enable-all: false
disable-all: true
fast: false
enable: enable:
- bidichk - bidichk
- depguard - depguard
@@ -11,172 +9,134 @@ linters:
- errcheck - errcheck
- forbidigo - forbidigo
- gocritic - gocritic
- govet
- ineffassign
- mirror
- nakedret
- nolintlint
- perfsprint
- revive
- staticcheck
- testifylint
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- wastedassign
settings:
depguard:
rules:
main:
deny:
- pkg: encoding/json
desc: use gitea's modules/json instead of encoding/json
- pkg: github.com/unknwon/com
desc: use gitea's util and replacements
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: code.gitea.io/gitea/modules/git/internal
desc: do not use the internal package, use AddXxx function instead
- pkg: gopkg.in/ini.v1
desc: do not use the ini package, use gitea's config system instead
- pkg: gitea.com/go-chi/cache
desc: do not use the go-chi cache package, use gitea's cache system
nolintlint:
allow-unused: false
require-explanation: true
require-specific: true
gocritic:
enabled-checks:
- equalFold
disabled-checks:
- ifElseChain
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
revive:
severity: error
rules:
- name: atomic
- name: bare-return
- name: blank-imports
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: duplicated-imports
- name: empty-lines
- name: error-naming
- name: error-return
- name: error-strings
- name: errorf
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: indent-error-flow
- name: modifies-value-receiver
- name: package-comments
- name: range
- name: receiver-naming
- name: redefines-builtin-id
- name: string-of-int
- name: superfluous-else
- name: time-naming
- name: unconditional-recursion
- name: unexported-return
- name: unreachable-code
- name: var-declaration
- name: var-naming
arguments:
- [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
- [] # DenyList
- - skip-package-name-checks: true # supress errors from underscore in migration packages
staticcheck:
checks:
- all
- -ST1003
- -ST1005
- -QF1001
- -QF1006
- -QF1008
testifylint:
disable:
- go-require
- require-error
usetesting:
os-temp-dir: true
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- dupl
- errcheck
- gocyclo
- gosec
- staticcheck
- unparam
path: _test\.go
- linters:
- dupl
- errcheck
- gocyclo
- gosec
path: models/migrations/v
- linters:
- forbidigo
path: cmd
- linters:
- dupl
text: (?i)webhook
- linters:
- gocritic
text: (?i)`ID' should not be capitalized
- linters:
- deadcode
- unused
text: (?i)swagger
- linters:
- staticcheck
text: (?i)argument x is overwritten before first use
- linters:
- gocritic
text: '(?i)commentFormatting: put a space between `//` and comment text'
- linters:
- gocritic
text: '(?i)exitAfterDefer:'
paths:
- node_modules
- public
- web_src
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gofmt - gofmt
- gofumpt - gofumpt
settings: - gosimple
gofumpt: - govet
extra-rules: true - ineffassign
exclusions: - nakedret
generated: lax - nolintlint
paths: - revive
- node_modules - staticcheck
- public - stylecheck
- web_src - typecheck
- third_party$ - unconvert
- builtin$ - unused
- examples$ - wastedassign
run: run:
timeout: 10m timeout: 10m
output:
sort-results: true
linters-settings:
stylecheck:
checks: ["all", "-ST1005", "-ST1003"]
nakedret:
max-func-lines: 0
gocritic:
disabled-checks:
- ifElseChain
- singleCaseSwitch # Every time this occurred in the code, there was no other way.
revive:
ignore-generated-header: false
severity: warning
confidence: 0.8
errorCode: 1
warningCode: 1
rules:
- name: atomic
- name: bare-return
- name: blank-imports
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: duplicated-imports
- name: empty-lines
- name: error-naming
- name: error-return
- name: error-strings
- name: errorf
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: indent-error-flow
- name: modifies-value-receiver
- name: package-comments
- name: range
- name: receiver-naming
- name: redefines-builtin-id
- name: string-of-int
- name: superfluous-else
- name: time-naming
- name: unconditional-recursion
- name: unexported-return
- name: unreachable-code
- name: var-declaration
- name: var-naming
gofumpt:
extra-rules: true
depguard:
rules:
main:
deny:
- pkg: encoding/json
desc: use gitea's modules/json instead of encoding/json
- pkg: github.com/unknwon/com
desc: use gitea's util and replacements
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: code.gitea.io/gitea/modules/git/internal
desc: do not use the internal package, use AddXxx function instead
- pkg: gopkg.in/ini.v1
desc: do not use the ini package, use gitea's config system instead
- pkg: gitea.com/go-chi/cache
desc: do not use the go-chi cache package, use gitea's cache system
issues:
max-issues-per-linter: 0
max-same-issues: 0
exclude-dirs: [node_modules, public, web_src]
exclude-case-sensitive: true
exclude-rules:
- path: _test\.go
linters:
- gocyclo
- errcheck
- dupl
- gosec
- unparam
- staticcheck
- path: models/migrations/v
linters:
- gocyclo
- errcheck
- dupl
- gosec
- path: cmd
linters:
- forbidigo
- text: "webhook"
linters:
- dupl
- text: "`ID' should not be capitalized"
linters:
- gocritic
- text: "swagger"
linters:
- unused
- deadcode
- text: "argument x is overwritten before first use"
linters:
- staticcheck
- text: "commentFormatting: put a space between `//` and comment text"
linters:
- gocritic
- text: "exitAfterDefer:"
linters:
- gocritic

View File

@@ -1,6 +1,9 @@
*.min.css *.min.css
*.min.js *.min.js
/assets/*.json /assets/*.json
/modules/options/bindata.go
/modules/public/bindata.go
/modules/templates/bindata.go
/options/gitignore /options/gitignore
/options/license /options/license
/public/assets /public/assets

View File

@@ -1,2 +0,0 @@
Unknwon <u@gogs.io> <joe2010xtmf@163.com>
Unknwon <u@gogs.io> 无闻 <u@gogs.io>

5
.npmrc
View File

@@ -1,7 +1,6 @@
audit=false audit=false
fund=false fund=false
update-notifier=false update-notifier=false
package-lock=true
save-exact=true save-exact=true
auto-install-peers=true lockfile-version=3
dedupe-peer-dependents=false
enable-pre-post-scripts=true

View File

@@ -4,954 +4,6 @@ This changelog goes through the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.com). been added to each release, please refer to the [blog](https://blog.gitea.com).
## [1.24.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
* Fix LFS URL (#33840) (#33843)
* Update jwt and redis packages (#33984) (#33987)
* Update golang crypto and net (#33989)
* BUGFIXES
* Drop timeout for requests made to the internal hook api (#33947) (#33970)
* Fix maven panic when no package exists (#33888) (#33889)
* Fix markdown render (#33870) (#33875)
* Fix auto concurrency cancellation skips commit status updates (#33764) (#33849)
* Fix oauth2 auth (#33961) (#33962)
* Fix incorrect 1.23 translations (#33932)
* Try to figure out attribute checker problem (#33901) (#33902)
* Ignore trivial errors when updating push data (#33864) (#33887)
* Fix some UI problems for 1.23 (#33856)
* Removing unwanted ui container (#33833) (#33835)
* Support disable passkey auth (#33348) (#33819)
* Do not call "git diff" when listing PRs (#33817)
* Try to fix ACME (3rd) (#33807) (#33808)
* Fix incorrect code search indexer options (#33992) #33999
## [1.23.5](https://github.com/go-gitea/gitea/releases/tag/v1.23.5) - 2025-03-04
* SECURITY
* Bump x/oauth2 & x/crypto (#33704) (#33727)
* PERFORMANCE
* Optimize user dashboard loading (#33686) (#33708)
* BUGFIXES
* Fix navbar dropdown item align (#33782)
* Fix inconsistent closed issue list icon (#33722) (#33728)
* Fix for Maven Package Naming Convention Handling (#33678) (#33679)
* Improve Open-with URL encoding (#33666) (#33680)
* Deleting repository should unlink all related packages (#33653) (#33673)
* Fix omitempty bug (#33663) (#33670)
* Upgrade go-crypto from 1.1.4 to 1.1.6 (#33745) (#33754)
* Fix OCI image.version annotation for releases to use full semver (#33698) (#33701)
* Try to fix ACME path when renew (#33668) (#33693)
* Fix mCaptcha bug (#33659) (#33661)
* Git graph: don't show detached commits (#33645) (#33650)
* Use MatchPhraseQuery for bleve code search (#33628)
* Adjust appearence of commit status webhook (#33778) #33789
* Upgrade golang net from 0.35.0 -> 0.36.0 (#33795) #33796
## [1.23.4](https://github.com/go-gitea/gitea/releases/tag/v1.23.4) - 2025-02-16
* SECURITY
* Enhance routers for the Actions variable operations (#33547) (#33553)
* Enhance routers for the Actions runner operations (#33549) (#33555)
* Fix project issues list and counting (#33594) #33619
* PERFORMANCES
* Performance optimization for pull request files loading comments attachments (#33585) (#33592)
* BUGFIXES
* Add a transaction to `pickTask` (#33543) (#33563)
* Fix mirror bug (#33597) (#33607)
* Use default Git timeout when checking repo health (#33593) (#33598)
* Fix PR's target branch dropdown (#33589) (#33591)
* Fix various problems (artifact order, api empty slice, assignee check, fuzzy prompt, mirror proxy, adopt git) (#33569) (#33577)
* Rework suggestion backend (#33538) (#33546)
* Fix context usage (#33554) (#33557)
* Only show the latest version in the Arch index (#33262) (#33580)
* Skip deletion error for action artifacts (#33476) (#33568)
* Make actions URL in commit status webhooks absolute (#33620) #33632
* Add missing locale (#33641) #33642
## [1.23.3](https://github.com/go-gitea/gitea/releases/tag/v1.23.3) - 2025-02-06
* Security
* Build Gitea with Golang v1.23.6 to fix security bugs
* BUGFIXES
* Fix a bug caused by status webhook template #33512
## [1.23.2](https://github.com/go-gitea/gitea/releases/tag/1.23.2) - 2025-02-04
* BREAKING
* Add tests for webhook and fix some webhook bugs (#33396) (#33442)
* Package webhooks Organization was incorrectly used as the User struct. This PR fixes the issue.
* This changelog is just a hint. The change is not really breaking because most fields are the same, most users are not affected.
* ENHANCEMENTS
* Clone button enhancements (#33362) (#33404)
* Repo homepage styling tweaks (#33289) (#33381)
* Add a confirm dialog for "sync fork" (#33270) (#33273)
* Make tracked time representation display as hours (#33315) (#33334)
* Improve sync fork behavior (#33319) (#33332)
* BUGFIXES
* Fix code button alignment (#33345) (#33351)
* Correct bot label `vertical-align` (#33477) (#33480)
* Fix SSH LFS memory usage (#33455) (#33460)
* Fix issue sidebar dropdown keyboard support (#33447) (#33450)
* Fix user avatar (#33439)
* Fix `GetCommitBranchStart` bug (#33298) (#33421)
* Add pubdate for repository rss and add some tests (#33411) (#33416)
* Add missed auto merge feed message on dashboard (#33309) (#33405)
* Fix issue suggestion bug (#33389) (#33391)
* Make issue suggestion work for all editors (#33340) (#33342)
* Fix issue count (#33338) (#33341)
* Fix Account linking page (#33325) (#33327)
* Fix closed dependency title (#33285) (#33287)
* Fix sidebar milestone link (#33269) (#33272)
* Fix missing license when sync mirror (#33255) (#33258)
* Fix upload file form (#33230) (#33233)
* Fix mirror bug (#33224) (#33225)
* Fix system admin cannot fork or get private fork with API (#33401) (#33417)
* Fix push message behavior (#33215) (#33317)
* Trivial fixes (#33304) (#33312)
* Fix "stop time tracking button" on navbar (#33084) (#33300)
* Fix tag route and empty repo (#33253)
* Fix cache test triggered by non memory cache (#33220) (#33221)
* Revert empty lfs ref name (#33454) (#33457)
* Fix flex width (#33414) (#33418)
* Fix commit status events (#33320) #33493
* Fix unnecessary comment when moving issue on the same project column (#33496) #33499
* Add timetzdata build tag to binary releases (#33463) #33503
* MISC
* Use ProtonMail/go-crypto to replace keybase/go-crypto (#33402) (#33410)
* Update katex to latest version (#33361)
* Update go tool dependencies (#32916) (#33355)
## [1.23.1](https://github.com/go-gitea/gitea/releases/tag/v1.23.1) - 2025-01-09
* ENHANCEMENTS
* Move repo size to sidebar (#33155) (#33182)
* BUGFIXES
* Use updated path to s6-svscan after alpine upgrade (#33185) (#33188)
* Fix fuzz test (#33156) (#33158)
* Fix raw file API ref handling (#33172) (#33189)
* Fix ACME panic (#33178) (#33186)
* Fix branch dropdown not display ref name (#33159) (#33183)
* Fix assignee list overlapping in Issue sidebar (#33176) (#33181)
* Fix sync fork for consistency (#33147) #33192
* Fix editor markdown not incrementing in a numbered list (#33187) #33193
## [1.23.0](https://github.com/go-gitea/gitea/releases/tag/v1.23.0) - 2025-01-08
* BREAKING
* Rename config option `[camo].Allways` to `[camo].Always` (#32097)
* Remove SHA1 for support for ssh rsa signing (#31857)
* Use UTC as default timezone when schedule Actions cron tasks (#31742)
* Delete Actions logs older than 1 year by default (#31735)
* Make OIDC introspection authentication strictly require Client ID and secret (#31632)
* SECURITY
* Include file extension checks in attachment API (#32151)
* Include all security fixes which have been backported to v1.22
* FEATURES
* Allow to fork repository into the same owner (#32819)
* Support "merge upstream branch" (Sync fork) (#32741)
* Add Arch package registry (#32692)
* Allow to disable the password-based login (sign-in) form (#32687)
* Allow cropping an avatar before setting it (#32565)
* Support quote selected comments to reply (#32431)
* Add reviewers selection to new pull request (#32403)
* Suggestions for issues (#32327)
* Add priority to protected branch (#32286)
* Included tag search capabilities (#32045)
* Add option to filter board cards by labels and assignees (#31999)
* Add automatic light/dark option for the colorblind theme (#31997)
* Support migration from AWS CodeCommit (#31981)
* Introduce globallock as distributed locks (#31908 & #31813)
* Support compression for Actions logs & enable by default (#31761 & #32013)
* Add pure SSH LFS support (#31516)
* Add Passkey login support (#31504)
* Actions support workflow dispatch event (#28163)
* Support repo license (#24872)
* Issue time estimate, meaningful time tracking (#23113)
* GitHub like repo home page (#32213 & #32847)
* Rearrange Clone Panel (#31142)
* Enhancing Gitea OAuth2 Provider with Granular Scopes for Resource Access (#32573)
* Use env GITEA_RUNNER_REGISTRATION_TOKEN as global runner token (#32946) #32964
* Update i18n.go - Language Picker (#32933) #32935
* PERFORMANCE
* Perf: add extra index to notification table (#32395)
* Introduce OrgList and add LoadTeams, optimaze Load teams for orgs (#32543)
* Improve performance of diffs (#32393)
* Make LFS http_client parallel within a batch. (#32369)
* Add new index for action to resolve the performance problem (#32333)
* Improve get feed with pagination (#31821)
* Performance improvements for pull request list API (#30490)
* Use batch database operations instead of one by one to optimze api pulls (#32680)
* Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987) #33046
* ENHANCEMENTS
* Code
* Remove unnecessary border in repo home page sidebar (#32767)
* Add 'Copy path' button to file view (#32584)
* Improve diff file tree (#32658)
* Add new [lfs_client].BATCH_SIZE and [server].LFS_MAX_BATCH_SIZE config settings. (#32307)
* Updated tokenizer to better matching when search for code snippets (#32261)
* Change the code search to sort results by relevance (#32134)
* Support migrating GitHub/GitLab PR draft status (#32242)
* Move lock icon position and add additional tooltips to branch list page (#31839)
* Add tag name in the commits list (#31082)
* Add `MAX_ROWS` option for CSV rendering (#30268)
* Allow code search by filename (#32210)
* Make git push options accept short name (#32245)
* Repo file list enhancements (#32835)
* Markdown & Editor
* Refactor markdown math render, add dollor-backquote syntax support (#32831)
* Make Monaco theme follow browser, fully type codeeditor.ts (#32756)
* Refactor markdown editor and use it for milestone description editor (#32688)
* Add some handy markdown editor features (#32400)
* Improve markdown textarea for indentation and lists (#31406)
* Issue
* Add label/author/assignee filters to the user/org home issue list (#32779)
* Refactor issue filter (labels, poster, assignee) (#32771)
* Style unification for the issue_management area (#32605)
* Add "View all branches/tags" entry to Branch Selector (#32653)
* Improve textarea paste (#31948)
* Add avif image file support (#32508)
* Prevent from submitting issue/comment on uploading (#32263)
* Issue Templates: add option to have dropdown printed list (#31577)
* Allow searching issues by ID (#31479)
* Add `is_archived` option for issue indexer (#32735)
* Improve attachment upload methods (#30513)
* Support issue template assignees (#31083)
* Prevent simultaneous editing of comments and issues (#31053)
* Add issue comment when moving issues from one column to another of the project (#29311)
* Pull Request
* Display head branch more comfortable on pull request view (#32000)
* Simplify review UI (#31062)
* Allow force push to protected branches (#28086)
* Add line-through for deleted branch on pull request view page (#32500)
* Support requested_reviewers data in comment webhook events (#26178)
* Allow maintainers to view and edit files of private repos when "Allow maintainers to edit" is enabled (#32215)
* Allow including `Reviewed-on`/`Reviewed-by` lines for custom merge messages (#31211)
* Actions
* Render job title as commit message (#32748)
* Refactor RepoActionView.vue, add `::group::` support (#32713)
* Make RepoActionView.vue support `##[group]` (#32770)
* Support `pull_request_target` event for commit status (#31703)
* Detect whether action view branch was deleted (#32764)
* Allow users with write permission to run actions (#32644)
* Show latest run when visit /run/latest (#31808)
* Packages
* Improve rubygems package registry (#31357)
* Add support for npm bundleDependencies (#30751)
* Add signature support for the RPM module (#27069)
* Extract and display readme and comments for Composer packages (#30927)
* Project
* Add title to project view page (#32747)
* Set the columns height to hug all its contents (#31726)
* Rename project `board` -> `column` to make the UI less confusing (#30170)
* User & Organazition
* Use better name for userinfo structure (#32544)
* Use user.FullName in Oauth2 id_token response (#32542)
* Limit org member view of restricted users (#32211)
* Allow disabling authentication related user features (#31535)
* Add option to change mail from user display name (#31528)
* Use FullName in Emails to address the recipient if possible (#31527)
* Administration
* Add support for a credentials chain for minio access (#31051)
* Move admin routers from /admin to /-/admin (#32189)
* Add cache test for admins (#31265)
* Add option for mailer to override mail headers (#27860)
* Azure blob storage support (#30995)
* Supports forced use of S3 virtual-hosted style (#30969)
* Move repository visibility to danger zone in the settings area (#31126)
* Others
* Remove urls from translations (#31950)
* Simplify 404/500 page (#31409)
* Optimize installation-page experience (#32558)
* Refactor login page (#31530)
* Add new event commit status creation and webhook implementation (#27151)
* Repo Activity: count new issues that were closed (#31776)
* Set manual `tabindex`es on login page (#31689)
* Add `YEAR`, `MONTH`, `MONTH_ENGLISH`, `DAY` variables for template repos (#31584)
* Add typescript guideline and typescript-specific eslint plugins and fix issues (#31521)
* Make toast support preventDuplicates (#31501)
* Fix tautological conditions (#30735)
* Issue change title notifications (#33050) #33065
* API
* Implement update branch API (#32433)
* Fix missing outputs for jobs with matrix (#32823)
* Make API "compare" accept commit IDs (#32801)
* Add github compatible tarball download API endpoints (#32572)
* Harden runner updateTask and updateLog api (#32462)
* Add `DISABLE_ORGANIZATIONS_PAGE` and `DISABLE_CODE_PAGE` settings for explore pages and fix an issue related to user search (#32288)
* Make admins adhere to branch protection rules (#32248)
* Calculate `PublicOnly` for org membership only once (#32234)
* Allow filtering PRs by poster in the ListPullRequests API (#32209)
* Return 404 instead of error when commit not exist (#31977)
* Save initial signup information for users to aid in spam prevention (#31852)
* Fix upload maven pacakge parallelly (#31851)
* Fix null requested_reviewer from API (#31773)
* Add permission description for API to add repo collaborator (#31744)
* Add return type to GetRawFileOrLFS and GetRawFile (#31680)
* Add skip secondary authorization option for public oauth2 clients (#31454)
* Add tag protection via rest api #17862 (#31295)
* Document possible action types for the user activity feed API (#31196)
* Add topics for repository API (#31127)
* Add support for searching users by email (#30908)
* Add API endpoints for getting action jobs status (#26673)
* REFACTOR
* Update JS and PY dependencies (#31940)
* Enable `no-jquery/no-parse-html-literal` and fix violation (#31684)
* Refactor image diff (#31444)
* Refactor CSRF token (#32216)
* Fix some typescript issues (#32586)
* Refactor names (#31405)
* Use per package global lock for container uploads instead of memory lock (#31860)
* Move team related functions to service layer (#32537)
* Move GetFeeds to service layer (#32526)
* Resolve lint for unused parameter and unnecessary type arguments (#30750)
* Reimplement GetUserOrgsList to make it simple and clear (#32486)
* Move some functions from issue.go to standalone files (#32468)
* Refactor sidebar assignee&milestone&project selectors (#32465)
* Refactor sidebar label selector (#32460)
* Fix a number of typescript issues (#32459)
* Refactor language menu and dom utils (#32450)
* Refactor issue page info (#32445)
* Split issue sidebar into small templates (#32444)
* Refactor template ctx and render utils (#32422)
* Refactor repo legacy (#32404)
* Refactor markup package (#32399)
* Refactor markup render system (#32533 & #32589 & #32612)
* Refactor the DB migration system slightly (#32344)
* Remove jQuery import from some files (#32512)
* Strict pagination check (#32548)
* Split mail sender sub package from mailer service package (#32618)
* Remove outdated code about fixture generation (#32708)
* Refactor RepoBranchTagSelector (#32681)
* Refactor issue list (#32755)
* Refactor LabelEdit (#32752)
* Split issue/pull view router function as multiple smaller functions (#32749)
* Refactor some LDAP code (#32849)
* Unify repo search order by logic (#30876)
* Remove duplicate empty repo check in delete branch API (#32569)
* Replace deprecated `math/rand` functions (#30733)
* Remove fomantic dimmer module (#30723)
* Add types to fetch,toast,bootstrap,svg (#31627)
* Refactor webhook (#31587)
* Move AddCollabrator and CreateRepositoryByExample to service layer (#32419)
* Refactor RepoRefByType (#32413)
* Refactor: remove redundant err declarations (#32381)
* Refactor markup code (#31399)
* Refactor render system (orgmode) (#32671)
* Refactor render system (#32492)
* Refactor markdown render (#32736 & #32728)
* Refactor repo unit "disabled" check (#31389)
* Refactor route path normalization (#31381)
* Refactor to use UnsafeStringToBytes (#31358)
* Migrate vue components to setup (#32329)
* Refactor globallock (#31933)
* Use correct function name (#31887)
* Use a common message template instead of a special one (#31878)
* Fix a number of Typescript issues (#31877)
* Refactor dropzone (#31482)
* Move custom `tw-` helpers to tailwind plugin (#31184)
* Replace `gt-word-break` with `tw-break-anywhere` (#31183)
* Drop `IDOrderDesc` for listing Actions task and always order by `id DESC` (#31150)
* Split common-global.js into separate files (#31438)
* Improve detecting empty files (#31332)
* Use `querySelector` over alternative DOM methods (#31280)
* Remove jQuery `.text()` (#30506)
* Use repo as of renderctx's member rather than a repoPath on metas (#29222)
* Refactor some frontend problems (#32646)
* Refactor DateUtils and merge TimeSince (#32409)
* Replace DateTime with proper functions (#32402)
* Replace DateTime with DateUtils (#32383)
* Convert frontend code to typescript (#31559)
* Refactor maven package registry (#33049) #33057
* Refactor testfixtures #33028
* BUGFIXES
* Fix issues with inconsistent spacing in areas (#32607)
* Fix incomplete Actions status aggregations (#32859)
* In some lfs server implementations, they require the ref attribute. (#32838)
* Update the list of watchers and stargazers when clicking watch/unwatch or star/unstar (#32570)
* Fix `recentupdate` sorting bugs (#32505)
* Fix incorrect "Target branch does not exist" in PR title (#32222)
* Handle "close" actionable references for manual merges (#31879)
* render plain text file if the LFS object doesn't exist (#31812)
* Fix Null Pointer error for CommitStatusesHideActionsURL (#31731)
* Fix loadRepository error when access user dashboard (#31719)
* Hide the "Details" link of commit status when the user cannot access actions (#30156)
* Fix duplicate dropdown dividers (#32760)
* Fix SSPI button visibility when SSPI is the only enabled method (#32841)
* Fix overflow on org header (#32837)
* Exclude protected branches from recently pushed (#31748)
* Fix large image overflow in comment page (#31740)
* Fix milestone deadline and date related problems (#32339)
* Fix markdown preview $$ support (#31514)
* Fix a compilation error in the Gitpod environment (#32559)
* Fix PR diff review form submit (#32596)
* Fix a number of typescript issues (#32308)
* Fix some function names in comment (#32300)
* Fix absolute-date (#32375)
* Clarify Actions resources ownership (#31724)
* Try to fix ACME directory problem (#33072) #33077
* Inherit submodules from template repository content (#16237) #33068
* Use project's redirect url instead of composing url (#33058) #33064
* Fix toggle commit body button ui when latest commit message is long (#32997) #33034
* Fix package error handling and npm meta and empty repo guide #33112
* Fix empty git repo handling logic and fix mobile view (#33101) #33102
* Fix line-number and scroll bugs (#33094) #33095
* Fix bleve fuzziness search (#33078) #33087
* Fix broken forms #33082
* Fix empty repo updated time (#33120) #33124
* Add missing transaction when set merge #33113
* Fix issue comment number (#30556) #33055
* Fix duplicate co-author in squashed merge commit messages (#33020) #33054
* Fix Agit pull request permission check (#32999) #33005
* Fix scoped label ui when contains emoji (#33007) #33014
* Fix bug on activities (#33008) #33016
* Fix review code comment avatar alignment (#33031) #33032
* Fix templating in pull request comparison (#33025) #33038
* Fix bug automerge cannot be chosed when there is only 1 merge style (#33040) #33043
* Fix settings not being loaded at CLI (#26402) #33048
* Support for email addresses containing uppercase characters when activating user account (#32998) #33001
* Support org labels when adding labels by label names (#32988) #32996
* Do not render truncated links in markdown (#32980) #32983
* Demilestone should not include milestone (#32923) #32979
* Fix Azure blob object Seek (#32974) #32975
* Fix maven pom inheritance (#32943) #32976
* Fix textarea newline handle (#32966) #32977
* Fix outdated tmpl code (#32953) #32961
* Fix commit range paging (#32944) #32962
* Fix repo avatar conflict (#32958) #32960
* Fix trailing comma not matched in the case of alphanumeric issue (#32945)
* Relax the version checking for Arch packages (#32908) #32913
* Add more load functions to make sure the reference object loaded (#32901) #32912
* Filter reviews of one pull request in memory instead of database to reduce slow response because of lacking database index (#33106) #33128
* Fix git remote error check, fix dependencies, fix js error (#33129) #33133
* MISC
* Optimize branch protection rule loading (#32280)
* Bump to go 1.23 (#31855)
* Remove unused call to $.HeadRepo in view_title template (#32317)
* Do not display `attestation-manifest` and use short sha256 instead of full sha256 (#32851)
* Upgrade htmx to 2.0.4 (#32834)
* Improve JSX/TSX support in code editor (#32833)
* Add User-Agent for gitea's self-implemented lfs client. (#32832)
* Use errors.New to replace fmt.Errorf with no parameters (#32800)
* Add "n commits" link to contributors in contributors graph page (#32799)
* Update dependencies, tweak eslint (#32719)
* Remove all "floated" CSS styles (#32691)
* Show tag name on branch/tag selector if repo shown from tag ref (#32689)
* Use new mail package instead of an unmintained one (#32682)
* Optimize the styling of icon buttons within file-header-right (#32675)
* Validate OAuth Redirect URIs (#32643)
* Support optional/configurable IAMEndpoint for Minio Client (#32581) (#32581)
* Make search box in issue sidebar dropdown list always show when scrolling (#32576)
* Bump CI,Flake and Snap to Node 22 (#32487)
* Update `github.com/meilisearch/meilisearch-go` (#32484)
* Add `DEFAULT_MIRROR_REPO_UNITS` and `DEFAULT_TEMPLATE_REPO_UNITS` options (#32416)
* Update go dependencies (#32389)
* Update JS and PY dependencies (#32388)
* Upgrade rollup to 4.24.0 (#32312)
* Upgrade vue to 3.5.12 (#32311)
* Improve the maintainblity of the reserved username list (#32229)
* Upgrade htmx to 2.0.3 (#32192)
* Count typescript files as frontend for labeling (#32088)
* Only use Host header from reverse proxy (#32060)
* Failed authentications are logged to level Warning (#32016)
* Enhance USER_DISABLED_FEATURES to allow disabling change username or full name (#31959)
* Distinguish official vs non-official reviews, add tool tips, and upgr… (#31924)
* Update mermaid to v11 (#31913)
* Bump relative-time-element to v4.4.3 (#31910)
* Upgrade `htmx` to `2.0.2` (#31847)
* Add warning message in merge instructions when `AutodetectManualMerge` was not enabled (#31805)
* Add types to various low-level functions (#31781)
* Update JS dependencies (#31766)
* Remove unused code from models/repos/release.go (#31756)
* Support delete user email in admin panel (#31690)
* Add `username` to OIDC introspection response (#31688)
* Use GetDisplayName() instead of DisplayName() to generate rss feeds (#31687)
* Code editor theme enhancements (#31629)
* Update JS dependencies (#31616)
* Add types for js globals (#31586)
* Add back esbuild-loader for .js files (#31585)
* Don't show hidden labels when filling out an issue template (#31576)
* Allow synchronizing user status from OAuth2 login providers (#31572)
* Display app name in the registration email title (#31562)
* Use stable version of fabric (#31526)
* Support legacy _links LFS batch responses (#31513)
* Fix JS error with disabled attachment and easymde (#31511)
* Always use HTML attributes for avatar size (#31509)
* Use nolyfill to remove some polyfills (#31468)
* Disable issue/PR comment button given empty input (#31463)
* Add simple JS init performance trace (#31459)
* Bump htmx to 2.0.0 (#31413)
* Update JS dependencies, remove `eslint-plugin-jquery` (#31402)
* Split org Propfile README to a new tab `overview` (#31373)
* Update nix flake and add gofumpt (#31320)
* Code optimization (#31315)
* Enable poetry non-package mode (#31282)
* Optimize profile layout to enhance visual experience (#31278)
* Update `golang.org/x/net` (#31260)
* Bump `@github/relative-time-element` to v4.4.1 (#31232)
* Remove unnecessary inline style for tab-size (#31224)
* Update golangci-lint to v1.59.0 (#31221)
* Update chroma to v2.14.0 (#31177)
* Update JS dependencies (#31120)
* Improve the handling of `jobs.<job_id>.if` (#31070)
* Clean up revive linter config, tweak golangci output (#30980)
* Use CSS `inset` shorthand (#30939)
* Forbid deprecated `break-word` in CSS (#30934)
* Remove obsolete monaco workaround (#30893)
* Update JS dependencies, add new eslint rules (#30840)
* Fix body margin shifting with modals, fix error on project column edit (#30831)
* Remove disk-clean workflow (#30741)
* Bump `github.com/google/go-github` to v61 (#30738)
* Add built js files to eslint ignore (#30737)
* Use `ProtonMail/go-crypto` for `opengpg` in tests (#30736)
* Upgrade xorm to v1.3.9 and improve some migrations Sync (#29899)
* Added default sorting milestones by name (#27084)
* Enable `unparam` linter (#31277)
* Use Alpine 3.21 for the docker images (#32924) #32951
* Bump x/net (#32896) #32899
* Use -s -w ldflags for release artifacts (#33041) #33042
* Remove aws go sdk package dependency (#33029) #33047
## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12 ## [1.22.6](https://github.com/go-gitea/gitea/releases/tag/v1.22.6) - 2024-12-12
* SECURITY * SECURITY

View File

@@ -30,7 +30,7 @@ These are the values to which people in the Gitea community should aspire.
- **Be constructive.** - **Be constructive.**
- Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation. - Avoid derailing: stay on topic; if you want to talk about something else, start a new conversation.
- Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved. - Avoid unconstructive criticism: don't merely decry the current state of affairs; offer—or at least solicit—suggestions as to how things may be improved.
- Avoid snarking (pithy, unproductive, sniping comments). - Avoid snarking (pithy, unproductive, sniping comments)
- Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict. - Avoid discussing potentially offensive or sensitive issues; this all too often leads to unnecessary conflict.
- Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group). - Avoid microaggressions (brief and commonplace verbal, behavioral and environmental indignities that communicate hostile, derogatory or negative slights and insults to a person or group).
- **Be responsible.** - **Be responsible.**
@@ -42,7 +42,7 @@ People are complicated. You should expect to be misunderstood and to misundersta
### Our Pledge ### Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
### Our Standards ### Our Standards

View File

@@ -182,7 +182,7 @@ Here's how to run the test suite:
## Translation ## Translation
All translation work happens on [Crowdin](https://translate.gitea.com). All translation work happens on [Crowdin](https://crowdin.com/project/gitea).
The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini). The only translation that is maintained in this repository is [the English translation](https://github.com/go-gitea/gitea/blob/main/options/locale/locale_en-US.ini).
It is synced regularly with Crowdin. \ It is synced regularly with Crowdin. \
Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \ Other locales on main branch **should not** be updated manually as they will be overwritten with each sync. \
@@ -358,8 +358,7 @@ $REWRITTEN_PR_SUMMARY
## Documentation ## Documentation
If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in another PR at [https://gitea.com/gitea/docs](https://gitea.com/gitea/docs). If you add a new feature or change an existing aspect of Gitea, the documentation for that feature must be created or updated in the same PR.
**The docs directory on main repository will be removed at some time. We will have a yaml file to store configuration file's meta data. After that completed, configuration documentation should be in the main repository.**
## API v1 ## API v1
@@ -591,7 +590,7 @@ be reviewed by two maintainers and must pass the automatic tests.
## Releasing Gitea ## Releasing Gitea
- Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future. - Let $vmaj, $vmin and $vpat be Major, Minor and Patch version numbers, $vpat should be rc1, rc2, 0, 1, ...... $vmaj.$vmin will be kept the same as milestones on github or gitea in future.
- Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on Discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody is against it in about several hours. - Before releasing, confirm all the version's milestone issues or PRs has been resolved. Then discuss the release on Discord channel #maintainers and get agreed with almost all the owners and mergers. Or you can declare the version and if nobody against in about serval hours.
- If this is a big version first you have to create PR for changelog on branch `main` with PRs with label `changelog` and after it has been merged do following steps: - If this is a big version first you have to create PR for changelog on branch `main` with PRs with label `changelog` and after it has been merged do following steps:
- Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`. - Create `-dev` tag as `git tag -s -F release.notes v$vmaj.$vmin.0-dev` and push the tag as `git push origin v$vmaj.$vmin.0-dev`.
- When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin` - When CI has finished building tag then you have to create a new branch named `release/v$vmaj.$vmin`

View File

@@ -1,5 +1,5 @@
# Build stage # Build stage
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct} ENV GOPROXY=${GOPROXY:-direct}
@@ -15,7 +15,6 @@ RUN apk --no-cache add \
git \ git \
nodejs \ nodejs \
npm \ npm \
&& npm install -g pnpm@10 \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
# Setup repo # Setup repo
@@ -40,8 +39,9 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \
/tmp/local/etc/s6/.s6-svscan/* \ /tmp/local/etc/s6/.s6-svscan/* \
/go/src/code.gitea.io/gitea/gitea \ /go/src/code.gitea.io/gitea/gitea \
/go/src/code.gitea.io/gitea/environment-to-ini /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.22 FROM docker.io/library/alpine:3.20
LABEL maintainer="maintainers@gitea.io" LABEL maintainer="maintainers@gitea.io"
EXPOSE 22 3000 EXPOSE 22 3000
@@ -78,8 +78,9 @@ ENV GITEA_CUSTOM=/data/gitea
VOLUME ["/data"] VOLUME ["/data"]
ENTRYPOINT ["/usr/bin/entrypoint"] ENTRYPOINT ["/usr/bin/entrypoint"]
CMD ["/usr/bin/s6-svscan", "/etc/s6"] CMD ["/bin/s6-svscan", "/etc/s6"]
COPY --from=build-env /tmp/local / COPY --from=build-env /tmp/local /
COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea COPY --from=build-env /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini COPY --from=build-env /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh

View File

@@ -1,5 +1,5 @@
# Build stage # Build stage
FROM docker.io/library/golang:1.25-alpine3.22 AS build-env FROM docker.io/library/golang:1.22-alpine3.20 AS build-env
ARG GOPROXY ARG GOPROXY
ENV GOPROXY=${GOPROXY:-direct} ENV GOPROXY=${GOPROXY:-direct}
@@ -15,7 +15,6 @@ RUN apk --no-cache add \
git \ git \
nodejs \ nodejs \
npm \ npm \
&& npm install -g pnpm@10 \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
# Setup repo # Setup repo
@@ -38,8 +37,9 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \
/tmp/local/usr/local/bin/gitea \ /tmp/local/usr/local/bin/gitea \
/go/src/code.gitea.io/gitea/gitea \ /go/src/code.gitea.io/gitea/gitea \
/go/src/code.gitea.io/gitea/environment-to-ini /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.22 FROM docker.io/library/alpine:3.20
LABEL maintainer="maintainers@gitea.io" LABEL maintainer="maintainers@gitea.io"
EXPOSE 2222 3000 EXPOSE 2222 3000
@@ -52,7 +52,6 @@ RUN apk --no-cache add \
git \ git \
curl \ curl \
gnupg \ gnupg \
openssh-keygen \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
RUN addgroup \ RUN addgroup \
@@ -72,6 +71,7 @@ RUN chown git:git /var/lib/gitea /etc/gitea
COPY --from=build-env /tmp/local / COPY --from=build-env /tmp/local /
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/gitea /app/gitea/gitea
COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini COPY --from=build-env --chown=root:root /go/src/code.gitea.io/gitea/environment-to-ini /usr/local/bin/environment-to-ini
COPY --from=build-env /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete /etc/profile.d/gitea_bash_autocomplete.sh
# git:git # git:git
USER 1000:1000 USER 1000:1000

View File

@@ -31,18 +31,22 @@ Gary Kim <gary@garykim.dev> (@gary-kim)
Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k) Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
Mura Li <typeless@ctli.io> (@typeless) Mura Li <typeless@ctli.io> (@typeless)
6543 <6543@obermui.de> (@6543) 6543 <6543@obermui.de> (@6543)
jaqra <jaqra@hotmail.com> (@jaqra)
David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson) David Svantesson <davidsvantesson@gmail.com> (@davidsvantesson)
a1012112796 <1012112796@qq.com> (@a1012112796) a1012112796 <1012112796@qq.com> (@a1012112796)
Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise) Karl Heinz Marbaise <kama@soebes.de> (@khmarbaise)
Norwin Roosen <git@nroo.de> (@noerw) Norwin Roosen <git@nroo.de> (@noerw)
Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu) Kyle Dumont <kdumontnu@gmail.com> (@kdumontnu)
Patrick Schratz <patrick.schratz@gmail.com> (@pat-s)
Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R) Janis Estelmann <admin@oldschoolhack.me> (@KN4CK3R)
Steven Kriegler <sk.bunsenbrenner@gmail.com> (@justusbunsi)
Jimmy Praet <jimmy.praet@telenet.be> (@jpraet) Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
Leon Hofmeister <dev.lh@web.de> (@delvh) Leon Hofmeister <dev.lh@web.de> (@delvh)
Wim <wim@42.be> (@42wim) Wim <wim@42.be> (@42wim)
Jason Song <i@wolfogre.com> (@wolfogre) Jason Song <i@wolfogre.com> (@wolfogre)
Yarden Shoham <git@yardenshoham.com> (@yardenshoham) Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
Yu Tian <zettat123@gmail.com> (@Zettat123) Yu Tian <zettat123@gmail.com> (@Zettat123)
Eddie Yang <576951401@qq.com> (@yp05327)
Dong Ge <gedong_1994@163.com> (@sillyguodong) Dong Ge <gedong_1994@163.com> (@sillyguodong)
Xinyi Gong <hestergong@gmail.com> (@HesterG) Xinyi Gong <hestergong@gmail.com> (@HesterG)
wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang) wxiaoguang <wxiaoguang@gmail.com> (@wxiaoguang)
@@ -57,10 +61,3 @@ kerwin612 <kerwin612@qq.com> (@kerwin612)
Gary Wang <git@blumia.net> (@BLumia) Gary Wang <git@blumia.net> (@BLumia)
Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis) Tim-Niclas Oelschläger <zokki.softwareschmiede@gmail.com> (@zokkis)
Yu Liu <1240335630@qq.com> (@HEREYUA) Yu Liu <1240335630@qq.com> (@HEREYUA)
Kemal Zebari <kemalzebra@gmail.com> (@kemzeb)
Rowan Bohde <rowan.bohde@gmail.com> (@bohde)
hiifong <i@hiif.ong> (@hiifong)
metiftikci <metiftikci@hotmail.com> (@metiftikci)
Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
TheFox <thefox0x7@gmail.com> (@TheFox0x7)

434
Makefile
View File

@@ -23,21 +23,19 @@ SHASUM ?= shasum -a 256
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes) HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
COMMA := , COMMA := ,
XGO_VERSION := go-1.25.x XGO_VERSION := go-1.22.x
AIR_PACKAGE ?= github.com/air-verse/air@v1 AIR_PACKAGE ?= github.com/air-verse/air@v1
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.9.1 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.4.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.7.0 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@717e3cb29becaaf00e56953556c6d80f8a01b286 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1 GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1 ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.20.0
GOPLS_MODERNIZE_PACKAGE ?= golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@v0.20.0
DOCKER_IMAGE ?= gitea/gitea DOCKER_IMAGE ?= gitea/gitea
DOCKER_TAG ?= latest DOCKER_TAG ?= latest
@@ -48,17 +46,6 @@ ifeq ($(HAS_GO), yes)
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
endif endif
CGO_ENABLED ?= 0
ifneq (,$(findstring sqlite,$(TAGS))$(findstring pam,$(TAGS)))
CGO_ENABLED = 1
endif
STATIC ?=
EXTLDFLAGS ?=
ifneq ($(STATIC),)
EXTLDFLAGS = -extldflags "-static"
endif
ifeq ($(GOOS),windows) ifeq ($(GOOS),windows)
IS_WINDOWS := yes IS_WINDOWS := yes
else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
@@ -85,26 +72,17 @@ EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1) MAKE_VERSION := $(shell "$(MAKE)" -v | cat | head -n 1)
MAKE_EVIDENCE_DIR := .make_evidence MAKE_EVIDENCE_DIR := .make_evidence
GOTESTFLAGS ?=
ifeq ($(RACE_ENABLED),true) ifeq ($(RACE_ENABLED),true)
GOFLAGS += -race GOFLAGS += -race
GOTESTFLAGS += -race GOTESTFLAGS += -race
endif endif
STORED_VERSION_FILE := VERSION STORED_VERSION_FILE := VERSION
HUGO_VERSION ?= 0.111.3
GITHUB_REF_TYPE ?= branch GITHUB_REF_TYPE ?= branch
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD) GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
# Enable typescript support in Node.js before 22.18
# TODO: Remove this once we can raise the minimum Node.js version to 22.18 (alpine >= 3.23)
NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v 2>/dev/null | cut -c2- | tr '.' ' '))
ifeq ($(shell test "$(NODE_VERSION)" -lt "022018000"; echo $$?),0)
NODE_VARS := NODE_OPTIONS="--experimental-strip-types"
else
NODE_VARS :=
endif
ifneq ($(GITHUB_REF_TYPE),branch) ifneq ($(GITHUB_REF_TYPE),branch)
VERSION ?= $(subst v,,$(GITHUB_REF_NAME)) VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
GITEA_VERSION ?= $(VERSION) GITEA_VERSION ?= $(VERSION)
@@ -130,17 +108,20 @@ endif
LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64 LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/)) GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list code.gitea.io/gitea/models/migrations/...)
FOMANTIC_WORK_DIR := web_src/fomantic
WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f) WEBPACK_SOURCES := $(shell find web_src/js web_src/css -type f)
WEBPACK_CONFIGS := webpack.config.ts tailwind.config.ts WEBPACK_CONFIGS := webpack.config.js tailwind.config.js
WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css WEBPACK_DEST := public/assets/js/index.js public/assets/css/index.css
WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts WEBPACK_DEST_ENTRIES := public/assets/js public/assets/css public/assets/fonts
BINDATA_DEST_WILDCARD := modules/migration/bindata.* modules/public/bindata.* modules/options/bindata.* modules/templates/bindata.* BINDATA_DEST := modules/public/bindata.go modules/options/bindata.go modules/templates/bindata.go
BINDATA_HASH := $(addsuffix .hash,$(BINDATA_DEST))
GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go GENERATED_GO_DEST := modules/charset/invisible_gen.go modules/charset/ambiguous_gen.go
@@ -157,19 +138,25 @@ TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify TEST_TAGS ?= $(TAGS_SPLIT) sqlite sqlite_unlock_notify
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR) TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMANTIC_WORK_DIR)/node_modules $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR) $(GO_LICENSE_TMP_DIR)
GO_DIRS := build cmd models modules routers services tests GO_DIRS := build cmd models modules routers services tests
WEB_DIRS := web_src/js web_src/css WEB_DIRS := web_src/js web_src/css
ESLINT_FILES := web_src/js tools *.ts tests/e2e ESLINT_FILES := web_src/js tools *.js tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue STYLELINT_FILES := web_src/css web_src/js/components/*.vue
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml)) $(filter-out tools/misspellings.csv, $(wildcard tools/*)) SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(filter-out CHANGELOG.md, $(wildcard *.go *.js *.md *.yml *.yaml *.toml))
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.ini
GO_SOURCES := $(wildcard *.go) GO_SOURCES := $(wildcard *.go)
GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go") GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go)
GO_SOURCES += $(GENERATED_GO_DEST) GO_SOURCES += $(GENERATED_GO_DEST)
GO_SOURCES_NO_BINDATA := $(GO_SOURCES)
ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
GO_SOURCES += $(BINDATA_DEST)
GENERATED_GO_DEST += $(BINDATA_DEST)
endif
# Force installation of playwright dependencies by setting this flag # Force installation of playwright dependencies by setting this flag
ifdef DEPS_PLAYWRIGHT ifdef DEPS_PLAYWRIGHT
@@ -177,8 +164,10 @@ ifdef DEPS_PLAYWRIGHT
endif endif
SWAGGER_SPEC := templates/swagger/v1_json.tmpl SWAGGER_SPEC := templates/swagger/v1_json.tmpl
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g
SWAGGER_EXCLUDE := code.gitea.io/sdk SWAGGER_EXCLUDE := code.gitea.io/sdk
SWAGGER_NEWLINE_COMMAND := -e '$$a\'
TEST_MYSQL_HOST ?= mysql:3306 TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea TEST_MYSQL_DBNAME ?= testgitea
@@ -189,7 +178,6 @@ TEST_PGSQL_DBNAME ?= testgitea
TEST_PGSQL_USERNAME ?= postgres TEST_PGSQL_USERNAME ?= postgres
TEST_PGSQL_PASSWORD ?= postgres TEST_PGSQL_PASSWORD ?= postgres
TEST_PGSQL_SCHEMA ?= gtestschema TEST_PGSQL_SCHEMA ?= gtestschema
TEST_MINIO_ENDPOINT ?= minio:9000
TEST_MSSQL_HOST ?= mssql:1433 TEST_MSSQL_HOST ?= mssql:1433
TEST_MSSQL_DBNAME ?= gitea TEST_MSSQL_DBNAME ?= gitea
TEST_MSSQL_USERNAME ?= sa TEST_MSSQL_USERNAME ?= sa
@@ -199,11 +187,66 @@ TEST_MSSQL_PASSWORD ?= MwantsaSecurePassword1
all: build all: build
.PHONY: help .PHONY: help
help: Makefile ## print Makefile help information. help:
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile #$(MAKEFILE_LIST) @echo "Make Routines:"
@printf " \033[36m%-46s\033[0m %s\n" "test-e2e[#TestSpecificName]" "test end to end using playwright" @echo " - \"\" equivalent to \"build\""
@printf " \033[36m%-46s\033[0m %s\n" "test[#TestSpecificName]" "run unit test" @echo " - build build everything"
@printf " \033[36m%-46s\033[0m %s\n" "test-sqlite[#TestSpecificName]" "run integration test for sqlite" @echo " - frontend build frontend files"
@echo " - backend build backend files"
@echo " - watch watch everything and continuously rebuild"
@echo " - watch-frontend watch frontend files and continuously rebuild"
@echo " - watch-backend watch backend files and continuously rebuild"
@echo " - clean delete backend and integration files"
@echo " - clean-all delete backend, frontend and integration files"
@echo " - deps install dependencies"
@echo " - deps-frontend install frontend dependencies"
@echo " - deps-backend install backend dependencies"
@echo " - deps-tools install tool dependencies"
@echo " - deps-py install python dependencies"
@echo " - lint lint everything"
@echo " - lint-fix lint everything and fix issues"
@echo " - lint-actions lint action workflow files"
@echo " - lint-frontend lint frontend files"
@echo " - lint-frontend-fix lint frontend files and fix issues"
@echo " - lint-backend lint backend files"
@echo " - lint-backend-fix lint backend files and fix issues"
@echo " - lint-go lint go files"
@echo " - lint-go-fix lint go files and fix issues"
@echo " - lint-go-vet lint go files with vet"
@echo " - lint-js lint js files"
@echo " - lint-js-fix lint js files and fix issues"
@echo " - lint-css lint css files"
@echo " - lint-css-fix lint css files and fix issues"
@echo " - lint-md lint markdown files"
@echo " - lint-swagger lint swagger files"
@echo " - lint-templates lint template files"
@echo " - lint-yaml lint yaml files"
@echo " - lint-spell lint spelling"
@echo " - lint-spell-fix lint spelling and fix issues"
@echo " - checks run various consistency checks"
@echo " - checks-frontend check frontend files"
@echo " - checks-backend check backend files"
@echo " - test test everything"
@echo " - test-frontend test frontend files"
@echo " - test-backend test backend files"
@echo " - test-e2e[\#TestSpecificName] test end to end using playwright"
@echo " - update update js and py dependencies"
@echo " - update-js update js dependencies"
@echo " - update-py update py dependencies"
@echo " - webpack build webpack files"
@echo " - svg build svg files"
@echo " - fomantic build fomantic files"
@echo " - generate run \"go generate\""
@echo " - fmt format the Go code"
@echo " - generate-license update license files"
@echo " - generate-gitignore update gitignore files"
@echo " - generate-manpage generate manpage"
@echo " - generate-swagger generate the swagger spec from code comments"
@echo " - swagger-validate check if the swagger spec is valid"
@echo " - go-licenses regenerate go licenses"
@echo " - tidy run go mod tidy"
@echo " - test[\#TestSpecificName] run unit test"
@echo " - test-sqlite[\#TestSpecificName] run integration test for sqlite"
.PHONY: go-check .PHONY: go-check
go-check: go-check:
@@ -226,23 +269,20 @@ git-check:
node-check: node-check:
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p')) $(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' '))) $(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
$(eval PNPM_MISSING := $(shell hash pnpm > /dev/null 2>&1 || echo 1)) $(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" ]; then \ $(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater to build. You can get it at https://nodejs.org/en/download/"; \ @if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
exit 1; \ echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
fi
@if [ "$(PNPM_MISSING)" = "1" ]; then \
echo "Gitea requires pnpm to build. You can install it at https://pnpm.io/installation"; \
exit 1; \ exit 1; \
fi fi
.PHONY: clean-all .PHONY: clean-all
clean-all: clean ## delete backend, frontend and integration files clean-all: clean
rm -rf $(WEBPACK_DEST_ENTRIES) node_modules rm -rf $(WEBPACK_DEST_ENTRIES) node_modules
.PHONY: clean .PHONY: clean
clean: ## delete backend and integration files clean:
rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST_WILDCARD) \ rm -rf $(EXECUTABLE) $(DIST) $(BINDATA_DEST) $(BINDATA_HASH) \
integrations*.test \ integrations*.test \
e2e*.test \ e2e*.test \
tests/integration/gitea-integration-* \ tests/integration/gitea-integration-* \
@@ -253,7 +293,7 @@ clean: ## delete backend and integration files
tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/ tests/e2e/reports/ tests/e2e/test-artifacts/ tests/e2e/test-snapshots/
.PHONY: fmt .PHONY: fmt
fmt: ## format the Go and template code fmt:
@GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' @GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
$(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl')) $(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl'))
@# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only @# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only
@@ -268,20 +308,7 @@ fmt-check: fmt
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \ @diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \ echo "Please run 'make fmt' and commit the result:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
exit 1; \
fi
.PHONY: fix
fix: ## apply automated fixes to Go code
$(GO) run $(GOPLS_MODERNIZE_PACKAGE) -fix ./...
.PHONY: fix-check
fix-check: fix
@diff=$$(git diff --color=always $(GO_SOURCES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fix' and commit the result:"; \
printf "%s" "$${diff}"; \
exit 1; \ exit 1; \
fi fi
@@ -295,95 +322,93 @@ TAGS_PREREQ := $(TAGS_EVIDENCE)
endif endif
.PHONY: generate-swagger .PHONY: generate-swagger
generate-swagger: $(SWAGGER_SPEC) ## generate the swagger spec from code comments generate-swagger: $(SWAGGER_SPEC)
$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT) $(SWAGGER_SPEC): $(GO_SOURCES_NO_BINDATA)
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' $(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
.PHONY: swagger-check .PHONY: swagger-check
swagger-check: generate-swagger swagger-check: generate-swagger
@diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \ @diff=$$(git diff --color=always '$(SWAGGER_SPEC)'); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "Please run 'make generate-swagger' and commit the result:"; \ echo "Please run 'make generate-swagger' and commit the result:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
exit 1; \ exit 1; \
fi fi
.PHONY: swagger-validate .PHONY: swagger-validate
swagger-validate: ## check if the swagger spec is valid swagger-validate:
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}" $(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
@# FIXME: there are some warnings
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' $(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath $(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
.PHONY: checks .PHONY: checks
checks: checks-frontend checks-backend ## run various consistency checks checks: checks-frontend checks-backend
.PHONY: checks-frontend .PHONY: checks-frontend
checks-frontend: lockfile-check svg-check ## check frontend files checks-frontend: lockfile-check svg-check
.PHONY: checks-backend .PHONY: checks-backend
checks-backend: tidy-check swagger-check fmt-check fix-check swagger-validate security-check ## check backend files checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check
.PHONY: lint .PHONY: lint
lint: lint-frontend lint-backend lint-spell ## lint everything lint: lint-frontend lint-backend lint-spell
.PHONY: lint-fix .PHONY: lint-fix
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix
.PHONY: lint-frontend .PHONY: lint-frontend
lint-frontend: lint-js lint-css ## lint frontend files lint-frontend: lint-js lint-css
.PHONY: lint-frontend-fix .PHONY: lint-frontend-fix
lint-frontend-fix: lint-js-fix lint-css-fix ## lint frontend files and fix issues lint-frontend-fix: lint-js-fix lint-css-fix
.PHONY: lint-backend .PHONY: lint-backend
lint-backend: lint-go lint-go-gitea-vet lint-go-gopls lint-editorconfig ## lint backend files lint-backend: lint-go lint-go-vet lint-editorconfig
.PHONY: lint-backend-fix .PHONY: lint-backend-fix
lint-backend-fix: lint-go-fix lint-go-gitea-vet lint-editorconfig ## lint backend files and fix issues lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
.PHONY: lint-js .PHONY: lint-js
lint-js: node_modules ## lint js files lint-js: node_modules
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES)
$(NODE_VARS) pnpm exec vue-tsc
.PHONY: lint-js-fix .PHONY: lint-js-fix
lint-js-fix: node_modules ## lint js files and fix issues lint-js-fix: node_modules
$(NODE_VARS) pnpm exec eslint --color --max-warnings=0 --flag unstable_native_nodejs_ts_config $(ESLINT_FILES) --fix npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES) --fix
$(NODE_VARS) pnpm exec vue-tsc
.PHONY: lint-css .PHONY: lint-css
lint-css: node_modules ## lint css files lint-css: node_modules
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
.PHONY: lint-css-fix .PHONY: lint-css-fix
lint-css-fix: node_modules ## lint css files and fix issues lint-css-fix: node_modules
$(NODE_VARS) pnpm exec stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
.PHONY: lint-swagger .PHONY: lint-swagger
lint-swagger: node_modules ## lint swagger files lint-swagger: node_modules
$(NODE_VARS) pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC) npx spectral lint -q -F hint $(SWAGGER_SPEC)
.PHONY: lint-md .PHONY: lint-md
lint-md: node_modules ## lint markdown files lint-md: node_modules
$(NODE_VARS) pnpm exec markdownlint *.md npx markdownlint docs *.md
.PHONY: lint-spell .PHONY: lint-spell
lint-spell: ## lint spelling lint-spell:
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES) @go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
.PHONY: lint-spell-fix .PHONY: lint-spell-fix
lint-spell-fix: ## lint spelling and fix issues lint-spell-fix:
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES) @go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
.PHONY: lint-go .PHONY: lint-go
lint-go: ## lint go files lint-go:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: lint-go-fix .PHONY: lint-go-fix
lint-go-fix: ## lint go files and fix issues lint-go-fix:
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix $(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
# workaround step for the lint-go-windows CI task because 'go run' can not # workaround step for the lint-go-windows CI task because 'go run' can not
@@ -393,59 +418,53 @@ lint-go-windows:
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE) @GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
golangci-lint run golangci-lint run
.PHONY: lint-go-gitea-vet .PHONY: lint-go-vet
lint-go-gitea-vet: ## lint go files with gitea-vet lint-go-vet:
@echo "Running gitea-vet..." @echo "Running go vet..."
@GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet @GOOS= GOARCH= $(GO) build code.gitea.io/gitea-vet
@$(GO) vet -vettool=gitea-vet ./... @$(GO) vet -vettool=gitea-vet ./...
.PHONY: lint-go-gopls
lint-go-gopls: ## lint go files with gopls
@echo "Running gopls check..."
@GO=$(GO) GOPLS_PACKAGE=$(GOPLS_PACKAGE) tools/lint-go-gopls.sh $(GO_SOURCES)
.PHONY: lint-editorconfig .PHONY: lint-editorconfig
lint-editorconfig: lint-editorconfig:
@echo "Running editorconfig check..."
@$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES) @$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) $(EDITORCONFIG_FILES)
.PHONY: lint-actions .PHONY: lint-actions
lint-actions: ## lint action workflow files lint-actions:
$(GO) run $(ACTIONLINT_PACKAGE) $(GO) run $(ACTIONLINT_PACKAGE)
.PHONY: lint-templates .PHONY: lint-templates
lint-templates: .venv node_modules ## lint template files lint-templates: .venv node_modules
@node tools/lint-templates-svg.ts @node tools/lint-templates-svg.js
@uv run --frozen djlint $(shell find templates -type f -iname '*.tmpl') @poetry run djlint $(shell find templates -type f -iname '*.tmpl')
.PHONY: lint-yaml .PHONY: lint-yaml
lint-yaml: .venv ## lint yaml files lint-yaml: .venv
@uv run --frozen yamllint -s . @poetry run yamllint .
.PHONY: watch .PHONY: watch
watch: ## watch everything and continuously rebuild watch:
@bash tools/watch.sh @bash tools/watch.sh
.PHONY: watch-frontend .PHONY: watch-frontend
watch-frontend: node-check node_modules ## watch frontend files and continuously rebuild watch-frontend: node-check node_modules
@rm -rf $(WEBPACK_DEST_ENTRIES) @rm -rf $(WEBPACK_DEST_ENTRIES)
NODE_ENV=development $(NODE_VARS) pnpm exec webpack --watch --progress --disable-interpret NODE_ENV=development npx webpack --watch --progress
.PHONY: watch-backend .PHONY: watch-backend
watch-backend: go-check ## watch backend files and continuously rebuild watch-backend: go-check
GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml GITEA_RUN_MODE=dev $(GO) run $(AIR_PACKAGE) -c .air.toml
.PHONY: test .PHONY: test
test: test-frontend test-backend ## test everything test: test-frontend test-backend
.PHONY: test-backend .PHONY: test-backend
test-backend: ## test backend files test-backend:
@echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..." @echo "Running go test with $(GOTESTFLAGS) -tags '$(TEST_TAGS)'..."
@$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES) @$(GO) test $(GOTESTFLAGS) -tags='$(TEST_TAGS)' $(GO_TEST_PACKAGES)
.PHONY: test-frontend .PHONY: test-frontend
test-frontend: node_modules ## test frontend files test-frontend: node_modules
$(NODE_VARS) pnpm exec vitest npx vitest
.PHONY: test-check .PHONY: test-check
test-check: test-check:
@@ -453,7 +472,7 @@ test-check:
@diff=$$(git status -s); \ @diff=$$(git status -s); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "make test-backend has changed files in the source tree:"; \ echo "make test-backend has changed files in the source tree:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
echo "You should change the tests to create these files in a temporary directory."; \ echo "You should change the tests to create these files in a temporary directory."; \
echo "Do not simply add these files to .gitignore"; \ echo "Do not simply add these files to .gitignore"; \
exit 1; \ exit 1; \
@@ -476,7 +495,7 @@ unit-test-coverage:
@$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1 @$(GO) test $(GOTESTFLAGS) -timeout=20m -tags='$(TEST_TAGS)' -cover -coverprofile coverage.out $(GO_TEST_PACKAGES) && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: tidy .PHONY: tidy
tidy: ## run go mod tidy tidy:
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2)) $(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION) $(GO) mod tidy -compat=$(MIN_GO_VERSION)
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE) @$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
@@ -490,17 +509,15 @@ tidy-check: tidy
@diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \ @diff=$$(git diff --color=always go.mod go.sum $(GO_LICENSE_FILE)); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "Please run 'make tidy' and commit the result:"; \ echo "Please run 'make tidy' and commit the result:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
exit 1; \ exit 1; \
fi fi
.PHONY: go-licenses .PHONY: go-licenses
go-licenses: $(GO_LICENSE_FILE) ## regenerate go licenses go-licenses: $(GO_LICENSE_FILE)
$(GO_LICENSE_FILE): go.mod go.sum $(GO_LICENSE_FILE): go.mod go.sum
@rm -rf $(GO_LICENSE_FILE) -$(GO) run $(GO_LICENSES_PACKAGE) save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
$(GO) install $(GO_LICENSES_PACKAGE)
-GOOS=linux CGO_ENABLED=1 go-licenses save . --force --save_path=$(GO_LICENSE_TMP_DIR) 2>/dev/null
$(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_TMP_DIR) $(GO_LICENSE_FILE)
@rm -rf $(GO_LICENSE_TMP_DIR) @rm -rf $(GO_LICENSE_TMP_DIR)
@@ -548,7 +565,6 @@ generate-ini-pgsql:
-e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \ -e 's|{{TEST_PGSQL_USERNAME}}|${TEST_PGSQL_USERNAME}|g' \
-e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \ -e 's|{{TEST_PGSQL_PASSWORD}}|${TEST_PGSQL_PASSWORD}|g' \
-e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \ -e 's|{{TEST_PGSQL_SCHEMA}}|${TEST_PGSQL_SCHEMA}|g' \
-e 's|{{TEST_MINIO_ENDPOINT}}|${TEST_MINIO_ENDPOINT}|g' \
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \ -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \ -e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
@@ -588,7 +604,7 @@ test-mssql-migration: migrations.mssql.test migrations.individual.mssql.test
.PHONY: playwright .PHONY: playwright
playwright: deps-frontend playwright: deps-frontend
$(NODE_VARS) pnpm exec playwright install $(PLAYWRIGHT_FLAGS) npx playwright install $(PLAYWRIGHT_FLAGS)
.PHONY: test-e2e% .PHONY: test-e2e%
test-e2e%: TEST_TYPE ?= e2e test-e2e%: TEST_TYPE ?= e2e
@@ -744,17 +760,17 @@ install: $(wildcard *.go)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)'
.PHONY: build .PHONY: build
build: frontend backend ## build everything build: frontend backend
.PHONY: frontend .PHONY: frontend
frontend: $(WEBPACK_DEST) ## build frontend files frontend: $(WEBPACK_DEST)
.PHONY: backend .PHONY: backend
backend: go-check generate-backend $(EXECUTABLE) ## build backend files backend: go-check generate-backend $(EXECUTABLE)
# We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend # We generate the backend before the frontend in case we in future we want to generate things in the frontend from generated files in backend
.PHONY: generate .PHONY: generate
generate: generate-backend ## run "go generate" generate: generate-backend
.PHONY: generate-backend .PHONY: generate-backend
generate-backend: $(TAGS_PREREQ) generate-go generate-backend: $(TAGS_PREREQ) generate-go
@@ -766,38 +782,35 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check .PHONY: security-check
security-check: security-check:
go run $(GOVULNCHECK_PACKAGE) -show color ./... go run $(GOVULNCHECK_PACKAGE) ./...
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ) $(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),) CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
$(error pam support set via TAGS doesn't support static builds)
endif
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
.PHONY: release .PHONY: release
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-docs release-check
$(DIST_DIRS): $(DIST_DIRS):
mkdir -p $(DIST_DIRS) mkdir -p $(DIST_DIRS)
.PHONY: release-windows .PHONY: release-windows
release-windows: | $(DIST_DIRS) release-windows: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq (,$(findstring gogit,$(TAGS))) ifeq (,$(findstring gogit,$(TAGS)))
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
endif endif
.PHONY: release-linux .PHONY: release-linux
release-linux: | $(DIST_DIRS) release-linux: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
.PHONY: release-darwin .PHONY: release-darwin
release-darwin: | $(DIST_DIRS) release-darwin: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
.PHONY: release-freebsd .PHONY: release-freebsd
release-freebsd: | $(DIST_DIRS) release-freebsd: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
.PHONY: release-copy .PHONY: release-copy
release-copy: | $(DIST_DIRS) release-copy: | $(DIST_DIRS)
@@ -821,77 +834,89 @@ release-sources: | $(DIST_DIRS)
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz . tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
rm -f $(STORED_VERSION_FILE) rm -f $(STORED_VERSION_FILE)
.PHONY: release-docs
release-docs: | $(DIST_DIRS) docs
tar -czf $(DIST)/release/gitea-docs-$(VERSION).tar.gz -C ./docs .
.PHONY: deps .PHONY: deps
deps: deps-frontend deps-backend deps-tools deps-py ## install dependencies deps: deps-frontend deps-backend deps-tools deps-py
.PHONY: deps-py .PHONY: deps-py
deps-py: .venv ## install python dependencies deps-py: .venv
.PHONY: deps-frontend .PHONY: deps-frontend
deps-frontend: node_modules ## install frontend dependencies deps-frontend: node_modules
.PHONY: deps-backend .PHONY: deps-backend
deps-backend: ## install backend dependencies deps-backend:
$(GO) mod download $(GO) mod download
.PHONY: deps-tools .PHONY: deps-tools
deps-tools: ## install tool dependencies deps-tools:
$(GO) install $(AIR_PACKAGE) & \ $(GO) install $(AIR_PACKAGE)
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \ $(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
$(GO) install $(GOFUMPT_PACKAGE) & \ $(GO) install $(GOFUMPT_PACKAGE)
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \ $(GO) install $(GOLANGCI_LINT_PACKAGE)
$(GO) install $(GXZ_PACKAGE) & \ $(GO) install $(GXZ_PACKAGE)
$(GO) install $(MISSPELL_PACKAGE) & \ $(GO) install $(MISSPELL_PACKAGE)
$(GO) install $(SWAGGER_PACKAGE) & \ $(GO) install $(SWAGGER_PACKAGE)
$(GO) install $(XGO_PACKAGE) & \ $(GO) install $(XGO_PACKAGE)
$(GO) install $(GO_LICENSES_PACKAGE) & \ $(GO) install $(GO_LICENSES_PACKAGE)
$(GO) install $(GOVULNCHECK_PACKAGE) & \ $(GO) install $(GOVULNCHECK_PACKAGE)
$(GO) install $(ACTIONLINT_PACKAGE) & \ $(GO) install $(ACTIONLINT_PACKAGE)
$(GO) install $(GOPLS_PACKAGE) & \
$(GO) install $(GOPLS_MODERNIZE_PACKAGE) & \
wait
node_modules: pnpm-lock.yaml node_modules: package-lock.json
$(NODE_VARS) pnpm install --frozen-lockfile npm install --no-save
@touch node_modules @touch node_modules
.venv: uv.lock .venv: poetry.lock
uv sync poetry install --no-root
@touch .venv @touch .venv
.PHONY: update .PHONY: update
update: update-js update-py ## update js and py dependencies update: update-js update-py
.PHONY: update-js .PHONY: update-js
update-js: node-check | node_modules ## update js dependencies update-js: node-check | node_modules
$(NODE_VARS) pnpm exec updates -u -f package.json npx updates -u -f package.json
rm -rf node_modules pnpm-lock.yaml rm -rf node_modules package-lock.json
$(NODE_VARS) pnpm install npm install --package-lock
$(NODE_VARS) pnpm exec nolyfill install
$(NODE_VARS) pnpm install
@touch node_modules @touch node_modules
.PHONY: update-py .PHONY: update-py
update-py: node-check | node_modules ## update py dependencies update-py: node-check | node_modules
$(NODE_VARS) pnpm exec updates -u -f pyproject.toml npx updates -u -f pyproject.toml
rm -rf .venv uv.lock rm -rf .venv poetry.lock
uv sync poetry install --no-root
@touch .venv @touch .venv
.PHONY: webpack .PHONY: fomantic
webpack: $(WEBPACK_DEST) ## build webpack files fomantic:
rm -rf $(FOMANTIC_WORK_DIR)/build
cd $(FOMANTIC_WORK_DIR) && npm install --no-save
cp -f $(FOMANTIC_WORK_DIR)/theme.config.less $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/theme.config
cp -rf $(FOMANTIC_WORK_DIR)/_site $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/src/
$(SED_INPLACE) -e 's/ overrideBrowserslist\r/ overrideBrowserslist: ["defaults"]\r/g' $(FOMANTIC_WORK_DIR)/node_modules/fomantic-ui/tasks/config/tasks.js
cd $(FOMANTIC_WORK_DIR) && npx gulp -f node_modules/fomantic-ui/gulpfile.js build
# fomantic uses "touchstart" as click event for some browsers, it's not ideal, so we force fomantic to always use "click" as click event
$(SED_INPLACE) -e 's/clickEvent[ \t]*=/clickEvent = "click", unstableClickEvent =/g' $(FOMANTIC_WORK_DIR)/build/semantic.js
$(SED_INPLACE) -e 's/\r//g' $(FOMANTIC_WORK_DIR)/build/semantic.css $(FOMANTIC_WORK_DIR)/build/semantic.js
rm -f $(FOMANTIC_WORK_DIR)/build/*.min.*
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) pnpm-lock.yaml .PHONY: webpack
webpack: $(WEBPACK_DEST)
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules @$(MAKE) -s node-check node_modules
@rm -rf $(WEBPACK_DEST_ENTRIES) @rm -rf $(WEBPACK_DEST_ENTRIES)
@echo "Running webpack..." @echo "Running webpack..."
@BROWSERSLIST_IGNORE_OLD_DATA=true $(NODE_VARS) pnpm exec webpack --disable-interpret @BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
@touch $(WEBPACK_DEST) @touch $(WEBPACK_DEST)
.PHONY: svg .PHONY: svg
svg: node-check | node_modules ## build svg files svg: node-check | node_modules
rm -rf $(SVG_DEST_DIR) rm -rf $(SVG_DEST_DIR)
node tools/generate-svg.ts node tools/generate-svg.js
.PHONY: svg-check .PHONY: svg-check
svg-check: svg svg-check: svg
@@ -899,18 +924,18 @@ svg-check: svg
@diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \ @diff=$$(git diff --color=always --cached $(SVG_DEST_DIR)); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \ echo "Please run 'make svg' and 'git add $(SVG_DEST_DIR)' and commit the result:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
exit 1; \ exit 1; \
fi fi
.PHONY: lockfile-check .PHONY: lockfile-check
lockfile-check: lockfile-check:
$(NODE_VARS) pnpm install --frozen-lockfile npm install --package-lock-only
@diff=$$(git diff --color=always pnpm-lock.yaml); \ @diff=$$(git diff --color=always package-lock.json); \
if [ -n "$$diff" ]; then \ if [ -n "$$diff" ]; then \
echo "pnpm-lock.yaml is inconsistent with package.json"; \ echo "package-lock.json is inconsistent with package.json"; \
echo "Please run 'pnpm install --frozen-lockfile' and commit the result:"; \ echo "Please run 'npm install --package-lock-only' and commit the result:"; \
printf "%s" "$${diff}"; \ echo "$${diff}"; \
exit 1; \ exit 1; \
fi fi
@@ -924,16 +949,21 @@ update-translations:
mv ./translations/*.ini ./options/locale/ mv ./translations/*.ini ./options/locale/
rmdir ./translations rmdir ./translations
.PHONY: generate-license
generate-license:
$(GO) run build/generate-licenses.go
.PHONY: generate-gitignore .PHONY: generate-gitignore
generate-gitignore: ## update gitignore files generate-gitignore:
$(GO) run build/generate-gitignores.go $(GO) run build/generate-gitignores.go
.PHONY: generate-images .PHONY: generate-images
generate-images: | node_modules ## generate images generate-images: | node_modules
cd tools && node generate-images.ts $(TAGS) npm install --no-save fabric@6.0.0-beta20 imagemin-zopfli@7
node tools/generate-images.js $(TAGS)
.PHONY: generate-manpage .PHONY: generate-manpage
generate-manpage: ## generate manpage generate-manpage:
@[ -f gitea ] || make backend @[ -f gitea ] || make backend
@mkdir -p man/man1/ man/man5 @mkdir -p man/man1/ man/man5
@./gitea docs --man > man/man1/gitea.1 @./gitea docs --man > man/man1/gitea.1

115
README.md
View File

@@ -8,10 +8,11 @@
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source") [![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea") [![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT") [![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea) [![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin") [![](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea "Crowdin")
[![](https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main)](https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main "TODOs")
[繁體中文](./README.zh-tw.md) | [简体中文](./README.zh-cn.md) [View this document in Chinese](./README_ZH.md)
## Purpose ## Purpose
@@ -31,14 +32,6 @@ For accessing free Gitea service (with a limited number of repositories), you ca
To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com). To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com).
## Documentation
You can find comprehensive documentation on our official [documentation website](https://docs.gitea.com/).
It includes installation, administration, usage, development, contributing guides, and more to help you get started and explore all features effectively.
If you have any suggestions or would like to contribute to it, you can visit the [documentation repository](https://gitea.com/gitea/docs)
## Building ## Building
From the root of the source tree, run: From the root of the source tree, run:
@@ -52,7 +45,7 @@ or if SQLite support is required:
The `build` target is split into two sub-targets: The `build` target is split into two sub-targets:
- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod). - `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod).
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and [pnpm](https://pnpm.io/installation). - `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater.
Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js. Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js.
@@ -60,8 +53,6 @@ More info: https://docs.gitea.com/installation/install-from-source
## Using ## Using
After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use:
./gitea web ./gitea web
> [!NOTE] > [!NOTE]
@@ -78,25 +69,22 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
## Translating ## Translating
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com) Translations are done through Crowdin. If you want to translate to a new language ask one of the managers in the Crowdin project to add a new language there.
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there. You can also just create an issue for adding a language or ask on discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty but we hope to fill it as questions pop up.
You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty, but we hope to fill it as questions pop up. https://docs.gitea.com/contributing/localization
Get more information from [documentation](https://docs.gitea.com/contributing/localization). [![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
## Official and Third-Party Projects ## Further information
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action. For more information and instructions about how to install Gitea, please look at our [documentation](https://docs.gitea.com/).
If you have questions that are not covered by the documentation, you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more. We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea).
## Communication The official Gitea CLI is developed at [gitea/tea](https://gitea.com/gitea/tea).
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
If you have questions that are not covered by the [documentation](https://docs.gitea.com/), you can get in contact with us on our [Discord server](https://discord.gg/Gitea) or create a post in the [discourse forum](https://forum.gitea.com/).
## Authors ## Authors
@@ -135,79 +123,18 @@ Gitea is pronounced [/ɡɪti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
We're [working on it](https://github.com/go-gitea/gitea/issues/1029). We're [working on it](https://github.com/go-gitea/gitea/issues/1029).
**Where can I find the security patches?**
In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
## License ## License
This project is licensed under the MIT License. This project is licensed under the MIT License.
See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file See the [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) file
for the full license text. for the full license text.
## Further information ## Screenshots
<details> Looking for an overview of the interface? Check it out!
<summary>Looking for an overview of the interface? Check it out!</summary>
### Login/Register Page |![Dashboard](https://dl.gitea.com/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.com/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.com/screenshots/global_issues.png)|
|:---:|:---:|:---:|
![Login](https://dl.gitea.com/screenshots/login.png) |![Branches](https://dl.gitea.com/screenshots/branches.png)|![Web Editor](https://dl.gitea.com/screenshots/web_editor.png)|![Activity](https://dl.gitea.com/screenshots/activity.png)|
![Register](https://dl.gitea.com/screenshots/register.png) |![New Migration](https://dl.gitea.com/screenshots/migration.png)|![Migrating](https://dl.gitea.com/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|![Pull Request Dark](https://dl.gitea.com/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.com/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.com/screenshots/diff_dark.png)|
### User Dashboard
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### User Profile
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### Explore
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### Repository
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### Repository Issue
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### Repository Pull Requests
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### Repository Actions
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### Repository Activity
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### Organization
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>

View File

@@ -1,206 +0,0 @@
# Gitea
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
[English](./README.md) | [繁體中文](./README.zh-tw.md)
## 目的
这个项目的目标是提供最简单、最快速、最无痛的方式来设置自托管的 Git 服务。
由于 Gitea 是用 Go 语言编写的,它可以在 Go 支持的所有平台和架构上运行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架构。这个项目自 2016 年 11 月从 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而来,但已经有了很多变化。
在线演示可以访问 [demo.gitea.com](https://demo.gitea.com)。
要访问免费的 Gitea 服务(有一定数量的仓库限制),可以访问 [gitea.com](https://gitea.com/user/login)。
要快速部署您自己的专用 Gitea 实例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
## 文件
您可以在我们的官方 [文件网站](https://docs.gitea.com/) 上找到全面的文件。
它包括安装、管理、使用、开发、贡献指南等,帮助您快速入门并有效地探索所有功能。
如果您有任何建议或想要贡献,可以访问 [文件仓库](https://gitea.com/gitea/docs)
## 构建
从源代码树的根目录运行:
TAGS="bindata" make build
如果需要 SQLite 支持:
TAGS="bindata sqlite sqlite_unlock_notify" make build
`build` 目标分为两个子目标:
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定义。
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
需要互联网连接来下载 go 和 npm 模块。从包含预构建前端文件的官方源代码压缩包构建时,不会触发 `frontend` 目标,因此可以在没有 Node.js 的情况下构建。
更多信息https://docs.gitea.com/installation/install-from-source
## 使用
构建后,默认情况下会在源代码树的根目录生成一个名为 `gitea` 的二进制文件。要运行它,请使用:
./gitea web
> [!注意]
> 如果您对使用我们的 API 感兴趣,我们提供了实验性支持,并附有 [文件](https://docs.gitea.com/api)。
## 贡献
预期的工作流程是Fork -> Patch -> Push -> Pull Request
> [!注意]
>
> 1. **在开始进行 Pull Request 之前,您必须阅读 [贡献者指南](CONTRIBUTING.md)。**
> 2. 如果您在项目中发现了漏洞,请私下写信给 **security@gitea.io**。谢谢!
## 翻译
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
翻译通过 [Crowdin](https://translate.gitea.com) 进行。如果您想翻译成新的语言,请在 Crowdin 项目中请求管理员添加新语言。
您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。
更多信息请参阅 [文件](https://docs.gitea.com/contributing/localization)。
## 官方和第三方项目
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
## 通讯
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
如果您有任何文件未涵盖的问题,可以在我们的 [Discord 服务器](https://discord.gg/Gitea) 上与我们联系,或者在 [discourse 论坛](https://forum.gitea.com/) 上创建帖子。
## 作者
- [维护者](https://github.com/orgs/go-gitea/people)
- [贡献者](https://github.com/go-gitea/gitea/graphs/contributors)
- [翻译者](options/locale/TRANSLATORS)
## 支持者
感谢所有支持者! 🙏 [[成为支持者](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## 赞助商
通过成为赞助商来支持这个项目。您的标志将显示在这里,并带有链接到您的网站。 [[成为赞助商](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## 常见问题
**Gitea 怎么发音?**
Gitea 的发音是 [/ɡɪti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一样g 是硬音。
**为什么这个项目没有托管在 Gitea 实例上?**
我们正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
**在哪里可以找到安全补丁?**
在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。
## 许可证
这个项目是根据 MIT 许可证授权的。
请参阅 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以获取完整的许可证文本。
## 进一步信息
<details>
<summary>寻找界面概述?查看这里!</summary>
### 登录/注册页面
![Login](https://dl.gitea.com/screenshots/login.png)
![Register](https://dl.gitea.com/screenshots/register.png)
### 用户仪表板
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### 用户资料
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### 探索
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### 仓库
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### 仓库问题
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### 仓库拉取请求
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### 仓库操作
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### 仓库活动
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### 组织
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>

View File

@@ -1,206 +0,0 @@
# Gitea
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=green)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com "Crowdin")
[English](./README.md) | [简体中文](./README.zh-cn.md)
## 目的
這個項目的目標是提供最簡單、最快速、最無痛的方式來設置自託管的 Git 服務。
由於 Gitea 是用 Go 語言編寫的,它可以在 Go 支援的所有平台和架構上運行,包括 Linux、macOS 和 Windows 的 x86、amd64、ARM 和 PowerPC 架構。這個項目自 2016 年 11 月從 [Gogs](https://gogs.io) [分叉](https://blog.gitea.com/welcome-to-gitea/) 而來,但已經有了很多變化。
在線演示可以訪問 [demo.gitea.com](https://demo.gitea.com)。
要訪問免費的 Gitea 服務(有一定數量的倉庫限制),可以訪問 [gitea.com](https://gitea.com/user/login)。
要快速部署您自己的專用 Gitea 實例,可以在 [cloud.gitea.com](https://cloud.gitea.com) 開始免費試用。
## 文件
您可以在我們的官方 [文件網站](https://docs.gitea.com/) 上找到全面的文件。
它包括安裝、管理、使用、開發、貢獻指南等,幫助您快速入門並有效地探索所有功能。
如果您有任何建議或想要貢獻,可以訪問 [文件倉庫](https://gitea.com/gitea/docs)
## 構建
從源代碼樹的根目錄運行:
TAGS="bindata" make build
如果需要 SQLite 支援:
TAGS="bindata sqlite sqlite_unlock_notify" make build
`build` 目標分為兩個子目標:
- `make backend` 需要 [Go Stable](https://go.dev/dl/),所需版本在 [go.mod](/go.mod) 中定義。
- `make frontend` 需要 [Node.js LTS](https://nodejs.org/en/download/) 或更高版本。
需要互聯網連接來下載 go 和 npm 模塊。從包含預構建前端文件的官方源代碼壓縮包構建時,不會觸發 `frontend` 目標,因此可以在沒有 Node.js 的情況下構建。
更多信息https://docs.gitea.com/installation/install-from-source
## 使用
構建後,默認情況下會在源代碼樹的根目錄生成一個名為 `gitea` 的二進制文件。要運行它,請使用:
./gitea web
> [!注意]
> 如果您對使用我們的 API 感興趣,我們提供了實驗性支援,並附有 [文件](https://docs.gitea.com/api)。
## 貢獻
預期的工作流程是Fork -> Patch -> Push -> Pull Request
> [!注意]
>
> 1. **在開始進行 Pull Request 之前,您必須閱讀 [貢獻者指南](CONTRIBUTING.md)。**
> 2. 如果您在項目中發現了漏洞,請私下寫信給 **security@gitea.io**。謝謝!
## 翻譯
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://translate.gitea.com)
翻譯通過 [Crowdin](https://translate.gitea.com) 進行。如果您想翻譯成新的語言,請在 Crowdin 項目中請求管理員添加新語言。
您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。
更多信息請參閱 [文件](https://docs.gitea.com/contributing/localization)。
## 官方和第三方項目
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
## 通訊
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
如果您有任何文件未涵蓋的問題,可以在我們的 [Discord 服務器](https://discord.gg/Gitea) 上與我們聯繫,或者在 [discourse 論壇](https://forum.gitea.com/) 上創建帖子。
## 作者
- [維護者](https://github.com/orgs/go-gitea/people)
- [貢獻者](https://github.com/go-gitea/gitea/graphs/contributors)
- [翻譯者](options/locale/TRANSLATORS)
## 支持者
感謝所有支持者! 🙏 [[成為支持者](https://opencollective.com/gitea#backer)]
<a href="https://opencollective.com/gitea#backers" target="_blank"><img src="https://opencollective.com/gitea/backers.svg?width=890"></a>
## 贊助商
通過成為贊助商來支持這個項目。您的標誌將顯示在這裡,並帶有鏈接到您的網站。 [[成為贊助商](https://opencollective.com/gitea#sponsor)]
<a href="https://opencollective.com/gitea/sponsor/0/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/1/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/2/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/3/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/4/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/5/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/6/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/7/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/8/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/gitea/sponsor/9/website" target="_blank"><img src="https://opencollective.com/gitea/sponsor/9/avatar.svg"></a>
## 常見問題
**Gitea 怎麼發音?**
Gitea 的發音是 [/ɡɪti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea" 一樣g 是硬音。
**為什麼這個項目沒有託管在 Gitea 實例上?**
我們正在 [努力](https://github.com/go-gitea/gitea/issues/1029)。
**在哪裡可以找到安全補丁?**
在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
## 許可證
這個項目是根據 MIT 許可證授權的。
請參閱 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件以獲取完整的許可證文本。
## 進一步信息
<details>
<summary>尋找界面概述?查看這裡!</summary>
### 登錄/註冊頁面
![Login](https://dl.gitea.com/screenshots/login.png)
![Register](https://dl.gitea.com/screenshots/register.png)
### 用戶儀表板
![Home](https://dl.gitea.com/screenshots/home.png)
![Issues](https://dl.gitea.com/screenshots/issues.png)
![Pull Requests](https://dl.gitea.com/screenshots/pull_requests.png)
![Milestones](https://dl.gitea.com/screenshots/milestones.png)
### 用戶資料
![Profile](https://dl.gitea.com/screenshots/user_profile.png)
### 探索
![Repos](https://dl.gitea.com/screenshots/explore_repos.png)
![Users](https://dl.gitea.com/screenshots/explore_users.png)
![Orgs](https://dl.gitea.com/screenshots/explore_orgs.png)
### 倉庫
![Home](https://dl.gitea.com/screenshots/repo_home.png)
![Commits](https://dl.gitea.com/screenshots/repo_commits.png)
![Branches](https://dl.gitea.com/screenshots/repo_branches.png)
![Labels](https://dl.gitea.com/screenshots/repo_labels.png)
![Milestones](https://dl.gitea.com/screenshots/repo_milestones.png)
![Releases](https://dl.gitea.com/screenshots/repo_releases.png)
![Tags](https://dl.gitea.com/screenshots/repo_tags.png)
#### 倉庫問題
![List](https://dl.gitea.com/screenshots/repo_issues.png)
![Issue](https://dl.gitea.com/screenshots/repo_issue.png)
#### 倉庫拉取請求
![List](https://dl.gitea.com/screenshots/repo_pull_requests.png)
![Pull Request](https://dl.gitea.com/screenshots/repo_pull_request.png)
![File](https://dl.gitea.com/screenshots/repo_pull_request_file.png)
![Commits](https://dl.gitea.com/screenshots/repo_pull_request_commits.png)
#### 倉庫操作
![List](https://dl.gitea.com/screenshots/repo_actions.png)
![Details](https://dl.gitea.com/screenshots/repo_actions_run.png)
#### 倉庫活動
![Activity](https://dl.gitea.com/screenshots/repo_activity.png)
![Contributors](https://dl.gitea.com/screenshots/repo_contributors.png)
![Code Frequency](https://dl.gitea.com/screenshots/repo_code_frequency.png)
![Recent Commits](https://dl.gitea.com/screenshots/repo_recent_commits.png)
### 組織
![Home](https://dl.gitea.com/screenshots/org_home.png)
</details>

62
README_ZH.md Normal file
View File

@@ -0,0 +1,62 @@
# Gitea
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea "Go Report Card")
[![](https://pkg.go.dev/badge/code.gitea.io/gitea?status.svg)](https://pkg.go.dev/code.gitea.io/gitea "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
[![](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea "Become a backer/sponsor of gitea")
[![](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT "License: MIT")
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod)](https://gitpod.io/#https://github.com/go-gitea/gitea)
[![](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea "Crowdin")
[![](https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gitea/gitea/main)](https://www.tickgit.com/browse?repo=github.com/go-gitea/gitea&branch=main "TODOs")
[View this document in English](./README.md)
## 目标
Gitea 的首要目标是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。我们采用 Go 作为后端语言,这使我们只要生成一个可执行程序即可。并且他还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了 x86amd64还包括 ARM 和 PowerPC。
如果你想试用在线演示和报告问题,请访问 [demo.gitea.com](https://demo.gitea.com/)。
如果你想使用免费的 Gitea 服务(有仓库数量限制),请访问 [gitea.com](https://gitea.com/user/login)。
如果你想在 Gitea Cloud 上快速部署你自己独享的 Gitea 实例,请访问 [cloud.gitea.com](https://cloud.gitea.com) 开始免费试用。
## 提示
1. **开始贡献代码之前请确保你已经看过了 [贡献者向导(英文)](CONTRIBUTING.md)**.
2. 所有的安全问题,请私下发送邮件给 **security@gitea.io**。谢谢!
3. 如果你要使用API请参见 [API 文档](https://godoc.org/code.gitea.io/sdk/gitea).
## 文档
关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。
## 贡献流程
Fork -> Patch -> Push -> Pull Request
## 翻译
多语言翻译是基于Crowdin进行的.
[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
## 作者
* [Maintainers](https://github.com/orgs/go-gitea/people)
* [Contributors](https://github.com/go-gitea/gitea/graphs/contributors)
* [Translators](options/locale/TRANSLATORS)
## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/go-gitea/gitea/blob/main/LICENSE) 文件中。
## 截图
|![Dashboard](https://dl.gitea.com/screenshots/home_timeline.png)|![User Profile](https://dl.gitea.com/screenshots/user_profile.png)|![Global Issues](https://dl.gitea.com/screenshots/global_issues.png)|
|:---:|:---:|:---:|
|![Branches](https://dl.gitea.com/screenshots/branches.png)|![Web Editor](https://dl.gitea.com/screenshots/web_editor.png)|![Activity](https://dl.gitea.com/screenshots/activity.png)|
|![New Migration](https://dl.gitea.com/screenshots/migration.png)|![Migrating](https://dl.gitea.com/screenshots/migration.gif)|![Pull Request View](https://image.ibb.co/e02dSb/6.png)|
|![Pull Request Dark](https://dl.gitea.com/screenshots/pull_requests_dark.png)|![Diff Review Dark](https://dl.gitea.com/screenshots/review_dark.png)|![Diff Dark](https://dl.gitea.com/screenshots/diff_dark.png)|

View File

@@ -14,12 +14,12 @@ Please **DO NOT** file a public issue, instead send your report privately to `se
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body. Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
The PGP key is valid until July 4, 2026. The PGP key is valid until June 24, 2024.
``` ```
Key ID: 6FCD2D5B Key ID: 6FCD2D5B
Key Type: RSA Key Type: RSA
Expires: 7/4/2026 Expires: 6/24/2024
Key Size: 4096/4096 Key Size: 4096/4096
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
``` ```
@@ -40,20 +40,20 @@ q+pHZl24JYR0Kf3T/ZiOC0cGd2QJqpJtg5J6S/OqfX9NH6MsCczO8pUC1N/aHH2X
CTme2nF56izORqDWKoiICteL3GpYsCV9nyCidcCmoQsS+DKvE86YhIhVIVWGRY2F CTme2nF56izORqDWKoiICteL3GpYsCV9nyCidcCmoQsS+DKvE86YhIhVIVWGRY2F
lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBFiEE
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N PeA9HhRKfwaTWZncqv0jgW/NLVsFAmK1Z/4CGwMFCQPCZwAFCwkIBwICIgIGFQoJ
LVsFAmhoHmkFCQeT6esACgkQqv0jgW/NLVuFLRAAmjBQSKRAgs2bFIEj7HLAbDp4 CAsCBBYCAwECHgcCF4AACgkQqv0jgW/NLVvnyxAAhxyNnWzw/rQO2qhzqicmZM94
f+XkdH+GsT3jRPOZ9QZgmtM+TfoE4yNgIVfOl+s4RdjM/W4QzqZuPQ55hbEHd056 njSbOg+U2qMBvCdaqCQQeC+uaMmMzkDPanUUmLcyCkWqfCjPNjeSXAkE9npepVJI
cJmm7B+6GsHFcdrPmh65sOCEIyh4+t45dUfeWpFsDPqm9j1UHXAJQIpB8vDEVAPH 4HtmgxZQ94OU/h3CLbft+9GVRzUkVI29TSYGdvNtV2/BkNGoFFnKWQr119um0o6A
t+3wLCk8GMPJs1o5tIyMmaO23ngvkwn8eG7KgY+rp2PzObrb5g7ppci0ILzILkrp bgha2Uy5uY8o3ZIoiKkiHRaEoWIjjeBxJxYAojsZY4YElUmsQ3ik2joG6rhFesTa
HVjZsEfUWRgSVF7LuU5ppqDKrlcqwUpQq6n3kGMZcLrCp6ACKP04TBmTfUxNwdL7 ofVt/bL8G2xzpOG26WGIxBbqf2qjV6OtZ0hu/vtTPHeIWMLq0Mz0V3PEDQWfkGPE
I0N7apI2Pbct9T1Gv/lYAUFWyU2c3gh/EBLbO6BukaLOFRQHrtNfdJV/YnMPlcXr i2RYxxYDs2xzJhSQWqTNVLSq0m5xTJnbHhQPfdCX4C2jvFKgLdfmytQq49S7jiJb
LUJjK9K4eAH9DsrZqrisz/LthsC2BaNIN3KRMTk5YTYgmIh8GXzSgihORmtDFELC Z03HVOZ/PsyBlQfH9xJi06R5yQCMEA8h8Z5r3/NXW09kQ6OFRe6xshoTcxZGRPTo
RroID3pTuS0zjXh+wpY9GuPTh7UW23p42Daxca4fAT4k5EclvDRUrL21xMopPMiL srhwr3uPbmCRh+YEl7qBLU6+BC5k8IRTZXqhrj/aPJu3MxgbgwV8u3vLoFSXM2lb
HuNdELz4FVchRTy05PjzKVyjVInDNojE2KUxnjxZDzYJ6aT/g+coD5yfntYm8BEj a61FgeCQ0O7lkgVswwF0RppCaH9Ul3ZDapet/vCRg4NVwm9zOI/8q/Vj0FKA1GDR
+ZzL0ndZES54hzKLpv7zwBQwFzam68clZYmDPILOPTflQDfpGEWmJK4undFU5obz JhRu8+Ce8zlFL65D34t+PprAzSeTlbv9um3x/ZIjCco7EEKSBylt+AZj/VyA6+e5
ZsQRz0R3ulspChATbZxO0d5LX2obLpKO9X3b5VoO1KF+R8Vjw1Y0KxrNZ6rIcfqH kjOQwRRc6dFJWBcorsSI2dG+H+QMF7ZabzmeCcz1v9HjLOPzYHoZAHhCmSppWTvX
Z50QVQKSe9dm08K0ON+5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen AJy6+lhfW2OUTqQeYSi5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46 GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
@@ -64,20 +64,20 @@ qoExANj5lUTZPe8M50lI182FrcjAN7dClO3QI6pg7wy0erMxfFly3j8UQ91ysS9T
s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/ s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYWIQQ94D0eFEp/BpNZmdyq/SOBb80tWwUC
WwUCaGgeJAUJB5PppgAKCRCq/SOBb80tW/NWEACB6Jrf0gWlk7e+hNCdnbM0ZVWU YrVn/gIbDAUJA8JnAAAKCRCq/SOBb80tWyTBD/9AGpW6QoDF7zYjHAozH9S5RGCA
f2sHNFfXxxsdhpcDgKbNHtkZb8nZgv8AX+5fTtUwMVa3vKcdw30xFiIM5N7cCIPV Y7E82dG/0xmFUwPprAG0BKmmgU6TiipyVGmKIXGYYYU92pMnbvXkYQMoa+WJNncN
vg/5z5BtfEaitnabEUG2iiVDIy8IHXIcK10rX+7BosA3QDl2PsiBHwyi5G13lRk8 D3fY52UeXeffTf4cFpStlzi9xgYtOLhFamzYu/4xhkjOX+xhOSXscCiFRyT8cF3B
zGTSNDuOalug33h5/lr2dPigamkq74Aoy29q8Rjad6GfWHipL2bFimgtY+Zdi0BH O6c5BHU+Zj0/rGPgOyPUbx7l7B9MubB/41nNX35k08e+8T3wtWDb4XF+15HnRfva
NLk4EJXxj1SgVx5dtkQzWJReBA5M+FQ4QYQZBO+f4TDoOLmjui152uhkoLBQbGAa 6fblO8wgU25Orv2Rm1jnKGa9DxJ8nE40IMrqDapENtDuL+zKJsvR0+ptWvEyL56U
WWJFTVxm0bG5MXloEL3gA8DfU7XDwuW/sHJC5pBko8RpQViooOhckMepZV3Y83DK GtJJG5un6mXiLKuRQT0DEv4MdZRHDgDstDnqcbEiazVEbUuvhZZob6lRY2A19m1+
bwLYa3JmPgj2rEv4993dvrJbQhpGd082HOxOsllCs8pgNq1SnXpWYfcGTgGKC3ts 7zfnDxkhqCA1RCnv4fdvcPdCMMFHwLpdhjgW0aI/uwgwrvsEz5+JRlnLvdQHlPAg
U8YZUUJUQ7mi2L8Tv3ix20c9EiGmA30JAmA8eZTC3cWup91ZkkVBFRml2czTXajd q7l2fGcBSpz9U0ayyfRPjPntsNCtZl1UDxGLeciPkZhyG84zEWQbk/j52ZpRN+Ik
RWZ6GbHV5503ueDQcB8yBVgF3CSixs67+dGSbD3p86OqGrjAcJzM5TFbNKcnGLdE ALpRLa8RBFmFSmXDUmwQrmm1EmARyQXwweKU31hf8ZGbCp2lPuRYm1LuGiirXSVP
kGbZpNwAISy750lXzXKmyrh5RTCeTOQerbwCMBvHZO+HAevA/LXDTw2OAiSIQlP5 GysjRAJgW+VRpBKOzFQoUAUbReVWSaCwT8s17THzf71DdDb6CTj31jMLLYWwBpA/
sYA4sFYLQ30OAkgJcmdp/pSgVj/erNtSN07ClrOpDb/uFpQymO6K2h0Pst3feNVK i73DgobDZMIGEZZC1EKqza8eh11xfyHFzGec03tbh+lIen+5IiRtWiEWkDS9ll0G
9M2VbqL9C51z/wyHLg== zgS/ZdziCvdAutqnGA==
=SfZA =gZWO
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
``` ```

355
assets/go-licenses.json generated

File diff suppressed because one or more lines are too long

View File

@@ -5,10 +5,19 @@
package main package main
// Libraries that are included to vendor utilities used during Makefile build. // Libraries that are included to vendor utilities used during build.
// These libraries will not be included in a normal compilation. // These libraries will not be included in a normal compilation.
import ( import (
// for embed
_ "github.com/shurcooL/vfsgen"
// for cover merge
_ "golang.org/x/tools/cover"
// for vet // for vet
_ "code.gitea.io/gitea-vet" _ "code.gitea.io/gitea-vet"
// for swagger
_ "github.com/go-swagger/go-swagger/cmd/swagger"
) )

View File

@@ -181,7 +181,7 @@ func parseArgs() (mainOptions map[string]string, subCmd string, subArgs []string
break break
} }
} }
return mainOptions, subCmd, subArgs return
} }
func showUsage() { func showUsage() {

View File

@@ -6,22 +6,87 @@
package main package main
import ( import (
"bytes"
"crypto/sha1"
"fmt" "fmt"
"log"
"net/http"
"os" "os"
"path/filepath"
"strconv"
"code.gitea.io/gitea/modules/assetfs" "github.com/shurcooL/vfsgen"
) )
func main() { func needsUpdate(dir, filename string) (bool, []byte) {
if len(os.Args) != 3 { needRegen := false
fmt.Println("usage: ./generate-bindata {local-directory} {bindata-filename}") _, err := os.Stat(filename)
os.Exit(1) if err != nil {
needRegen = true
} }
dir, filename := os.Args[1], os.Args[2] oldHash, err := os.ReadFile(filename + ".hash")
fmt.Printf("generating bindata for %s to %s\n", dir, filename) if err != nil {
if err := assetfs.GenerateEmbedBindata(dir, filename); err != nil { oldHash = []byte{}
fmt.Printf("failed: %s\n", err.Error())
os.Exit(1)
} }
hasher := sha1.New()
err = filepath.WalkDir(dir, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
info, err := d.Info()
if err != nil {
return err
}
_, _ = hasher.Write([]byte(d.Name()))
_, _ = hasher.Write([]byte(info.ModTime().String()))
_, _ = hasher.Write([]byte(strconv.FormatInt(info.Size(), 16)))
return nil
})
if err != nil {
return true, oldHash
}
newHash := hasher.Sum([]byte{})
if bytes.Compare(oldHash, newHash) != 0 {
return true, newHash
}
return needRegen, newHash
}
func main() {
if len(os.Args) < 4 {
log.Fatal("Insufficient number of arguments. Need: directory packageName filename")
}
dir, packageName, filename := os.Args[1], os.Args[2], os.Args[3]
var useGlobalModTime bool
if len(os.Args) == 5 {
useGlobalModTime, _ = strconv.ParseBool(os.Args[4])
}
update, newHash := needsUpdate(dir, filename)
if !update {
fmt.Printf("bindata for %s already up-to-date\n", packageName)
return
}
fmt.Printf("generating bindata for %s\n", packageName)
var fsTemplates http.FileSystem = http.Dir(dir)
err := vfsgen.Generate(fsTemplates, vfsgen.Options{
PackageName: packageName,
BuildTags: "bindata",
VariableName: "Assets",
Filename: filename,
UseGlobalModTime: useGlobalModTime,
})
if err != nil {
log.Fatalf("%v\n", err)
}
_ = os.WriteFile(filename+".hash", newHash, 0o666)
} }

View File

@@ -53,6 +53,8 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
} }
func main() { func main() {
var err error
flag.Parse() flag.Parse()
// generate data // generate data
@@ -81,6 +83,8 @@ var replacer = strings.NewReplacer(
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`) var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
func generate() ([]byte, error) { func generate() ([]byte, error) {
var err error
// load gemoji data // load gemoji data
res, err := http.Get(gemojiURL) res, err := http.Get(gemojiURL)
if err != nil { if err != nil {

122
build/generate-licenses.go Normal file
View File

@@ -0,0 +1,122 @@
//go:build ignore
package main
import (
"archive/tar"
"compress/gzip"
"flag"
"fmt"
"io"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/util"
)
func main() {
var (
prefix = "gitea-licenses"
url = "https://api.github.com/repos/spdx/license-list-data/tarball"
githubApiToken = ""
githubUsername = ""
destination = ""
)
flag.StringVar(&destination, "dest", "options/license/", "destination for the licenses")
flag.StringVar(&githubUsername, "username", "", "github username")
flag.StringVar(&githubApiToken, "token", "", "github api token")
flag.Parse()
file, err := os.CreateTemp(os.TempDir(), prefix)
if err != nil {
log.Fatalf("Failed to create temp file. %s", err)
}
defer util.Remove(file.Name())
if err := os.RemoveAll(destination); err != nil {
log.Fatalf("Cannot clean destination folder: %v", err)
}
if err := os.MkdirAll(destination, 0o755); err != nil {
log.Fatalf("Cannot create destination: %v", err)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Fatalf("Failed to download archive. %s", err)
}
if len(githubApiToken) > 0 && len(githubUsername) > 0 {
req.SetBasicAuth(githubUsername, githubApiToken)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatalf("Failed to download archive. %s", err)
}
defer resp.Body.Close()
if _, err := io.Copy(file, resp.Body); err != nil {
log.Fatalf("Failed to copy archive to file. %s", err)
}
if _, err := file.Seek(0, 0); err != nil {
log.Fatalf("Failed to reset seek on archive. %s", err)
}
gz, err := gzip.NewReader(file)
if err != nil {
log.Fatalf("Failed to gunzip the archive. %s", err)
}
tr := tar.NewReader(gz)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Failed to iterate archive. %s", err)
}
if !strings.Contains(hdr.Name, "/text/") {
continue
}
if filepath.Ext(hdr.Name) != ".txt" {
continue
}
if strings.HasPrefix(filepath.Base(hdr.Name), "README") {
continue
}
if strings.HasPrefix(filepath.Base(hdr.Name), "deprecated_") {
continue
}
out, err := os.Create(path.Join(destination, strings.TrimSuffix(filepath.Base(hdr.Name), ".txt")))
if err != nil {
log.Fatalf("Failed to create new file. %s", err)
}
defer out.Close()
if _, err := io.Copy(out, tr); err != nil {
log.Fatalf("Failed to write new file. %s", err)
} else {
fmt.Printf("Written %s\n", out.Name())
}
}
fmt.Println("Done")
}

View File

@@ -4,13 +4,12 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -18,7 +17,7 @@ var (
CmdActions = &cli.Command{ CmdActions = &cli.Command{
Name: "actions", Name: "actions",
Usage: "Manage Gitea Actions", Usage: "Manage Gitea Actions",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdActionsGenRunnerToken, subcmdActionsGenRunnerToken,
}, },
} }
@@ -39,7 +38,10 @@ var (
} }
) )
func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error { func runGenerateActionsRunnerToken(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setting.MustInstalled() setting.MustInstalled()
scope := c.String("scope") scope := c.String("scope")

View File

@@ -15,7 +15,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -23,7 +23,7 @@ var (
CmdAdmin = &cli.Command{ CmdAdmin = &cli.Command{
Name: "admin", Name: "admin",
Usage: "Perform common administrative operations", Usage: "Perform common administrative operations",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdUser, subcmdUser,
subcmdRepoSyncReleases, subcmdRepoSyncReleases,
subcmdRegenerate, subcmdRegenerate,
@@ -41,7 +41,7 @@ var (
subcmdRegenerate = &cli.Command{ subcmdRegenerate = &cli.Command{
Name: "regenerate", Name: "regenerate",
Usage: "Regenerate specific files", Usage: "Regenerate specific files",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
microcmdRegenHooks, microcmdRegenHooks,
microcmdRegenKeys, microcmdRegenKeys,
}, },
@@ -50,15 +50,15 @@ var (
subcmdAuth = &cli.Command{ subcmdAuth = &cli.Command{
Name: "auth", Name: "auth",
Usage: "Modify external auth providers", Usage: "Modify external auth providers",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
microcmdAuthAddOauth(), microcmdAuthAddOauth,
microcmdAuthUpdateOauth(), microcmdAuthUpdateOauth,
microcmdAuthAddLdapBindDn(), microcmdAuthAddLdapBindDn,
microcmdAuthUpdateLdapBindDn(), microcmdAuthUpdateLdapBindDn,
microcmdAuthAddLdapSimpleAuth(), microcmdAuthAddLdapSimpleAuth,
microcmdAuthUpdateLdapSimpleAuth(), microcmdAuthUpdateLdapSimpleAuth,
microcmdAuthAddSMTP(), microcmdAuthAddSMTP,
microcmdAuthUpdateSMTP(), microcmdAuthUpdateSMTP,
microcmdAuthList, microcmdAuthList,
microcmdAuthDelete, microcmdAuthDelete,
}, },
@@ -70,9 +70,9 @@ var (
Action: runSendMail, Action: runSendMail,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "title", Name: "title",
Usage: "a title of a message", Usage: `a title of a message`,
Required: true, Value: "",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "content", Name: "content",
@@ -86,27 +86,28 @@ var (
}, },
}, },
} }
)
func idFlag() *cli.Int64Flag { idFlag = &cli.Int64Flag{
return &cli.Int64Flag{
Name: "id", Name: "id",
Usage: "ID of authentication source", Usage: "ID of authentication source",
} }
} )
func runRepoSyncReleases(_ *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
if err := git.InitSimple(); err != nil { if err := git.InitSimple(ctx); err != nil {
return err return err
} }
log.Trace("Synchronizing repository releases (this may take a while)") log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ { for page := 1; ; page++ {
repos, count, err := repo_model.SearchRepositoryByName(ctx, repo_model.SearchRepoOptions{ repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
PageSize: repo_model.RepositoryListDefaultPageSize, PageSize: repo_model.RepositoryListDefaultPageSize,
Page: page, Page: page,

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -14,14 +13,14 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
auth_service "code.gitea.io/gitea/services/auth" auth_service "code.gitea.io/gitea/services/auth"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
microcmdAuthDelete = &cli.Command{ microcmdAuthDelete = &cli.Command{
Name: "delete", Name: "delete",
Usage: "Delete specific auth source", Usage: "Delete specific auth source",
Flags: []cli.Flag{idFlag()}, Flags: []cli.Flag{idFlag},
Action: runDeleteAuth, Action: runDeleteAuth,
} }
microcmdAuthList = &cli.Command{ microcmdAuthList = &cli.Command{
@@ -57,7 +56,10 @@ var (
} }
) )
func runListAuth(ctx context.Context, c *cli.Command) error { func runListAuth(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
@@ -88,11 +90,14 @@ func runListAuth(ctx context.Context, c *cli.Command) error {
return nil return nil
} }
func runDeleteAuth(ctx context.Context, c *cli.Command) error { func runDeleteAuth(c *cli.Context) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }

View File

@@ -9,10 +9,9 @@ import (
"strings" "strings"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/ldap" "code.gitea.io/gitea/services/auth/source/ldap"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
type ( type (
@@ -24,8 +23,8 @@ type (
} }
) )
func commonLdapCLIFlags() []cli.Flag { var (
return []cli.Flag{ commonLdapCLIFlags = []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Usage: "Authentication name.", Usage: "Authentication name.",
@@ -103,10 +102,8 @@ func commonLdapCLIFlags() []cli.Flag {
Usage: "The attribute of the users LDAP record containing the users avatar.", Usage: "The attribute of the users LDAP record containing the users avatar.",
}, },
} }
}
func ldapBindDnCLIFlags() []cli.Flag { ldapBindDnCLIFlags = append(commonLdapCLIFlags,
return append(commonLdapCLIFlags(),
&cli.StringFlag{ &cli.StringFlag{
Name: "bind-dn", Name: "bind-dn",
Usage: "The DN to bind to the LDAP server with when searching for the user.", Usage: "The DN to bind to the LDAP server with when searching for the user.",
@@ -130,88 +127,50 @@ func ldapBindDnCLIFlags() []cli.Flag {
&cli.UintFlag{ &cli.UintFlag{
Name: "page-size", Name: "page-size",
Usage: "Search page size.", Usage: "Search page size.",
},
&cli.BoolFlag{
Name: "enable-groups",
Usage: "Enable LDAP groups",
},
&cli.StringFlag{
Name: "group-search-base-dn",
Usage: "The LDAP base DN at which group accounts will be searched for",
},
&cli.StringFlag{
Name: "group-member-attribute",
Usage: "Group attribute containing list of users",
},
&cli.StringFlag{
Name: "group-user-attribute",
Usage: "User attribute listed in group",
},
&cli.StringFlag{
Name: "group-filter",
Usage: "Verify group membership in LDAP",
},
&cli.StringFlag{
Name: "group-team-map",
Usage: "Map LDAP groups to Organization teams",
},
&cli.BoolFlag{
Name: "group-team-map-removal",
Usage: "Remove users from synchronized teams if user does not belong to corresponding LDAP group",
}) })
}
func ldapSimpleAuthCLIFlags() []cli.Flag { ldapSimpleAuthCLIFlags = append(commonLdapCLIFlags,
return append(commonLdapCLIFlags(),
&cli.StringFlag{ &cli.StringFlag{
Name: "user-dn", Name: "user-dn",
Usage: "The user's DN.", Usage: "The user's DN.",
}) })
}
func microcmdAuthAddLdapBindDn() *cli.Command { microcmdAuthAddLdapBindDn = &cli.Command{
return &cli.Command{
Name: "add-ldap", Name: "add-ldap",
Usage: "Add new LDAP (via Bind DN) authentication source", Usage: "Add new LDAP (via Bind DN) authentication source",
Action: func(ctx context.Context, cmd *cli.Command) error { Action: func(c *cli.Context) error {
return newAuthService().addLdapBindDn(ctx, cmd) return newAuthService().addLdapBindDn(c)
}, },
Flags: ldapBindDnCLIFlags(), Flags: ldapBindDnCLIFlags,
} }
}
func microcmdAuthUpdateLdapBindDn() *cli.Command { microcmdAuthUpdateLdapBindDn = &cli.Command{
return &cli.Command{
Name: "update-ldap", Name: "update-ldap",
Usage: "Update existing LDAP (via Bind DN) authentication source", Usage: "Update existing LDAP (via Bind DN) authentication source",
Action: func(ctx context.Context, cmd *cli.Command) error { Action: func(c *cli.Context) error {
return newAuthService().updateLdapBindDn(ctx, cmd) return newAuthService().updateLdapBindDn(c)
}, },
Flags: append([]cli.Flag{idFlag()}, ldapBindDnCLIFlags()...), Flags: append([]cli.Flag{idFlag}, ldapBindDnCLIFlags...),
} }
}
func microcmdAuthAddLdapSimpleAuth() *cli.Command { microcmdAuthAddLdapSimpleAuth = &cli.Command{
return &cli.Command{
Name: "add-ldap-simple", Name: "add-ldap-simple",
Usage: "Add new LDAP (simple auth) authentication source", Usage: "Add new LDAP (simple auth) authentication source",
Action: func(ctx context.Context, cmd *cli.Command) error { Action: func(c *cli.Context) error {
return newAuthService().addLdapSimpleAuth(ctx, cmd) return newAuthService().addLdapSimpleAuth(c)
}, },
Flags: ldapSimpleAuthCLIFlags(), Flags: ldapSimpleAuthCLIFlags,
} }
}
func microcmdAuthUpdateLdapSimpleAuth() *cli.Command { microcmdAuthUpdateLdapSimpleAuth = &cli.Command{
return &cli.Command{
Name: "update-ldap-simple", Name: "update-ldap-simple",
Usage: "Update existing LDAP (simple auth) authentication source", Usage: "Update existing LDAP (simple auth) authentication source",
Action: func(ctx context.Context, cmd *cli.Command) error { Action: func(c *cli.Context) error {
return newAuthService().updateLdapSimpleAuth(ctx, cmd) return newAuthService().updateLdapSimpleAuth(c)
}, },
Flags: append([]cli.Flag{idFlag()}, ldapSimpleAuthCLIFlags()...), Flags: append([]cli.Flag{idFlag}, ldapSimpleAuthCLIFlags...),
} }
} )
// newAuthService creates a service with default functions. // newAuthService creates a service with default functions.
func newAuthService() *authService { func newAuthService() *authService {
@@ -223,8 +182,8 @@ func newAuthService() *authService {
} }
} }
// parseAuthSourceLdap assigns values on authSource according to command line flags. // parseAuthSource assigns values on authSource according to command line flags.
func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) { func parseAuthSource(c *cli.Context, authSource *auth.Source) {
if c.IsSet("name") { if c.IsSet("name") {
authSource.Name = c.String("name") authSource.Name = c.String("name")
} }
@@ -240,11 +199,10 @@ func parseAuthSourceLdap(c *cli.Command, authSource *auth.Source) {
if c.IsSet("disable-synchronize-users") { if c.IsSet("disable-synchronize-users") {
authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users") authSource.IsSyncEnabled = !c.Bool("disable-synchronize-users")
} }
authSource.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
} }
// parseLdapConfig assigns values on config according to command line flags. // parseLdapConfig assigns values on config according to command line flags.
func parseLdapConfig(c *cli.Command, config *ldap.Source) error { func parseLdapConfig(c *cli.Context, config *ldap.Source) error {
if c.IsSet("name") { if c.IsSet("name") {
config.Name = c.String("name") config.Name = c.String("name")
} }
@@ -257,7 +215,7 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("security-protocol") { if c.IsSet("security-protocol") {
p, ok := findLdapSecurityProtocolByName(c.String("security-protocol")) p, ok := findLdapSecurityProtocolByName(c.String("security-protocol"))
if !ok { if !ok {
return fmt.Errorf("unknown security protocol name: %s", c.String("security-protocol")) return fmt.Errorf("Unknown security protocol name: %s", c.String("security-protocol"))
} }
config.SecurityProtocol = p config.SecurityProtocol = p
} }
@@ -312,26 +270,8 @@ func parseLdapConfig(c *cli.Command, config *ldap.Source) error {
if c.IsSet("allow-deactivate-all") { if c.IsSet("allow-deactivate-all") {
config.AllowDeactivateAll = c.Bool("allow-deactivate-all") config.AllowDeactivateAll = c.Bool("allow-deactivate-all")
} }
if c.IsSet("enable-groups") { if c.IsSet("skip-local-2fa") {
config.GroupsEnabled = c.Bool("enable-groups") config.SkipLocalTwoFA = c.Bool("skip-local-2fa")
}
if c.IsSet("group-search-base-dn") {
config.GroupDN = c.String("group-search-base-dn")
}
if c.IsSet("group-member-attribute") {
config.GroupMemberUID = c.String("group-member-attribute")
}
if c.IsSet("group-user-attribute") {
config.UserUID = c.String("group-user-attribute")
}
if c.IsSet("group-filter") {
config.GroupFilter = c.String("group-filter")
}
if c.IsSet("group-team-map") {
config.GroupTeamMap = c.String("group-team-map")
}
if c.IsSet("group-team-map-removal") {
config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
} }
return nil return nil
} }
@@ -349,27 +289,32 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
// getAuthSource gets the login source by its id defined in the command line flags. // getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type. // It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) { func (a *authService) getAuthSource(ctx context.Context, c *cli.Context, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil { if err := argsSet(c, "id"); err != nil {
return nil, err return nil, err
} }
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id")) authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if authSource.Type != authType { if authSource.Type != authType {
return nil, fmt.Errorf("invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String()) return nil, fmt.Errorf("Invalid authentication type. expected: %s, actual: %s", authType.String(), authSource.Type.String())
} }
return authSource, nil return authSource, nil
} }
// addLdapBindDn adds a new LDAP via Bind DN authentication source. // addLdapBindDn adds a new LDAP via Bind DN authentication source.
func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error { func (a *authService) addLdapBindDn(c *cli.Context) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil { if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
return err return err
} }
ctx, cancel := installSignals()
defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
return err return err
} }
@@ -382,7 +327,7 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
}, },
} }
parseAuthSourceLdap(c, authSource) parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err return err
} }
@@ -391,7 +336,10 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
} }
// updateLdapBindDn updates a new LDAP via Bind DN authentication source. // updateLdapBindDn updates a new LDAP via Bind DN authentication source.
func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) error { func (a *authService) updateLdapBindDn(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
return err return err
} }
@@ -401,7 +349,7 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
return err return err
} }
parseAuthSourceLdap(c, authSource) parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err return err
} }
@@ -410,11 +358,14 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
} }
// addLdapSimpleAuth adds a new LDAP (simple auth) authentication source. // addLdapSimpleAuth adds a new LDAP (simple auth) authentication source.
func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) error { func (a *authService) addLdapSimpleAuth(c *cli.Context) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil { if err := argsSet(c, "name", "security-protocol", "host", "port", "user-dn", "user-filter", "email-attribute"); err != nil {
return err return err
} }
ctx, cancel := installSignals()
defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
return err return err
} }
@@ -427,7 +378,7 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
}, },
} }
parseAuthSourceLdap(c, authSource) parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err return err
} }
@@ -435,8 +386,11 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
return a.createAuthSource(ctx, authSource) return a.createAuthSource(ctx, authSource)
} }
// updateLdapSimpleAuth updates a new LDAP (simple auth) authentication source. // updateLdapBindDn updates a new LDAP (simple auth) authentication source.
func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command) error { func (a *authService) updateLdapSimpleAuth(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := a.initDB(ctx); err != nil { if err := a.initDB(ctx); err != nil {
return err return err
} }
@@ -446,7 +400,7 @@ func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command)
return err return err
} }
parseAuthSourceLdap(c, authSource) parseAuthSource(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil { if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
return err return err
} }

View File

@@ -8,16 +8,17 @@ import (
"testing" "testing"
"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/services/auth/source/ldap" "code.gitea.io/gitea/services/auth/source/ldap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func TestAddLdapBindDn(t *testing.T) { func TestAddLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error // Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
// Test cases // Test cases
cases := []struct { cases := []struct {
@@ -50,13 +51,6 @@ func TestAddLdapBindDn(t *testing.T) {
"--attributes-in-bind", "--attributes-in-bind",
"--synchronize-users", "--synchronize-users",
"--page-size", "99", "--page-size", "99",
"--enable-groups",
"--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
"--group-member-attribute", "memberUid",
"--group-user-attribute", "uid",
"--group-filter", "(|(cn=gitea_users)(cn=admins))",
"--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
"--group-team-map-removal",
}, },
source: &auth.Source{ source: &auth.Source{
Type: auth.LDAP, Type: auth.LDAP,
@@ -84,13 +78,6 @@ func TestAddLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true, Enabled: true,
GroupsEnabled: true,
GroupDN: "ou=group,dc=full-domain-bind,dc=org",
GroupMemberUID: "memberUid",
UserUID: "uid",
GroupFilter: "(|(cn=gitea_users)(cn=admins))",
GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
GroupTeamMapRemoval: true,
}, },
}, },
}, },
@@ -134,7 +121,7 @@ func TestAddLdapBindDn(t *testing.T) {
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)", "--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
"--email-attribute", "mail", "--email-attribute", "mail",
}, },
errMsg: "unknown security protocol name: zzzzz", errMsg: "Unknown security protocol name: zzzzz",
}, },
// case 3 // case 3
{ {
@@ -228,23 +215,22 @@ func TestAddLdapBindDn(t *testing.T) {
return nil return nil
}, },
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n) assert.FailNow(t, "case %d: should not call updateAuthSource", n)
return nil return nil
}, },
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
assert.FailNow(t, "getAuthSourceByID called", "case %d: should not call getAuthSourceByID", n) assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
return nil, nil return nil, nil
}, },
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.Command{ app := cli.NewApp()
Flags: microcmdAuthAddLdapBindDn().Flags, app.Flags = microcmdAuthAddLdapBindDn.Flags
Action: service.addLdapBindDn, app.Action = service.addLdapBindDn
}
// Run it // Run it
err := app.Run(t.Context(), c.args) err := app.Run(c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@@ -256,7 +242,9 @@ func TestAddLdapBindDn(t *testing.T) {
func TestAddLdapSimpleAuth(t *testing.T) { func TestAddLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error // Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
// Test cases // Test cases
cases := []struct { cases := []struct {
@@ -346,12 +334,12 @@ func TestAddLdapSimpleAuth(t *testing.T) {
"--name", "ldap (simple auth) source", "--name", "ldap (simple auth) source",
"--security-protocol", "zzzzz", "--security-protocol", "zzzzz",
"--host", "ldap-server", "--host", "ldap-server",
"--port", "1234", "--port", "123",
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))", "--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
"--email-attribute", "mail", "--email-attribute", "mail",
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org", "--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
}, },
errMsg: "unknown security protocol name: zzzzz", errMsg: "Unknown security protocol name: zzzzz",
}, },
// case 3 // case 3
{ {
@@ -458,23 +446,22 @@ func TestAddLdapSimpleAuth(t *testing.T) {
return nil return nil
}, },
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "updateAuthSource called", "case %d: should not call updateAuthSource", n) assert.FailNow(t, "case %d: should not call updateAuthSource", n)
return nil return nil
}, },
getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) { getAuthSourceByID: func(ctx context.Context, id int64) (*auth.Source, error) {
assert.FailNow(t, "getAuthSourceById called", "case %d: should not call getAuthSourceByID", n) assert.FailNow(t, "case %d: should not call getAuthSourceByID", n)
return nil, nil return nil, nil
}, },
} }
// Create a copy of command to test // Create a copy of command to test
app := &cli.Command{ app := cli.NewApp()
Flags: microcmdAuthAddLdapSimpleAuth().Flags, app.Flags = microcmdAuthAddLdapSimpleAuth.Flags
Action: service.addLdapSimpleAuth, app.Action = service.addLdapSimpleAuth
}
// Run it // Run it
err := app.Run(t.Context(), c.args) err := app.Run(c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@@ -486,7 +473,9 @@ func TestAddLdapSimpleAuth(t *testing.T) {
func TestUpdateLdapBindDn(t *testing.T) { func TestUpdateLdapBindDn(t *testing.T) {
// Mock cli functions to do not exit on error // Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
// Test cases // Test cases
cases := []struct { cases := []struct {
@@ -521,13 +510,6 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--bind-password", "secret-bind-full", "--bind-password", "secret-bind-full",
"--synchronize-users", "--synchronize-users",
"--page-size", "99", "--page-size", "99",
"--enable-groups",
"--group-search-base-dn", "ou=group,dc=full-domain-bind,dc=org",
"--group-member-attribute", "memberUid",
"--group-user-attribute", "uid",
"--group-filter", "(|(cn=gitea_users)(cn=admins))",
"--group-team-map", `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
"--group-team-map-removal",
}, },
id: 23, id: 23,
existingAuthSource: &auth.Source{ existingAuthSource: &auth.Source{
@@ -563,13 +545,6 @@ func TestUpdateLdapBindDn(t *testing.T) {
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)", AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=full-domain-bind,dc=org)",
RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)", RestrictedFilter: "(memberOf=cn=restricted-group,ou=example,dc=full-domain-bind,dc=org)",
Enabled: true, Enabled: true,
GroupsEnabled: true,
GroupDN: "ou=group,dc=full-domain-bind,dc=org",
GroupMemberUID: "memberUid",
UserUID: "uid",
GroupFilter: "(|(cn=gitea_users)(cn=admins))",
GroupTeamMap: `{"cn=my-group,cn=groups,dc=example,dc=org": {"MyGiteaOrganization": ["MyGiteaTeam1", "MyGiteaTeam2"]}}`,
GroupTeamMapRemoval: true,
}, },
}, },
}, },
@@ -861,7 +836,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
"--id", "1", "--id", "1",
"--security-protocol", "xxxxx", "--security-protocol", "xxxxx",
}, },
errMsg: "unknown security protocol name: xxxxx", errMsg: "Unknown security protocol name: xxxxx",
}, },
// case 22 // case 22
{ {
@@ -880,7 +855,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
Type: auth.OAuth2, Type: auth.OAuth2,
Cfg: &ldap.Source{}, Cfg: &ldap.Source{},
}, },
errMsg: "invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2", errMsg: "Invalid authentication type. expected: LDAP (via BindDN), actual: OAuth2",
}, },
// case 24 // case 24
{ {
@@ -922,7 +897,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
return nil return nil
}, },
createAuthSource: func(ctx context.Context, authSource *auth.Source) error { createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n) assert.FailNow(t, "case %d: should not call createAuthSource", n)
return nil return nil
}, },
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -944,12 +919,12 @@ func TestUpdateLdapBindDn(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.Command{ app := cli.NewApp()
Flags: microcmdAuthUpdateLdapBindDn().Flags, app.Flags = microcmdAuthUpdateLdapBindDn.Flags
Action: service.updateLdapBindDn, app.Action = service.updateLdapBindDn
}
// Run it // Run it
err := app.Run(t.Context(), c.args) err := app.Run(c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {
@@ -961,7 +936,9 @@ func TestUpdateLdapBindDn(t *testing.T) {
func TestUpdateLdapSimpleAuth(t *testing.T) { func TestUpdateLdapSimpleAuth(t *testing.T) {
// Mock cli functions to do not exit on error // Mock cli functions to do not exit on error
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})() osExiter := cli.OsExiter
defer func() { cli.OsExiter = osExiter }()
cli.OsExiter = func(code int) {}
// Test cases // Test cases
cases := []struct { cases := []struct {
@@ -1252,7 +1229,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
"--id", "1", "--id", "1",
"--security-protocol", "xxxxx", "--security-protocol", "xxxxx",
}, },
errMsg: "unknown security protocol name: xxxxx", errMsg: "Unknown security protocol name: xxxxx",
}, },
// case 18 // case 18
{ {
@@ -1271,7 +1248,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
Type: auth.PAM, Type: auth.PAM,
Cfg: &ldap.Source{}, Cfg: &ldap.Source{},
}, },
errMsg: "invalid authentication type. expected: LDAP (simple auth), actual: PAM", errMsg: "Invalid authentication type. expected: LDAP (simple auth), actual: PAM",
}, },
// case 20 // case 20
{ {
@@ -1310,7 +1287,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
return nil return nil
}, },
createAuthSource: func(ctx context.Context, authSource *auth.Source) error { createAuthSource: func(ctx context.Context, authSource *auth.Source) error {
assert.FailNow(t, "createAuthSource called", "case %d: should not call createAuthSource", n) assert.FailNow(t, "case %d: should not call createAuthSource", n)
return nil return nil
}, },
updateAuthSource: func(ctx context.Context, authSource *auth.Source) error { updateAuthSource: func(ctx context.Context, authSource *auth.Source) error {
@@ -1332,12 +1309,12 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
} }
// Create a copy of command to test // Create a copy of command to test
app := cli.Command{ app := cli.NewApp()
Flags: microcmdAuthUpdateLdapSimpleAuth().Flags, app.Flags = microcmdAuthUpdateLdapSimpleAuth.Flags
Action: service.updateLdapSimpleAuth, app.Action = service.updateLdapSimpleAuth
}
// Run it // Run it
err := app.Run(t.Context(), c.args) err := app.Run(c.args)
if c.errMsg != "" { if c.errMsg != "" {
assert.EqualError(t, err, c.errMsg, "case %d: error should match", n) assert.EqualError(t, err, c.errMsg, "case %d: error should match", n)
} else { } else {

View File

@@ -4,20 +4,18 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"net/url" "net/url"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/auth/source/oauth2"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func oauthCLIFlags() []cli.Flag { var (
return []cli.Flag{ oauthCLIFlags = []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Value: "", Value: "",
@@ -87,14 +85,6 @@ func oauthCLIFlags() []cli.Flag {
Value: nil, Value: nil,
Usage: "Scopes to request when to authenticate against this OAuth2 source", Usage: "Scopes to request when to authenticate against this OAuth2 source",
}, },
&cli.StringFlag{
Name: "ssh-public-key-claim-name",
Usage: "Claim name that provides SSH public keys",
},
&cli.StringFlag{
Name: "full-name-claim-name",
Usage: "Claim name that provides user's full name",
},
&cli.StringFlag{ &cli.StringFlag{
Name: "required-claim-name", Name: "required-claim-name",
Value: "", Value: "",
@@ -130,34 +120,23 @@ func oauthCLIFlags() []cli.Flag {
Usage: "Activate automatic team membership removal depending on groups", Usage: "Activate automatic team membership removal depending on groups",
}, },
} }
}
func microcmdAuthAddOauth() *cli.Command { microcmdAuthAddOauth = &cli.Command{
return &cli.Command{ Name: "add-oauth",
Name: "add-oauth", Usage: "Add new Oauth authentication source",
Usage: "Add new Oauth authentication source", Action: runAddOauth,
Action: func(ctx context.Context, cmd *cli.Command) error { Flags: oauthCLIFlags,
return newAuthService().runAddOauth(ctx, cmd)
},
Flags: oauthCLIFlags(),
} }
}
func microcmdAuthUpdateOauth() *cli.Command { microcmdAuthUpdateOauth = &cli.Command{
return &cli.Command{ Name: "update-oauth",
Name: "update-oauth", Usage: "Update existing Oauth authentication source",
Usage: "Update existing Oauth authentication source", Action: runUpdateOauth,
Action: func(ctx context.Context, cmd *cli.Command) error { Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
return newAuthService().runUpdateOauth(ctx, cmd)
},
Flags: append(oauthCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}}, oauthCLIFlags()[1:]...)...),
} }
} )
func parseOAuth2Config(c *cli.Command) *oauth2.Source { func parseOAuth2Config(c *cli.Context) *oauth2.Source {
var customURLMapping *oauth2.CustomURLMapping var customURLMapping *oauth2.CustomURLMapping
if c.IsSet("use-custom-urls") { if c.IsSet("use-custom-urls") {
customURLMapping = &oauth2.CustomURLMapping{ customURLMapping = &oauth2.CustomURLMapping{
@@ -177,6 +156,7 @@ func parseOAuth2Config(c *cli.Command) *oauth2.Source {
OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"), OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
CustomURLMapping: customURLMapping, CustomURLMapping: customURLMapping,
IconURL: c.String("icon-url"), IconURL: c.String("icon-url"),
SkipLocalTwoFA: c.Bool("skip-local-2fa"),
Scopes: c.StringSlice("scopes"), Scopes: c.StringSlice("scopes"),
RequiredClaimName: c.String("required-claim-name"), RequiredClaimName: c.String("required-claim-name"),
RequiredClaimValue: c.String("required-claim-value"), RequiredClaimValue: c.String("required-claim-value"),
@@ -185,13 +165,14 @@ func parseOAuth2Config(c *cli.Command) *oauth2.Source {
RestrictedGroup: c.String("restricted-group"), RestrictedGroup: c.String("restricted-group"),
GroupTeamMap: c.String("group-team-map"), GroupTeamMap: c.String("group-team-map"),
GroupTeamMapRemoval: c.Bool("group-team-map-removal"), GroupTeamMapRemoval: c.Bool("group-team-map-removal"),
SSHPublicKeyClaimName: c.String("ssh-public-key-claim-name"),
FullNameClaimName: c.String("full-name-claim-name"),
} }
} }
func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error { func runAddOauth(c *cli.Context) error {
if err := a.initDB(ctx); err != nil { ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err return err
} }
@@ -203,25 +184,27 @@ func (a *authService) runAddOauth(ctx context.Context, c *cli.Command) error {
} }
} }
return a.createAuthSource(ctx, &auth_model.Source{ return auth_model.CreateSource(ctx, &auth_model.Source{
Type: auth_model.OAuth2, Type: auth_model.OAuth2,
Name: c.String("name"), Name: c.String("name"),
IsActive: true, IsActive: true,
Cfg: config, Cfg: config,
TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
}) })
} }
func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error { func runUpdateOauth(c *cli.Context) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
if err := a.initDB(ctx); err != nil { ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err return err
} }
source, err := a.getAuthSourceByID(ctx, c.Int64("id")) source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil { if err != nil {
return err return err
} }
@@ -278,12 +261,6 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
if c.IsSet("group-team-map-removal") { if c.IsSet("group-team-map-removal") {
oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal") oAuth2Config.GroupTeamMapRemoval = c.Bool("group-team-map-removal")
} }
if c.IsSet("ssh-public-key-claim-name") {
oAuth2Config.SSHPublicKeyClaimName = c.String("ssh-public-key-claim-name")
}
if c.IsSet("full-name-claim-name") {
oAuth2Config.FullNameClaimName = c.String("full-name-claim-name")
}
// update custom URL mapping // update custom URL mapping
customURLMapping := &oauth2.CustomURLMapping{} customURLMapping := &oauth2.CustomURLMapping{}
@@ -317,6 +294,6 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
oAuth2Config.CustomURLMapping = customURLMapping oAuth2Config.CustomURLMapping = customURLMapping
source.Cfg = oAuth2Config source.Cfg = oAuth2Config
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
return a.updateAuthSource(ctx, source) return auth_model.UpdateSource(ctx, source)
} }

View File

@@ -1,343 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/services/auth/source/oauth2"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestAddOauth(t *testing.T) {
testCases := []struct {
name string
args []string
source *auth_model.Source
errMsg string
}{
{
name: "valid config",
args: []string{
"--name", "test",
"--provider", "github",
"--key", "some_key",
"--secret", "some_secret",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Scopes: []string{},
Provider: "github",
ClientID: "some_key",
ClientSecret: "some_secret",
},
TwoFactorPolicy: "",
},
},
{
name: "valid config with openid connect",
args: []string{
"--name", "test",
"--provider", "openidConnect",
"--key", "some_key",
"--secret", "some_secret",
"--auto-discover-url", "https://example.com",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Scopes: []string{},
Provider: "openidConnect",
ClientID: "some_key",
ClientSecret: "some_secret",
OpenIDConnectAutoDiscoveryURL: "https://example.com",
},
TwoFactorPolicy: "",
},
},
{
name: "valid config with options",
args: []string{
"--name", "test",
"--provider", "gitlab",
"--key", "some_key",
"--secret", "some_secret",
"--use-custom-urls", "true",
"--custom-token-url", "https://example.com/token",
"--custom-auth-url", "https://example.com/auth",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
"--custom-tenant-id", "some_tenant",
"--icon-url", "https://example.com/icon",
"--scopes", "scope1,scope2",
"--skip-local-2fa", "true",
"--required-claim-name", "claim_name",
"--required-claim-value", "claim_value",
"--group-claim-name", "group_name",
"--admin-group", "admin",
"--restricted-group", "restricted",
"--group-team-map", `{"group1": [1,2]}`,
"--group-team-map-removal=true",
"--ssh-public-key-claim-name", "attr_ssh_pub_key",
"--full-name-claim-name", "attr_full_name",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "gitlab",
ClientID: "some_key",
ClientSecret: "some_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://example.com/token",
AuthURL: "https://example.com/auth",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "some_tenant",
},
IconURL: "https://example.com/icon",
Scopes: []string{"scope1", "scope2"},
RequiredClaimName: "claim_name",
RequiredClaimValue: "claim_value",
GroupClaimName: "group_name",
AdminGroup: "admin",
RestrictedGroup: "restricted",
GroupTeamMap: `{"group1": [1,2]}`,
GroupTeamMapRemoval: true,
SSHPublicKeyClaimName: "attr_ssh_pub_key",
FullNameClaimName: "attr_full_name",
},
TwoFactorPolicy: "skip",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var createdSource *auth_model.Source
a := &authService{
initDB: func(ctx context.Context) error {
return nil
},
createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
createdSource = source
return nil
},
}
app := &cli.Command{
Flags: microcmdAuthAddOauth().Flags,
Action: a.runAddOauth,
}
args := []string{"oauth-test"}
args = append(args, tc.args...)
err := app.Run(t.Context(), args)
if tc.errMsg != "" {
assert.EqualError(t, err, tc.errMsg)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.source, createdSource)
}
})
}
}
func TestUpdateOauth(t *testing.T) {
testCases := []struct {
name string
args []string
id int64
existingAuthSource *auth_model.Source
authSource *auth_model.Source
errMsg string
}{
{
name: "missing id",
args: []string{
"--name", "test",
},
errMsg: "--id flag is missing",
},
{
name: "valid config",
id: 1,
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "old name",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "github",
ClientID: "old_key",
ClientSecret: "old_secret",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",
"--provider", "gitlab",
"--key", "new_key",
"--secret", "new_secret",
},
authSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "gitlab",
ClientID: "new_key",
ClientSecret: "new_secret",
CustomURLMapping: &oauth2.CustomURLMapping{},
},
TwoFactorPolicy: "",
},
},
{
name: "valid config with options",
id: 1,
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "old name",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "gitlab",
ClientID: "old_key",
ClientSecret: "old_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://old.example.com/token",
AuthURL: "https://old.example.com/auth",
ProfileURL: "https://old.example.com/profile",
EmailURL: "https://old.example.com/email",
Tenant: "old_tenant",
},
IconURL: "https://old.example.com/icon",
Scopes: []string{"old_scope1", "old_scope2"},
RequiredClaimName: "old_claim_name",
RequiredClaimValue: "old_claim_value",
GroupClaimName: "old_group_name",
AdminGroup: "old_admin",
RestrictedGroup: "old_restricted",
GroupTeamMap: `{"old_group1": [1,2]}`,
GroupTeamMapRemoval: true,
SSHPublicKeyClaimName: "old_ssh_pub_key",
FullNameClaimName: "old_full_name",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",
"--provider", "github",
"--key", "new_key",
"--secret", "new_secret",
"--use-custom-urls", "true",
"--custom-token-url", "https://example.com/token",
"--custom-auth-url", "https://example.com/auth",
"--custom-profile-url", "https://example.com/profile",
"--custom-email-url", "https://example.com/email",
"--custom-tenant-id", "new_tenant",
"--icon-url", "https://example.com/icon",
"--scopes", "scope1,scope2",
"--skip-local-2fa=true",
"--required-claim-name", "claim_name",
"--required-claim-value", "claim_value",
"--group-claim-name", "group_name",
"--admin-group", "admin",
"--restricted-group", "restricted",
"--group-team-map", `{"group1": [1,2]}`,
"--group-team-map-removal=false",
"--ssh-public-key-claim-name", "new_ssh_pub_key",
"--full-name-claim-name", "new_full_name",
},
authSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "github",
ClientID: "new_key",
ClientSecret: "new_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://example.com/token",
AuthURL: "https://example.com/auth",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "new_tenant",
},
IconURL: "https://example.com/icon",
Scopes: []string{"scope1", "scope2"},
RequiredClaimName: "claim_name",
RequiredClaimValue: "claim_value",
GroupClaimName: "group_name",
AdminGroup: "admin",
RestrictedGroup: "restricted",
GroupTeamMap: `{"group1": [1,2]}`,
GroupTeamMapRemoval: false,
SSHPublicKeyClaimName: "new_ssh_pub_key",
FullNameClaimName: "new_full_name",
},
TwoFactorPolicy: "skip",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
a := &authService{
initDB: func(ctx context.Context) error {
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
return &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
CustomURLMapping: &oauth2.CustomURLMapping{},
},
TwoFactorPolicy: "skip",
}, nil
},
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
assert.Equal(t, tc.authSource, source)
return nil
},
}
app := &cli.Command{
Flags: microcmdAuthUpdateOauth().Flags,
Action: a.runUpdateOauth,
}
args := []string{"oauth-test"}
args = append(args, tc.args...)
err := app.Run(t.Context(), args)
if tc.errMsg != "" {
assert.EqualError(t, err, tc.errMsg)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -1,271 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/services/auth/source/smtp"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestAddSMTP(t *testing.T) {
testCases := []struct {
name string
args []string
source *auth_model.Source
errMsg string
}{
{
name: "missing name",
args: []string{
"--host", "localhost",
"--port", "25",
},
errMsg: "name must be set",
},
{
name: "missing host",
args: []string{
"--name", "test",
"--port", "25",
},
errMsg: "host must be set",
},
{
name: "missing port",
args: []string{
"--name", "test",
"--host", "localhost",
},
errMsg: "port must be set",
},
{
name: "valid config",
args: []string{
"--name", "test",
"--host", "localhost",
"--port", "25",
},
source: &auth_model.Source{
Type: auth_model.SMTP,
Name: "test",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "localhost",
Port: 25,
},
TwoFactorPolicy: "",
},
},
{
name: "valid config with options",
args: []string{
"--name", "test",
"--host", "localhost",
"--port", "25",
"--auth-type", "LOGIN",
"--force-smtps",
"--skip-verify",
"--helo-hostname", "example.com",
"--disable-helo=true",
"--allowed-domains", "example.com,example.org",
"--skip-local-2fa",
"--active=false",
},
source: &auth_model.Source{
Type: auth_model.SMTP,
Name: "test",
IsActive: false,
Cfg: &smtp.Source{
Auth: "LOGIN",
Host: "localhost",
Port: 25,
ForceSMTPS: true,
SkipVerify: true,
HeloHostname: "example.com",
DisableHelo: true,
AllowedDomains: "example.com,example.org",
},
TwoFactorPolicy: "skip",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
a := &authService{
initDB: func(ctx context.Context) error {
return nil
},
createAuthSource: func(ctx context.Context, source *auth_model.Source) error {
assert.Equal(t, tc.source, source)
return nil
},
}
cmd := &cli.Command{
Flags: microcmdAuthAddSMTP().Flags,
Action: a.runAddSMTP,
}
args := []string{"smtp-test"}
args = append(args, tc.args...)
t.Log(args)
err := cmd.Run(t.Context(), args)
if tc.errMsg != "" {
assert.EqualError(t, err, tc.errMsg)
} else {
assert.NoError(t, err)
}
})
}
}
func TestUpdateSMTP(t *testing.T) {
testCases := []struct {
name string
args []string
existingAuthSource *auth_model.Source
authSource *auth_model.Source
errMsg string
}{
{
name: "missing id",
args: []string{
"--name", "test",
"--host", "localhost",
"--port", "25",
},
errMsg: "--id flag is missing",
},
{
name: "valid config",
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "old name",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "old host",
Port: 26,
},
},
args: []string{
"--id", "1",
"--name", "test",
"--host", "localhost",
"--port", "25",
},
authSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "test",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "localhost",
Port: 25,
},
},
},
{
name: "valid config with options",
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "old name",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "old host",
Port: 26,
HeloHostname: "old.example.com",
AllowedDomains: "old.example.com",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",
"--host", "localhost",
"--port", "25",
"--auth-type", "LOGIN",
"--force-smtps",
"--skip-verify",
"--helo-hostname", "example.com",
"--disable-helo",
"--allowed-domains", "example.com,example.org",
"--skip-local-2fa",
"--active=false",
},
authSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "test",
IsActive: false,
Cfg: &smtp.Source{
Auth: "LOGIN",
Host: "localhost",
Port: 25,
ForceSMTPS: true,
SkipVerify: true,
HeloHostname: "example.com",
DisableHelo: true,
AllowedDomains: "example.com,example.org",
},
TwoFactorPolicy: "skip",
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
a := &authService{
initDB: func(ctx context.Context) error {
return nil
},
getAuthSourceByID: func(ctx context.Context, id int64) (*auth_model.Source, error) {
return &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "test",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
},
}, nil
},
updateAuthSource: func(ctx context.Context, source *auth_model.Source) error {
assert.Equal(t, tc.authSource, source)
return nil
},
}
app := &cli.Command{
Flags: microcmdAuthUpdateSMTP().Flags,
Action: a.runUpdateSMTP,
}
args := []string{"smtp-tests"}
args = append(args, tc.args...)
err := app.Run(t.Context(), args)
if tc.errMsg != "" {
assert.EqualError(t, err, tc.errMsg)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"strings" "strings"
@@ -12,11 +11,11 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth/source/smtp" "code.gitea.io/gitea/services/auth/source/smtp"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func smtpCLIFlags() []cli.Flag { var (
return []cli.Flag{ smtpCLIFlags = []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "name", Name: "name",
Value: "", Value: "",
@@ -39,10 +38,12 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{ &cli.BoolFlag{
Name: "force-smtps", Name: "force-smtps",
Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.", Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
Value: true,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-verify", Name: "skip-verify",
Usage: "Skip TLS verify.", Usage: "Skip TLS verify.",
Value: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "helo-hostname", Name: "helo-hostname",
@@ -52,6 +53,7 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{ &cli.BoolFlag{
Name: "disable-helo", Name: "disable-helo",
Usage: "Disable SMTP helo.", Usage: "Disable SMTP helo.",
Value: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "allowed-domains", Name: "allowed-domains",
@@ -61,6 +63,7 @@ func smtpCLIFlags() []cli.Flag {
&cli.BoolFlag{ &cli.BoolFlag{
Name: "skip-local-2fa", Name: "skip-local-2fa",
Usage: "Skip 2FA to log on.", Usage: "Skip 2FA to log on.",
Value: true,
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "active", Name: "active",
@@ -68,34 +71,23 @@ func smtpCLIFlags() []cli.Flag {
Value: true, Value: true,
}, },
} }
}
func microcmdAuthUpdateSMTP() *cli.Command { microcmdAuthAddSMTP = &cli.Command{
return &cli.Command{ Name: "add-smtp",
Name: "update-smtp", Usage: "Add new SMTP authentication source",
Usage: "Update existing SMTP authentication source", Action: runAddSMTP,
Action: func(ctx context.Context, cmd *cli.Command) error { Flags: smtpCLIFlags,
return newAuthService().runUpdateSMTP(ctx, cmd)
},
Flags: append(smtpCLIFlags()[:1], append([]cli.Flag{&cli.Int64Flag{
Name: "id",
Usage: "ID of authentication source",
}}, smtpCLIFlags()[1:]...)...),
} }
}
func microcmdAuthAddSMTP() *cli.Command { microcmdAuthUpdateSMTP = &cli.Command{
return &cli.Command{ Name: "update-smtp",
Name: "add-smtp", Usage: "Update existing SMTP authentication source",
Usage: "Add new SMTP authentication source", Action: runUpdateSMTP,
Action: func(ctx context.Context, cmd *cli.Command) error { Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
return newAuthService().runAddSMTP(ctx, cmd)
},
Flags: smtpCLIFlags(),
} }
} )
func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error { func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
if c.IsSet("auth-type") { if c.IsSet("auth-type") {
conf.Auth = c.String("auth-type") conf.Auth = c.String("auth-type")
validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"} validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
@@ -125,11 +117,17 @@ func parseSMTPConfig(c *cli.Command, conf *smtp.Source) error {
if c.IsSet("disable-helo") { if c.IsSet("disable-helo") {
conf.DisableHelo = c.Bool("disable-helo") conf.DisableHelo = c.Bool("disable-helo")
} }
if c.IsSet("skip-local-2fa") {
conf.SkipLocalTwoFA = c.Bool("skip-local-2fa")
}
return nil return nil
} }
func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error { func runAddSMTP(c *cli.Context) error {
if err := a.initDB(ctx); err != nil { ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err return err
} }
@@ -157,25 +155,27 @@ func (a *authService) runAddSMTP(ctx context.Context, c *cli.Command) error {
smtpConfig.Auth = "PLAIN" smtpConfig.Auth = "PLAIN"
} }
return a.createAuthSource(ctx, &auth_model.Source{ return auth_model.CreateSource(ctx, &auth_model.Source{
Type: auth_model.SMTP, Type: auth_model.SMTP,
Name: c.String("name"), Name: c.String("name"),
IsActive: active, IsActive: active,
Cfg: &smtpConfig, Cfg: &smtpConfig,
TwoFactorPolicy: util.Iif(c.Bool("skip-local-2fa"), "skip", ""),
}) })
} }
func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error { func runUpdateSMTP(c *cli.Context) error {
if !c.IsSet("id") { if !c.IsSet("id") {
return errors.New("--id flag is missing") return errors.New("--id flag is missing")
} }
if err := a.initDB(ctx); err != nil { ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err return err
} }
source, err := a.getAuthSourceByID(ctx, c.Int64("id")) source, err := auth_model.GetSourceByID(ctx, c.Int64("id"))
if err != nil { if err != nil {
return err return err
} }
@@ -195,6 +195,6 @@ func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
} }
source.Cfg = smtpConfig source.Cfg = smtpConfig
source.TwoFactorPolicy = util.Iif(c.Bool("skip-local-2fa"), "skip", "")
return a.updateAuthSource(ctx, source) return auth_model.UpdateSource(ctx, source)
} }

View File

@@ -4,13 +4,11 @@
package cmd package cmd
import ( import (
"context"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
asymkey_service "code.gitea.io/gitea/services/asymkey" asymkey_service "code.gitea.io/gitea/services/asymkey"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -27,14 +25,20 @@ var (
} }
) )
func runRegenerateHooks(ctx context.Context, _ *cli.Command) error { func runRegenerateHooks(_ *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
} }
func runRegenerateKeys(ctx context.Context, _ *cli.Command) error { func runRegenerateKeys(_ *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }

View File

@@ -4,18 +4,18 @@
package cmd package cmd
import ( import (
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var subcmdUser = &cli.Command{ var subcmdUser = &cli.Command{
Name: "user", Name: "user",
Usage: "Modify users", Usage: "Modify users",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
microcmdUserCreate(), microcmdUserCreate,
microcmdUserList, microcmdUserList,
microcmdUserChangePassword(), microcmdUserChangePassword,
microcmdUserDelete(), microcmdUserDelete,
microcmdUserGenerateAccessToken, microcmdUserGenerateAccessToken,
microcmdUserMustChangePassword(), microcmdUserMustChangePassword,
}, },
} }

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
@@ -14,41 +13,44 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
user_service "code.gitea.io/gitea/services/user" user_service "code.gitea.io/gitea/services/user"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func microcmdUserChangePassword() *cli.Command { var microcmdUserChangePassword = &cli.Command{
return &cli.Command{ Name: "change-password",
Name: "change-password", Usage: "Change a user's password",
Usage: "Change a user's password", Action: runChangePassword,
Action: runChangePassword, Flags: []cli.Flag{
Flags: []cli.Flag{ &cli.StringFlag{
&cli.StringFlag{ Name: "username",
Name: "username", Aliases: []string{"u"},
Aliases: []string{"u"}, Value: "",
Usage: "The user to change password for", Usage: "The user to change password for",
Required: true,
},
&cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Usage: "New password to set for user",
Required: true,
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password (can be disabled by --must-change-password=false)",
Value: true,
},
}, },
} &cli.StringFlag{
Name: "password",
Aliases: []string{"p"},
Value: "",
Usage: "New password to set for user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password (can be disabled by --must-change-password=false)",
Value: true,
},
},
} }
func runChangePassword(ctx context.Context, c *cli.Command) error { func runChangePassword(c *cli.Context) error {
if !setting.IsInTesting { if err := argsSet(c, "username", "password"); err != nil {
if err := initDB(ctx); err != nil { return err
return err }
}
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err
} }
user, err := user_model.GetUserByName(ctx, c.String("username")) user, err := user_model.GetUserByName(ctx, c.String("username"))

View File

@@ -1,91 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"testing"
"code.gitea.io/gitea/models/db"
"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 TestChangePasswordCommand(t *testing.T) {
ctx := t.Context()
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
}()
t.Run("change password successfully", func(t *testing.T) {
// defer func() {
// require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
// }()
// Prepare test user
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserCreate().Run(ctx, []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
// load test user
userBase := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
// Change the password
err = microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "newpassword"})
require.NoError(t, err)
// Verify the password has been changed
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.NotEqual(t, userBase.Passwd, user.Passwd)
assert.NotEqual(t, userBase.Salt, user.Salt)
// Additional check for must-change-password flag
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "anotherpassword", "--must-change-password=false"}))
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, user.MustChangePassword)
require.NoError(t, microcmdUserChangePassword().Run(ctx, []string{"change-password", "--username", "testuser", "--password", "yetanotherpassword", "--must-change-password"}))
user = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, user.MustChangePassword)
})
t.Run("failure cases", func(t *testing.T) {
testCases := []struct {
name string
args []string
expectedErr string
}{
{
name: "user does not exist",
args: []string{"change-password", "--username", "nonexistentuser", "--password", "newpassword"},
expectedErr: "user does not exist",
},
{
name: "missing username",
args: []string{"change-password", "--password", "newpassword"},
expectedErr: `"username" not set`,
},
{
name: "missing password",
args: []string{"change-password", "--username", "testuser"},
expectedErr: `"password" not set`,
},
{
name: "too short password",
args: []string{"change-password", "--username", "testuser", "--password", "1"},
expectedErr: "password is not long enough",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := microcmdUserChangePassword().Run(ctx, tc.args)
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
})
}
})
}

View File

@@ -7,7 +7,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strings"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@@ -16,110 +15,69 @@ import (
"code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func microcmdUserCreate() *cli.Command { var microcmdUserCreate = &cli.Command{
return &cli.Command{ Name: "create",
Name: "create", Usage: "Create a new user in database",
Usage: "Create a new user in database", Action: runCreateUser,
Action: runCreateUser, Flags: []cli.Flag{
MutuallyExclusiveFlags: []cli.MutuallyExclusiveFlags{ &cli.StringFlag{
{ Name: "name",
Flags: [][]cli.Flag{ Usage: "Username. DEPRECATED: use username instead",
{
&cli.StringFlag{
Name: "name",
Usage: "Username. DEPRECATED: use username instead",
},
&cli.StringFlag{
Name: "username",
Usage: "Username",
},
},
},
Required: true,
},
}, },
Flags: []cli.Flag{ &cli.StringFlag{
&cli.StringFlag{ Name: "username",
Name: "user-type", Usage: "Username",
Usage: "Set user's type: individual or bot",
Value: "individual",
},
&cli.StringFlag{
Name: "password",
Usage: "User password",
},
&cli.StringFlag{
Name: "email",
Usage: "User email address",
Required: true,
},
&cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
&cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
HideDefault: true,
},
&cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
&cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
&cli.StringFlag{
Name: "access-token-name",
Usage: `Name of the generated access token`,
Value: "gitea-admin",
},
&cli.StringFlag{
Name: "access-token-scopes",
Usage: `Scopes of the generated access token, comma separated. Examples: "all", "public-only,read:issue", "write:repository,write:user"`,
Value: "all",
},
&cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},
&cli.StringFlag{
Name: "fullname",
Usage: `The full, human-readable name of the user`,
},
}, },
} &cli.StringFlag{
Name: "password",
Usage: "User password",
},
&cli.StringFlag{
Name: "email",
Usage: "User email address",
},
&cli.BoolFlag{
Name: "admin",
Usage: "User is an admin",
},
&cli.BoolFlag{
Name: "random-password",
Usage: "Generate a random password for the user",
},
&cli.BoolFlag{
Name: "must-change-password",
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
DisableDefaultText: true,
},
&cli.IntFlag{
Name: "random-password-length",
Usage: "Length of the random password to be generated",
Value: 12,
},
&cli.BoolFlag{
Name: "access-token",
Usage: "Generate access token for the user",
},
&cli.BoolFlag{
Name: "restricted",
Usage: "Make a restricted user account",
},
},
} }
func runCreateUser(ctx context.Context, c *cli.Command) error { func runCreateUser(c *cli.Context) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first if err := argsSet(c, "email"); err != nil {
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future. return err
setting.LoadSettings() }
userTypes := map[string]user_model.UserType{ if c.IsSet("name") && c.IsSet("username") {
"individual": user_model.UserTypeIndividual, return errors.New("cannot set both --name and --username flags")
"bot": user_model.UserTypeBot,
} }
userType, ok := userTypes[c.String("user-type")] if !c.IsSet("name") && !c.IsSet("username") {
if !ok { return errors.New("one of --name or --username flags must be set")
return fmt.Errorf("invalid user type: %s", c.String("user-type"))
}
if userType != user_model.UserTypeIndividual {
// Some other commands like "change-password" also only support individual users.
// It needs to clarify the "password" behavior for bot users in the future.
// At the moment, we do not allow setting password for bot users.
if c.IsSet("password") || c.IsSet("random-password") {
return errors.New("password can only be set for individual users")
}
} }
if c.IsSet("password") && c.IsSet("random-password") { if c.IsSet("password") && c.IsSet("random-password") {
@@ -131,12 +89,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
username = c.String("username") username = c.String("username")
} else { } else {
username = c.String("name") username = c.String("name")
_, _ = fmt.Fprintf(c.ErrWriter, "--name flag is deprecated. Use --username instead.\n") _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
} }
ctx := c.Context
if !setting.IsInTesting { if !setting.IsInTesting {
// FIXME: need to refactor the "initDB" related code later // FIXME: need to refactor the "installSignals/initDB" related code later
// it doesn't make sense to call it in (almost) every command action function // it doesn't make sense to call it in (almost) every command action function
var cancel context.CancelFunc
ctx, cancel = installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
@@ -152,19 +114,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
return err return err
} }
fmt.Printf("generated random password is '%s'\n", password) fmt.Printf("generated random password is '%s'\n", password)
} else if userType == user_model.UserTypeIndividual { } else {
return errors.New("must set either password or random-password flag") return errors.New("must set either password or random-password flag")
} }
isAdmin := c.Bool("admin") isAdmin := c.Bool("admin")
mustChangePassword := true // always default to true mustChangePassword := true // always default to true
if c.IsSet("must-change-password") { if c.IsSet("must-change-password") {
if userType != user_model.UserTypeIndividual {
return errors.New("must-change-password flag can only be set for individual users")
}
// if the flag is set, use the value provided by the user // if the flag is set, use the value provided by the user
mustChangePassword = c.Bool("must-change-password") mustChangePassword = c.Bool("must-change-password")
} else if userType == user_model.UserTypeIndividual { } else {
// check whether there are users in the database // check whether there are users in the database
hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{}) hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{})
if err != nil { if err != nil {
@@ -188,12 +147,10 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
u := &user_model.User{ u := &user_model.User{
Name: username, Name: username,
Email: c.String("email"), Email: c.String("email"),
IsAdmin: isAdmin,
Type: userType,
Passwd: password, Passwd: password,
IsAdmin: isAdmin,
MustChangePassword: mustChangePassword, MustChangePassword: mustChangePassword,
Visibility: visibility, Visibility: visibility,
FullName: c.String("fullname"),
} }
overwriteDefault := &user_model.CreateUserOverwriteOptions{ overwriteDefault := &user_model.CreateUserOverwriteOptions{
@@ -201,40 +158,23 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
IsRestricted: restricted, IsRestricted: restricted,
} }
var accessTokenName string if err := user_model.CreateUser(ctx, u, overwriteDefault); err != nil {
var accessTokenScope auth_model.AccessTokenScope
if c.IsSet("access-token") {
accessTokenName = strings.TrimSpace(c.String("access-token-name"))
if accessTokenName == "" {
return errors.New("access-token-name cannot be empty")
}
var err error
accessTokenScope, err = auth_model.AccessTokenScope(c.String("access-token-scopes")).Normalize()
if err != nil {
return fmt.Errorf("invalid access token scope provided: %w", err)
}
if !accessTokenScope.HasPermissionScope() {
return errors.New("access token does not have any permission")
}
} else if c.IsSet("access-token-name") || c.IsSet("access-token-scopes") {
return errors.New("access-token-name and access-token-scopes flags are only valid when access-token flag is set")
}
// arguments should be prepared before creating the user & access token, in case there is anything wrong
// create the user
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
return fmt.Errorf("CreateUser: %w", err) return fmt.Errorf("CreateUser: %w", err)
} }
fmt.Printf("New user '%s' has been successfully created!\n", username)
// create the access token if c.Bool("access-token") {
if accessTokenScope != "" { t := &auth_model.AccessToken{
t := &auth_model.AccessToken{Name: accessTokenName, UID: u.ID, Scope: accessTokenScope} Name: "gitea-admin",
UID: u.ID,
}
if err := auth_model.NewAccessToken(ctx, t); err != nil { if err := auth_model.NewAccessToken(ctx, t); err != nil {
return err return err
} }
fmt.Printf("Access token was successfully created... %s\n", t.Token) fmt.Printf("Access token was successfully created... %s\n", t.Token)
} }
fmt.Printf("New user '%s' has been successfully created!\n", username)
return nil return nil
} }

View File

@@ -8,127 +8,37 @@ import (
"strings" "strings"
"testing" "testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestAdminUserCreate(t *testing.T) { func TestAdminUserCreate(t *testing.T) {
app := NewMainApp(AppVersion{})
reset := func() { reset := func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{})) assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{})) assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
} }
t.Run("MustChangePassword", func(t *testing.T) { type createCheck struct{ IsAdmin, MustChangePassword bool }
type check struct { createUser := func(name, args string) createCheck {
IsAdmin bool assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
MustChangePassword bool u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
} return createCheck{u.IsAdmin, u.MustChangePassword}
createCheck := func(name, args string) check {
require.NoError(t, microcmdUserCreate().Run(t.Context(), strings.Fields(fmt.Sprintf("create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
return check{IsAdmin: u.IsAdmin, MustChangePassword: u.MustChangePassword}
}
reset()
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u", ""), "first non-admin user doesn't need to change password")
reset()
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u", "--admin"), "first admin user doesn't need to change password")
reset()
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u", "--admin --must-change-password"))
assert.Equal(t, check{IsAdmin: true, MustChangePassword: true}, createCheck("u2", "--admin"))
assert.Equal(t, check{IsAdmin: true, MustChangePassword: false}, createCheck("u3", "--admin --must-change-password=false"))
assert.Equal(t, check{IsAdmin: false, MustChangePassword: true}, createCheck("u4", ""))
assert.Equal(t, check{IsAdmin: false, MustChangePassword: false}, createCheck("u5", "--must-change-password=false"))
})
createUser := func(name string, args ...string) error {
return microcmdUserCreate().Run(t.Context(), append([]string{"create", "--username", name, "--email", name + "@gitea.local"}, args...))
} }
reset()
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
t.Run("UserType", func(t *testing.T) { reset()
reset() assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
assert.NoError(t, createUser("u", "--user-type", "bot")) reset()
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"}) assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
assert.Equal(t, user_model.UserTypeBot, u.Type) assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
assert.Empty(t, u.Passwd) assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
}) assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
t.Run("AccessToken", func(t *testing.T) {
// no generated access token
reset()
assert.NoError(t, createUser("u", "--random-password"))
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
// using "--access-token" only means "all" access
reset()
assert.NoError(t, createUser("u", "--random-password", "--access-token"))
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
accessToken := unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "gitea-admin"})
hasScopes, err := accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
assert.NoError(t, err)
assert.True(t, hasScopes)
// using "--access-token" with name & scopes
reset()
assert.NoError(t, createUser("u", "--random-password", "--access-token", "--access-token-name", "new-token-name", "--access-token-scopes", "read:issue,read:user"))
assert.Equal(t, 1, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 1, unittest.GetCount(t, &auth_model.AccessToken{}))
accessToken = unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{Name: "new-token-name"})
hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopeReadUser)
assert.NoError(t, err)
assert.True(t, hasScopes)
hasScopes, err = accessToken.Scope.HasScope(auth_model.AccessTokenScopeWriteAdmin, auth_model.AccessTokenScopeWriteRepository)
assert.NoError(t, err)
assert.False(t, hasScopes)
// using "--access-token-name" without "--access-token"
reset()
err = createUser("u", "--random-password", "--access-token-name", "new-token-name")
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
// using "--access-token-scopes" without "--access-token"
reset()
err = createUser("u", "--random-password", "--access-token-scopes", "read:issue")
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
assert.ErrorContains(t, err, "access-token-name and access-token-scopes flags are only valid when access-token flag is set")
// empty permission
reset()
err = createUser("u", "--random-password", "--access-token", "--access-token-scopes", "public-only")
assert.Equal(t, 0, unittest.GetCount(t, &user_model.User{}))
assert.Equal(t, 0, unittest.GetCount(t, &auth_model.AccessToken{}))
assert.ErrorContains(t, err, "access token does not have any permission")
})
t.Run("UserFields", func(t *testing.T) {
reset()
assert.NoError(t, createUser("u-FullNameWithSpace", "--random-password", "--fullname", "First O'Middle Last"))
unittest.AssertExistsAndLoadBean(t, &user_model.User{
Name: "u-FullNameWithSpace",
LowerName: "u-fullnamewithspace",
FullName: "First O'Middle Last",
Email: "u-FullNameWithSpace@gitea.local",
})
assert.NoError(t, createUser("u-FullNameEmpty", "--random-password", "--fullname", ""))
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u-fullnameempty"})
assert.Empty(t, u.FullName)
})
} }

View File

@@ -4,56 +4,53 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
user_service "code.gitea.io/gitea/services/user" user_service "code.gitea.io/gitea/services/user"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func microcmdUserDelete() *cli.Command { var microcmdUserDelete = &cli.Command{
return &cli.Command{ Name: "delete",
Name: "delete", Usage: "Delete specific user by id, name or email",
Usage: "Delete specific user by id, name or email", Flags: []cli.Flag{
Flags: []cli.Flag{ &cli.Int64Flag{
&cli.Int64Flag{ Name: "id",
Name: "id", Usage: "ID of user of the user to delete",
Usage: "ID of user of the user to delete",
},
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to delete",
},
&cli.StringFlag{
Name: "email",
Aliases: []string{"e"},
Usage: "Email of the user to delete",
},
&cli.BoolFlag{
Name: "purge",
Usage: "Purge user, all their repositories, organizations and comments",
},
}, },
Action: runDeleteUser, &cli.StringFlag{
} Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to delete",
},
&cli.StringFlag{
Name: "email",
Aliases: []string{"e"},
Usage: "Email of the user to delete",
},
&cli.BoolFlag{
Name: "purge",
Usage: "Purge user, all their repositories, organizations and comments",
},
},
Action: runDeleteUser,
} }
func runDeleteUser(ctx context.Context, c *cli.Command) error { func runDeleteUser(c *cli.Context) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") { if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return errors.New("You must provide the id, username or email of a user to delete") return errors.New("You must provide the id, username or email of a user to delete")
} }
if !setting.IsInTesting { ctx, cancel := installSignals()
if err := initDB(ctx); err != nil { defer cancel()
return err
} if err := initDB(ctx); err != nil {
return err
} }
if err := storage.Init(); err != nil { if err := storage.Init(); err != nil {
@@ -73,11 +70,11 @@ func runDeleteUser(ctx context.Context, c *cli.Command) error {
return err return err
} }
if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) { if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
return fmt.Errorf("the user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username")) return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
} }
if c.IsSet("id") && user.ID != c.Int64("id") { if c.IsSet("id") && user.ID != c.Int64("id") {
return fmt.Errorf("the user %s does not match the provided id %d", user.Name, c.Int64("id")) return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
} }
return user_service.DeleteUser(ctx, user, c.Bool("purge")) return user_service.DeleteUser(ctx, user, c.Bool("purge"))

View File

@@ -1,111 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"strconv"
"strings"
"testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/require"
)
func TestAdminUserDelete(t *testing.T) {
ctx := t.Context()
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{}))
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
}()
setupTestUser := func(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
}
t.Run("delete user by id", func(t *testing.T) {
setupTestUser(t)
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by username", func(t *testing.T) {
setupTestUser(t)
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by email", func(t *testing.T) {
setupTestUser(t)
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by all 3 attributes", func(t *testing.T) {
setupTestUser(t)
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
}
func TestAdminUserDeleteFailure(t *testing.T) {
testCases := []struct {
name string
args []string
expectedErr string
}{
{
name: "no user to delete",
args: []string{"delete", "--username", "nonexistentuser"},
expectedErr: "user does not exist",
},
{
name: "user exists but provided username does not match",
args: []string{"delete", "--email", "testuser@gitea.local", "--username", "wrongusername"},
expectedErr: "the user testuser who has email testuser@gitea.local does not match the provided username wrongusername",
},
{
name: "user exists but provided id does not match",
args: []string{"delete", "--username", "testuser", "--id", "999"},
expectedErr: "the user testuser does not match the provided id 999",
},
{
name: "no required flags are provided",
args: []string{"delete"},
expectedErr: "You must provide the id, username or email of a user to delete",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ctx := t.Context()
if strings.Contains(tc.name, "user exists") {
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
}
err := microcmdUserDelete().Run(ctx, tc.args)
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
})
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.EmailAddress{}))
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
}
}

View File

@@ -4,14 +4,13 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
auth_model "code.gitea.io/gitea/models/auth" auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var microcmdUserGenerateAccessToken = &cli.Command{ var microcmdUserGenerateAccessToken = &cli.Command{
@@ -35,18 +34,21 @@ var microcmdUserGenerateAccessToken = &cli.Command{
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "scopes", Name: "scopes",
Value: "all", Value: "",
Usage: `Comma separated list of scopes to apply to access token, examples: "all", "public-only,read:issue", "write:repository,write:user"`, Usage: "Comma separated list of scopes to apply to access token",
}, },
}, },
Action: runGenerateAccessToken, Action: runGenerateAccessToken,
} }
func runGenerateAccessToken(ctx context.Context, c *cli.Command) error { func runGenerateAccessToken(c *cli.Context) error {
if !c.IsSet("username") { if !c.IsSet("username") {
return errors.New("you must provide a username to generate a token for") return errors.New("You must provide a username to generate a token for")
} }
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }
@@ -75,9 +77,6 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
if err != nil { if err != nil {
return fmt.Errorf("invalid access token scope provided: %w", err) return fmt.Errorf("invalid access token scope provided: %w", err)
} }
if !accessTokenScope.HasPermissionScope() {
return errors.New("access token does not have any permission")
}
t.Scope = accessTokenScope t.Scope = accessTokenScope
// create the token // create the token

View File

@@ -4,14 +4,13 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"text/tabwriter" "text/tabwriter"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var microcmdUserList = &cli.Command{ var microcmdUserList = &cli.Command{
@@ -26,7 +25,10 @@ var microcmdUserList = &cli.Command{
}, },
} }
func runListUsers(ctx context.Context, c *cli.Command) error { func runListUsers(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if err := initDB(ctx); err != nil { if err := initDB(ctx); err != nil {
return err return err
} }

View File

@@ -4,41 +4,40 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func microcmdUserMustChangePassword() *cli.Command { var microcmdUserMustChangePassword = &cli.Command{
return &cli.Command{ Name: "must-change-password",
Name: "must-change-password", Usage: "Set the must change password flag for the provided users or all users",
Usage: "Set the must change password flag for the provided users or all users", Action: runMustChangePassword,
Action: runMustChangePassword, Flags: []cli.Flag{
Flags: []cli.Flag{ &cli.BoolFlag{
&cli.BoolFlag{ Name: "all",
Name: "all", Aliases: []string{"A"},
Aliases: []string{"A"}, Usage: "All users must change password, except those explicitly excluded with --exclude",
Usage: "All users must change password, except those explicitly excluded with --exclude",
},
&cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"e"},
Usage: "Do not change the must-change-password flag for these users",
},
&cli.BoolFlag{
Name: "unset",
Usage: "Instead of setting the must-change-password flag, unset it",
},
}, },
} &cli.StringSliceFlag{
Name: "exclude",
Aliases: []string{"e"},
Usage: "Do not change the must-change-password flag for these users",
},
&cli.BoolFlag{
Name: "unset",
Usage: "Instead of setting the must-change-password flag, unset it",
},
},
} }
func runMustChangePassword(ctx context.Context, c *cli.Command) error { func runMustChangePassword(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
if c.NArg() == 0 && !c.IsSet("all") { if c.NArg() == 0 && !c.IsSet("all") {
return errors.New("either usernames or --all must be provided") return errors.New("either usernames or --all must be provided")
} }
@@ -47,10 +46,8 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
all := c.Bool("all") all := c.Bool("all")
exclude := c.StringSlice("exclude") exclude := c.StringSlice("exclude")
if !setting.IsInTesting { if err := initDB(ctx); err != nil {
if err := initDB(ctx); err != nil { return err
return err
}
} }
n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude) n, err := user_model.SetMustChangePassword(ctx, all, mustChangePassword, c.Args().Slice(), exclude)

View File

@@ -1,78 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"testing"
"code.gitea.io/gitea/models/db"
"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 TestMustChangePassword(t *testing.T) {
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
}()
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
require.NoError(t, err)
// Reset password change flag
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
require.NoError(t, err)
testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Make all users change password
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.True(t, testUserExclude.MustChangePassword)
// Reset password change flag but exclude all tested users
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.True(t, testUserExclude.MustChangePassword)
// Reset password change flag by listing multiple users
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Exclude a user from all user
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Unset a flag for single user
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
}

View File

@@ -6,7 +6,6 @@
package cmd package cmd
import ( import (
"context"
"crypto/ecdsa" "crypto/ecdsa"
"crypto/elliptic" "crypto/elliptic"
"crypto/rand" "crypto/rand"
@@ -14,7 +13,6 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"fmt"
"log" "log"
"math/big" "math/big"
"net" "net"
@@ -22,59 +20,47 @@ import (
"strings" "strings"
"time" "time"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// cmdCert represents the available cert sub-command. // CmdCert represents the available cert sub-command.
func cmdCert() *cli.Command { var CmdCert = &cli.Command{
return &cli.Command{ Name: "cert",
Name: "cert", Usage: "Generate self-signed certificate",
Usage: "Generate self-signed certificate", Description: `Generate a self-signed X.509 certificate for a TLS server.
Description: `Generate a self-signed X.509 certificate for a TLS server.
Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`,
Action: runCert, Action: runCert,
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "host", Name: "host",
Usage: "Comma-separated hostnames and IPs to generate a certificate for", Value: "",
Required: true, Usage: "Comma-separated hostnames and IPs to generate a certificate for",
},
&cli.StringFlag{
Name: "ecdsa-curve",
Value: "",
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
},
&cli.IntFlag{
Name: "rsa-bits",
Value: 3072,
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
},
&cli.StringFlag{
Name: "start-date",
Value: "",
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
},
&cli.DurationFlag{
Name: "duration",
Value: 365 * 24 * time.Hour,
Usage: "Duration that certificate is valid for",
},
&cli.BoolFlag{
Name: "ca",
Usage: "whether this cert should be its own Certificate Authority",
},
&cli.StringFlag{
Name: "out",
Value: "cert.pem",
Usage: "Path to the file where there certificate will be saved",
},
&cli.StringFlag{
Name: "keyout",
Value: "key.pem",
Usage: "Path to the file where there certificate key will be saved",
},
}, },
} &cli.StringFlag{
Name: "ecdsa-curve",
Value: "",
Usage: "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521",
},
&cli.IntFlag{
Name: "rsa-bits",
Value: 3072,
Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set",
},
&cli.StringFlag{
Name: "start-date",
Value: "",
Usage: "Creation date formatted as Jan 1 15:04:05 2011",
},
&cli.DurationFlag{
Name: "duration",
Value: 365 * 24 * time.Hour,
Usage: "Duration that certificate is valid for",
},
&cli.BoolFlag{
Name: "ca",
Usage: "whether this cert should be its own Certificate Authority",
},
},
} }
func publicKey(priv any) any { func publicKey(priv any) any {
@@ -103,7 +89,11 @@ func pemBlockForKey(priv any) *pem.Block {
} }
} }
func runCert(_ context.Context, c *cli.Command) error { func runCert(c *cli.Context) error {
if err := argsSet(c, "host"); err != nil {
return err
}
var priv any var priv any
var err error var err error
switch c.String("ecdsa-curve") { switch c.String("ecdsa-curve") {
@@ -118,17 +108,17 @@ func runCert(_ context.Context, c *cli.Command) error {
case "P521": case "P521":
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
default: default:
err = fmt.Errorf("unrecognized elliptic curve: %q", c.String("ecdsa-curve")) log.Fatalf("Unrecognized elliptic curve: %q", c.String("ecdsa-curve"))
} }
if err != nil { if err != nil {
return fmt.Errorf("failed to generate private key: %w", err) log.Fatalf("Failed to generate private key: %v", err)
} }
var notBefore time.Time var notBefore time.Time
if startDate := c.String("start-date"); startDate != "" { if startDate := c.String("start-date"); startDate != "" {
notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate) notBefore, err = time.Parse("Jan 2 15:04:05 2006", startDate)
if err != nil { if err != nil {
return fmt.Errorf("failed to parse creation date %w", err) log.Fatalf("Failed to parse creation date: %v", err)
} }
} else { } else {
notBefore = time.Now() notBefore = time.Now()
@@ -139,7 +129,7 @@ func runCert(_ context.Context, c *cli.Command) error {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil { if err != nil {
return fmt.Errorf("failed to generate serial number: %w", err) log.Fatalf("Failed to generate serial number: %v", err)
} }
template := x509.Certificate{ template := x509.Certificate{
@@ -156,8 +146,8 @@ func runCert(_ context.Context, c *cli.Command) error {
BasicConstraintsValid: true, BasicConstraintsValid: true,
} }
hosts := strings.SplitSeq(c.String("host"), ",") hosts := strings.Split(c.String("host"), ",")
for h := range hosts { for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil { if ip := net.ParseIP(h); ip != nil {
template.IPAddresses = append(template.IPAddresses, ip) template.IPAddresses = append(template.IPAddresses, ip)
} else { } else {
@@ -172,35 +162,35 @@ func runCert(_ context.Context, c *cli.Command) error {
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv) derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
if err != nil { if err != nil {
return fmt.Errorf("failed to create certificate: %w", err) log.Fatalf("Failed to create certificate: %v", err)
} }
certOut, err := os.Create(c.String("out")) certOut, err := os.Create("cert.pem")
if err != nil { if err != nil {
return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err) log.Fatalf("Failed to open cert.pem for writing: %v", err)
} }
err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
if err != nil { if err != nil {
return fmt.Errorf("failed to encode certificate: %w", err) log.Fatalf("Failed to encode certificate: %v", err)
} }
err = certOut.Close() err = certOut.Close()
if err != nil { if err != nil {
return fmt.Errorf("failed to write cert: %w", err) log.Fatalf("Failed to write cert: %v", err)
} }
fmt.Fprintf(c.Writer, "Written cert to %s\n", c.String("out")) log.Println("Written cert.pem")
keyOut, err := os.OpenFile(c.String("keyout"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600) keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
if err != nil { if err != nil {
return fmt.Errorf("failed to open %s for writing: %w", c.String("keyout"), err) log.Fatalf("Failed to open key.pem for writing: %v", err)
} }
err = pem.Encode(keyOut, pemBlockForKey(priv)) err = pem.Encode(keyOut, pemBlockForKey(priv))
if err != nil { if err != nil {
return fmt.Errorf("failed to encode key: %w", err) log.Fatalf("Failed to encode key: %v", err)
} }
err = keyOut.Close() err = keyOut.Close()
if err != nil { if err != nil {
return fmt.Errorf("failed to write key: %w", err) log.Fatalf("Failed to write key: %v", err)
} }
fmt.Fprintf(c.Writer, "Written key to %s\n", c.String("keyout")) log.Println("Written key.pem")
return nil return nil
} }

View File

@@ -1,123 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCertCommand(t *testing.T) {
cases := []struct {
name string
args []string
}{
{
name: "RSA cert generation",
args: []string{
"cert-test",
"--host", "localhost",
"--rsa-bits", "2048",
"--duration", "1h",
"--start-date", "Jan 1 00:00:00 2024",
},
},
{
name: "ECDSA cert generation",
args: []string{
"cert-test",
"--host", "localhost",
"--ecdsa-curve", "P256",
"--duration", "1h",
"--start-date", "Jan 1 00:00:00 2024",
},
},
{
name: "mixed host, certificate authority",
args: []string{
"cert-test",
"--host", "localhost,127.0.0.1",
"--duration", "1h",
"--start-date", "Jan 1 00:00:00 2024",
},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
app := cmdCert()
tempDir := t.TempDir()
certFile := filepath.Join(tempDir, "cert.pem")
keyFile := filepath.Join(tempDir, "key.pem")
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
require.NoError(t, err)
assert.FileExists(t, certFile)
assert.FileExists(t, keyFile)
})
}
}
func TestCertCommandFailures(t *testing.T) {
cases := []struct {
name string
args []string
errMsg string
}{
{
name: "Start Date Parsing failure",
args: []string{
"cert-test",
"--host", "localhost",
"--start-date", "invalid-date",
},
errMsg: "parsing time",
},
{
name: "Unknown curve",
args: []string{
"cert-test",
"--host", "localhost",
"--ecdsa-curve", "invalid-curve",
},
errMsg: "unrecognized elliptic curve",
},
{
name: "Key generation failure",
args: []string{
"cert-test",
"--host", "localhost",
"--rsa-bits", "invalid-bits",
},
},
{
name: "Missing parameters",
args: []string{
"cert-test",
},
errMsg: `"host" not set`,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
app := cmdCert()
tempDir := t.TempDir()
certFile := filepath.Join(tempDir, "cert.pem")
keyFile := filepath.Join(tempDir, "key.pem")
err := app.Run(t.Context(), append(c.args, "--out", certFile, "--keyout", keyFile))
require.Error(t, err)
if c.errMsg != "" {
assert.ErrorContains(t, err, c.errMsg)
}
assert.NoFileExists(t, certFile)
assert.NoFileExists(t, keyFile)
})
}
}

View File

@@ -18,19 +18,20 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// argsSet checks that all the required arguments are set. args is a list of // argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context. // arguments that must be set in the passed Context.
func argsSet(c *cli.Command, args ...string) error { func argsSet(c *cli.Context, args ...string) error {
for _, a := range args { for _, a := range args {
if !c.IsSet(a) { if !c.IsSet(a) {
return errors.New(a + " is not set") return errors.New(a + " is not set")
} }
if c.Value(a) == nil { if util.IsEmptyString(c.String(a)) {
return errors.New(a + " is required") return errors.New(a + " is required")
} }
} }
@@ -108,7 +109,7 @@ func setupConsoleLogger(level log.Level, colorize bool, out io.Writer) {
log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer) log.GetManager().GetLogger(log.DEFAULT).ReplaceAllWriters(writer)
} }
func globalBool(c *cli.Command, name string) bool { func globalBool(c *cli.Context, name string) bool {
for _, ctx := range c.Lineage() { for _, ctx := range c.Lineage() {
if ctx.Bool(name) { if ctx.Bool(name) {
return true return true
@@ -119,8 +120,8 @@ func globalBool(c *cli.Command, name string) bool {
// PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout. // PrepareConsoleLoggerLevel by default, use INFO level for console logger, but some sub-commands (for git/ssh protocol) shouldn't output any log to stdout.
// Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever. // Any log appears in git stdout pipe will break the git protocol, eg: client can't push and hangs forever.
func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cli.Command) (context.Context, error) { func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(*cli.Context) error {
return func(ctx context.Context, c *cli.Command) (context.Context, error) { return func(c *cli.Context) error {
level := defaultLevel level := defaultLevel
if globalBool(c, "quiet") { if globalBool(c, "quiet") {
level = log.FATAL level = log.FATAL
@@ -129,16 +130,6 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl
level = log.TRACE level = log.TRACE
} }
log.SetConsoleLogger(log.DEFAULT, "console-default", level) log.SetConsoleLogger(log.DEFAULT, "console-default", level)
return ctx, nil return nil
} }
} }
func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
// Dirty patch for urfave/cli's strange design.
// "./gitea bad-cmd" should not start the web server.
rootArgs := cmd.Root().Args().Slice()
if len(rootArgs) != 0 && rootArgs[0] != cmd.Name {
return rootArgs[0], false
}
return "", true
}

View File

@@ -1,38 +0,0 @@
// Copyright 2025 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3"
)
func TestDefaultCommand(t *testing.T) {
test := func(t *testing.T, args []string, expectedRetName string, expectedRetValid bool) {
called := false
cmd := &cli.Command{
DefaultCommand: "test",
Commands: []*cli.Command{
{
Name: "test",
Action: func(ctx context.Context, command *cli.Command) error {
retName, retValid := isValidDefaultSubCommand(command)
assert.Equal(t, expectedRetName, retName)
assert.Equal(t, expectedRetValid, retValid)
called = true
return nil
},
},
},
}
assert.NoError(t, cmd.Run(t.Context(), args))
assert.True(t, called)
}
test(t, []string{"./gitea"}, "", true)
test(t, []string{"./gitea", "test"}, "", true)
test(t, []string{"./gitea", "other"}, "other", false)
}

View File

@@ -4,13 +4,11 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"strings" "strings"
cli_docs "github.com/urfave/cli-docs/v3" "github.com/urfave/cli/v2"
"github.com/urfave/cli/v3"
) )
// CmdDocs represents the available docs sub-command. // CmdDocs represents the available docs sub-command.
@@ -32,16 +30,16 @@ var CmdDocs = &cli.Command{
}, },
} }
func runDocs(_ context.Context, cmd *cli.Command) error { func runDocs(ctx *cli.Context) error {
docs, err := cli_docs.ToMarkdown(cmd.Root()) docs, err := ctx.App.ToMarkdown()
if cmd.Bool("man") { if ctx.Bool("man") {
docs, err = cli_docs.ToMan(cmd.Root()) docs, err = ctx.App.ToMan()
} }
if err != nil { if err != nil {
return err return err
} }
if !cmd.Bool("man") { if !ctx.Bool("man") {
// Clean up markdown. The following bug was fixed in v2, but is present in v1. // Clean up markdown. The following bug was fixed in v2, but is present in v1.
// It affects markdown output (even though the issue is referring to man pages) // It affects markdown output (even though the issue is referring to man pages)
// https://github.com/urfave/cli/issues/1040 // https://github.com/urfave/cli/issues/1040
@@ -53,8 +51,8 @@ func runDocs(_ context.Context, cmd *cli.Command) error {
} }
out := os.Stdout out := os.Stdout
if cmd.String("output") != "" { if ctx.String("output") != "" {
fi, err := os.Create(cmd.String("output")) fi, err := os.Create(ctx.String("output"))
if err != nil { if err != nil {
return err return err
} }

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
golog "log" golog "log"
"os" "os"
@@ -20,7 +19,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/doctor" "code.gitea.io/gitea/services/doctor"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
"xorm.io/xorm" "xorm.io/xorm"
) )
@@ -30,7 +29,7 @@ var CmdDoctor = &cli.Command{
Usage: "Diagnose and optionally fix problems, convert or re-create database tables", Usage: "Diagnose and optionally fix problems, convert or re-create database tables",
Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.", Description: "A command to diagnose problems with the current Gitea instance according to the given configuration. Some problems can optionally be fixed by modifying the database or data storage.",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
cmdDoctorCheck, cmdDoctorCheck,
cmdRecreateTable, cmdRecreateTable,
cmdDoctorConvert, cmdDoctorConvert,
@@ -93,13 +92,16 @@ You should back-up your database before doing this and ensure that your database
Action: runRecreateTable, Action: runRecreateTable,
} }
func runRecreateTable(ctx context.Context, cmd *cli.Command) error { func runRecreateTable(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
defer cancel()
// Redirect the default golog to here // Redirect the default golog to here
golog.SetFlags(0) golog.SetFlags(0)
golog.SetPrefix("") golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info)) golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
debug := cmd.Bool("debug") debug := ctx.Bool("debug")
setting.MustInstalled() setting.MustInstalled()
setting.LoadDBSetting() setting.LoadDBSetting()
@@ -110,15 +112,15 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
} }
setting.Database.LogSQL = debug setting.Database.LogSQL = debug
if err := db.InitEngine(ctx); err != nil { if err := db.InitEngine(stdCtx); err != nil {
fmt.Println(err) fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.") fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
return nil return nil
} }
args := cmd.Args() args := ctx.Args()
names := make([]string, 0, cmd.NArg()) names := make([]string, 0, ctx.NArg())
for i := 0; i < cmd.NArg(); i++ { for i := 0; i < ctx.NArg(); i++ {
names = append(names, args.Get(i)) names = append(names, args.Get(i))
} }
@@ -128,25 +130,24 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
} }
recreateTables := migrate_base.RecreateTables(beans...) recreateTables := migrate_base.RecreateTables(beans...)
return db.InitEngineWithMigration(context.Background(), func(ctx context.Context, x *xorm.Engine) error { return db.InitEngineWithMigration(stdCtx, func(x *xorm.Engine) error {
if err := migrations.EnsureUpToDate(ctx, x); err != nil { if err := migrations.EnsureUpToDate(x); err != nil {
return err return err
} }
return recreateTables(x) return recreateTables(x)
}) })
} }
func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) { func setupDoctorDefaultLogger(ctx *cli.Context, colorize bool) {
// Silence the default loggers // Silence the default loggers
setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr) setupConsoleLogger(log.FATAL, log.CanColorStderr, os.Stderr)
logFile := cmd.String("log-file") logFile := ctx.String("log-file")
switch logFile { if logFile == "" {
case "":
return // if no doctor log-file is set, do not show any log from default logger return // if no doctor log-file is set, do not show any log from default logger
case "-": } else if logFile == "-" {
setupConsoleLogger(log.TRACE, colorize, os.Stdout) setupConsoleLogger(log.TRACE, colorize, os.Stdout)
default: } else {
logFile, _ = filepath.Abs(logFile) logFile, _ = filepath.Abs(logFile)
writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}} writeMode := log.WriterMode{Level: log.TRACE, WriterOption: log.WriterFileOption{FileName: logFile}}
writer, err := log.NewEventWriter("console-to-file", "file", writeMode) writer, err := log.NewEventWriter("console-to-file", "file", writeMode)
@@ -158,20 +159,23 @@ func setupDoctorDefaultLogger(cmd *cli.Command, colorize bool) {
} }
} }
func runDoctorCheck(ctx context.Context, cmd *cli.Command) error { func runDoctorCheck(ctx *cli.Context) error {
stdCtx, cancel := installSignals()
defer cancel()
colorize := log.CanColorStdout colorize := log.CanColorStdout
if cmd.IsSet("color") { if ctx.IsSet("color") {
colorize = cmd.Bool("color") colorize = ctx.Bool("color")
} }
setupDoctorDefaultLogger(cmd, colorize) setupDoctorDefaultLogger(ctx, colorize)
// Finally redirect the default golang's log to here // Finally redirect the default golang's log to here
golog.SetFlags(0) golog.SetFlags(0)
golog.SetPrefix("") golog.SetPrefix("")
golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info)) golog.SetOutput(log.LoggerToWriter(log.GetLogger(log.DEFAULT).Info))
if cmd.IsSet("list") { if ctx.IsSet("list") {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
_, _ = w.Write([]byte("Default\tName\tTitle\n")) _, _ = w.Write([]byte("Default\tName\tTitle\n"))
doctor.SortChecks(doctor.Checks) doctor.SortChecks(doctor.Checks)
@@ -189,12 +193,12 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
} }
var checks []*doctor.Check var checks []*doctor.Check
if cmd.Bool("all") { if ctx.Bool("all") {
checks = make([]*doctor.Check, len(doctor.Checks)) checks = make([]*doctor.Check, len(doctor.Checks))
copy(checks, doctor.Checks) copy(checks, doctor.Checks)
} else if cmd.IsSet("run") { } else if ctx.IsSet("run") {
addDefault := cmd.Bool("default") addDefault := ctx.Bool("default")
runNamesSet := container.SetOf(cmd.StringSlice("run")...) runNamesSet := container.SetOf(ctx.StringSlice("run")...)
for _, check := range doctor.Checks { for _, check := range doctor.Checks {
if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) { if (addDefault && check.IsDefault) || runNamesSet.Contains(check.Name) {
checks = append(checks, check) checks = append(checks, check)
@@ -211,5 +215,5 @@ func runDoctorCheck(ctx context.Context, cmd *cli.Command) error {
} }
} }
} }
return doctor.RunChecks(ctx, colorize, cmd.Bool("fix"), checks) return doctor.RunChecks(stdCtx, colorize, ctx.Bool("fix"), checks)
} }

View File

@@ -4,14 +4,13 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// cmdDoctorConvert represents the available convert sub-command. // cmdDoctorConvert represents the available convert sub-command.
@@ -22,8 +21,11 @@ var cmdDoctorConvert = &cli.Command{
Action: runDoctorConvert, Action: runDoctorConvert,
} }
func runDoctorConvert(ctx context.Context, cmd *cli.Command) error { func runDoctorConvert(ctx *cli.Context) error {
if err := initDB(ctx); err != nil { stdCtx, cancel := installSignals()
defer cancel()
if err := initDB(stdCtx); err != nil {
return err return err
} }

View File

@@ -11,7 +11,7 @@ import (
"code.gitea.io/gitea/services/doctor" "code.gitea.io/gitea/services/doctor"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func TestDoctorRun(t *testing.T) { func TestDoctorRun(t *testing.T) {
@@ -22,13 +22,12 @@ func TestDoctorRun(t *testing.T) {
SkipDatabaseInitialization: true, SkipDatabaseInitialization: true,
}) })
app := &cli.Command{ app := cli.NewApp()
Commands: []*cli.Command{cmdDoctorCheck}, app.Commands = []*cli.Command{cmdDoctorCheck}
} err := app.Run([]string{"./gitea", "check", "--run", "test-check"})
err := app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check"})
assert.NoError(t, err) assert.NoError(t, err)
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "no-such"}) err = app.Run([]string{"./gitea", "check", "--run", "no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`) assert.ErrorContains(t, err, `unknown checks: "no-such"`)
err = app.Run(t.Context(), []string{"./gitea", "check", "--run", "test-check,no-such"}) err = app.Run([]string{"./gitea", "check", "--run", "test-check,no-such"})
assert.ErrorContains(t, err, `unknown checks: "no-such"`) assert.ErrorContains(t, err, `unknown checks: "no-such"`)
} }

View File

@@ -5,7 +5,7 @@
package cmd package cmd
import ( import (
"context" "fmt"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@@ -20,7 +20,8 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"gitea.com/go-chi/session" "gitea.com/go-chi/session"
"github.com/urfave/cli/v3" "github.com/mholt/archiver/v3"
"github.com/urfave/cli/v2"
) )
// CmdDump represents the available dump sub-command. // CmdDump represents the available dump sub-command.
@@ -92,7 +93,7 @@ var CmdDump = &cli.Command{
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "type", Name: "type",
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "), Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
}, },
}, },
} }
@@ -101,17 +102,17 @@ func fatal(format string, args ...any) {
log.Fatal(format, args...) log.Fatal(format, args...)
} }
func runDump(ctx context.Context, cmd *cli.Command) error { func runDump(ctx *cli.Context) error {
setting.MustInstalled() setting.MustInstalled()
quite := cmd.Bool("quiet") quite := ctx.Bool("quiet")
verbose := cmd.Bool("verbose") verbose := ctx.Bool("verbose")
if verbose && quite { if verbose && quite {
fatal("Option --quiet and --verbose cannot both be set") fatal("Option --quiet and --verbose cannot both be set")
} }
// outFileName is either "-" or a file name (will be made absolute) // outFileName is either "-" or a file name (will be made absolute)
outFileName, outType := dump.PrepareFileNameAndType(cmd.String("file"), cmd.String("type")) outFileName, outType := dump.PrepareFileNameAndType(ctx.String("file"), ctx.String("type"))
if outType == "" { if outType == "" {
fatal("Invalid output type") fatal("Invalid output type")
} }
@@ -136,7 +137,10 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
setting.DisableLoggerInit() setting.DisableLoggerInit()
setting.LoadSettings() // cannot access session settings otherwise setting.LoadSettings() // cannot access session settings otherwise
err := db.InitEngine(ctx) stdCtx, cancel := installSignals()
defer cancel()
err := db.InitEngine(stdCtx)
if err != nil { if err != nil {
return err return err
} }
@@ -145,20 +149,24 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
return err return err
} }
dumper, err := dump.NewDumper(ctx, outType, outFile) archiverGeneric, err := archiver.ByExtension("." + outType)
if err != nil { if err != nil {
fatal("Failed to create archive %q: %v", outFile, err) fatal("Unable to get archiver for extension: %v", err)
return err
} }
dumper.Verbose = verbose
dumper.GlobalExcludeAbsPath(outFileName)
defer func() {
if err := dumper.Close(); err != nil {
fatal("Failed to save archive %q: %v", outFileName, err)
}
}()
if cmd.IsSet("skip-repository") && cmd.Bool("skip-repository") { archiverWriter := archiverGeneric.(archiver.Writer)
if err := archiverWriter.Create(outFile); err != nil {
fatal("Creating archiver.Writer failed: %v", err)
}
defer archiverWriter.Close()
dumper := &dump.Dumper{
Writer: archiverWriter,
Verbose: verbose,
}
dumper.GlobalExcludeAbsPath(outFileName)
if ctx.IsSet("skip-repository") && ctx.Bool("skip-repository") {
log.Info("Skip dumping local repositories") log.Info("Skip dumping local repositories")
} else { } else {
log.Info("Dumping local repositories... %s", setting.RepoRootPath) log.Info("Dumping local repositories... %s", setting.RepoRootPath)
@@ -166,7 +174,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
fatal("Failed to include repositories: %v", err) fatal("Failed to include repositories: %v", err)
} }
if cmd.IsSet("skip-lfs-data") && cmd.Bool("skip-lfs-data") { if ctx.IsSet("skip-lfs-data") && ctx.Bool("skip-lfs-data") {
log.Info("Skip dumping LFS data") log.Info("Skip dumping LFS data")
} else if !setting.LFS.StartServer { } else if !setting.LFS.StartServer {
log.Info("LFS isn't enabled. Skip dumping LFS data") log.Info("LFS isn't enabled. Skip dumping LFS data")
@@ -175,18 +183,18 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
if err != nil { if err != nil {
return err return err
} }
return dumper.AddFileByReader(object, info, path.Join("data", "lfs", objPath)) return dumper.AddReader(object, info, path.Join("data", "lfs", objPath))
}); err != nil { }); err != nil {
fatal("Failed to dump LFS objects: %v", err) fatal("Failed to dump LFS objects: %v", err)
} }
} }
if cmd.Bool("skip-db") { if ctx.Bool("skip-db") {
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere. // Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
dumper.GlobalExcludeAbsPath(setting.Database.Path) dumper.GlobalExcludeAbsPath(setting.Database.Path)
log.Info("Skipping database") log.Info("Skipping database")
} else { } else {
tmpDir := cmd.String("tempdir") tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) { if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir) fatal("Path does not exist: %s", tmpDir)
} }
@@ -202,7 +210,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
} }
}() }()
targetDBType := cmd.String("database") targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() { if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType) log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else { } else {
@@ -213,17 +221,17 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
fatal("Failed to dump database: %v", err) fatal("Failed to dump database: %v", err)
} }
if err = dumper.AddFileByPath("gitea-db.sql", dbDump.Name()); err != nil { if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
fatal("Failed to include gitea-db.sql: %v", err) fatal("Failed to include gitea-db.sql: %v", err)
} }
} }
log.Info("Adding custom configuration file from %s", setting.CustomConf) log.Info("Adding custom configuration file from %s", setting.CustomConf)
if err = dumper.AddFileByPath("app.ini", setting.CustomConf); err != nil { if err = dumper.AddFile("app.ini", setting.CustomConf); err != nil {
fatal("Failed to include specified app.ini: %v", err) fatal("Failed to include specified app.ini: %v", err)
} }
if cmd.IsSet("skip-custom-dir") && cmd.Bool("skip-custom-dir") { if ctx.IsSet("skip-custom-dir") && ctx.Bool("skip-custom-dir") {
log.Info("Skipping custom directory") log.Info("Skipping custom directory")
} else { } else {
customDir, err := os.Stat(setting.CustomPath) customDir, err := os.Stat(setting.CustomPath)
@@ -256,7 +264,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
excludes = append(excludes, opts.ProviderConfig) excludes = append(excludes, opts.ProviderConfig)
} }
if cmd.IsSet("skip-index") && cmd.Bool("skip-index") { if ctx.IsSet("skip-index") && ctx.Bool("skip-index") {
excludes = append(excludes, setting.Indexer.RepoPath) excludes = append(excludes, setting.Indexer.RepoPath)
excludes = append(excludes, setting.Indexer.IssuePath) excludes = append(excludes, setting.Indexer.IssuePath)
} }
@@ -265,26 +273,25 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
excludes = append(excludes, setting.LFS.Storage.Path) excludes = append(excludes, setting.LFS.Storage.Path)
excludes = append(excludes, setting.Attachment.Storage.Path) excludes = append(excludes, setting.Attachment.Storage.Path)
excludes = append(excludes, setting.Packages.Storage.Path) excludes = append(excludes, setting.Packages.Storage.Path)
excludes = append(excludes, setting.RepoArchive.Storage.Path)
excludes = append(excludes, setting.Log.RootPath) excludes = append(excludes, setting.Log.RootPath)
if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil { if err := dumper.AddRecursiveExclude("data", setting.AppDataPath, excludes); err != nil {
fatal("Failed to include data directory: %v", err) fatal("Failed to include data directory: %v", err)
} }
} }
if cmd.IsSet("skip-attachment-data") && cmd.Bool("skip-attachment-data") { if ctx.IsSet("skip-attachment-data") && ctx.Bool("skip-attachment-data") {
log.Info("Skip dumping attachment data") log.Info("Skip dumping attachment data")
} else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error { } else if err := storage.Attachments.IterateObjects("", func(objPath string, object storage.Object) error {
info, err := object.Stat() info, err := object.Stat()
if err != nil { if err != nil {
return err return err
} }
return dumper.AddFileByReader(object, info, path.Join("data", "attachments", objPath)) return dumper.AddReader(object, info, path.Join("data", "attachments", objPath))
}); err != nil { }); err != nil {
fatal("Failed to dump attachments: %v", err) fatal("Failed to dump attachments: %v", err)
} }
if cmd.IsSet("skip-package-data") && cmd.Bool("skip-package-data") { if ctx.IsSet("skip-package-data") && ctx.Bool("skip-package-data") {
log.Info("Skip dumping package data") log.Info("Skip dumping package data")
} else if !setting.Packages.Enabled { } else if !setting.Packages.Enabled {
log.Info("Packages isn't enabled. Skip dumping package data") log.Info("Packages isn't enabled. Skip dumping package data")
@@ -293,7 +300,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
if err != nil { if err != nil {
return err return err
} }
return dumper.AddFileByReader(object, info, path.Join("data", "packages", objPath)) return dumper.AddReader(object, info, path.Join("data", "packages", objPath))
}); err != nil { }); err != nil {
fatal("Failed to dump packages: %v", err) fatal("Failed to dump packages: %v", err)
} }
@@ -301,7 +308,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
// Doesn't check if LogRootPath exists before processing --skip-log intentionally, // Doesn't check if LogRootPath exists before processing --skip-log intentionally,
// ensuring that it's clear the dump is skipped whether the directory's initialized // ensuring that it's clear the dump is skipped whether the directory's initialized
// yet or not. // yet or not.
if cmd.IsSet("skip-log") && cmd.Bool("skip-log") { if ctx.IsSet("skip-log") && ctx.Bool("skip-log") {
log.Info("Skip dumping log files") log.Info("Skip dumping log files")
} else { } else {
isExist, err := util.IsExist(setting.Log.RootPath) isExist, err := util.IsExist(setting.Log.RootPath)
@@ -318,6 +325,10 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
if outFileName == "-" { if outFileName == "-" {
log.Info("Finish dumping to stdout") log.Info("Finish dumping to stdout")
} else { } else {
if err = archiverWriter.Close(); err != nil {
_ = os.Remove(outFileName)
fatal("Failed to save %q: %v", outFileName, err)
}
if err = os.Chmod(outFileName, 0o600); err != nil { if err = os.Chmod(outFileName, 0o600); err != nil {
log.Info("Can't change file access permissions mask to 0600: %v", err) log.Info("Can't change file access permissions mask to 0600: %v", err)
} }

View File

@@ -19,7 +19,7 @@ import (
"code.gitea.io/gitea/services/convert" "code.gitea.io/gitea/services/convert"
"code.gitea.io/gitea/services/migrations" "code.gitea.io/gitea/services/migrations"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdDumpRepository represents the available dump repository sub-command. // CmdDumpRepository represents the available dump repository sub-command.
@@ -79,18 +79,16 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}, },
} }
func runDumpRepository(ctx context.Context, cmd *cli.Command) error { func runDumpRepository(ctx *cli.Context) error {
setupConsoleLogger(log.INFO, log.CanColorStderr, os.Stderr) stdCtx, cancel := installSignals()
defer cancel()
setting.DisableLoggerInit() if err := initDB(stdCtx); err != nil {
setting.LoadSettings() // cannot access skip_tls_verify settings otherwise
if err := initDB(ctx); err != nil {
return err return err
} }
// migrations.GiteaLocalUploader depends on git module // migrations.GiteaLocalUploader depends on git module
if err := git.InitSimple(); err != nil { if err := git.InitSimple(context.Background()); err != nil {
return err return err
} }
@@ -102,8 +100,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
var ( var (
serviceType structs.GitServiceType serviceType structs.GitServiceType
cloneAddr = cmd.String("clone_addr") cloneAddr = ctx.String("clone_addr")
serviceStr = cmd.String("git_service") serviceStr = ctx.String("git_service")
) )
if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") { if strings.HasPrefix(strings.ToLower(cloneAddr), "https://github.com/") {
@@ -121,13 +119,13 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
opts := base.MigrateOptions{ opts := base.MigrateOptions{
GitServiceType: serviceType, GitServiceType: serviceType,
CloneAddr: cloneAddr, CloneAddr: cloneAddr,
AuthUsername: cmd.String("auth_username"), AuthUsername: ctx.String("auth_username"),
AuthPassword: cmd.String("auth_password"), AuthPassword: ctx.String("auth_password"),
AuthToken: cmd.String("auth_token"), AuthToken: ctx.String("auth_token"),
RepoName: cmd.String("repo_name"), RepoName: ctx.String("repo_name"),
} }
if len(cmd.String("units")) == 0 { if len(ctx.String("units")) == 0 {
opts.Wiki = true opts.Wiki = true
opts.Issues = true opts.Issues = true
opts.Milestones = true opts.Milestones = true
@@ -137,8 +135,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
opts.PullRequests = true opts.PullRequests = true
opts.ReleaseAssets = true opts.ReleaseAssets = true
} else { } else {
units := strings.SplitSeq(cmd.String("units"), ",") units := strings.Split(ctx.String("units"), ",")
for unit := range units { for _, unit := range units {
switch strings.ToLower(strings.TrimSpace(unit)) { switch strings.ToLower(strings.TrimSpace(unit)) {
case "": case "":
continue continue
@@ -166,7 +164,7 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
// the repo_dir will be removed if error occurs in DumpRepository // the repo_dir will be removed if error occurs in DumpRepository
// make sure the directory doesn't exist or is empty, prevent from deleting user files // make sure the directory doesn't exist or is empty, prevent from deleting user files
repoDir := cmd.String("repo_dir") repoDir := ctx.String("repo_dir")
if exists, err := util.IsExist(repoDir); err != nil { if exists, err := util.IsExist(repoDir); err != nil {
return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err) return fmt.Errorf("unable to stat repo_dir %q: %w", repoDir, err)
} else if exists { } else if exists {
@@ -179,9 +177,9 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
} }
if err := migrations.DumpRepository( if err := migrations.DumpRepository(
ctx, context.Background(),
repoDir, repoDir,
cmd.String("owner_name"), ctx.String("owner_name"),
opts, opts,
); err != nil { ); err != nil {
log.Fatal("Failed to dump repository: %v", err) log.Fatal("Failed to dump repository: %v", err)

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -12,7 +11,6 @@ import (
"strings" "strings"
"code.gitea.io/gitea/modules/assetfs" "code.gitea.io/gitea/modules/assetfs"
"code.gitea.io/gitea/modules/glob"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options" "code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/public"
@@ -20,7 +18,8 @@ import (
"code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/urfave/cli/v3" "github.com/gobwas/glob"
"github.com/urfave/cli/v2"
) )
// CmdEmbedded represents the available extract sub-command. // CmdEmbedded represents the available extract sub-command.
@@ -29,7 +28,7 @@ var (
Name: "embedded", Name: "embedded",
Usage: "Extract embedded resources", Usage: "Extract embedded resources",
Description: "A command for extracting embedded resources, like templates and images", Description: "A command for extracting embedded resources, like templates and images",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdList, subcmdList,
subcmdView, subcmdView,
subcmdExtract, subcmdExtract,
@@ -101,7 +100,7 @@ type assetFile struct {
path string path string
} }
func initEmbeddedExtractor(c *cli.Command) error { func initEmbeddedExtractor(c *cli.Context) error {
setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr) setupConsoleLogger(log.ERROR, log.CanColorStderr, os.Stderr)
patterns, err := compileCollectPatterns(c.Args().Slice()) patterns, err := compileCollectPatterns(c.Args().Slice())
@@ -116,31 +115,31 @@ func initEmbeddedExtractor(c *cli.Command) error {
return nil return nil
} }
func runList(_ context.Context, c *cli.Command) error { func runList(c *cli.Context) error {
if err := runListDo(c); err != nil { if err := runListDo(c); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runView(_ context.Context, c *cli.Command) error { func runView(c *cli.Context) error {
if err := runViewDo(c); err != nil { if err := runViewDo(c); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runExtract(_ context.Context, c *cli.Command) error { func runExtract(c *cli.Context) error {
if err := runExtractDo(c); err != nil { if err := runExtractDo(c); err != nil {
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err) fmt.Fprintf(os.Stderr, "%v\n", err)
return err return err
} }
return nil return nil
} }
func runListDo(c *cli.Command) error { func runListDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(c); err != nil {
return err return err
} }
@@ -152,7 +151,7 @@ func runListDo(c *cli.Command) error {
return nil return nil
} }
func runViewDo(c *cli.Command) error { func runViewDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(c); err != nil {
return err return err
} }
@@ -175,7 +174,7 @@ func runViewDo(c *cli.Command) error {
return nil return nil
} }
func runExtractDo(c *cli.Command) error { func runExtractDo(c *cli.Context) error {
if err := initEmbeddedExtractor(c); err != nil { if err := initEmbeddedExtractor(c); err != nil {
return err return err
} }
@@ -217,7 +216,7 @@ func runExtractDo(c *cli.Command) error {
for _, a := range matchedAssetFiles { for _, a := range matchedAssetFiles {
if err := extractAsset(destdir, a, overwrite, rename); err != nil { if err := extractAsset(destdir, a, overwrite, rename); err != nil {
// Non-fatal error // Non-fatal error
_, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err) fmt.Fprintf(os.Stderr, "%s: %v", a.path, err)
} }
} }
@@ -272,7 +271,7 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return nil return nil
} }
func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string, layer *assetfs.Layer) { func collectAssetFilesByPattern(c *cli.Context, globs []glob.Glob, path string, layer *assetfs.Layer) {
fs := assetfs.Layered(layer) fs := assetfs.Layered(layer)
files, err := fs.ListAllFiles(".", true) files, err := fs.ListAllFiles(".", true)
if err != nil { if err != nil {
@@ -295,14 +294,16 @@ func collectAssetFilesByPattern(c *cli.Command, globs []glob.Glob, path string,
} }
} }
func compileCollectPatterns(args []string) (_ []glob.Glob, err error) { func compileCollectPatterns(args []string) ([]glob.Glob, error) {
if len(args) == 0 { if len(args) == 0 {
args = []string{"**"} args = []string{"**"}
} }
pat := make([]glob.Glob, len(args)) pat := make([]glob.Glob, len(args))
for i := range args { for i := range args {
if pat[i], err = glob.Compile(args[i], '/'); err != nil { if g, err := glob.Compile(args[i], '/'); err != nil {
return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err) return nil, fmt.Errorf("'%s': Invalid glob pattern: %w", args[i], err)
} else { //nolint:revive
pat[i] = g
} }
} }
return pat, nil return pat, nil

View File

@@ -5,14 +5,13 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"os" "os"
"code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/generate"
"github.com/mattn/go-isatty" "github.com/mattn/go-isatty"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -20,7 +19,7 @@ var (
CmdGenerate = &cli.Command{ CmdGenerate = &cli.Command{
Name: "generate", Name: "generate",
Usage: "Generate Gitea's secrets/keys/tokens", Usage: "Generate Gitea's secrets/keys/tokens",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdSecret, subcmdSecret,
}, },
} }
@@ -28,7 +27,7 @@ var (
subcmdSecret = &cli.Command{ subcmdSecret = &cli.Command{
Name: "secret", Name: "secret",
Usage: "Generate a secret token", Usage: "Generate a secret token",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
microcmdGenerateInternalToken, microcmdGenerateInternalToken,
microcmdGenerateLfsJwtSecret, microcmdGenerateLfsJwtSecret,
microcmdGenerateSecretKey, microcmdGenerateSecretKey,
@@ -55,7 +54,7 @@ var (
} }
) )
func runGenerateInternalToken(_ context.Context, c *cli.Command) error { func runGenerateInternalToken(c *cli.Context) error {
internalToken, err := generate.NewInternalToken() internalToken, err := generate.NewInternalToken()
if err != nil { if err != nil {
return err return err
@@ -70,7 +69,7 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
return nil return nil
} }
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error { func runGenerateLfsJwtSecret(c *cli.Context) error {
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64() _, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
if err != nil { if err != nil {
return err return err
@@ -85,7 +84,7 @@ func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
return nil return nil
} }
func runGenerateSecretKey(_ context.Context, c *cli.Command) error { func runGenerateSecretKey(c *cli.Context) error {
secretKey, err := generate.NewSecretKey() secretKey, err := generate.NewSecretKey()
if err != nil { if err != nil {
return err return err

View File

@@ -15,17 +15,16 @@ import (
"time" "time"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
const ( const (
hookBatchSize = 500 hookBatchSize = 30
) )
var ( var (
@@ -33,10 +32,9 @@ var (
CmdHook = &cli.Command{ CmdHook = &cli.Command{
Name: "hook", Name: "hook",
Usage: "(internal) Should only be called by Git", Usage: "(internal) Should only be called by Git",
Hidden: true, // internal commands shouldn't be visible
Description: "Delegate commands to corresponding Git hooks", Description: "Delegate commands to corresponding Git hooks",
Before: PrepareConsoleLoggerLevel(log.FATAL), Before: PrepareConsoleLoggerLevel(log.FATAL),
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdHookPreReceive, subcmdHookPreReceive,
subcmdHookUpdate, subcmdHookUpdate,
subcmdHookPostReceive, subcmdHookPostReceive,
@@ -163,10 +161,12 @@ func (n *nilWriter) WriteString(s string) (int, error) {
return len(s), nil return len(s), nil
} }
func runHookPreReceive(ctx context.Context, c *cli.Command) error { func runHookPreReceive(c *cli.Context) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil return nil
} }
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
@@ -292,7 +292,7 @@ Gitea or set your environment appropriately.`, "")
// runHookUpdate avoid to do heavy operations on update hook because it will be // runHookUpdate avoid to do heavy operations on update hook because it will be
// invoked for every ref update which does not like pre-receive and post-receive // invoked for every ref update which does not like pre-receive and post-receive
func runHookUpdate(_ context.Context, c *cli.Command) error { func runHookUpdate(c *cli.Context) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal { if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil return nil
} }
@@ -309,12 +309,15 @@ func runHookUpdate(_ context.Context, c *cli.Command) error {
return nil return nil
} }
func runHookPostReceive(ctx context.Context, c *cli.Command) error { func runHookPostReceive(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
// First of all run update-server-info no matter what // First of all run update-server-info no matter what
if _, _, err := gitcmd.NewCommand("update-server-info").RunStdString(ctx, nil); err != nil { if _, _, err := git.NewCommand(ctx, "update-server-info").RunStdString(nil); err != nil {
return fmt.Errorf("failed to call 'git update-server-info': %w", err) return fmt.Errorf("Failed to call 'git update-server-info': %w", err)
} }
// Now if we're an internal don't do anything else // Now if we're an internal don't do anything else
@@ -482,7 +485,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
func pushOptions() map[string]string { func pushOptions() map[string]string {
opts := make(map[string]string) opts := make(map[string]string)
if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil { if pushCount, err := strconv.Atoi(os.Getenv(private.GitPushOptionCount)); err == nil {
for idx := range pushCount { for idx := 0; idx < pushCount; idx++ {
opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx)) opt := os.Getenv(fmt.Sprintf("GIT_PUSH_OPTION_%d", idx))
kv := strings.SplitN(opt, "=", 2) kv := strings.SplitN(opt, "=", 2)
if len(kv) == 2 { if len(kv) == 2 {
@@ -493,7 +496,10 @@ func pushOptions() map[string]string {
return opts return opts
} }
func runHookProcReceive(ctx context.Context, c *cli.Command) error { func runHookProcReceive(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 { if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
@@ -536,14 +542,14 @@ Gitea or set your environment appropriately.`, "")
index := bytes.IndexByte(rs.Data, byte(0)) index := bytes.IndexByte(rs.Data, byte(0))
if index >= len(rs.Data) { if index >= len(rs.Data) {
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data) return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
} }
if index < 0 { if index < 0 {
if len(rs.Data) == 10 && rs.Data[9] == '\n' { if len(rs.Data) == 10 && rs.Data[9] == '\n' {
index = 9 index = 9
} else { } else {
return fail(ctx, "Protocol: format error", "pkt-line: format error %s", rs.Data) return fail(ctx, "Protocol: format error", "pkt-line: format error "+fmt.Sprint(rs.Data))
} }
} }
@@ -585,9 +591,8 @@ Gitea or set your environment appropriately.`, "")
// S: ... ... // S: ... ...
// S: flush-pkt // S: flush-pkt
hookOptions := private.HookOptions{ hookOptions := private.HookOptions{
UserName: pusherName, UserName: pusherName,
UserID: pusherID, UserID: pusherID,
GitPushOptions: make(map[string]string),
} }
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize) hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize) hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
@@ -612,6 +617,8 @@ Gitea or set your environment appropriately.`, "")
hookOptions.RefFullNames = append(hookOptions.RefFullNames, git.RefName(t[2])) hookOptions.RefFullNames = append(hookOptions.RefFullNames, git.RefName(t[2]))
} }
hookOptions.GitPushOptions = make(map[string]string)
if hasPushOptions { if hasPushOptions {
for { for {
rs, err = readPktLine(ctx, reader, pktLineTypeUnknow) rs, err = readPktLine(ctx, reader, pktLineTypeUnknow)
@@ -622,7 +629,11 @@ Gitea or set your environment appropriately.`, "")
if rs.Type == pktLineTypeFlush { if rs.Type == pktLineTypeFlush {
break break
} }
hookOptions.GitPushOptions.AddFromKeyValue(string(rs.Data))
kv := strings.SplitN(string(rs.Data), "=", 2)
if len(kv) == 2 {
hookOptions.GitPushOptions[kv[0]] = kv[1]
}
} }
} }
@@ -734,7 +745,7 @@ func readPktLine(ctx context.Context, in *bufio.Reader, requestType pktLineType)
// read prefix // read prefix
lengthBytes := make([]byte, 4) lengthBytes := make([]byte, 4)
for i := range 4 { for i := 0; i < 4; i++ {
lengthBytes[i], err = in.ReadByte() lengthBytes[i], err = in.ReadByte()
if err != nil { if err != nil {
return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err) return nil, fail(ctx, "Protocol: stdin error", "Pkt-Line: read stdin failed : %v", err)

View File

@@ -6,6 +6,7 @@ package cmd
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"context"
"strings" "strings"
"testing" "testing"
@@ -14,7 +15,7 @@ import (
func TestPktLine(t *testing.T) { func TestPktLine(t *testing.T) {
// test read // test read
ctx := t.Context() ctx := context.Background()
s := strings.NewReader("0000") s := strings.NewReader("0000")
r := bufio.NewReader(s) r := bufio.NewReader(s)
result, err := readPktLine(ctx, r, pktLineTypeFlush) result, err := readPktLine(ctx, r, pktLineTypeFlush)

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@@ -12,14 +11,13 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdKeys represents the available keys sub-command // CmdKeys represents the available keys sub-command
var CmdKeys = &cli.Command{ var CmdKeys = &cli.Command{
Name: "keys", Name: "keys",
Usage: "(internal) Should only be called by SSH server", Usage: "(internal) Should only be called by SSH server",
Hidden: true, // internal commands shouldn't not be visible
Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint", Description: "Queries the Gitea database to get the authorized command for a given ssh key fingerprint",
Before: PrepareConsoleLoggerLevel(log.FATAL), Before: PrepareConsoleLoggerLevel(log.FATAL),
Action: runKeys, Action: runKeys,
@@ -51,7 +49,7 @@ var CmdKeys = &cli.Command{
}, },
} }
func runKeys(ctx context.Context, c *cli.Command) error { func runKeys(c *cli.Context) error {
if !c.IsSet("username") { if !c.IsSet("username") {
return errors.New("No username provided") return errors.New("No username provided")
} }
@@ -70,6 +68,9 @@ func runKeys(ctx context.Context, c *cli.Command) error {
return errors.New("No key type and content provided") return errors.New("No key type and content provided")
} }
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content) authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
@@ -77,6 +78,6 @@ func runKeys(ctx context.Context, c *cli.Command) error {
if extra.Error != nil { if extra.Error != nil {
return extra.Error return extra.Error
} }
_, _ = fmt.Fprintln(c.Root().Writer, strings.TrimSpace(authorizedString.Text)) _, _ = fmt.Fprintln(c.App.Writer, strings.TrimSpace(authorizedString.Text))
return nil return nil
} }

View File

@@ -4,18 +4,24 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func runSendMail(ctx context.Context, c *cli.Command) error { func runSendMail(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setting.MustInstalled() setting.MustInstalled()
if err := argsSet(c, "title"); err != nil {
return err
}
subject := c.String("title") subject := c.String("title")
confirmSkiped := c.Bool("force") confirmSkiped := c.Bool("force")
body := c.String("content") body := c.String("content")

View File

@@ -4,40 +4,36 @@
package cmd package cmd
import ( import (
"context"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var cliHelpPrinterOld = cli.HelpPrinter // cmdHelp is our own help subcommand with more information
// Keep in mind that the "./gitea help"(subcommand) is different from "./gitea --help"(flag), the flag doesn't parse the config or output "DEFAULT CONFIGURATION:" information
func init() { func cmdHelp() *cli.Command {
cli.HelpPrinter = cliHelpPrinterNew c := &cli.Command{
} Name: "help",
Aliases: []string{"h"},
// cliHelpPrinterNew helps to print "DEFAULT CONFIGURATION" for the following cases ( "-c" can apper in any position): Usage: "Shows a list of commands or help for one command",
// * ./gitea -c /dev/null -h ArgsUsage: "[command]",
// * ./gitea -c help /dev/null help Action: func(c *cli.Context) (err error) {
// * ./gitea help -c /dev/null lineage := c.Lineage() // The order is from child to parent: help, doctor, Gitea, {Command:nil}
// * ./gitea help -c /dev/null web targetCmdIdx := 0
// * ./gitea help web -c /dev/null if c.Command.Name == "help" {
// * ./gitea web help -c /dev/null targetCmdIdx = 1
// * ./gitea web -h -c /dev/null }
func cliHelpPrinterNew(out io.Writer, templ string, data any) { if lineage[targetCmdIdx+1].Command != nil {
cmd, _ := data.(*cli.Command) err = cli.ShowCommandHelp(lineage[targetCmdIdx+1], lineage[targetCmdIdx].Command.Name)
if cmd != nil { } else {
prepareWorkPathAndCustomConf(cmd) err = cli.ShowAppHelp(c)
} }
cliHelpPrinterOld(out, templ, data) _, _ = fmt.Fprintf(c.App.Writer, `
if setting.CustomConf != "" {
_, _ = fmt.Fprintf(out, `
DEFAULT CONFIGURATION: DEFAULT CONFIGURATION:
AppPath: %s AppPath: %s
WorkPath: %s WorkPath: %s
@@ -45,71 +41,94 @@ DEFAULT CONFIGURATION:
ConfigFile: %s ConfigFile: %s
`, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf) `, setting.AppPath, setting.AppWorkPath, setting.CustomPath, setting.CustomConf)
return err
},
}
return c
}
func appGlobalFlags() []cli.Flag {
return []cli.Flag{
// make the builtin flags at the top
cli.HelpFlag,
// shared configuration flags, they are for global and for each sub-command at the same time
// eg: such command is valid: "./gitea --config /tmp/app.ini web --config /tmp/app.ini", while it's discouraged indeed
// keep in mind that the short flags like "-C", "-c" and "-w" are globally polluted, they can't be used for sub-commands anymore.
&cli.StringFlag{
Name: "custom-path",
Aliases: []string{"C"},
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Value: setting.CustomConf,
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
},
&cli.StringFlag{
Name: "work-path",
Aliases: []string{"w"},
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
},
} }
} }
func prepareSubcommandWithGlobalFlags(originCmd *cli.Command) { func prepareSubcommandWithConfig(command *cli.Command, globalFlags []cli.Flag) {
originBefore := originCmd.Before command.Flags = append(append([]cli.Flag{}, globalFlags...), command.Flags...)
originCmd.Before = func(ctx context.Context, cmd *cli.Command) (context.Context, error) { command.Action = prepareWorkPathAndCustomConf(command.Action)
prepareWorkPathAndCustomConf(cmd) command.HideHelp = true
if originBefore != nil { if command.Name != "help" {
return originBefore(ctx, cmd) command.Subcommands = append(command.Subcommands, cmdHelp())
}
for i := range command.Subcommands {
prepareSubcommandWithConfig(command.Subcommands[i], globalFlags)
}
}
// prepareWorkPathAndCustomConf wraps the Action to prepare the work path and custom config
// It can't use "Before", because each level's sub-command's Before will be called one by one, so the "init" would be done multiple times
func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context) error {
return func(ctx *cli.Context) error {
var args setting.ArgWorkPathAndCustomConf
// from children to parent, check the global flags
for _, curCtx := range ctx.Lineage() {
if curCtx.IsSet("work-path") && args.WorkPath == "" {
args.WorkPath = curCtx.String("work-path")
}
if curCtx.IsSet("custom-path") && args.CustomPath == "" {
args.CustomPath = curCtx.String("custom-path")
}
if curCtx.IsSet("config") && args.CustomConf == "" {
args.CustomConf = curCtx.String("config")
}
} }
return ctx, nil setting.InitWorkPathAndCommonConfig(os.Getenv, args)
if ctx.Bool("help") || action == nil {
// the default behavior of "urfave/cli": "nil action" means "show help"
return cmdHelp().Action(ctx)
}
return action(ctx)
} }
} }
// prepareWorkPathAndCustomConf tries to prepare the work path, custom path and custom config from various inputs:
// command line flags, environment variables, config file
func prepareWorkPathAndCustomConf(cmd *cli.Command) {
var args setting.ArgWorkPathAndCustomConf
if cmd.IsSet("work-path") {
args.WorkPath = cmd.String("work-path")
}
if cmd.IsSet("custom-path") {
args.CustomPath = cmd.String("custom-path")
}
if cmd.IsSet("config") {
args.CustomConf = cmd.String("config")
}
setting.InitWorkPathAndCommonConfig(os.Getenv, args)
}
type AppVersion struct { type AppVersion struct {
Version string Version string
Extra string Extra string
} }
func NewMainApp(appVer AppVersion) *cli.Command { func NewMainApp(appVer AppVersion) *cli.App {
app := &cli.Command{} app := cli.NewApp()
app.Name = "gitea" // must be lower-cased because it appears in the "USAGE" section like "gitea doctor [command [command options]]" app.Name = "Gitea"
app.HelpName = "gitea"
app.Usage = "A painless self-hosted Git service" app.Usage = "A painless self-hosted Git service"
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.` app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = appVer.Version + appVer.Extra app.Version = appVer.Version + appVer.Extra
app.EnableShellCompletion = true app.EnableBashCompletion = true
app.Flags = []cli.Flag{
&cli.StringFlag{ // these sub-commands need to use config file
Name: "work-path",
Aliases: []string{"w"},
TakesFile: true,
Usage: "Set Gitea's working path (defaults to the Gitea's binary directory)",
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
TakesFile: true,
Value: setting.CustomConf,
Usage: "Set custom config file (defaults to '{WorkPath}/custom/conf/app.ini')",
},
&cli.StringFlag{
Name: "custom-path",
Aliases: []string{"C"},
TakesFile: true,
Usage: "Set custom path (defaults to '{WorkPath}/custom')",
},
}
// these sub-commands need to use a config file
subCmdWithConfig := []*cli.Command{ subCmdWithConfig := []*cli.Command{
cmdHelp(), // the "help" sub-command was used to show the more information for "work path" and "custom config"
CmdWeb, CmdWeb,
CmdServ, CmdServ,
CmdHook, CmdHook,
@@ -128,30 +147,29 @@ func NewMainApp(appVer AppVersion) *cli.Command {
// these sub-commands do not need the config file, and they do not depend on any path or environment variable. // these sub-commands do not need the config file, and they do not depend on any path or environment variable.
subCmdStandalone := []*cli.Command{ subCmdStandalone := []*cli.Command{
cmdCert(), CmdCert,
CmdGenerate, CmdGenerate,
CmdDocs, CmdDocs,
} }
// TODO: we should eventually drop the default command,
// but not sure whether it would break Windows users who used to double-click the EXE to run.
app.DefaultCommand = CmdWeb.Name app.DefaultCommand = CmdWeb.Name
globalFlags := appGlobalFlags()
app.Flags = append(app.Flags, cli.VersionFlag)
app.Flags = append(app.Flags, globalFlags...)
app.HideHelp = true // use our own help action to show helps (with more information like default config)
app.Before = PrepareConsoleLoggerLevel(log.INFO) app.Before = PrepareConsoleLoggerLevel(log.INFO)
for i := range subCmdWithConfig { for i := range subCmdWithConfig {
prepareSubcommandWithGlobalFlags(subCmdWithConfig[i]) prepareSubcommandWithConfig(subCmdWithConfig[i], globalFlags)
} }
app.Commands = append(app.Commands, subCmdWithConfig...) app.Commands = append(app.Commands, subCmdWithConfig...)
app.Commands = append(app.Commands, subCmdStandalone...) app.Commands = append(app.Commands, subCmdStandalone...)
setting.InitGiteaEnvVars()
return app return app
} }
func RunMainApp(app *cli.Command, args ...string) error { func RunMainApp(app *cli.App, args ...string) error {
ctx, cancel := installSignals() err := app.Run(args)
defer cancel()
err := app.Run(ctx, args)
if err == nil { if err == nil {
return nil return nil
} }

View File

@@ -4,10 +4,9 @@
package cmd package cmd
import ( import (
"context"
"errors"
"fmt" "fmt"
"io" "io"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
@@ -17,7 +16,7 @@ import (
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@@ -28,10 +27,10 @@ func makePathOutput(workPath, customPath, customConf string) string {
return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf) return fmt.Sprintf("WorkPath=%s\nCustomPath=%s\nCustomConf=%s", workPath, customPath, customConf)
} }
func newTestApp(testCmdAction cli.ActionFunc) *cli.Command { func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
app := NewMainApp(AppVersion{}) app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
prepareSubcommandWithGlobalFlags(testCmd) prepareSubcommandWithConfig(testCmd, appGlobalFlags())
app.Commands = append(app.Commands, testCmd) app.Commands = append(app.Commands, testCmd)
app.DefaultCommand = testCmd.Name app.DefaultCommand = testCmd.Name
return app return app
@@ -43,7 +42,7 @@ type runResult struct {
ExitCode int ExitCode int
} }
func runTestApp(app *cli.Command, args ...string) (runResult, error) { func runTestApp(app *cli.App, args ...string) (runResult, error) {
outBuf := new(strings.Builder) outBuf := new(strings.Builder)
errBuf := new(strings.Builder) errBuf := new(strings.Builder)
app.Writer = outBuf app.Writer = outBuf
@@ -66,7 +65,7 @@ func TestCliCmd(t *testing.T) {
defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini") defaultCustomConf := filepath.Join(defaultCustomPath, "conf/app.ini")
cli.CommandHelpTemplate = "(command help template)" cli.CommandHelpTemplate = "(command help template)"
cli.RootCommandHelpTemplate = "(app help template)" cli.AppHelpTemplate = "(app help template)"
cli.SubcommandHelpTemplate = "(subcommand help template)" cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct { cases := []struct {
@@ -74,56 +73,12 @@ func TestCliCmd(t *testing.T) {
cmd string cmd string
exp string exp string
}{ }{
// help commands // main command help
{
cmd: "./gitea -h",
exp: "DEFAULT CONFIGURATION:",
},
{ {
cmd: "./gitea help", cmd: "./gitea help",
exp: "DEFAULT CONFIGURATION:", exp: "DEFAULT CONFIGURATION:",
}, },
{
cmd: "./gitea -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -h -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
// parse paths // parse paths
{ {
cmd: "./gitea test-cmd", cmd: "./gitea test-cmd",
@@ -154,50 +109,70 @@ func TestCliCmd(t *testing.T) {
}, },
} }
for _, c := range cases { app := newTestApp(func(ctx *cli.Context) error {
t.Run(c.cmd, func(t *testing.T) { _, _ = fmt.Fprint(ctx.App.Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf)) })
return nil var envBackup []string
}) for _, s := range os.Environ() {
for k, v := range c.env { if strings.HasPrefix(s, "GITEA_") && strings.Contains(s, "=") {
t.Setenv(k, v) envBackup = append(envBackup, s)
}
}
clearGiteaEnv := func() {
for _, s := range os.Environ() {
if strings.HasPrefix(s, "GITEA_") {
_ = os.Unsetenv(s)
} }
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough }
r, err := runTestApp(app, args...) }
assert.NoError(t, err, c.cmd) defer func() {
assert.NotEmpty(t, c.exp, c.cmd) clearGiteaEnv()
assert.Contains(t, r.Stdout, c.exp, c.cmd) for _, s := range envBackup {
}) k, v, _ := strings.Cut(s, "=")
_ = os.Setenv(k, v)
}
}()
for _, c := range cases {
clearGiteaEnv()
for k, v := range c.env {
_ = os.Setenv(k, v)
}
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
r, err := runTestApp(app, args...)
assert.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
assert.Contains(t, r.Stdout, c.exp, c.cmd)
} }
} }
func TestCliCmdError(t *testing.T) { func TestCliCmdError(t *testing.T) {
app := newTestApp(func(ctx context.Context, cmd *cli.Command) error { return errors.New("normal error") }) app := newTestApp(func(ctx *cli.Context) error { return fmt.Errorf("normal error") })
r, err := runTestApp(app, "./gitea", "test-cmd") r, err := runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Equal(t, "", r.Stdout)
assert.Equal(t, "Command error: normal error\n", r.Stderr) assert.Equal(t, "Command error: normal error\n", r.Stderr)
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return cli.Exit("exit error", 2) }) app = newTestApp(func(ctx *cli.Context) error { return cli.Exit("exit error", 2) })
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 2, r.ExitCode) assert.Equal(t, 2, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Equal(t, "", r.Stdout)
assert.Equal(t, "exit error\n", r.Stderr) assert.Equal(t, "exit error\n", r.Stderr)
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil }) app = newTestApp(func(ctx *cli.Context) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such") r, err = runTestApp(app, "./gitea", "test-cmd", "--no-such")
assert.Error(t, err) assert.Error(t, err)
assert.Equal(t, 1, r.ExitCode) assert.Equal(t, 1, r.ExitCode)
assert.Empty(t, r.Stdout) assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stdout)
assert.Equal(t, "Incorrect Usage: flag provided but not defined: -no-such\n\n", r.Stderr) assert.Equal(t, "", r.Stderr) // the cli package's strange behavior, the error message is not in stderr ....
app = newTestApp(func(ctx context.Context, cmd *cli.Command) error { return nil }) app = newTestApp(func(ctx *cli.Context) error { return nil })
r, err = runTestApp(app, "./gitea", "test-cmd") r, err = runTestApp(app, "./gitea", "test-cmd")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called assert.Equal(t, -1, r.ExitCode) // the cli.OsExiter is not called
assert.Empty(t, r.Stdout) assert.Equal(t, "", r.Stdout)
assert.Empty(t, r.Stderr) assert.Equal(t, "", r.Stderr)
} }

View File

@@ -4,13 +4,12 @@
package cmd package cmd
import ( import (
"context"
"os" "os"
"time" "time"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -19,7 +18,7 @@ var (
Name: "manager", Name: "manager",
Usage: "Manage the running gitea process", Usage: "Manage the running gitea process",
Description: "This is a command for managing the running gitea process", Description: "This is a command for managing the running gitea process",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
subcmdShutdown, subcmdShutdown,
subcmdRestart, subcmdRestart,
subcmdReloadTemplates, subcmdReloadTemplates,
@@ -109,31 +108,46 @@ var (
} }
) )
func runShutdown(ctx context.Context, c *cli.Command) error { func runShutdown(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.Shutdown(ctx) extra := private.Shutdown(ctx)
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runRestart(ctx context.Context, c *cli.Command) error { func runRestart(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.Restart(ctx) extra := private.Restart(ctx)
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runReloadTemplates(ctx context.Context, c *cli.Command) error { func runReloadTemplates(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.ReloadTemplates(ctx) extra := private.ReloadTemplates(ctx)
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runFlushQueues(ctx context.Context, c *cli.Command) error { func runFlushQueues(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking")) extra := private.FlushQueues(ctx, c.Duration("timeout"), c.Bool("non-blocking"))
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runProcesses(ctx context.Context, c *cli.Command) error { func runProcesses(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel")) extra := private.Processes(ctx, os.Stdout, c.Bool("flat"), c.Bool("no-system"), c.Bool("stacktraces"), c.Bool("json"), c.String("cancel"))
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)

View File

@@ -4,7 +4,6 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"os" "os"
@@ -12,7 +11,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
var ( var (
@@ -61,7 +60,7 @@ var (
subcmdLogging = &cli.Command{ subcmdLogging = &cli.Command{
Name: "logging", Name: "logging",
Usage: "Adjust logging commands", Usage: "Adjust logging commands",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "pause", Name: "pause",
Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)", Usage: "Pause logging (Gitea will buffer logs up to a certain point and will drop them after that point)",
@@ -105,7 +104,7 @@ var (
}, { }, {
Name: "add", Name: "add",
Usage: "Add a logger", Usage: "Add a logger",
Commands: []*cli.Command{ Subcommands: []*cli.Command{
{ {
Name: "file", Name: "file",
Usage: "Add a file logger", Usage: "Add a file logger",
@@ -119,6 +118,7 @@ var (
Name: "rotate", Name: "rotate",
Aliases: []string{"r"}, Aliases: []string{"r"},
Usage: "Rotate logs", Usage: "Rotate logs",
Value: true,
}, },
&cli.Int64Flag{ &cli.Int64Flag{
Name: "max-size", Name: "max-size",
@@ -129,6 +129,7 @@ var (
Name: "daily", Name: "daily",
Aliases: []string{"d"}, Aliases: []string{"d"},
Usage: "Rotate logs daily", Usage: "Rotate logs daily",
Value: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "max-days", Name: "max-days",
@@ -139,6 +140,7 @@ var (
Name: "compress", Name: "compress",
Aliases: []string{"z"}, Aliases: []string{"z"},
Usage: "Compress rotated logs", Usage: "Compress rotated logs",
Value: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
Name: "compression-level", Name: "compression-level",
@@ -193,7 +195,10 @@ var (
} }
) )
func runRemoveLogger(ctx context.Context, c *cli.Command) error { func runRemoveLogger(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
logger := c.String("logger") logger := c.String("logger")
if len(logger) == 0 { if len(logger) == 0 {
@@ -205,7 +210,10 @@ func runRemoveLogger(ctx context.Context, c *cli.Command) error {
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runAddConnLogger(ctx context.Context, c *cli.Command) error { func runAddConnLogger(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
vals := map[string]any{} vals := map[string]any{}
mode := "conn" mode := "conn"
@@ -229,10 +237,13 @@ func runAddConnLogger(ctx context.Context, c *cli.Command) error {
if c.IsSet("reconnect-on-message") { if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message") vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
} }
return commonAddLogger(ctx, c, mode, vals) return commonAddLogger(c, mode, vals)
} }
func runAddFileLogger(ctx context.Context, c *cli.Command) error { func runAddFileLogger(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
vals := map[string]any{} vals := map[string]any{}
mode := "file" mode := "file"
@@ -259,10 +270,10 @@ func runAddFileLogger(ctx context.Context, c *cli.Command) error {
if c.IsSet("compression-level") { if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level") vals["compressionLevel"] = c.Int("compression-level")
} }
return commonAddLogger(ctx, c, mode, vals) return commonAddLogger(c, mode, vals)
} }
func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error { func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 { if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String() vals["level"] = log.LevelFromString(c.String("level")).String()
} }
@@ -289,33 +300,46 @@ func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[
if c.IsSet("writer") { if c.IsSet("writer") {
writer = c.String("writer") writer = c.String("writer")
} }
ctx, cancel := installSignals()
defer cancel()
extra := private.AddLogger(ctx, logger, writer, mode, vals) extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra) return handleCliResponseExtra(extra)
} }
func runPauseLogging(ctx context.Context, c *cli.Command) error { func runPauseLogging(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
userMsg := private.PauseLogging(ctx) userMsg := private.PauseLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg) _, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil return nil
} }
func runResumeLogging(ctx context.Context, c *cli.Command) error { func runResumeLogging(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
userMsg := private.ResumeLogging(ctx) userMsg := private.ResumeLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg) _, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil return nil
} }
func runReleaseReopenLogging(ctx context.Context, c *cli.Command) error { func runReleaseReopenLogging(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
userMsg := private.ReleaseReopenLogging(ctx) userMsg := private.ReleaseReopenLogging(ctx)
_, _ = fmt.Fprintln(os.Stdout, userMsg) _, _ = fmt.Fprintln(os.Stdout, userMsg)
return nil return nil
} }
func runSetLogSQL(ctx context.Context, c *cli.Command) error { func runSetLogSQL(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
extra := private.SetLogSQL(ctx, !c.Bool("off")) extra := private.SetLogSQL(ctx, !c.Bool("off"))

View File

@@ -7,23 +7,26 @@ import (
"context" "context"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/services/versioned_migration"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdMigrate represents the available migrate sub-command. // CmdMigrate represents the available migrate sub-command.
var CmdMigrate = &cli.Command{ var CmdMigrate = &cli.Command{
Name: "migrate", Name: "migrate",
Usage: "Migrate the database", Usage: "Migrate the database",
Description: `This is a command for migrating the database, so that you can run "gitea admin create user" before starting the server.`, Description: "This is a command for migrating the database, so that you can run gitea admin create-user before starting the server.",
Action: runMigrate, Action: runMigrate,
} }
func runMigrate(ctx context.Context, c *cli.Command) error { func runMigrate(ctx *cli.Context) error {
if err := initDB(ctx); err != nil { stdCtx, cancel := installSignals()
defer cancel()
if err := initDB(stdCtx); err != nil {
return err return err
} }
@@ -33,7 +36,7 @@ func runMigrate(ctx context.Context, c *cli.Command) error {
log.Info("Log path: %s", setting.Log.RootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err) log.Fatal("Failed to initialize ORM engine: %v", err)
return err return err
} }

View File

@@ -13,6 +13,7 @@ import (
actions_model "code.gitea.io/gitea/models/actions" actions_model "code.gitea.io/gitea/models/actions"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/migrations"
packages_model "code.gitea.io/gitea/models/packages" packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@@ -20,9 +21,8 @@ import (
packages_module "code.gitea.io/gitea/modules/packages" packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/versioned_migration"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdMigrateStorage represents the available migrate storage sub-command. // CmdMigrateStorage represents the available migrate storage sub-command.
@@ -42,7 +42,7 @@ var CmdMigrateStorage = &cli.Command{
Name: "storage", Name: "storage",
Aliases: []string{"s"}, Aliases: []string{"s"},
Value: "", Value: "",
Usage: "New storage type: local (default), minio or azureblob", Usage: "New storage type: local (default) or minio",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "path", Name: "path",
@@ -50,7 +50,6 @@ var CmdMigrateStorage = &cli.Command{
Value: "", Value: "",
Usage: "New storage placement if store is local (leave blank for default)", Usage: "New storage placement if store is local (leave blank for default)",
}, },
// Minio Storage special configurations
&cli.StringFlag{ &cli.StringFlag{
Name: "minio-endpoint", Name: "minio-endpoint",
Value: "", Value: "",
@@ -94,37 +93,6 @@ var CmdMigrateStorage = &cli.Command{
Value: "", Value: "",
Usage: "Minio checksum algorithm (default/md5)", Usage: "Minio checksum algorithm (default/md5)",
}, },
&cli.StringFlag{
Name: "minio-bucket-lookup-type",
Value: "",
Usage: "Minio bucket lookup type",
},
// Azure Blob Storage special configurations
&cli.StringFlag{
Name: "azureblob-endpoint",
Value: "",
Usage: "Azure Blob storage endpoint",
},
&cli.StringFlag{
Name: "azureblob-account-name",
Value: "",
Usage: "Azure Blob storage account name",
},
&cli.StringFlag{
Name: "azureblob-account-key",
Value: "",
Usage: "Azure Blob storage account key",
},
&cli.StringFlag{
Name: "azureblob-container",
Value: "",
Usage: "Azure Blob storage container",
},
&cli.StringFlag{
Name: "azureblob-base-path",
Value: "",
Usage: "Azure Blob storage base path",
},
}, },
} }
@@ -196,7 +164,7 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error { func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error { return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
if artifact.Status == actions_model.ArtifactStatusExpired { if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
return nil return nil
} }
@@ -213,8 +181,11 @@ func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStora
}) })
} }
func runMigrateStorage(ctx context.Context, cmd *cli.Command) error { func runMigrateStorage(ctx *cli.Context) error {
if err := initDB(ctx); err != nil { stdCtx, cancel := installSignals()
defer cancel()
if err := initDB(stdCtx); err != nil {
return err return err
} }
@@ -224,7 +195,7 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
log.Info("Log path: %s", setting.Log.RootPath) log.Info("Log path: %s", setting.Log.RootPath)
log.Info("Configuration file: %s", setting.CustomConf) log.Info("Configuration file: %s", setting.CustomConf)
if err := db.InitEngineWithMigration(context.Background(), versioned_migration.Migrate); err != nil { if err := db.InitEngineWithMigration(context.Background(), migrations.Migrate); err != nil {
log.Fatal("Failed to initialize ORM engine: %v", err) log.Fatal("Failed to initialize ORM engine: %v", err)
return err return err
} }
@@ -235,51 +206,38 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
var dstStorage storage.ObjectStorage var dstStorage storage.ObjectStorage
var err error var err error
switch strings.ToLower(cmd.String("storage")) { switch strings.ToLower(ctx.String("storage")) {
case "": case "":
fallthrough fallthrough
case string(setting.LocalStorageType): case string(setting.LocalStorageType):
p := cmd.String("path") p := ctx.String("path")
if p == "" { if p == "" {
log.Fatal("Path must be given when storage is local") log.Fatal("Path must be given when storage is local")
return nil return nil
} }
dstStorage, err = storage.NewLocalStorage( dstStorage, err = storage.NewLocalStorage(
ctx, stdCtx,
&setting.Storage{ &setting.Storage{
Path: p, Path: p,
}) })
case string(setting.MinioStorageType): case string(setting.MinioStorageType):
dstStorage, err = storage.NewMinioStorage( dstStorage, err = storage.NewMinioStorage(
ctx, stdCtx,
&setting.Storage{ &setting.Storage{
MinioConfig: setting.MinioStorageConfig{ MinioConfig: setting.MinioStorageConfig{
Endpoint: cmd.String("minio-endpoint"), Endpoint: ctx.String("minio-endpoint"),
AccessKeyID: cmd.String("minio-access-key-id"), AccessKeyID: ctx.String("minio-access-key-id"),
SecretAccessKey: cmd.String("minio-secret-access-key"), SecretAccessKey: ctx.String("minio-secret-access-key"),
Bucket: cmd.String("minio-bucket"), Bucket: ctx.String("minio-bucket"),
Location: cmd.String("minio-location"), Location: ctx.String("minio-location"),
BasePath: cmd.String("minio-base-path"), BasePath: ctx.String("minio-base-path"),
UseSSL: cmd.Bool("minio-use-ssl"), UseSSL: ctx.Bool("minio-use-ssl"),
InsecureSkipVerify: cmd.Bool("minio-insecure-skip-verify"), InsecureSkipVerify: ctx.Bool("minio-insecure-skip-verify"),
ChecksumAlgorithm: cmd.String("minio-checksum-algorithm"), ChecksumAlgorithm: ctx.String("minio-checksum-algorithm"),
BucketLookUpType: cmd.String("minio-bucket-lookup-type"),
},
})
case string(setting.AzureBlobStorageType):
dstStorage, err = storage.NewAzureBlobStorage(
ctx,
&setting.Storage{
AzureBlobConfig: setting.AzureBlobStorageConfig{
Endpoint: cmd.String("azureblob-endpoint"),
AccountName: cmd.String("azureblob-account-name"),
AccountKey: cmd.String("azureblob-account-key"),
Container: cmd.String("azureblob-container"),
BasePath: cmd.String("azureblob-base-path"),
}, },
}) })
default: default:
return fmt.Errorf("unsupported storage type: %s", cmd.String("storage")) return fmt.Errorf("unsupported storage type: %s", ctx.String("storage"))
} }
if err != nil { if err != nil {
return err return err
@@ -296,14 +254,14 @@ func runMigrateStorage(ctx context.Context, cmd *cli.Command) error {
"actions-artifacts": migrateActionsArtifacts, "actions-artifacts": migrateActionsArtifacts,
} }
tp := strings.ToLower(cmd.String("type")) tp := strings.ToLower(ctx.String("type"))
if m, ok := migratedMethods[tp]; ok { if m, ok := migratedMethods[tp]; ok {
if err := m(ctx, dstStorage); err != nil { if err := m(stdCtx, dstStorage); err != nil {
return err return err
} }
log.Info("%s files have successfully been copied to the new storage.", tp) log.Info("%s files have successfully been copied to the new storage.", tp)
return nil return nil
} }
return fmt.Errorf("unsupported storage: %s", cmd.String("type")) return fmt.Errorf("unsupported storage: %s", ctx.String("type"))
} }

View File

@@ -4,10 +4,12 @@
package cmd package cmd
import ( import (
"context"
"os" "os"
"strings" "strings"
"testing" "testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
@@ -29,7 +31,7 @@ func TestMigratePackages(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
defer buf.Close() defer buf.Close()
v, f, err := packages_service.CreatePackageAndAddFile(t.Context(), &packages_service.PackageCreationInfo{ v, f, err := packages_service.CreatePackageAndAddFile(db.DefaultContext, &packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{ PackageInfo: packages_service.PackageInfo{
Owner: creator, Owner: creator,
PackageType: packages.TypeGeneric, PackageType: packages.TypeGeneric,
@@ -51,7 +53,7 @@ func TestMigratePackages(t *testing.T) {
assert.NotNil(t, v) assert.NotNil(t, v)
assert.NotNil(t, f) assert.NotNil(t, f)
ctx := t.Context() ctx := context.Background()
p := t.TempDir() p := t.TempDir()
@@ -68,6 +70,6 @@ func TestMigratePackages(t *testing.T) {
entries, err := os.ReadDir(p) entries, err := os.ReadDir(p)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, entries, 2) assert.Len(t, entries, 2)
assert.Equal(t, "01", entries[0].Name()) assert.EqualValues(t, "01", entries[0].Name())
assert.Equal(t, "tmp", entries[1].Name()) assert.EqualValues(t, "tmp", entries[1].Name())
} }

View File

@@ -4,13 +4,12 @@
package cmd package cmd
import ( import (
"context"
"strings" "strings"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// CmdRestoreRepository represents the available restore a repository sub-command. // CmdRestoreRepository represents the available restore a repository sub-command.
@@ -49,7 +48,10 @@ wiki, issues, labels, releases, release_assets, milestones, pull_requests, comme
}, },
} }
func runRestoreRepository(ctx context.Context, c *cli.Command) error { func runRestoreRepository(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
setting.MustInstalled() setting.MustInstalled()
var units []string var units []string
if s := c.String("units"); s != "" { if s := c.String("units"); s != "" {

View File

@@ -11,6 +11,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -19,11 +20,8 @@ import (
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/gitcmd"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/lfstransfer"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/pprof" "code.gitea.io/gitea/modules/pprof"
"code.gitea.io/gitea/modules/private" "code.gitea.io/gitea/modules/private"
@@ -34,7 +32,11 @@ import (
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
)
const (
lfsAuthenticateVerb = "git-lfs-authenticate"
) )
// CmdServ represents the available serv sub-command. // CmdServ represents the available serv sub-command.
@@ -42,7 +44,6 @@ var CmdServ = &cli.Command{
Name: "serv", Name: "serv",
Usage: "(internal) Should only be called by SSH shell", Usage: "(internal) Should only be called by SSH shell",
Description: "Serv provides access auth for repositories", Description: "Serv provides access auth for repositories",
Hidden: true, // Internal commands shouldn't be visible in help
Before: PrepareConsoleLoggerLevel(log.FATAL), Before: PrepareConsoleLoggerLevel(log.FATAL),
Action: runServ, Action: runServ,
Flags: []cli.Flag{ Flags: []cli.Flag{
@@ -66,11 +67,21 @@ func setup(ctx context.Context, debug bool) {
_ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err) _ = fail(ctx, "Unable to access repository path", "Unable to access repository path %q, err: %v", setting.RepoRootPath, err)
return return
} }
if err := git.InitSimple(); err != nil { if err := git.InitSimple(context.Background()); err != nil {
_ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err) _ = fail(ctx, "Failed to init git", "Failed to init git, err: %v", err)
} }
} }
var (
allowedCommands = map[string]perm.AccessMode{
"git-upload-pack": perm.AccessModeRead,
"git-upload-archive": perm.AccessModeRead,
"git-receive-pack": perm.AccessModeWrite,
lfsAuthenticateVerb: perm.AccessModeNone,
}
alphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
)
// fail prints message to stdout, it's mainly used for git serv and git hook commands. // 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. // The output will be passed to git client and shown to user.
func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error { func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error {
@@ -81,20 +92,19 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
// There appears to be a chance to cause a zombie process and failure to read the Exit status // There appears to be a chance to cause a zombie process and failure to read the Exit status
// if nothing is outputted on stdout. // if nothing is outputted on stdout.
_, _ = fmt.Fprintln(os.Stdout, "") _, _ = fmt.Fprintln(os.Stdout, "")
// add extra empty lines to separate our message from other git errors to get more attention _, _ = fmt.Fprintln(os.Stderr, "Gitea:", userMessage)
_, _ = fmt.Fprintln(os.Stderr, "error:")
_, _ = fmt.Fprintln(os.Stderr, "error:", userMessage)
_, _ = fmt.Fprintln(os.Stderr, "error:")
if logMsgFmt != "" { if logMsgFmt != "" {
logMsg := fmt.Sprintf(logMsgFmt, args...) logMsg := fmt.Sprintf(logMsgFmt, args...)
if !setting.IsProd { if !setting.IsProd {
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg) _, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg)
} }
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) { if userMessage != "" {
logMsg = userMessage + " " + logMsg if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
} else { logMsg = userMessage + " " + logMsg
logMsg = userMessage + ". " + logMsg } else {
logMsg = userMessage + ". " + logMsg
}
} }
_ = private.SSHLog(ctx, true, logMsg) _ = private.SSHLog(ctx, true, logMsg)
} }
@@ -114,47 +124,10 @@ func handleCliResponseExtra(extra private.ResponseExtra) error {
return nil return nil
} }
func getAccessMode(verb, lfsVerb string) perm.AccessMode { func runServ(c *cli.Context) error {
switch verb { ctx, cancel := installSignals()
case git.CmdVerbUploadPack, git.CmdVerbUploadArchive: defer cancel()
return perm.AccessModeRead
case git.CmdVerbReceivePack:
return perm.AccessModeWrite
case git.CmdVerbLfsAuthenticate, git.CmdVerbLfsTransfer:
switch lfsVerb {
case git.CmdSubVerbLfsUpload:
return perm.AccessModeWrite
case git.CmdSubVerbLfsDownload:
return perm.AccessModeRead
}
}
// should be unreachable
setting.PanicInDevOrTesting("unknown verb: %s %s", verb, lfsVerb)
return perm.AccessModeNone
}
func getLFSAuthToken(ctx context.Context, lfsVerb string, results *private.ServCommandResults) (string, error) {
now := time.Now()
claims := lfs.Claims{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
NotBefore: jwt.NewNumericDate(now),
},
RepoID: results.RepoID,
Op: lfsVerb,
UserID: results.UserID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
if err != nil {
return "", fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
}
return "Bearer " + tokenString, nil
}
func runServ(ctx context.Context, c *cli.Command) error {
// FIXME: This needs to internationalised // FIXME: This needs to internationalised
setup(ctx, c.Bool("debug")) setup(ctx, c.Bool("debug"))
@@ -205,32 +178,50 @@ func runServ(ctx context.Context, c *cli.Command) error {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND")) log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
} }
sshCmdArgs, err := shellquote.Split(cmd) words, err := shellquote.Split(cmd)
if err != nil { if err != nil {
return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err) return fail(ctx, "Error parsing arguments", "Failed to parse arguments: %v", err)
} }
if len(sshCmdArgs) < 2 { if len(words) < 2 {
if git.DefaultFeatures().SupportProcReceive { if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow // for AGit Flow
if cmd == "ssh_info" { if cmd == "ssh_info" {
fmt.Print(`{"type":"agit","version":1}`) fmt.Print(`{"type":"gitea","version":1}`)
return nil return nil
} }
} }
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd) return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
} }
repoPath := strings.TrimPrefix(sshCmdArgs[1], "/") verb := words[0]
repoPathFields := strings.SplitN(repoPath, "/", 2) repoPath := strings.TrimPrefix(words[1], "/")
if len(repoPathFields) != 2 {
var lfsVerb string
if verb == lfsAuthenticateVerb {
if !setting.LFS.StartServer {
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
}
if len(words) > 2 {
lfsVerb = words[2]
}
}
rr := strings.SplitN(repoPath, "/", 2)
if len(rr) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath) return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
} }
username := repoPathFields[0] username := rr[0]
reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki" reponame := strings.TrimSuffix(rr[1], ".git")
if !repo.IsValidSSHAccessRepoName(reponame) { // 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) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
} }
@@ -252,58 +243,53 @@ func runServ(ctx context.Context, c *cli.Command) error {
}() }()
} }
verb, lfsVerb := sshCmdArgs[0], "" requestedMode, has := allowedCommands[verb]
if !git.IsAllowedVerbForServe(verb) { if !has {
return fail(ctx, "Unknown git command", "Unknown git command %s", verb) return fail(ctx, "Unknown git command", "Unknown git command %s", verb)
} }
if git.IsAllowedVerbForServeLfs(verb) { if verb == lfsAuthenticateVerb {
if !setting.LFS.StartServer { if lfsVerb == "upload" {
return fail(ctx, "LFS Server is not enabled", "") requestedMode = perm.AccessModeWrite
} } else if lfsVerb == "download" {
if verb == git.CmdVerbLfsTransfer && !setting.LFS.AllowPureSSH { requestedMode = perm.AccessModeRead
return fail(ctx, "LFS SSH transfer is not enabled", "") } else {
} return fail(ctx, "Unknown LFS verb", "Unknown lfs verb %s", lfsVerb)
if len(sshCmdArgs) > 2 {
lfsVerb = sshCmdArgs[2]
} }
} }
requestedMode := getAccessMode(verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb) results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
if extra.HasError() { if extra.HasError() {
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error) return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
} }
// 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(results.OwnerName + "/" + results.RepoName + ".git")
// LFS SSH protocol
if verb == git.CmdVerbLfsTransfer {
token, err := getLFSAuthToken(ctx, lfsVerb, results)
if err != nil {
return err
}
return lfstransfer.Main(ctx, repoPath, lfsVerb, token)
}
// LFS token authentication // LFS token authentication
if verb == git.CmdVerbLfsAuthenticate { if verb == lfsAuthenticateVerb {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName)) 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) now := time.Now()
claims := lfs.Claims{
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(now.Add(setting.LFS.HTTPAuthExpiry)),
NotBefore: jwt.NewNumericDate(now),
},
RepoID: results.RepoID,
Op: lfsVerb,
UserID: results.UserID,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Sign and get the complete encoded token as a string using the secret
tokenString, err := token.SignedString(setting.LFS.JWTSecretBytes)
if err != nil { if err != nil {
return err return fail(ctx, "Failed to sign JWT Token", "Failed to sign JWT token: %v", err)
} }
tokenAuthentication := &git_model.LFSTokenResponse{ tokenAuthentication := &git_model.LFSTokenResponse{
Header: make(map[string]string), Header: make(map[string]string),
Href: url, Href: url,
} }
tokenAuthentication.Header["Authorization"] = token tokenAuthentication.Header["Authorization"] = fmt.Sprintf("Bearer %s", tokenString)
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
err = enc.Encode(tokenAuthentication) err = enc.Encode(tokenAuthentication)
@@ -313,30 +299,30 @@ func runServ(ctx context.Context, c *cli.Command) error {
return nil return nil
} }
var command *exec.Cmd var gitcmd *exec.Cmd
gitBinPath := filepath.Dir(gitcmd.GitExecutable) // e.g. /usr/bin gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
if _, err := os.Stat(gitBinVerb); err != nil { if _, err := os.Stat(gitBinVerb); err != nil {
// if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git // if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git
// ps: Windows only has "git.exe" in the bin path, so Windows always uses this way // ps: Windows only has "git.exe" in the bin path, so Windows always uses this way
verbFields := strings.SplitN(verb, "-", 2) verbFields := strings.SplitN(verb, "-", 2)
if len(verbFields) == 2 { if len(verbFields) == 2 {
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
command = exec.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], repoPath) gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath)
} }
} }
if command == nil { if gitcmd == nil {
// by default, use the verb (it has been checked above by allowedCommands) // by default, use the verb (it has been checked above by allowedCommands)
command = exec.CommandContext(ctx, gitBinVerb, repoPath) gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath)
} }
process.SetSysProcAttribute(command) process.SetSysProcAttribute(gitcmd)
command.Dir = setting.RepoRootPath gitcmd.Dir = setting.RepoRootPath
command.Stdout = os.Stdout gitcmd.Stdout = os.Stdout
command.Stdin = os.Stdin gitcmd.Stdin = os.Stdin
command.Stderr = os.Stderr gitcmd.Stderr = os.Stderr
command.Env = append(command.Env, os.Environ()...) gitcmd.Env = append(gitcmd.Env, os.Environ()...)
command.Env = append(command.Env, gitcmd.Env = append(gitcmd.Env,
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki), repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
repo_module.EnvRepoName+"="+results.RepoName, repo_module.EnvRepoName+"="+results.RepoName,
repo_module.EnvRepoUsername+"="+results.OwnerName, repo_module.EnvRepoUsername+"="+results.OwnerName,
@@ -344,16 +330,16 @@ func runServ(ctx context.Context, c *cli.Command) error {
repo_module.EnvPusherEmail+"="+results.UserEmail, repo_module.EnvPusherEmail+"="+results.UserEmail,
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10), repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10), repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
repo_module.EnvPRID+"="+strconv.Itoa(0), repo_module.EnvPRID+"="+fmt.Sprintf("%d", 0),
repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10), repo_module.EnvDeployKeyID+"="+fmt.Sprintf("%d", results.DeployKeyID),
repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10), repo_module.EnvKeyID+"="+fmt.Sprintf("%d", results.KeyID),
repo_module.EnvAppURL+"="+setting.AppURL, repo_module.EnvAppURL+"="+setting.AppURL,
) )
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command. // to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
// it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later. // it could be re-considered whether to use the same git.CommonGitCmdEnvs() as "git" command later.
command.Env = append(command.Env, gitcmd.CommonCmdServEnvs()...) gitcmd.Env = append(gitcmd.Env, git.CommonCmdServEnvs()...)
if err = command.Run(); err != nil { if err = gitcmd.Run(); err != nil {
return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err) return fail(ctx, "Failed to execute git command", "Failed to execute git command: %v", err)
} }

View File

@@ -12,23 +12,20 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time"
_ "net/http/pprof" // Used for debugging if enabled and a web server is running _ "net/http/pprof" // Used for debugging if enabled and a web server is running
"code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/gtprof"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/public" "code.gitea.io/gitea/modules/public"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers" "code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/install" "code.gitea.io/gitea/routers/install"
"github.com/felixge/fgprof" "github.com/felixge/fgprof"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
// PIDFile could be set from build tag // PIDFile could be set from build tag
@@ -118,31 +115,21 @@ func showWebStartupMessage(msg string) {
log.Info("* CustomPath: %s", setting.CustomPath) log.Info("* CustomPath: %s", setting.CustomPath)
log.Info("* ConfigFile: %s", setting.CustomConf) log.Info("* ConfigFile: %s", setting.CustomConf)
log.Info("%s", msg) // show startup message log.Info("%s", msg) // show startup message
if setting.CORSConfig.Enabled {
log.Info("CORS Service Enabled")
}
if setting.DefaultUILocation != time.Local {
log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
}
if setting.MailService != nil {
log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
}
} }
func serveInstall(cmd *cli.Command) error { func serveInstall(ctx *cli.Context) error {
showWebStartupMessage("Prepare to run install page") showWebStartupMessage("Prepare to run install page")
routers.InitWebInstallPage(graceful.GetManager().HammerContext()) routers.InitWebInstallPage(graceful.GetManager().HammerContext())
// Flag for port number in case first time run conflict // Flag for port number in case first time run conflict
if cmd.IsSet("port") { if ctx.IsSet("port") {
if err := setPort(cmd.String("port")); err != nil { if err := setPort(ctx.String("port")); err != nil {
return err return err
} }
} }
if cmd.IsSet("install-port") { if ctx.IsSet("install-port") {
if err := setPort(cmd.String("install-port")); err != nil { if err := setPort(ctx.String("install-port")); err != nil {
return err return err
} }
} }
@@ -163,7 +150,7 @@ func serveInstall(cmd *cli.Command) error {
return nil return nil
} }
func serveInstalled(c *cli.Command) error { func serveInstalled(ctx *cli.Context) error {
setting.InitCfgProvider(setting.CustomConf) setting.InitCfgProvider(setting.CustomConf)
setting.LoadCommonSettings() setting.LoadCommonSettings()
setting.MustInstalled() setting.MustInstalled()
@@ -213,19 +200,13 @@ func serveInstalled(c *cli.Command) error {
log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath) log.Fatal("Can not find APP_DATA_PATH %q", setting.AppDataPath)
} }
// the AppDataTempDir is fully managed by us with a safe sub-path
// so it's safe to automatically remove the outdated files
setting.AppDataTempDir("").RemoveOutdated(3 * 24 * time.Hour)
// Override the provided port number within the configuration // Override the provided port number within the configuration
if c.IsSet("port") { if ctx.IsSet("port") {
if err := setPort(c.String("port")); err != nil { if err := setPort(ctx.String("port")); err != nil {
return err return err
} }
} }
gtprof.EnableBuiltinTracer(util.Iif(setting.IsProd, 2000*time.Millisecond, 100*time.Millisecond))
// Set up Chi routes // Set up Chi routes
webRoutes := routers.NormalRoutes() webRoutes := routers.NormalRoutes()
err := listen(webRoutes, true) err := listen(webRoutes, true)
@@ -236,27 +217,22 @@ func serveInstalled(c *cli.Command) error {
} }
func servePprof() { func servePprof() {
// FIXME: it shouldn't use the global DefaultServeMux, and it should use a proper context
http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler()) http.DefaultServeMux.Handle("/debug/fgprof", fgprof.Handler())
_, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true) _, _, finished := process.GetManager().AddTypedContext(context.Background(), "Web: PProf Server", process.SystemProcessType, true)
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it. // The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment it's not worth to introduce a configurable option for it.
log.Info("Starting pprof server on localhost:6060") log.Info("Starting pprof server on localhost:6060")
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil)) log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", nil))
finished() finished()
} }
func runWeb(ctx context.Context, cmd *cli.Command) error { func runWeb(ctx *cli.Context) error {
defer func() { defer func() {
if panicked := recover(); panicked != nil { if panicked := recover(); panicked != nil {
log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2)) log.Fatal("PANIC: %v\n%s", panicked, log.Stack(2))
} }
}() }()
if subCmdName, valid := isValidDefaultSubCommand(cmd); !valid { managerCtx, cancel := context.WithCancel(context.Background())
return fmt.Errorf("unknown command: %s", subCmdName)
}
managerCtx, cancel := context.WithCancel(ctx)
graceful.InitManager(managerCtx) graceful.InitManager(managerCtx)
defer cancel() defer cancel()
@@ -267,12 +243,12 @@ func runWeb(ctx context.Context, cmd *cli.Command) error {
} }
// Set pid file setting // Set pid file setting
if cmd.IsSet("pid") { if ctx.IsSet("pid") {
createPIDFile(cmd.String("pid")) createPIDFile(ctx.String("pid"))
} }
if !setting.InstallLock { if !setting.InstallLock {
if err := serveInstall(cmd); err != nil { if err := serveInstall(ctx); err != nil {
return err return err
} }
} else { } else {
@@ -283,7 +259,7 @@ func runWeb(ctx context.Context, cmd *cli.Command) error {
go servePprof() go servePprof()
} }
return serveInstalled(cmd) return serveInstalled(ctx)
} }
func setPort(port string) error { func setPort(port string) error {

View File

@@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/caddyserver/certmagic" "github.com/caddyserver/certmagic"
) )
@@ -55,6 +54,8 @@ func runACME(listenAddr string, m http.Handler) error {
altTLSALPNPort = p altTLSALPNPort = p
} }
magic := certmagic.NewDefault()
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
// Try to use private CA root if provided, otherwise defaults to system's trust // Try to use private CA root if provided, otherwise defaults to system's trust
var certPool *x509.CertPool var certPool *x509.CertPool
if setting.AcmeCARoot != "" { if setting.AcmeCARoot != "" {
@@ -64,20 +65,8 @@ func runACME(listenAddr string, m http.Handler) error {
log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err) log.Warn("Failed to parse CA Root certificate, using default CA trust: %v", err)
} }
} }
// FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https" myACME := certmagic.NewACMEIssuer(magic, certmagic.ACMEIssuer{
// Ideally it should migrate to AppDataPath write to "AppDataPath/https" CA: setting.AcmeURL,
// And one more thing, no idea why we should set the global default variables here
// But it seems that the current ACME code needs these global variables to make renew work.
// Otherwise, "renew" will use incorrect storage path
oldDefaultACME := certmagic.DefaultACME
certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
certmagic.DefaultACME = certmagic.ACMEIssuer{
// try to use the default values provided by DefaultACME
CA: util.IfZero(setting.AcmeURL, oldDefaultACME.CA),
TestCA: oldDefaultACME.TestCA,
Logger: oldDefaultACME.Logger,
HTTPProxy: oldDefaultACME.HTTPProxy,
TrustedRoots: certPool, TrustedRoots: certPool,
Email: setting.AcmeEmail, Email: setting.AcmeEmail,
Agreed: setting.AcmeTOS, Agreed: setting.AcmeTOS,
@@ -86,10 +75,8 @@ func runACME(listenAddr string, m http.Handler) error {
ListenHost: setting.HTTPAddr, ListenHost: setting.HTTPAddr,
AltTLSALPNPort: altTLSALPNPort, AltTLSALPNPort: altTLSALPNPort,
AltHTTPPort: altHTTPPort, AltHTTPPort: altHTTPPort,
} })
magic := certmagic.NewDefault()
myACME := certmagic.NewACMEIssuer(magic, certmagic.DefaultACME)
magic.Issuers = []certmagic.Issuer{myACME} magic.Issuers = []certmagic.Issuer{myACME}
// this obtains certificates or renews them if necessary // this obtains certificates or renews them if necessary
@@ -136,7 +123,7 @@ func runACME(listenAddr string, m http.Handler) error {
} }
func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) { func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet && r.Method != http.MethodHead { if r.Method != "GET" && r.Method != "HEAD" {
http.Error(w, "Use HTTPS", http.StatusBadRequest) http.Error(w, "Use HTTPS", http.StatusBadRequest)
return return
} }

View File

@@ -23,6 +23,12 @@ func NoHTTPRedirector() {
graceful.GetManager().InformCleanup() graceful.GetManager().InformCleanup()
} }
// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
// for our main HTTP/HTTPS service
func NoMainListener() {
graceful.GetManager().InformCleanup()
}
// NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener // NoInstallListener tells our cleanup routine that we will not be using a possibly provided listener
// for our install HTTP/HTTPS service // for our install HTTP/HTTPS service
func NoInstallListener() { func NoInstallListener() {

View File

@@ -0,0 +1,17 @@
Bash and Zsh completion
=======================
From within the gitea root run:
```bash
source contrib/autocompletion/bash_autocomplete
```
or for zsh run:
```bash
source contrib/autocompletion/zsh_autocomplete
```
These scripts will check if gitea is on the path and if so add autocompletion for `gitea`. Or if not autocompletion will work for `./gitea`.
If gitea has been installed as a different program pass in the `PROG` environment variable to set the correct program name.

View File

@@ -0,0 +1,30 @@
#! /bin/bash
# Heavily inspired by https://github.com/urfave/cli
_cli_bash_autocomplete() {
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
local cur opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
if [[ "$cur" == "-"* ]]; then
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
else
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
fi
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
fi
}
if [ -z "$PROG" ] && [ ! "$(command -v gitea &> /dev/null)" ] ; then
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete gitea
elif [ -z "$PROG" ]; then
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete ./gitea
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PWD/gitea"
else
complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete "$PROG"
unset PROG
fi

View File

@@ -0,0 +1,30 @@
#compdef ${PROG:=gitea}
# Heavily inspired by https://github.com/urfave/cli
_cli_zsh_autocomplete() {
local -a opts
local cur
cur=${words[-1]}
if [[ "$cur" == "-"* ]]; then
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
else
opts=("${(@f)$(_CLI_ZSH_AUTOCOMPLETE_HACK=1 ${words[@]:0:#words[@]-1} --generate-bash-completion)}")
fi
if [[ "${opts[1]}" != "" ]]; then
_describe 'values' opts
else
_files
fi
return
}
if [ -z $PROG ] ; then
compdef _cli_zsh_autocomplete gitea
else
compdef _cli_zsh_autocomplete $(basename $PROG)
fi

View File

@@ -11,7 +11,7 @@ The default version will read from `docs/config.yml`. You can override this
using the option `--version`. using the option `--version`.
The upstream branches will be fetched, using the remote `origin`. This can The upstream branches will be fetched, using the remote `origin`. This can
be overridden using `--upstream`, and fetching can be avoided using be overrided using `--upstream`, and fetching can be avoided using
`--no-fetch`. `--no-fetch`.
By default the branch created will be called `backport-$PR-$VERSION`. You By default the branch created will be called `backport-$PR-$VERSION`. You

View File

@@ -1,30 +1,31 @@
// Copyright 2023 The Gitea Authors. All rights reserved. // Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
//nolint:forbidigo // use of print functions is allowed in cli //nolint:forbidigo
package main package main
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"path" "path"
"strconv" "strconv"
"strings" "strings"
"syscall"
"github.com/google/go-github/v74/github" "github.com/google/go-github/v57/github"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const defaultVersion = "v1.18" // to backport to const defaultVersion = "v1.18" // to backport to
func main() { func main() {
app := &cli.Command{} app := cli.NewApp()
app.Name = "backport" app.Name = "backport"
app.Usage = "Backport provided PR-number on to the current or previous released version" app.Usage = "Backport provided PR-number on to the current or previous released version"
app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version` app.Description = `Backport will look-up the PR in Gitea's git log and attempt to cherry-pick it on the current version`
@@ -63,11 +64,6 @@ func main() {
Value: "", Value: "",
Usage: "Forked user name on Github", Usage: "Forked user name on Github",
}, },
&cli.StringFlag{
Name: "gh-access-token",
Value: "",
Usage: "Access token for GitHub api request",
},
&cli.BoolFlag{ &cli.BoolFlag{
Name: "no-fetch", Name: "no-fetch",
Usage: "Set this flag to prevent fetch of remote branches", Usage: "Set this flag to prevent fetch of remote branches",
@@ -89,7 +85,7 @@ func main() {
Usage: "Set this flag to continue from a git cherry-pick that has broken", Usage: "Set this flag to continue from a git cherry-pick that has broken",
}, },
} }
cli.RootCommandHelpTemplate = `NAME: cli.AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}} {{.Name}} - {{.Usage}}
USAGE: USAGE:
{{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} {{.HelpName}} {{if .VisibleFlags}}[options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
@@ -103,12 +99,16 @@ OPTIONS:
` `
app.Action = runBackport app.Action = runBackport
if err := app.Run(context.Background(), os.Args); err != nil {
if err := app.Run(os.Args); err != nil {
fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err) fmt.Fprintf(os.Stderr, "Unable to backport: %v\n", err)
} }
} }
func runBackport(ctx context.Context, c *cli.Command) error { func runBackport(c *cli.Context) error {
ctx, cancel := installSignals()
defer cancel()
continuing := c.Bool("continue") continuing := c.Bool("continue")
var pr string var pr string
@@ -153,7 +153,7 @@ func runBackport(ctx context.Context, c *cli.Command) error {
args := c.Args().Slice() args := c.Args().Slice()
if len(args) == 0 && pr == "" { if len(args) == 0 && pr == "" {
return errors.New("no PR number provided\nProvide a PR number to backport") return fmt.Errorf("no PR number provided\nProvide a PR number to backport")
} else if len(args) != 1 && pr == "" { } else if len(args) != 1 && pr == "" {
return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args) return fmt.Errorf("multiple PRs provided %v\nOnly a single PR can be backported at a time", args)
} }
@@ -169,10 +169,9 @@ func runBackport(ctx context.Context, c *cli.Command) error {
fmt.Printf("* Backporting %s to %s as %s\n", pr, localReleaseBranch, backportBranch) fmt.Printf("* Backporting %s to %s as %s\n", pr, localReleaseBranch, backportBranch)
sha := c.String("cherry-pick") sha := c.String("cherry-pick")
accessToken := c.String("gh-access-token")
if sha == "" { if sha == "" {
var err error var err error
sha, err = determineSHAforPR(ctx, pr, accessToken) sha, err = determineSHAforPR(ctx, pr)
if err != nil { if err != nil {
return err return err
} }
@@ -337,8 +336,8 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out)) fmt.Fprintf(os.Stderr, "Unable to list git remotes:\n%s\n", string(out))
return "", "", fmt.Errorf("unable to determine forked remote: %w", err) return "", "", fmt.Errorf("unable to determine forked remote: %w", err)
} }
lines := strings.SplitSeq(string(out), "\n") lines := strings.Split(string(out), "\n")
for line := range lines { for _, line := range lines {
fields := strings.Split(line, "\t") fields := strings.Split(line, "\t")
name, remote := fields[0], fields[1] name, remote := fields[0], fields[1]
// only look at pushers // only look at pushers
@@ -356,12 +355,12 @@ func determineRemote(ctx context.Context, forkUser string) (string, string, erro
if !strings.Contains(remote, forkUser) { if !strings.Contains(remote, forkUser) {
continue continue
} }
if after, ok := strings.CutPrefix(remote, "git@github.com:"); ok { if strings.HasPrefix(remote, "git@github.com:") {
forkUser = after forkUser = strings.TrimPrefix(remote, "git@github.com:")
} else if after, ok := strings.CutPrefix(remote, "https://github.com/"); ok { } else if strings.HasPrefix(remote, "https://github.com/") {
forkUser = after forkUser = strings.TrimPrefix(remote, "https://github.com/")
} else if after, ok := strings.CutPrefix(remote, "https://www.github.com/"); ok { } else if strings.HasPrefix(remote, "https://www.github.com/") {
forkUser = after forkUser = strings.TrimPrefix(remote, "https://www.github.com/")
} else if forkUser == "" { } else if forkUser == "" {
return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote) return "", "", fmt.Errorf("unable to extract forkUser from remote %s: %s", name, remote)
} }
@@ -428,16 +427,13 @@ func readVersion() string {
return strings.Join(split[:2], ".") return strings.Join(split[:2], ".")
} }
func determineSHAforPR(ctx context.Context, prStr, accessToken string) (string, error) { func determineSHAforPR(ctx context.Context, prStr string) (string, error) {
prNum, err := strconv.Atoi(prStr) prNum, err := strconv.Atoi(prStr)
if err != nil { if err != nil {
return "", err return "", err
} }
client := github.NewClient(http.DefaultClient) client := github.NewClient(http.DefaultClient)
if accessToken != "" {
client = client.WithAuthToken(accessToken)
}
pr, _, err := client.PullRequests.Get(ctx, "go-gitea", "gitea", prNum) pr, _, err := client.PullRequests.Get(ctx, "go-gitea", "gitea", prNum)
if err != nil { if err != nil {
@@ -454,3 +450,25 @@ func determineSHAforPR(ctx context.Context, prStr, accessToken string) (string,
return "", nil return "", nil
} }
func installSignals() (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(context.Background())
go func() {
// install notify
signalChannel := make(chan os.Signal, 1)
signal.Notify(
signalChannel,
syscall.SIGINT,
syscall.SIGTERM,
)
select {
case <-signalChannel:
case <-ctx.Done():
}
cancel()
signal.Reset()
}()
return ctx, cancel
}

View File

@@ -4,17 +4,16 @@
package main package main
import ( import (
"context"
"os" "os"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/urfave/cli/v3" "github.com/urfave/cli/v2"
) )
func main() { func main() {
app := cli.Command{} app := cli.NewApp()
app.Name = "environment-to-ini" app.Name = "environment-to-ini"
app.Usage = "Use provided environment to update configuration ini" app.Usage = "Use provided environment to update configuration ini"
app.Description = `As a helper to allow docker users to update the gitea configuration app.Description = `As a helper to allow docker users to update the gitea configuration
@@ -73,13 +72,13 @@ func main() {
}, },
} }
app.Action = runEnvironmentToIni app.Action = runEnvironmentToIni
err := app.Run(context.Background(), os.Args) err := app.Run(os.Args)
if err != nil { if err != nil {
log.Fatal("Failed to run app with %s: %v", os.Args, err) log.Fatal("Failed to run app with %s: %v", os.Args, err)
} }
} }
func runEnvironmentToIni(_ context.Context, c *cli.Command) error { func runEnvironmentToIni(c *cli.Context) error {
// the config system may change the environment variables, so get a copy first, to be used later // the config system may change the environment variables, so get a copy first, to be used later
env := append([]string{}, os.Environ()...) env := append([]string{}, os.Environ()...)
setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{ setting.InitWorkPathAndCfgProvider(os.Getenv, setting.ArgWorkPathAndCustomConf{

View File

@@ -0,0 +1,80 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//nolint:forbidigo
package main
import (
"context"
"fmt"
"os"
"path/filepath"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/unittest"
)
// To generate derivative fixtures, execute the following from Gitea's repository base dir:
// go run -tags 'sqlite sqlite_unlock_notify' contrib/fixtures/fixture_generation.go [fixture...]
var (
generators = []struct {
gen func(ctx context.Context) (string, error)
name string
}{
{
models.GetYamlFixturesAccess, "access",
},
}
fixturesDir string
)
func main() {
pathToGiteaRoot := "."
fixturesDir = filepath.Join(pathToGiteaRoot, "models", "fixtures")
if err := unittest.CreateTestEngine(unittest.FixturesOptions{
Dir: fixturesDir,
}); err != nil {
fmt.Printf("CreateTestEngine: %+v", err)
os.Exit(1)
}
if err := unittest.PrepareTestDatabase(); err != nil {
fmt.Printf("PrepareTestDatabase: %+v\n", err)
os.Exit(1)
}
ctx := context.Background()
if len(os.Args) == 0 {
for _, r := range os.Args {
if err := generate(ctx, r); err != nil {
fmt.Printf("generate '%s': %+v\n", r, err)
os.Exit(1)
}
}
} else {
for _, g := range generators {
if err := generate(ctx, g.name); err != nil {
fmt.Printf("generate '%s': %+v\n", g.name, err)
os.Exit(1)
}
}
}
}
func generate(ctx context.Context, name string) error {
for _, g := range generators {
if g.name == name {
data, err := g.gen(ctx)
if err != nil {
return err
}
path := filepath.Join(fixturesDir, name+".yml")
if err := os.WriteFile(path, []byte(data), 0o644); err != nil {
return fmt.Errorf("%s: %+v", path, err)
}
fmt.Printf("%s created.\n", path)
return nil
}
}
return fmt.Errorf("generator not found")
}

View File

@@ -150,7 +150,7 @@
<p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p> <p>In general, Your Gitea Instance retains User Personal Information for as long as your account is active, or as needed to provide you service.</p>
<p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the basis of consent within 30 days.</p> <p>If you would like to cancel your account or delete your User Personal Information, you may do so in your user profile. We retain and use your information as necessary to comply with our legal obligations, resolve disputes, and enforce our agreements, but barring legal requirements, we will delete your full profile (within reason) within 90 days of your request. Feel free to contact our support to request erasure of the data we process on the bassis of consent within 30 days.</p>
<p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p> <p>After an account has been deleted, certain data, such as contributions to other Users' repositories and comments in others' issues, will remain. However, we will delete or de-identify your User Personal Information, including your username and email address, from the author field of issues, pull requests, and comments by associating them with a ghost user.</p>

View File

@@ -85,7 +85,7 @@ fi
# confirm update # confirm update
echo "Checking currently installed version..." echo "Checking currently installed version..."
current=$(giteacmd --version | cut -d ' ' -f 3) current=$(giteacmd --version | cut -d ' ' -f 3)
[[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 0 [[ "$current" == "$giteaversion" ]] && echo "$current is already installed, stopping." && exit 1
if [[ -z "${no_confirm:-}" ]]; then if [[ -z "${no_confirm:-}" ]]; then
echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md" echo "Make sure to read the changelog first: https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md"
echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)" echo "Are you ready to update Gitea from ${current} to ${giteaversion}? (y/N)"

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