Compare commits

...

64 Commits

Author SHA1 Message Date
Giteabot 8a03b7c01a
Merge branch 'main' into wbtw 2024-04-27 14:54:32 +08:00
GiteaBot 27861d711b [skip ci] Updated translations via Crowdin 2024-04-27 00:24:31 +00:00
silverwind c93eefb42b
Diff color enhancements, add line number background (#30670)
1. Bring back the background on line numbers. This feature was lost a
long time ago.

<img width="457" alt="Screenshot 2024-04-24 at 01 36 09"
src="https://github.com/go-gitea/gitea/assets/115237/76a7f5a9-c22a-4c72-9f0a-ebf16a66513e">
<img width="473" alt="Screenshot 2024-04-24 at 01 22 47"
src="https://github.com/go-gitea/gitea/assets/115237/eef06cf2-f1b9-40e3-947d-dd5852ec12a3">
<img width="457" alt="Screenshot 2024-04-24 at 02 13 18"
src="https://github.com/go-gitea/gitea/assets/115237/59e317d4-76a7-468c-8a19-10d88c675cc3">
<img width="459" alt="Screenshot 2024-04-24 at 01 23 21"
src="https://github.com/go-gitea/gitea/assets/115237/f1a46f8d-8846-4d78-a9d7-8b7dc18ac6e4">

2. Expanded lines background is now full-line, including line numbers:

<img width="1303" alt="Screenshot 2024-04-24 at 01 37 12"
src="https://github.com/go-gitea/gitea/assets/115237/271eefe2-0869-424e-93fb-ccd8adc87806">

3. Sort affected colors alphabetically in the CSS

Fixes #14603
2024-04-26 19:37:21 +00:00
Bo-Yi Wu 852547d0dc
feat(api): enhance Actions Secrets Management API for repository (#30656)
- Add endpoint to list repository action secrets in API routes
- Implement `ListActionsSecrets` function to retrieve action secrets
from the database
- Update Swagger documentation to include the new
`/repos/{owner}/{repo}/actions/secrets` endpoint
- Add `actions` package import and define new routes for actions,
secrets, variables, and runners in `api.go`.
- Refactor action-related API functions into `Action` struct methods in
`org/action.go` and `repo/action.go`.
- Remove `actionAPI` struct and related functions, replacing them with
`NewAction()` calls.
- Rename `variables.go` to `action.go` in `org` directory.
- Delete `runners.go` and `secrets.go` in both `org` and `repo`
directories, consolidating their content into `action.go`.
- Update copyright year and add new imports in `org/action.go`.
- Implement `API` interface in `services/actions/interface.go` for
action-related methods.
- Remove individual action-related functions and replace them with
methods on the `Action` struct in `repo/action.go`.

---------

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Signed-off-by: appleboy <appleboy.tw@gmail.com>
2024-04-26 21:11:49 +08:00
wxiaoguang 993736d838
Fix code search input for different views (#30678)
Now only show the "code search" on the repo home page, because it only
does global search.
So do not show it when viewing file or directory to avoid misleading
users (it doesn't search in a directory)
2024-04-26 11:21:04 +00:00
wxiaoguang cd70ab31cd
Fix incorrect object id hash function (#30708)
Great thanks to @oliverpool for figuring out the problem and proposing a
fix.

Regression of #28138

Incorrect hash causes the user's LFS files get all deleted when running
`doctor fix all`

(by the way, remove unused/non-standard comments)

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-26 09:49:48 +00:00
wxiaoguang 1e749b80d7
Add route handler info for debugging purpose (#30705)
Follow #30519
2024-04-26 09:09:49 +00:00
Yarden Shoham 68a3e6b5e6
Bump htmx version to 1.9.12 (#30711)
There are no breaking changes. I tested and everything works as before.

Signed-off-by: Yarden Shoham <git@yardenshoham.com>
2024-04-26 09:27:34 +02:00
wxiaoguang ed8c63cea3
Deduplicate lfs common code (#30704) 2024-04-26 02:53:30 +00:00
yp05327 2a3906d755
Improve job commit description (#30579)
Fix https://github.com/go-gitea/gitea/issues/30567

When job is a schedule:

![image](https://github.com/go-gitea/gitea/assets/18380374/b07e9d43-e8b7-4ee2-87b3-a7050c3a8ca5)
When it is a normal one:

![image](https://github.com/go-gitea/gitea/assets/18380374/0d58dab9-74bb-421b-8952-0578cdf21a52)

also add a 'space' behind  `:`

![image](https://github.com/go-gitea/gitea/assets/18380374/4cebece0-bfe6-4ad9-b806-e5c49bb9be43)


![image](https://github.com/go-gitea/gitea/assets/18380374/02da7681-474b-4c0f-9dad-b6558f6cb484)

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-26 02:22:45 +00:00
Lunny Xiao 2a6418abb1
Improve test for TestPullCompare (#30699) 2024-04-26 01:52:28 +00:00
wxiaoguang 6a0750177f
Allow to save empty comment (#30706)
Fix #29986
2024-04-26 01:17:43 +00:00
GiteaBot 935330b1b9 [skip ci] Updated translations via Crowdin 2024-04-26 00:26:00 +00:00
wxiaoguang fd63b96f6a
Refactor imagediff and fix regression bug (#30694)
Fix #30683
2024-04-25 21:01:38 +08:00
wxiaoguang bffbbf5470
Improve oauth2 client "preferred username field" logic and the error handling (#30622)
Follow #30454
And fix #24957

When using "preferred_username", if no such field,
`extractUserNameFromOAuth2` (old `getUserName`) shouldn't return an
error. All other USERNAME options do not return such error.

And fine tune some logic and error messages, make code more stable and
more friendly to end users.
2024-04-25 11:22:32 +00:00
silverwind d0bfc978de
Fix active item in tab menu (#30690)
Before, item would also resize on hover because of font weight:
<img width="381" alt="Screenshot 2024-04-25 at 01 28 53"
src="https://github.com/go-gitea/gitea/assets/115237/4f3291fc-90be-4d66-ae8b-3c2f763cb956">

After:
<img width="381" alt="Screenshot 2024-04-25 at 01 28 40"
src="https://github.com/go-gitea/gitea/assets/115237/06145bf2-1ddd-4171-9217-d92c100ea405">

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-25 12:53:39 +02:00
Lunny Xiao c685eefe4a
If a repository return no commitstatus, then still cache it but not query it from database (#30700)
The previous repository default branch commit status cache will only
store if the commit status has value. So the repository which have no
any commit status will always be fetched from database.

This PR will store the empty state of commit status of a repository into
cache because the cache will be updated once there is a commit status
stored.
2024-04-25 17:14:23 +08:00
GiteaBot 4ff54933f8 [skip ci] Updated translations via Crowdin 2024-04-25 00:26:13 +00:00
Jiaxin Zhu a63f14b908
Fix view of readme file in the home code page. (#30564)
Gitea attempts to display image file, pdf file, etc. named readme in the
home code page (but it cannot).
I think only the markdown and plain-text file should be displayed, which
is also the behavior of GitHub.

Co-authored-by: jxshin <zhujiaxinabc@gmail.com>
2024-04-25 00:07:38 +00:00
Lunny Xiao a19d2bbd90
Add test for #30674 (#30679) 2024-04-24 14:11:49 +00:00
silverwind 3f19a63345
Fix border-radius of header+segment boxes (#30667)
This is a very old bug with the bottom border-radiuses not being there
and the `:has` selector now makes it possible to cleanly solve it. It
affects all header+segment boxes, which there are many throughout the
UI:

<img width="1017" alt="Screenshot 2024-04-23 at 20 47 21"
src="https://github.com/go-gitea/gitea/assets/115237/870fe352-cc38-4bd6-bfe6-9fe8c3066f92">
2024-04-24 13:11:52 +00:00
Lunny Xiao 8b3632435e
Fix a panic bug when head repository deleting (#30674)
When visiting a pull request files which head repository has been
deleted, it will panic because headrepo is nil.
2024-04-24 13:26:50 +08:00
Lunny Xiao 2ad9ef4984
Fix some bug on migrations (#30647)
Fix https://github.com/go-gitea/gitea/pull/23894#discussion_r1573718690
2024-04-24 01:58:24 +00:00
silverwind 1a2ae64b16
Fix checkbox field markup (#30666)
Fixes https://github.com/go-gitea/gitea/issues/30664.

Previous use was not a supported way by fomantic and the misuse only
became visible after the checkbox migration.
2024-04-23 21:53:57 +00:00
wxiaoguang 2ee93ea178
Avoid doubled border for the PR info segment (#30663) 2024-04-23 19:24:10 +00:00
sillyguodong 2f6b1c46a1
Interpolate runs-on with variables when scheduling tasks (#30640)
Follow #29468
1. Interpolate runs-on with variables when scheduling tasks.
2. The `GetVariablesOfRun` function will check if the `Repo` of the run
is nil.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-23 20:55:25 +02:00
wxiaoguang b79e3db264
Initial support for colorblindness-friendly themes (#30625)
Initial support for #25680

This PR only adds some simple styles from GitHub, it is big enough and
it focuses on adding the necessary framework-level supports. More styles
could be fine-tuned later.
2024-04-24 00:18:41 +08:00
wxiaoguang dd2aaadce3
Fix flash message for flex-container (#30657) 2024-04-23 08:31:51 +00:00
Kemal Zebari 9b7af4340c
Perform Newest sort type correctly when sorting issues (#30644)
Should resolve #30642.

Before this commit, we were treating an empty `?sort=` query parameter
as the correct sorting type (which is to sort issues in descending order
by their created UNIX time). But when we perform `sort=latest`, we did
not include this as a type so we would sort by the most recently updated
when reaching the `default` switch statement block.

This commit fixes this by considering the empty string, "latest", and
just any other string that is not mentioned in the switch statement as
sorting by newest.
2024-04-23 15:10:01 +08:00
silverwind 370b1bdb37
Fix project name wrapping, remove horizontal margin on header (#30631)
Enable wrapping of unbroken lines:

<img width="1308" alt="Screenshot 2024-04-22 at 00 31 33"
src="https://github.com/go-gitea/gitea/assets/115237/1a28ade1-d708-4260-96a3-cf508b6dcb79">

Remove extra margin added by nested `.ui.container` on certain
viewports:

Before:
<img width="1305" alt="Screenshot 2024-04-22 at 00 40 23"
src="https://github.com/go-gitea/gitea/assets/115237/d3d8c0d1-380c-4867-b95c-4d53d70d4a93">

After:
<img width="1310" alt="Screenshot 2024-04-22 at 00 40 33"
src="https://github.com/go-gitea/gitea/assets/115237/2ba7b9f2-db2f-4bcc-8cce-5c415625ddea">
2024-04-23 04:17:51 +00:00
Zettat123 30dd4beeee
Add a db consistency check to remove runners that do not belong to a repository (#30614)
Follow #30406
2024-04-23 03:51:52 +00:00
Lunny Xiao e94864e86c
Fix wrong table name (#30557)
The table name should be `oauth2_application` but `o_auth2_application`

Caused by
https://github.com/go-gitea/gitea/pull/21316/files#diff-9610efbc608a41f1f2eaff5790423f0a187906f6ff0beb23a5e8d18366cc2ccfR38
2024-04-23 03:00:57 +00:00
Lunny Xiao 8924d9b2ef
Fix compare api swagger (#30648)
The swagger format on #30349 is not right. This PR will fix it.
2024-04-23 02:22:43 +00:00
GiteaBot 7d5a03fda2 [skip ci] Updated translations via Crowdin 2024-04-23 00:24:55 +00:00
wxiaoguang e6103955cc
Fix queue test (#30646)
Fix #30643

The old test code is not stable due to the data-race described in the
TODO added at that time.

Make it stable, and remove a debug-only field from old test code.
2024-04-22 23:55:43 +00:00
silverwind 99c5683da5
Enable jquery-related eslint rules that have no violations (#30632)
All these have no violations, so enable them.
2024-04-22 14:24:47 +00:00
silverwind 74f0c84fa4
Enable more `revive` linter rules (#30608)
Noteable additions:

- `redefines-builtin-id` forbid variable names that shadow go builtins
- `empty-lines` remove unnecessary empty lines that `gofumpt` does not
remove for some reason
- `superfluous-else` eliminate more superfluous `else` branches

Rules are also sorted alphabetically and I cleaned up various parts of
`.golangci.yml`.
2024-04-22 11:48:42 +00:00
silverwind aff7b7bdd2
Remove obsolete CSS text classes (#30576)
- `.text-thin` and `.text-italic` are not present in CSS so were doing nothing and I removed them.
- `.text.middle` was unused so I removed it.
- `.text.italic` is replaced with `tw-italic`.
- `.text.normal` had exactly one use and it wasn't even needed.
- add a `muted` class to the link to `org_profile_avatar.tmpl`.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-22 11:21:06 +00:00
silverwind 0386a42f70
Hide diff stats on empty PRs (#30629)
When a PR is empty, e.g. has neither additions nor deletions, we don't
need to show this:

<img width="125" alt="Screenshot 2024-04-21 at 23 25 38"
src="https://github.com/go-gitea/gitea/assets/115237/0b987eb5-66f5-4b9b-b5aa-7e9e267e9b52">
2024-04-22 10:48:14 +00:00
GiteaBot 31386dc2bb [skip ci] Updated licenses and gitignores 2024-04-22 00:25:56 +00:00
wxiaoguang 7cb88e9c18
Use correct hash for "git update-index" (#30626) 2024-04-22 00:13:44 +00:00
wxiaoguang f4a1cf7eab
Fix repo home UI when there is no repo description (#30552)
Fix #30502 by a new approach.


![image](https://github.com/go-gitea/gitea/assets/2114189/22f48bca-82d1-45cc-b1b7-ee2344b81a76)
2024-04-21 23:47:31 +00:00
wxiaoguang 1e4867730b
Fix dropdown text ellipsis (#30628)
Follow
https://github.com/go-gitea/gitea/pull/30547#discussion_r1573866519

Fix #30624

The Fomantic UI Dropdown wasn't designed to work that way, its "text"
element might contain images. So the "overflow" shouldn't be added to
any general dropdown text.


![image](https://github.com/go-gitea/gitea/assets/2114189/f6ceaabd-bc89-4bf2-baa2-a6f0324c1962)
2024-04-21 23:14:33 +00:00
Bo-Yi Wu 6459c50278
fix(api): refactor branch and tag existence checks (#30618)
- Update branch existence check to also include tag existence check
- Adjust error message for branch/tag existence check

ref: https://github.com/go-gitea/gitea/pull/30349

---------

Signed-off-by: appleboy <appleboy.tw@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-22 00:19:59 +02:00
Kemal Zebari 0606284fcf
Add --skip-db option to dump command (#30613)
Attempts to resolve #28720.

---

Note that I am not a Gitea administrator so I don't normally use the
gitea CLI. Just saw this issue and wanted an opportunity to understand
how this subcommand works and see if I can add this feature :^)

I tested both with `--skip-db` and without and it appears to not add any
database-specific files to the generated archive i.e. I don't see a
`gitea-db.sql` or `gitea.db` file:
```console
$ TAGS="bindata sqlite sqlite_unlock_notify" make backend
Running go generate...
bindata for migration already up-to-date
bindata for options already up-to-date
bindata for public already up-to-date
bindata for templates already up-to-date
$ ./gitea dump --skip-db
2024/04/20 01:16:11 ...s/setting/session.go:77:loadSessionFrom() [I] Session Service Enabled
2024/04/20 01:16:11 ...s/storage/storage.go:176:initAttachments() [I] Initialising Attachment storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/attachments
2024/04/20 01:16:11 ...s/storage/storage.go:166:initAvatars() [I] Initialising Avatar storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/avatars
2024/04/20 01:16:11 ...s/storage/storage.go:192:initRepoAvatars() [I] Initialising Repository Avatar storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/repo-avatars
2024/04/20 01:16:11 ...s/storage/storage.go:186:initLFS() [I] Initialising LFS storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/lfs
2024/04/20 01:16:11 ...s/storage/storage.go:198:initRepoArchives() [I] Initialising Repository Archive storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/repo-archive
2024/04/20 01:16:11 ...s/storage/storage.go:208:initPackages() [I] Initialising Packages storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/packages
2024/04/20 01:16:11 ...s/storage/storage.go:219:initActions() [I] Initialising Actions storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/actions_log
2024/04/20 01:16:11 ...s/storage/storage.go:223:initActions() [I] Initialising ActionsArtifacts storage with type: local
2024/04/20 01:16:11 ...les/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /workspaces/gitea/data/actions_artifacts
2024/04/20 01:16:11 cmd/dump.go:172:runDump() [I] Dumping local repositories... /workspaces/gitea/data/gitea-repositories
2024/04/20 01:16:11 cmd/dump.go:195:runDump() [I] Skipping database
2024/04/20 01:16:11 cmd/dump.go:229:runDump() [I] Adding custom configuration file from /workspaces/gitea/custom/conf/app.ini
2024/04/20 01:16:11 cmd/dump.go:256:runDump() [I] Packing data directory.../workspaces/gitea/data
2024/04/20 01:16:11 cmd/dump.go:335:runDump() [I] Finish dumping in file /workspaces/gitea/gitea-dump-1713575771.zip
$ unzip /workspaces/gitea/gitea-dump-1713575771.zip -d example
Archive:  /workspaces/gitea/gitea-dump-1713575771.zip
. . .
$ ls example/
app.ini  custom  data  repos
$ ls example/data/
actions_artifacts  actions_log  avatars  home  indexers  jwt  queues  repo-archive  repo-avatars  tmp
```

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-21 23:32:12 +02:00
silverwind 1b1b8500ae
Fix flash on dashboard (#30572)
Fixes https://github.com/go-gitea/gitea/issues/30566, regression from
https://github.com/go-gitea/gitea/pull/30214.
2024-04-21 20:24:56 +00:00
Cheng 9de443ced2
chore: use errors.New to replace fmt.Errorf with no parameters will much better (#30621)
use errors.New to replace fmt.Errorf with no parameters will much better
2024-04-21 19:44:03 +00:00
wxiaoguang f95622cddc
Fix issue comment form and quick-submit (#30623)
1. Rewrite initGlobalEnterQuickSubmit (by the way, remove jQuery)
2. Fix issue comment form layout
2024-04-22 01:00:04 +08:00
Lunny Xiao e865de1e9d
Use maintained gziphandler (#30592)
Replace #27894

---------

Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-04-21 00:53:45 +00:00
GiteaBot 99d789e8cd [skip ci] Updated translations via Crowdin 2024-04-21 00:26:57 +00:00
KN4CK3R b06aac40e6
Fix package list performance (#30520)
Fixes #28255

The new query uses the id field to sort by "newer". This most not be
correct (usually it is) but it's faster (see #28255).
If someone has a better idea, please propose changes.

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-20 13:07:00 +02:00
wxiaoguang 48d4580dd5
Clarify permission "HasAccess" behavior (#30585)
Follow #30495

"HasAccess" behavior wasn't clear, to make it clear:

* Use a new name `HasAnyUnitAccess`, it will be easier to review related
code and permission problems.
* Separate everyone access mode to a separate field, then all calls to
HasAccess are reverted to old behavior before #30495.
* Add new tests.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-20 03:15:04 +00:00
wxiaoguang 89e39872ff
Fix links in PyPI Simple Repository API page (#30594)
Thanks to @Zottelchen for looking into problem and proposing the fix.

Ref: https://github.com/astral-sh/uv/issues/3017 ,
https://peps.python.org/pep-0503/

This PR's change is from Zottelchen's work.

And I by the way rename the `$p` to `$pd` because `p` is used as
"package" in code, while `pd` is used as "package description".

----

Co-authored-by: Zottelchen
2024-04-20 01:15:14 +00:00
yp05327 cb6814adad
Use action user as the trigger user of schedules (#30581)
Follow https://github.com/go-gitea/gitea/pull/30357

When user push to default branch, the schedule trigger user will be the
user.
When disable then enable action units in settings, the schedule trigger
user will be action user.
When repo is a mirror, the schedule trigger user will be action user. (
before it will return error, fixed by #30357)

As scheduled job is a cron, the trigger user should be action user from
Gitea, not a real user.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-20 02:35:29 +02:00
wxiaoguang 53cf46cae7
Fix commit file status parser (#30602)
Try to fix  #30492
2024-04-19 14:41:03 +00:00
wxiaoguang f60e1a1af2
Fix HEAD method for robots.txt (#30603)
Fix #30601
2024-04-19 21:43:56 +08:00
wxiaoguang eb24d973b0
Fix project description rendering for org (#30587)
Fix #30263


![image](https://github.com/go-gitea/gitea/assets/2114189/41cabe6c-f94a-4874-a26f-d01bb89bb28c)

Co-authored-by: Giteabot <teabot@gitea.io>
2024-04-19 07:58:56 +00:00
wxiaoguang fe82991547
Fix changelog (main) (#30582)
1. The 1.21.11 changelog is missing
2. Split the old content to CHANGELOG-archived.md, to reduce the size of
CHANGELOG.md
2024-04-19 06:08:30 +00:00
Jason Song 61457cdf6b
Avoid importing `modules/web/middleware` in `modules/session` (#30584)
Related to #30375.

It doesn't make sense to import `modules/web/middleware` and
`modules/setting` in `modules/web/session` since the last one is more
low-level.

And it looks like a workaround to call `DeleteLegacySiteCookie` in
`RegenerateSession`, so maybe we could reverse the importing by
registering hook functions.
2024-04-19 04:03:53 +00:00
silverwind acfe29fc2b
Enable npm cache on `setup-node` action (#30577)
Enable npm dependency cache in
[setup-node](https://github.com/actions/setup-node). This should work
reliably and across branches as well.
2024-04-19 03:29:08 +00:00
GiteaBot ba9b124c34 [skip ci] Updated translations via Crowdin 2024-04-19 00:24:35 +00:00
wxiaoguang bcbeb24dba
Mock queue backoff duration (#30553)
During testing, the backoff duration shouldn't be longer than other
durations
2024-04-18 20:00:59 +00:00
silverwind dd8e6ae270
Improve "Reference in new issue" modal (#30547)
Fixes: https://github.com/go-gitea/gitea/issues/29994

Also some misc enhancements done to the form in the modal.

<img width="840" alt="Screenshot 2024-04-17 at 23 02 55"
src="https://github.com/go-gitea/gitea/assets/115237/e71fba55-55cd-4e48-a497-6b1025c36a43">
2024-04-18 19:31:53 +00:00
silverwind 354705450a
Add a few root files to lint-spell (#30530)
Files in root were not linted, add them. No new violations.
2024-04-18 18:54:40 +00:00
256 changed files with 7336 additions and 6527 deletions

View File

@ -310,7 +310,7 @@ rules:
jquery/no-merge: [2]
jquery/no-param: [2]
jquery/no-parent: [0]
jquery/no-parents: [0]
jquery/no-parents: [2]
jquery/no-parse-html: [2]
jquery/no-prop: [2]
jquery/no-proxy: [2]
@ -319,8 +319,8 @@ rules:
jquery/no-show: [2]
jquery/no-size: [2]
jquery/no-sizzle: [2]
jquery/no-slide: [0]
jquery/no-submit: [0]
jquery/no-slide: [2]
jquery/no-submit: [2]
jquery/no-text: [0]
jquery/no-toggle: [2]
jquery/no-trigger: [0]
@ -458,7 +458,7 @@ rules:
no-jquery/no-other-utils: [2]
no-jquery/no-param: [2]
no-jquery/no-parent: [0]
no-jquery/no-parents: [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]

View File

@ -38,6 +38,8 @@ jobs:
- 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-frontend
@ -65,6 +67,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
- run: make lint-swagger
@ -134,6 +138,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
- run: make lint-frontend
- run: make checks-frontend
@ -181,6 +187,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend
- run: make lint-md
- run: make docs

View File

@ -24,6 +24,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend frontend deps-backend
- run: npx playwright install --with-deps
- run: make test-e2e-sqlite

View File

@ -25,6 +25,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@ -24,6 +24,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@ -26,6 +26,8 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: package-lock.json
- run: make deps-frontend deps-backend
# xgo build
- run: make release

View File

@ -1,13 +1,14 @@
linters:
enable-all: false
disable-all: true
fast: false
enable:
- bidichk
# - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- depguard
- dupl
- errcheck
- forbidigo
- gocritic
# - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt
- gofumpt
- gosimple
@ -17,20 +18,18 @@ linters:
- nolintlint
- revive
- staticcheck
# - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- stylecheck
- typecheck
- unconvert
- unused
# - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- wastedassign
enable-all: false
disable-all: true
fast: false
run:
timeout: 10m
output:
sort-results: true
linters-settings:
stylecheck:
checks: ["all", "-ST1005", "-ST1003"]
@ -47,27 +46,37 @@ linters-settings:
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: error-naming
- name: errorf
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- 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: indent-error-flow
- name: errorf
- name: duplicated-imports
- name: modifies-value-receiver
- name: unreachable-code
- name: var-declaration
- name: var-naming
gofumpt:
extra-rules: true
depguard:
@ -93,8 +102,8 @@ issues:
max-issues-per-linter: 0
max-same-issues: 0
exclude-dirs: [node_modules, public, web_src]
exclude-case-sensitive: true
exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go
linters:
- gocyclo
@ -112,19 +121,19 @@ issues:
- path: cmd
linters:
- forbidigo
- linters:
- text: "webhook"
linters:
- dupl
text: "webhook"
- linters:
- text: "`ID' should not be capitalized"
linters:
- gocritic
text: "`ID' should not be capitalized"
- linters:
- text: "swagger"
linters:
- unused
- deadcode
text: "swagger"
- linters:
- text: "argument x is overwritten before first use"
linters:
- staticcheck
text: "argument x is overwritten before first use"
- text: "commentFormatting: put a space between `//` and comment text"
linters:
- gocritic

5223
CHANGELOG-archived.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -143,9 +143,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
GO_DIRS := build cmd models modules routers services tests
WEB_DIRS := web_src/js web_src/css
ESLINT_FILES := web_src/js tools *.config.js tests/e2e
ESLINT_FILES := web_src/js tools *.js tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
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
GO_SOURCES := $(wildcard *.go)

File diff suppressed because one or more lines are too long

View File

@ -4,6 +4,7 @@
package cmd
import (
"errors"
"fmt"
"os"
"text/tabwriter"
@ -91,7 +92,7 @@ func runListAuth(c *cli.Context) error {
func runDeleteAuth(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("--id flag is missing")
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()

View File

@ -4,6 +4,7 @@
package cmd
import (
"errors"
"fmt"
"net/url"
@ -193,7 +194,7 @@ func runAddOauth(c *cli.Context) error {
func runUpdateOauth(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("--id flag is missing")
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()

View File

@ -5,7 +5,6 @@ package cmd
import (
"errors"
"fmt"
"strings"
auth_model "code.gitea.io/gitea/models/auth"
@ -166,7 +165,7 @@ func runAddSMTP(c *cli.Context) error {
func runUpdateSMTP(c *cli.Context) error {
if !c.IsSet("id") {
return fmt.Errorf("--id flag is missing")
return errors.New("--id flag is missing")
}
ctx, cancel := installSignals()

View File

@ -4,6 +4,7 @@
package cmd
import (
"errors"
"fmt"
"strings"
@ -42,7 +43,7 @@ var microcmdUserDelete = &cli.Command{
func runDeleteUser(c *cli.Context) error {
if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
return fmt.Errorf("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")
}
ctx, cancel := installSignals()

View File

@ -4,6 +4,7 @@
package cmd
import (
"errors"
"fmt"
auth_model "code.gitea.io/gitea/models/auth"
@ -42,7 +43,7 @@ var microcmdUserGenerateAccessToken = &cli.Command{
func runGenerateAccessToken(c *cli.Context) error {
if !c.IsSet("username") {
return fmt.Errorf("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()
@ -68,7 +69,7 @@ func runGenerateAccessToken(c *cli.Context) error {
return err
}
if exist {
return fmt.Errorf("access token name has been used already")
return errors.New("access token name has been used already")
}
// make sure the scopes are valid

View File

@ -87,6 +87,10 @@ var CmdDump = &cli.Command{
Name: "skip-index",
Usage: "Skip bleve index data",
},
&cli.BoolFlag{
Name: "skip-db",
Usage: "Skip database",
},
&cli.StringFlag{
Name: "type",
Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
@ -185,35 +189,41 @@ func runDump(ctx *cli.Context) error {
}
}
tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
if err != nil {
fatal("Failed to create tmp file: %v", err)
}
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
}
}()
targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
if ctx.Bool("skip-db") {
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
dumper.GlobalExcludeAbsPath(setting.Database.Path)
log.Info("Skipping database")
} else {
log.Info("Dumping database...")
}
tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
fatal("Failed to dump database: %v", err)
}
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
if err != nil {
fatal("Failed to create tmp file: %v", err)
}
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
}
}()
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
fatal("Failed to include gitea-db.sql: %v", err)
targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
log.Info("Dumping database...")
}
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
fatal("Failed to dump database: %v", err)
}
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
fatal("Failed to include gitea-db.sql: %v", err)
}
}
log.Info("Adding custom configuration file from %s", setting.CustomConf)

View File

@ -157,9 +157,9 @@ func runViewDo(c *cli.Context) error {
}
if len(matchedAssetFiles) == 0 {
return fmt.Errorf("no files matched the given pattern")
return errors.New("no files matched the given pattern")
} else if len(matchedAssetFiles) > 1 {
return fmt.Errorf("too many files matched the given pattern, try to be more specific")
return errors.New("too many files matched the given pattern, try to be more specific")
}
data, err := matchedAssetFiles[0].fs.ReadFile(matchedAssetFiles[0].name)
@ -180,7 +180,7 @@ func runExtractDo(c *cli.Context) error {
}
if c.NArg() == 0 {
return fmt.Errorf("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
return errors.New("a list of pattern of files to extract is mandatory (e.g. '**' for all)")
}
destdir := "."

View File

@ -465,7 +465,7 @@ func hookPrintResult(output, isCreate bool, branch, url string) {
fmt.Fprintf(os.Stderr, " %s\n", url)
}
fmt.Fprintln(os.Stderr, "")
os.Stderr.Sync()
_ = os.Stderr.Sync()
}
func pushOptions() map[string]string {

View File

@ -4,6 +4,7 @@
package cmd
import (
"errors"
"fmt"
"os"
@ -249,7 +250,7 @@ func runAddFileLogger(c *cli.Context) error {
if c.IsSet("filename") {
vals["filename"] = c.String("filename")
} else {
return fmt.Errorf("filename must be set when creating a file logger")
return errors.New("filename must be set when creating a file logger")
}
if c.IsSet("rotate") {
vals["rotate"] = c.Bool("rotate")

View File

@ -1231,7 +1231,8 @@ LEVEL = Info
;DEFAULT_THEME = gitea-auto
;;
;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
;THEMES = gitea-auto,gitea-light,gitea-dark
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
;THEMES =
;;
;; All available reactions users can choose on issues/prs and comments.
;; Values can be emoji alias (:smile:) or a unicode emoji.
@ -1557,8 +1558,8 @@ LEVEL = Info
;; email = use the username part of the email attribute
;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
;; - diacritics are removed
;; - the characters in the set `['´\x60]` are removed
;; - the characters in the set `[\s~+]` are replaced with `-`
;; - the characters in the set ['´`] are removed
;; - the characters in the set [\s~+] are replaced with "-"
;USERNAME = nickname
;;
;; Update avatar if available from oauth2 provider.

View File

@ -214,10 +214,9 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
- `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: Set the default theme for the Gitea installation.
- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css".
- `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes.
regardless of the value of `DEFAULT_THEME`.
- `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes.
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
- `REACTIONS`: All available reactions users can choose on issues/prs and comments
@ -613,7 +612,7 @@ And the following unique queues:
- `email` - use the username part of the email attribute
- Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
- diacritics are removed
- the characters in the set `['´\x60]` are removed
- the characters in the set ```['´`]``` are removed
- the characters in the set `[\s~+]` are replaced with `-`
- `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login.
- `ACCOUNT_LINKING`: **login**: How to handle if an account / email already exists:

View File

@ -212,10 +212,9 @@ menu:
- `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
- `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
- `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: 在Gitea安装时候设置的默认主题。
- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供
- `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: 所有可用的主题。允许用户选择个性化的主题,
而不受DEFAULT_THEME 值的影响。
- `THEMES`: **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题,
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小默认为8MiB
- `REACTIONS`: 用户可以在问题Issue、Pull RequestPR以及评论中选择的所有可选的反应。
这些值可以是表情符号别名(例如::smile:或Unicode表情符号。

View File

@ -381,7 +381,7 @@ To make a custom theme available to all users:
1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`.
The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath".
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`, or leave `THEMES` empty to allow all themes.
Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes).

View File

@ -178,17 +178,6 @@ At some point, a customer or third party needs access to a specific repo and onl
Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns
## How to add/use custom themes
Gitea supports three official themes right now, `gitea-light`, `gitea-dark`, and `gitea-auto` (automatically switches between the previous two depending on operating system settings).
To add your own theme, currently the only way is to provide a complete theme (not just color overrides)
As an example, let's say our theme is `arc-blue` (this is a real theme, and can be found [in this issue](https://github.com/go-gitea/gitea/issues/6011))
Name the `.css` file `theme-arc-blue.css` and add it to your custom folder in `custom/public/assets/css`
Allow users to use it by adding `arc-blue` to the list of `THEMES` in your `app.ini`
## SSHD vs built-in SSH
SSHD is the built-in SSH server on most Unix systems.

View File

@ -182,17 +182,6 @@ Gitea不提供内置的Pages服务器。您需要一个专用的域名来提供
使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。
## 如何添加/使用自定义主题
Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark` 和 `gitea-auto`(根据操作系统设置自动切换前两个主题)。
要添加自己的主题,目前唯一的方法是提供一个完整的主题(不仅仅是颜色覆盖)。
假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)
将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中
通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题
## SSHD vs 内建SSH
SSHD是大多数Unix系统上内建的SSH服务器。

3
go.mod
View File

@ -16,7 +16,6 @@ require (
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.9.1
github.com/alecthomas/chroma/v2 v2.13.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@ -67,7 +66,7 @@ require (
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.17.7
github.com/klauspost/compress v1.17.8
github.com/klauspost/cpuid/v2 v2.2.7
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.79.0

6
go.sum
View File

@ -70,8 +70,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
@ -500,8 +498,8 @@ github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=

View File

@ -74,6 +74,13 @@ func (run *ActionRun) Link() string {
return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index)
}
func (run *ActionRun) WorkflowLink() string {
if run.Repo == nil {
return ""
}
return fmt.Sprintf("%s/actions/?workflow=%s", run.Repo.Link(), run.WorkflowID)
}
// RefLink return the url of run's ref
func (run *ActionRun) RefLink() string {
refName := git.RefName(run.Ref)
@ -98,13 +105,10 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
return nil
}
if run.Repo == nil {
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil {
return err
}
run.Repo = repo
if err := run.LoadRepo(ctx); err != nil {
return err
}
if err := run.Repo.LoadAttributes(ctx); err != nil {
return err
}
@ -120,6 +124,19 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
return nil
}
func (run *ActionRun) LoadRepo(ctx context.Context) error {
if run == nil || run.Repo != nil {
return nil
}
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil {
return err
}
run.Repo = repo
return nil
}
func (run *ActionRun) Duration() time.Duration {
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
}
@ -146,6 +163,10 @@ func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, err
return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
}
func (run *ActionRun) IsSchedule() bool {
return run.ScheduleID > 0
}
func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID).
SetExpr("num_action_runs",

View File

@ -270,7 +270,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
// Only affect action runners were a owner ID is set, as actions runners
// could also be created on a repository.
return db.GetEngine(ctx).Table("action_runner").
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
Where("`action_runner`.owner_id != ?", 0).
And(builder.IsNull{"`user`.id"}).
Count(new(ActionRunner))
@ -279,7 +279,7 @@ func CountRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
subQuery := builder.Select("`action_runner`.id").
From("`action_runner`").
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
Where(builder.Neq{"`action_runner`.owner_id": 0}).
And(builder.IsNull{"`user`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
@ -289,3 +289,25 @@ func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
}
return res.RowsAffected()
}
func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
return db.GetEngine(ctx).Table("action_runner").
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
Where("`action_runner`.repo_id != ?", 0).
And(builder.IsNull{"`repository`.id"}).
Count(new(ActionRunner))
}
func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
subQuery := builder.Select("`action_runner`.id").
From("`action_runner`").
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
Where(builder.Neq{"`action_runner`.repo_id": 0}).
And(builder.IsNull{"`repository`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
res, err := db.GetEngine(ctx).Exec(b)
if err != nil {
return 0, err
}
return res.RowsAffected()
}

View File

@ -92,6 +92,11 @@ func DeleteVariable(ctx context.Context, id int64) error {
func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
variables := map[string]string{}
if err := run.LoadRepo(ctx); err != nil {
log.Error("LoadRepo: %v", err)
return nil, err
}
// Global
globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
if err != nil {

View File

@ -110,7 +110,6 @@ func ParseCommitWithSignature(ctx context.Context, c *git.Commit) *CommitVerific
Reason: "gpg.error.no_committer_account",
}
}
}
}

View File

@ -8,6 +8,7 @@ import (
"crypto/sha256"
"encoding/base32"
"encoding/base64"
"errors"
"fmt"
"net"
"net/url"
@ -294,7 +295,7 @@ func UpdateOAuth2Application(ctx context.Context, opts UpdateOAuth2ApplicationOp
return nil, err
}
if app.UID != opts.UserID {
return nil, fmt.Errorf("UID mismatch")
return nil, errors.New("UID mismatch")
}
builtinApps := BuiltinApplications()
if _, builtin := builtinApps[app.ClientID]; builtin {

View File

@ -13,8 +13,6 @@ import (
"github.com/stretchr/testify/assert"
)
//////////////////// Application
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})

View File

@ -227,7 +227,6 @@ func NamesToBean(names ...string) ([]any, error) {
// Need to map provided names to beans...
beanMap := make(map[string]any)
for _, bean := range tables {
beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
beanMap[strings.ToLower(x.TableName(bean))] = bean
beanMap[strings.ToLower(x.TableName(bean, true))] = bean

View File

@ -5,7 +5,7 @@ package git
import (
"context"
"fmt"
"errors"
"strings"
"time"
@ -148,7 +148,7 @@ func DeleteLFSLockByID(ctx context.Context, id int64, repo *repo_model.Repositor
}
if !force && u.ID != lock.OwnerID {
return nil, fmt.Errorf("user doesn't own lock and force flag is not set")
return nil, errors.New("user doesn't own lock and force flag is not set")
}
if _, err := db.GetEngine(dbCtx).ID(id).Delete(new(LFSLock)); err != nil {

View File

@ -345,11 +345,9 @@ func CreateReview(ctx context.Context, opts CreateReviewOptions) (*Review, error
return nil, err
}
}
} else if opts.ReviewerTeam != nil {
review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID
} else {
return nil, fmt.Errorf("provide either reviewer or reviewer team")
}

View File

@ -177,7 +177,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
return err
}
case setting.Database.Type.IsMySQL():
// MySQL will drop all the constraints on the old table
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
@ -228,7 +227,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
return err
}
sequenceMap[sequence] = sequenceData
}
// CASCADE causes postgres to drop all the constraints on the old table
@ -293,9 +291,7 @@ func RecreateTable(sess *xorm.Session, bean any) error {
return err
}
}
}
case setting.Database.Type.IsMSSQL():
// MSSQL will drop all the constraints on the old table
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
@ -308,7 +304,6 @@ func RecreateTable(sess *xorm.Session, bean any) error {
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
return err
}
default:
log.Fatal("Unrecognized DB")
}

View File

@ -584,6 +584,8 @@ var migrations = []Migration{
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
// v297 -> v298
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
// v298 -> v299
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
}
// GetCurrentDBVersion returns the current db version

View File

@ -262,7 +262,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
for _, u := range units {
var found bool
for _, team := range teams {
var teamU []*TeamUnit
var unitEnabled bool
err = sess.Where("team_id = ?", team.ID).Find(&teamU)
@ -331,7 +330,6 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error {
}
if !protectedBranch.EnableApprovalsWhitelist {
perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
if err != nil {
return false, err

View File

@ -43,11 +43,6 @@ func RemigrateU2FCredentials(x *xorm.Engine) error {
if err != nil {
return err
}
case schemas.ORACLE:
_, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)")
if err != nil {
return err
}
case schemas.MSSQL:
// This column has an index on it. I could write all of the code to attempt to change the index OR
// I could just use recreate table.

View File

@ -9,9 +9,9 @@ import (
// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
type OAuth2Application struct {
type oauth2Application struct {
ID int64
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
}
return x.Sync(new(OAuth2Application))
return x.Sync(new(oauth2Application))
}

View File

@ -13,12 +13,12 @@ import (
func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
// premigration
type OAuth2Application struct {
type oauth2Application struct {
ID int64
}
// Prepare and load the testing database
x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application))
x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application))
defer deferable()
if x == nil || t.Failed() {
return
@ -36,7 +36,7 @@ func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
}
got := []ExpectedOAuth2Application{}
if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
return
}

View File

@ -104,7 +104,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
// Convert to new metadata format
new := &MetadataNew{
newMetadata := &MetadataNew{
Type: old.Type,
IsTagged: old.IsTagged,
Platform: old.Platform,
@ -119,7 +119,7 @@ func ChangeContainerMetadataMultiArch(x *xorm.Engine) error {
Manifests: manifests,
}
metadataJSON, err := json.Marshal(new)
metadataJSON, err := json.Marshal(newMetadata)
if err != nil {
return err
}

View File

@ -53,7 +53,7 @@ func expandHashReferencesToSha256(x *xorm.Engine) error {
if setting.Database.Type.IsMySQL() {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
} else if setting.Database.Type.IsMSSQL() {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1]))
_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] NVARCHAR(64)", alts[0], alts[1]))
} else {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1]))
}

View File

@ -0,0 +1,10 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import "xorm.io/xorm"
func DropWronglyCreatedTable(x *xorm.Engine) error {
return x.DropTables("o_auth2_application")
}

View File

@ -61,7 +61,6 @@ func AddScratchHash(x *xorm.Engine) error {
if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil {
return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err)
}
}
}

View File

@ -81,7 +81,6 @@ func HashAppToken(x *xorm.Engine) error {
if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
}
}
}

View File

@ -118,7 +118,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error
// Remove watches from all users and now unaccessible repos
for _, user := range t.Members {
has, err := access_model.HasAccess(ctx, user.ID, repo)
has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo)
if err != nil {
return err
} else if has {
@ -544,7 +544,7 @@ func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito
}
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
if has, err := access_model.HasAccess(ctx, user.ID, repo); err != nil || has {
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
return err
}
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {

View File

@ -226,9 +226,8 @@ func GetTeamIDsByNames(ctx context.Context, orgID int64, names []string, ignoreN
if err != nil {
if ignoreNonExistent {
continue
} else {
return nil, err
}
return nil, err
}
ids = append(ids, u.ID)
}

View File

@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) {
// SearchVersions gets all versions of packages matching the search options
func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
sess := db.GetEngine(ctx).
Where(opts.ToConds()).
Select("package_version.*").
Table("package_version").
Join("INNER", "package", "package.id = package_version.package_id")
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.ToConds())
opts.configureOrderBy(sess)
@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package
// SearchLatestVersions gets the latest version of every package matching the search options
func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) {
cond := opts.ToConds().
And(builder.Expr("pv2.id IS NULL"))
joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))")
if opts.IsInternal.Has() {
joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()})
}
in := builder.
Select("MAX(package_version.id)").
From("package_version").
InnerJoin("package", "package.id = package_version.package_id").
Where(opts.ToConds()).
GroupBy("package_version.package_id")
sess := db.GetEngine(ctx).
Select("package_version.*").
Table("package_version").
Join("LEFT", "package_version pv2", joinCond).
Join("INNER", "package", "package.id = package_version.package_id").
Where(cond)
Where(builder.In("package_version.id", in))
opts.configureOrderBy(sess)

View File

@ -79,17 +79,17 @@ func TestHasAccess(t *testing.T) {
repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, repo2.IsPrivate)
has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
has, err := access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo1)
assert.NoError(t, err)
assert.True(t, has)
_, err = access_model.HasAccess(db.DefaultContext, user1.ID, repo2)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user1.ID, repo2)
assert.NoError(t, err)
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo1)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo1)
assert.NoError(t, err)
_, err = access_model.HasAccess(db.DefaultContext, user2.ID, repo2)
_, err = access_model.HasAnyUnitAccess(db.DefaultContext, user2.ID, repo2)
assert.NoError(t, err)
}

View File

@ -24,6 +24,8 @@ type Permission struct {
units []*repo_model.RepoUnit
unitsMode map[unit.Type]perm_model.AccessMode
everyoneAccessMode map[unit.Type]perm_model.AccessMode
}
// IsOwner returns true if current user is the owner of repository.
@ -36,9 +38,24 @@ func (p *Permission) IsAdmin() bool {
return p.AccessMode >= perm_model.AccessModeAdmin
}
// HasAccess returns true if the current user might have at least read access to any unit of this repository
func (p *Permission) HasAccess() bool {
return len(p.unitsMode) > 0 || p.AccessMode >= perm_model.AccessModeRead
// HasAnyUnitAccess returns true if the user might have at least one access mode to any unit of this repository.
// It doesn't count the "everyone access mode".
func (p *Permission) HasAnyUnitAccess() bool {
for _, v := range p.unitsMode {
if v >= perm_model.AccessModeRead {
return true
}
}
return p.AccessMode >= perm_model.AccessModeRead
}
func (p *Permission) HasAnyUnitAccessOrEveryoneAccess() bool {
for _, v := range p.everyoneAccessMode {
if v >= perm_model.AccessModeRead {
return true
}
}
return p.HasAnyUnitAccess()
}
// HasUnits returns true if the permission contains attached units
@ -56,16 +73,16 @@ func (p *Permission) GetFirstUnitRepoID() int64 {
}
// UnitAccessMode returns current user access mode to the specify unit of the repository
// It also considers "everyone access mode"
func (p *Permission) UnitAccessMode(unitType unit.Type) perm_model.AccessMode {
if p.unitsMode != nil {
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map contains the access mode, use it, but admin/owner mode could override it
if m, ok := p.unitsMode[unitType]; ok {
return util.Iif(p.AccessMode >= perm_model.AccessModeAdmin, p.AccessMode, m)
}
// if the units map does not contain the access mode, return the default access mode if the unit exists
unitDefaultAccessMode := max(p.AccessMode, p.everyoneAccessMode[unitType])
hasUnit := slices.ContainsFunc(p.units, func(u *repo_model.RepoUnit) bool { return u.Type == unitType })
return util.Iif(hasUnit, p.AccessMode, perm_model.AccessModeNone)
return util.Iif(hasUnit, unitDefaultAccessMode, perm_model.AccessModeNone)
}
func (p *Permission) SetUnitsWithDefaultAccessMode(units []*repo_model.RepoUnit, mode perm_model.AccessMode) {
@ -159,14 +176,15 @@ func (p *Permission) LogString() string {
}
func applyEveryoneRepoPermission(user *user_model.User, perm *Permission) {
if user != nil && user.ID > 0 {
for _, u := range perm.units {
if perm.unitsMode == nil {
perm.unitsMode = make(map[unit.Type]perm_model.AccessMode)
}
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.unitsMode[u.Type] {
perm.unitsMode[u.Type] = u.EveryoneAccessMode
if user == nil || user.ID <= 0 {
return
}
for _, u := range perm.units {
if u.EveryoneAccessMode >= perm_model.AccessModeRead && u.EveryoneAccessMode > perm.everyoneAccessMode[u.Type] {
if perm.everyoneAccessMode == nil {
perm.everyoneAccessMode = make(map[unit.Type]perm_model.AccessMode)
}
perm.everyoneAccessMode[u.Type] = u.EveryoneAccessMode
}
}
}
@ -373,8 +391,8 @@ func CanBeAssigned(ctx context.Context, user *user_model.User, repo *repo_model.
perm.CanAccessAny(perm_model.AccessModeRead, unit.TypePullRequests), nil
}
// HasAccess returns true if user has access to repo
func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
// HasAnyUnitAccess see the comment of "perm.HasAnyUnitAccess"
func HasAnyUnitAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (bool, error) {
var user *user_model.User
var err error
if userID > 0 {
@ -387,7 +405,7 @@ func HasAccess(ctx context.Context, userID int64, repo *repo_model.Repository) (
if err != nil {
return false, err
}
return perm.HasAccess(), nil
return perm.HasAnyUnitAccess(), nil
}
// getUsersWithAccessMode returns users that have at least given access mode to the repository.

View File

@ -14,16 +14,54 @@ import (
"github.com/stretchr/testify/assert"
)
func TestHasAnyUnitAccess(t *testing.T) {
perm := Permission{}
assert.False(t, perm.HasAnyUnitAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.False(t, perm.HasAnyUnitAccess())
assert.False(t, perm.HasAnyUnitAccessOrEveryoneAccess())
perm = Permission{
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
everyoneAccessMode: map[unit.Type]perm_model.AccessMode{unit.TypeIssues: perm_model.AccessModeRead},
}
assert.False(t, perm.HasAnyUnitAccess())
assert.True(t, perm.HasAnyUnitAccessOrEveryoneAccess())
perm = Permission{
AccessMode: perm_model.AccessModeRead,
units: []*repo_model.RepoUnit{{Type: unit.TypeWiki}},
}
assert.True(t, perm.HasAnyUnitAccess())
perm = Permission{
unitsMode: map[unit.Type]perm_model.AccessMode{unit.TypeWiki: perm_model.AccessModeRead},
}
assert.True(t, perm.HasAnyUnitAccess())
}
func TestApplyEveryoneRepoPermission(t *testing.T) {
perm := Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeNone},
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
applyEveryoneRepoPermission(nil, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
{Type: unit.TypeWiki, EveryoneAccessMode: perm_model.AccessModeRead},
},
}
applyEveryoneRepoPermission(&user_model.User{ID: 0}, &perm)
assert.False(t, perm.CanRead(unit.TypeWiki))
perm = Permission{
AccessMode: perm_model.AccessModeNone,
units: []*repo_model.RepoUnit{
@ -40,8 +78,8 @@ func TestApplyEveryoneRepoPermission(t *testing.T) {
},
}
applyEveryoneRepoPermission(&user_model.User{ID: 1}, &perm)
assert.True(t, perm.CanRead(unit.TypeWiki))
assert.False(t, perm.CanWrite(unit.TypeWiki)) // because there is no unit mode, so the everyone-mode is used as the unit's access mode
// it should work the same as "EveryoneAccessMode: none" because the default AccessMode should be applied to units
assert.True(t, perm.CanWrite(unit.TypeWiki))
perm = Permission{
units: []*repo_model.RepoUnit{

View File

@ -110,13 +110,11 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error {
var items []string
switch project.BoardType {
case BoardTypeBugTriage:
items = setting.Project.ProjectBoardBugTriageType
case BoardTypeBasicKanban:
items = setting.Project.ProjectBoardBasicKanbanType
case BoardTypeNone:
fallthrough
default:

View File

@ -170,7 +170,6 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
// the owner of a private repo needs to be explicitly added.
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
}
} else {
// This is a "public" repository:
// Any user that has read access, is a watcher or organization member can be requested to review

View File

@ -5,6 +5,7 @@ package models
import (
"context"
"errors"
"fmt"
"code.gitea.io/gitea/models/db"
@ -147,7 +148,7 @@ func DeleteRepositoryTransfer(ctx context.Context, repoID int64) error {
func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error {
switch status {
case repo_model.RepositoryBeingMigrated:
return fmt.Errorf("repo is not ready, currently migrating")
return errors.New("repo is not ready, currently migrating")
case repo_model.RepositoryPendingTransfer:
return ErrRepoTransferInProgress{}
}

View File

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/setting/config"
@ -106,6 +107,7 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) {
fatalTestError("Error creating test engine: %v\n", err)
}
setting.IsInTesting = true
setting.AppURL = "https://try.gitea.io/"
setting.RunUser = "runuser"
setting.SSH.User = "sshuser"
@ -148,6 +150,9 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) {
config.SetDynGetter(system.NewDatabaseDynKeyGetter())
if err = cache.Init(); err != nil {
fatalTestError("cache.Init: %v\n", err)
}
if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err)
}

View File

@ -501,19 +501,19 @@ func GetUserSalt() (string, error) {
// Note: The set of characters here can safely expand without a breaking change,
// but characters removed from this set can cause user account linking to break
var (
customCharsReplacement = strings.NewReplacer("Æ", "AE")
removeCharsRE = regexp.MustCompile(`['´\x60]`)
removeDiacriticsTransform = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
customCharsReplacement = strings.NewReplacer("Æ", "AE")
removeCharsRE = regexp.MustCompile("['`´]")
transformDiacritics = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
)
// normalizeUserName returns a string with single-quotes and diacritics
// removed, and any other non-supported username characters replaced with
// a `-` character
// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
func NormalizeUserName(s string) (string, error) {
strDiacriticsRemoved, n, err := transform.String(removeDiacriticsTransform, customCharsReplacement.Replace(s))
s, _, _ = strings.Cut(s, "@")
strDiacriticsRemoved, n, err := transform.String(transformDiacritics, customCharsReplacement.Replace(s))
if err != nil {
return "", fmt.Errorf("Failed to normalize character `%v` in provided username `%v`", s[n], s)
return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
}
return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
}
@ -988,9 +988,8 @@ func GetUserIDsByNames(ctx context.Context, names []string, ignoreNonExistent bo
if err != nil {
if ignoreNonExistent {
continue
} else {
return nil, err
}
return nil, err
}
ids = append(ids, u.ID)
}

View File

@ -506,15 +506,16 @@ func Test_NormalizeUserFromEmail(t *testing.T) {
Expected string
IsNormalizedValid bool
}{
{"test", "test", true},
{"name@example.com", "name", true},
{"test'`´name", "testname", true},
{"Sinéad.O'Connor", "Sinead.OConnor", true},
{"Æsir", "AEsir", true},
// \u00e9\u0065\u0301
{"éé", "ee", true},
{"éé", "ee", true}, // \u00e9\u0065\u0301
{"Awareness Hub", "Awareness-Hub", true},
{"double__underscore", "double__underscore", false}, // We should consider squashing double non-alpha characters
{".bad.", ".bad.", false},
{"new😀user", "new😀user", false}, // No plans to support
{`"quoted"`, `"quoted"`, false}, // No plans to support
}
for _, testCase := range testCases {
normalizedName, err := user_model.NormalizeUserName(testCase.Input)

View File

@ -63,16 +63,16 @@ func NewComplexity() {
func setupComplexity(values []string) {
if len(values) != 1 || values[0] != "off" {
for _, val := range values {
if complex, ok := charComplexities[val]; ok {
validChars += complex.ValidChars
requiredList = append(requiredList, complex)
if complexity, ok := charComplexities[val]; ok {
validChars += complexity.ValidChars
requiredList = append(requiredList, complexity)
}
}
if len(requiredList) == 0 {
// No valid character classes found; use all classes as default
for _, complex := range charComplexities {
validChars += complex.ValidChars
requiredList = append(requiredList, complex)
for _, complexity := range charComplexities {
validChars += complexity.ValidChars
requiredList = append(requiredList, complexity)
}
}
}

View File

@ -307,10 +307,10 @@ func ParseTreeLine(objectFormat ObjectFormat, rd *bufio.Reader, modeBuf, fnameBu
// Deal with the binary hash
idx = 0
len := objectFormat.FullLength() / 2
for idx < len {
length := objectFormat.FullLength() / 2
for idx < length {
var read int
read, err = rd.Read(shaBuf[idx:len])
read, err = rd.Read(shaBuf[idx:length])
n += read
if err != nil {
return mode, fname, sha, n, err

View File

@ -468,7 +468,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) {
_, _ = rd.Discard(1)
}
for {
modifier, err := rd.ReadSlice('\x00')
modifier, err := rd.ReadString('\x00')
if err != nil {
if err != io.EOF {
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)

View File

@ -49,9 +49,8 @@ readLoop:
if len(line) > 0 && line[0] == ' ' {
_, _ = signatureSB.Write(line[1:])
continue
} else {
pgpsig = false
}
pgpsig = false
}
if !message {

View File

@ -33,7 +33,6 @@ type ObjectFormat interface {
ComputeHash(t ObjectType, content []byte) ObjectID
}
/* SHA1 Type */
type Sha1ObjectFormatImpl struct{}
var (
@ -70,14 +69,10 @@ func (h Sha1ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID
_, _ = hasher.Write([]byte(" "))
_, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10)))
_, _ = hasher.Write([]byte{0})
// HashSum generates a SHA1 for the provided hash
var sha1 Sha1Hash
copy(sha1[:], hasher.Sum(nil))
return &sha1
_, _ = hasher.Write(content)
return h.MustID(hasher.Sum(nil))
}
/* SHA256 Type */
type Sha256ObjectFormatImpl struct{}
var (
@ -116,11 +111,8 @@ func (h Sha256ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) Object
_, _ = hasher.Write([]byte(" "))
_, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10)))
_, _ = hasher.Write([]byte{0})
// HashSum generates a SHA256 for the provided hash
var sha256 Sha1Hash
copy(sha256[:], hasher.Sum(nil))
return &sha256
_, _ = hasher.Write(content)
return h.MustID(hasher.Sum(nil))
}
var (

View File

@ -16,7 +16,6 @@ type ObjectID interface {
Type() ObjectFormat
}
/* SHA1 */
type Sha1Hash [20]byte
func (h *Sha1Hash) String() string {
@ -40,7 +39,6 @@ func MustIDFromString(hexHash string) ObjectID {
return id
}
/* SHA256 */
type Sha256Hash [32]byte
func (h *Sha256Hash) String() string {
@ -54,7 +52,6 @@ func (h *Sha256Hash) IsZero() bool {
func (h *Sha256Hash) RawValue() []byte { return h[:] }
func (*Sha256Hash) Type() ObjectFormat { return Sha256ObjectFormat }
/* utility */
func NewIDFromString(hexHash string) (ObjectID, error) {
var theObjectFormat ObjectFormat
for _, objectFormat := range SupportedObjectFormats {

View File

@ -18,4 +18,8 @@ func TestIsValidSHAPattern(t *testing.T) {
assert.False(t, h.IsValid("abc"))
assert.False(t, h.IsValid("123g"))
assert.False(t, h.IsValid("some random text"))
assert.Equal(t, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", ComputeBlobHash(Sha1ObjectFormat, nil).String())
assert.Equal(t, "2e65efe2a145dda7ee51d1741299f848e5bf752e", ComputeBlobHash(Sha1ObjectFormat, []byte("a")).String())
assert.Equal(t, "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813", ComputeBlobHash(Sha256ObjectFormat, nil).String())
assert.Equal(t, "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ComputeBlobHash(Sha256ObjectFormat, []byte("a")).String())
}

View File

@ -0,0 +1,32 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package pipeline
import (
"fmt"
"time"
"code.gitea.io/gitea/modules/git"
)
// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentHashes []git.ObjectID
BranchName string
FullCommitName string
}
type lfsResultSlice []*LFSResult
func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
func lfsError(msg string, err error) error {
return fmt.Errorf("LFS error occurred, %s: err: %w", msg, err)
}

View File

@ -7,12 +7,10 @@ package pipeline
import (
"bufio"
"fmt"
"io"
"sort"
"strings"
"sync"
"time"
"code.gitea.io/gitea/modules/git"
@ -21,23 +19,6 @@ import (
"github.com/go-git/go-git/v5/plumbing/object"
)
// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentHashes []git.ObjectID
BranchName string
FullCommitName string
}
type lfsResultSlice []*LFSResult
func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
// FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{}
@ -51,7 +32,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
All: true,
})
if err != nil {
return nil, fmt.Errorf("Failed to get GoGit CommitsIter. Error: %w", err)
return nil, lfsError("failed to get GoGit CommitsIter", err)
}
err = commitsIter.ForEach(func(gitCommit *object.Commit) error {
@ -85,7 +66,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
return nil
})
if err != nil && err != io.EOF {
return nil, fmt.Errorf("Failure in CommitIter.ForEach: %w", err)
return nil, lfsError("failure in CommitIter.ForEach", err)
}
for _, result := range resultsMap {
@ -156,7 +137,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
select {
case err, has := <-errChan:
if has {
return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
return nil, lfsError("unable to obtain name for LFS files", err)
}
default:
}

View File

@ -8,33 +8,14 @@ package pipeline
import (
"bufio"
"bytes"
"fmt"
"io"
"sort"
"strings"
"sync"
"time"
"code.gitea.io/gitea/modules/git"
)
// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentIDs []git.ObjectID
BranchName string
FullCommitName string
}
type lfsResultSlice []*LFSResult
func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }
// FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{}
@ -137,11 +118,11 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
n += int64(count)
if bytes.Equal(binObjectID, objectID.RawValue()) {
result := LFSResult{
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentIDs: curCommit.Parents,
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentHashes: curCommit.Parents,
}
resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
} else if string(mode) == git.EntryModeTree.String() {
@ -183,7 +164,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
for _, result := range resultsMap {
hasParent := false
for _, parentID := range result.ParentIDs {
for _, parentID := range result.ParentHashes {
if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
break
}
@ -232,7 +213,6 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
errChan <- err
break
}
}
}()
@ -241,7 +221,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err
select {
case err, has := <-errChan:
if has {
return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
return nil, lfsError("unable to obtain name for LFS files", err)
}
default:
}

View File

@ -251,18 +251,18 @@ func (repo *Repository) CommitsByFileAndRange(opts CommitsByFileAndRangeOptions)
return nil, err
}
len := objectFormat.FullLength()
length := objectFormat.FullLength()
commits := []*Commit{}
shaline := make([]byte, len+1)
shaline := make([]byte, length+1)
for {
n, err := io.ReadFull(stdoutReader, shaline)
if err != nil || n < len {
if err != nil || n < length {
if err == io.EOF {
err = nil
}
return commits, err
}
objectID, err := NewIDFromString(string(shaline[0:len]))
objectID, err := NewIDFromString(string(shaline[0:length]))
if err != nil {
return nil, err
}

View File

@ -64,7 +64,6 @@ func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
// ex: git@try.gitea.io:go-gitea/gitea
match := scpSyntax.FindAllStringSubmatch(refURI, -1)
if len(match) > 0 {
m := match[0]
refHostname := m[2]
pth := m[3]

View File

@ -191,7 +191,6 @@ func (b *Indexer) addDelete(filename string, repo *repo_model.Repository, batch
func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
if len(changes.Updates) > 0 {
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
@ -335,7 +334,6 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
if result, err = b.inner.Indexer.Search(facetRequest); err != nil {
return 0, nil, nil, err
}
}
languagesFacet := result.Facets["languages"]
for _, term := range languagesFacet.Terms.Terms() {

View File

@ -68,7 +68,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.Paginator = opts.Paginator
switch opts.SortType {
case "":
case "", "latest":
searchOpt.SortBy = SortByCreatedDesc
case "oldest":
searchOpt.SortBy = SortByCreatedAsc
@ -86,7 +86,7 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.SortBy = SortByDeadlineDesc
case "priority", "priorityrepo", "project-column-sorting":
// Unsupported sort type for search
searchOpt.SortBy = SortByUpdatedDesc
fallthrough
default:
searchOpt.SortBy = SortByUpdatedDesc
}

View File

@ -145,7 +145,6 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query := elastic.NewBoolQuery()
if options.Keyword != "" {
searchType := esMultiMatchTypePhrasePrefix
if options.IsFuzzyKeyword {
searchType = esMultiMatchTypeBestFields

View File

@ -125,7 +125,6 @@ func EventFormatTextMessage(mode *WriterMode, event *Event, msgFormat string, ms
if mode.Colorize {
buf = append(buf, resetBytes...)
}
}
if flags&(Lshortfile|Llongfile) != 0 {
if mode.Colorize {

View File

@ -466,7 +466,6 @@ func TestColorPreview(t *testing.T) {
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
}
negativeTests := []string{
@ -549,7 +548,6 @@ func TestMathBlock(t *testing.T) {
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)
}
}

View File

@ -147,35 +147,35 @@ func (e *MarshalEncoder) marshalIntInternal(i int64) error {
return e.w.WriteByte(byte(i - 5))
}
var len int
var length int
if 122 < i && i <= 0xff {
len = 1
length = 1
} else if 0xff < i && i <= 0xffff {
len = 2
length = 2
} else if 0xffff < i && i <= 0xffffff {
len = 3
length = 3
} else if 0xffffff < i && i <= 0x3fffffff {
len = 4
length = 4
} else if -0x100 <= i && i < -123 {
len = -1
length = -1
} else if -0x10000 <= i && i < -0x100 {
len = -2
length = -2
} else if -0x1000000 <= i && i < -0x100000 {
len = -3
length = -3
} else if -0x40000000 <= i && i < -0x1000000 {
len = -4
length = -4
} else {
return ErrInvalidIntRange
}
if err := e.w.WriteByte(byte(len)); err != nil {
if err := e.w.WriteByte(byte(length)); err != nil {
return err
}
if len < 0 {
len = -len
if length < 0 {
length = -length
}
for c := 0; c < len; c++ {
for c := 0; c < length; c++ {
if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil {
return err
}
@ -244,13 +244,13 @@ func (e *MarshalEncoder) marshalArray(arr reflect.Value) error {
return err
}
len := arr.Len()
length := arr.Len()
if err := e.marshalIntInternal(int64(len)); err != nil {
if err := e.marshalIntInternal(int64(length)); err != nil {
return err
}
for i := 0; i < len; i++ {
for i := 0; i < length; i++ {
if err := e.marshal(arr.Index(i).Interface()); err != nil {
return err
}

View File

@ -339,7 +339,6 @@ func (pm *Manager) ProcessStacktraces(flat, noSystem bool) ([]*Process, int, int
}
sort.Slice(processes, after(processes))
if !flat {
var sortChildren func(process *Process)
sortChildren = func(process *Process) {

View File

@ -8,7 +8,7 @@ import (
"time"
)
const (
var (
backoffBegin = 50 * time.Millisecond
backoffUpper = 2 * time.Second
)
@ -18,6 +18,14 @@ type (
backoffFuncErr func() (retry bool, err error)
)
func mockBackoffDuration(d time.Duration) func() {
oldBegin, oldUpper := backoffBegin, backoffUpper
backoffBegin, backoffUpper = d, d
return func() {
backoffBegin, backoffUpper = oldBegin, oldUpper
}
}
func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
d := begin
for {

View File

@ -63,6 +63,8 @@ func (q *WorkerPoolQueue[T]) doDispatchBatchToWorker(wg *workerGroup[T], flushCh
// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
// This data-race is not serious, as long as a new worker will be started soon to make sure there are enough workers,
// so no need to hugely refactor at the moment.
q.workerNumMu.Lock()
noWorker := q.workerNum == 0
if full || noWorker {
@ -136,6 +138,14 @@ func (q *WorkerPoolQueue[T]) basePushForShutdown(items ...T) bool {
return true
}
func resetIdleTicker(t *time.Ticker, dur time.Duration) {
t.Reset(dur)
select {
case <-t.C:
default:
}
}
// doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
wp.wg.Add(1)
@ -146,8 +156,6 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
log.Debug("Queue %q starts new worker", q.GetName())
defer log.Debug("Queue %q stops idle worker", q.GetName())
atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging
t := time.NewTicker(workerIdleDuration)
defer t.Stop()
@ -169,11 +177,7 @@ func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
}
q.doWorkerHandle(batch)
// reset the idle ticker, and drain the tick after reset in case a tick is already triggered
t.Reset(workerIdleDuration)
select {
case <-t.C:
default:
}
resetIdleTicker(t, workerIdleDuration) // key code for TestWorkerPoolQueueWorkerIdleReset
case <-t.C:
q.workerNumMu.Lock()
keepWorking = q.workerNum <= 1 // keep the last worker running

View File

@ -40,8 +40,6 @@ type WorkerPoolQueue[T any] struct {
workerMaxNum int
workerActiveNum int
workerNumMu sync.Mutex
workerStartedCounter int32
}
type flushType chan struct{}

View File

@ -5,8 +5,10 @@ package queue
import (
"context"
"slices"
"strconv"
"sync"
"sync/atomic"
"testing"
"time"
@ -250,22 +252,34 @@ func TestWorkerPoolQueueShutdown(t *testing.T) {
func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
defer mockBackoffDuration(5 * time.Millisecond)()
var q *WorkerPoolQueue[int]
var handledCount atomic.Int32
var hasOnlyOneWorkerRunning atomic.Bool
handler := func(items ...int) (unhandled []int) {
time.Sleep(50 * time.Millisecond)
handledCount.Add(int32(len(items)))
// make each work have different duration, and check the active worker number periodically
var activeNums []int
for i := 0; i < 5-items[0]%2; i++ {
time.Sleep(workerIdleDuration * 2)
activeNums = append(activeNums, q.GetWorkerActiveNumber())
}
// When the queue never becomes empty, the existing workers should keep working
// It is not 100% true at the moment because the data-race in workergroup.go is not resolved, see that TODO */
// If the "active worker numbers" is like [2 2 ... 1 1], it means that an existing worker exited and the no new worker is started.
if slices.Equal([]int{1, 1}, activeNums[len(activeNums)-2:]) {
hasOnlyOneWorkerRunning.Store(true)
}
return nil
}
q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
stop := runWorkerPoolQueue(q)
for i := 0; i < 20; i++ {
for i := 0; i < 100; i++ {
assert.NoError(t, q.Push(i))
}
time.Sleep(500 * time.Millisecond)
assert.EqualValues(t, 2, q.GetWorkerNumber())
assert.EqualValues(t, 2, q.GetWorkerActiveNumber())
// when the queue never becomes empty, the existing workers should keep working
assert.EqualValues(t, 2, q.workerStartedCounter)
assert.Greater(t, int(handledCount.Load()), 4) // make sure there are enough items handled during the test
assert.False(t, hasOnlyOneWorkerRunning.Load(), "a slow handler should not block other workers from starting")
stop()
}

View File

@ -32,7 +32,6 @@ func CreateTemporaryPath(prefix string) (string, error) {
if err != nil {
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)
}
return basePath, nil
}

26
modules/session/mock.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package session
import (
"net/http"
"gitea.com/go-chi/session"
)
type MockStore struct {
*session.MemStore
}
func (m *MockStore) Destroy(writer http.ResponseWriter, request *http.Request) error {
return nil
}
type mockStoreContextKeyStruct struct{}
var MockStoreContextKey = mockStoreContextKeyStruct{}
func NewMockStore(sid string) *MockStore {
return &MockStore{session.NewMemStore(sid)}
}

View File

@ -7,7 +7,6 @@ import (
"net/http"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
"gitea.com/go-chi/session"
)
@ -17,14 +16,33 @@ type Store interface {
Get(any) any
Set(any, any) error
Delete(any) error
ID() string
Release() error
Flush() error
Destroy(http.ResponseWriter, *http.Request) error
}
// RegenerateSession regenerates the underlying session and returns the new store
func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) {
// Ensure that a cookie with a trailing slash does not take precedence over
// the cookie written by the middleware.
middleware.DeleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
s, err := session.RegenerateSession(resp, req)
return s, err
for _, f := range BeforeRegenerateSession {
f(resp, req)
}
if setting.IsInTesting {
if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
return store, nil
}
}
return session.RegenerateSession(resp, req)
}
func GetContextSession(req *http.Request) Store {
if setting.IsInTesting {
if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
return store
}
}
return session.GetSession(req)
}
// BeforeRegenerateSession is a list of functions that are called before a session is regenerated.
var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request)

View File

@ -318,7 +318,7 @@ func mustMapSetting(rootCfg ConfigProvider, sectionName string, setting any) {
// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
var StartupProblems []string
func logStartupProblem(skip int, level log.Level, format string, args ...any) {
func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
msg := fmt.Sprintf(format, args...)
log.Log(skip+1, level, "%s", msg)
StartupProblems = append(StartupProblems, msg)
@ -326,14 +326,14 @@ func logStartupProblem(skip int, level log.Level, format string, args ...any) {
func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
}
}
// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) {
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
}
}

View File

@ -16,14 +16,10 @@ import (
type OAuth2UsernameType string
const (
// OAuth2UsernameUserid oauth2 userid field will be used as gitea name
OAuth2UsernameUserid OAuth2UsernameType = "userid"
// OAuth2UsernameNickname oauth2 nickname field will be used as gitea name
OAuth2UsernameNickname OAuth2UsernameType = "nickname"
// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
OAuth2UsernameEmail OAuth2UsernameType = "email"
// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
OAuth2UsernameUserid OAuth2UsernameType = "userid" // use user id (sub) field as gitea's username
OAuth2UsernameNickname OAuth2UsernameType = "nickname" // use nickname field
OAuth2UsernameEmail OAuth2UsernameType = "email" // use email field
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username" // use preferred_username field
)
func (username OAuth2UsernameType) isValid() bool {
@ -71,8 +67,8 @@ func loadOAuth2ClientFrom(rootCfg ConfigProvider) {
OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
OAuth2Client.Username = OAuth2UsernameType(sec.Key("USERNAME").MustString(string(OAuth2UsernameNickname)))
if !OAuth2Client.Username.isValid() {
log.Warn("Username setting is not valid: '%s', will fallback to '%s'", OAuth2Client.Username, OAuth2UsernameNickname)
OAuth2Client.Username = OAuth2UsernameNickname
log.Warn("[oauth2_client].USERNAME setting is invalid, falls back to %q", OAuth2Client.Username)
}
OAuth2Client.UpdateAvatar = sec.Key("UPDATE_AVATAR").MustBool()
OAuth2Client.AccountLinking = OAuth2AccountLinkingType(sec.Key("ACCOUNT_LINKING").MustString(string(OAuth2AccountLinkingLogin)))
@ -174,7 +170,7 @@ func GetGeneralTokenSigningSecret() []byte {
}
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
return jwtSecret
}
return *generalSigningSecret.Load()

View File

@ -235,7 +235,7 @@ var configuredPaths = make(map[string]string)
func checkOverlappedPath(name, path string) {
// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
if targetName, ok := configuredPaths[path]; ok && targetName != name {
logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
}
configuredPaths[path] = name
}

View File

@ -19,9 +19,8 @@ func loadTimeFrom(rootCfg ConfigProvider) {
DefaultUILocation, err = time.LoadLocation(zone)
if err != nil {
log.Fatal("Load time zone failed: %v", err)
} else {
log.Info("Default UI Location is %v", zone)
}
log.Info("Default UI Location is %v", zone)
}
if DefaultUILocation == nil {
DefaultUILocation = time.Local

View File

@ -82,7 +82,6 @@ var UI = struct {
ReactionMaxUserNum: 10,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea-auto`,
Themes: []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},

View File

@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
"code.gitea.io/gitea/services/webtheme"
)
// NewFuncMap returns functions for injecting to templates
@ -137,12 +138,7 @@ func NewFuncMap() template.FuncMap {
"DisableImportLocal": func() bool {
return !setting.ImportLocalPaths
},
"ThemeName": func(user *user_model.User) string {
if user == nil || user.Theme == "" {
return setting.UI.DefaultTheme
}
return user.Theme
},
"UserThemeName": UserThemeName,
"NotificationSettings": func() map[string]any {
return map[string]any{
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
@ -261,3 +257,13 @@ func Eval(tokens ...any) (any, error) {
n, err := eval.Expr(tokens...)
return n.Value, err
}
func UserThemeName(user *user_model.User) string {
if user == nil || user.Theme == "" {
return setting.UI.DefaultTheme
}
if webtheme.IsThemeAvailable(user.Theme) {
return user.Theme
}
return setting.UI.DefaultTheme
}

View File

@ -138,10 +138,9 @@ func wrapTmplErrMsg(msg string) {
if setting.IsProd {
// in prod mode, Gitea must have correct templates to run
log.Fatal("Gitea can't run with template errors: %s", msg)
} else {
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
}
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
}
type templateErrorPrettier struct {

View File

@ -84,9 +84,8 @@ func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
if firstRun {
log.Fatal("Failed to parse mail template, err: %v", err)
} else {
log.Error("Failed to parse mail template, err: %v", err)
}
log.Error("Failed to parse mail template, err: %v", err)
}
}
}

View File

@ -121,9 +121,9 @@ func Test_NormalizeEOL(t *testing.T) {
}
func Test_RandomInt(t *testing.T) {
int, err := CryptoRandomInt(255)
assert.True(t, int >= 0)
assert.True(t, int <= 255)
randInt, err := CryptoRandomInt(255)
assert.True(t, randInt >= 0)
assert.True(t, randInt <= 255)
assert.NoError(t, err)
}

View File

@ -128,6 +128,16 @@ func hasResponseBeenWritten(argsIn []reflect.Value) bool {
return false
}
func wrapHandlerProvider[T http.Handler](hp func(next http.Handler) T, funcInfo *routing.FuncInfo) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
routing.UpdateFuncInfo(req.Context(), funcInfo)
h.ServeHTTP(resp, req)
})
}
}
// toHandlerProvider converts a handler to a handler provider
// A handler provider is a function that takes a "next" http.Handler, it can be used as a middleware
func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
@ -138,13 +148,9 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
}
if hp, ok := handler.(func(next http.Handler) http.Handler); ok {
return func(next http.Handler) http.Handler {
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
routing.UpdateFuncInfo(req.Context(), funcInfo)
h.ServeHTTP(resp, req)
})
}
return wrapHandlerProvider(hp, funcInfo)
} else if hp, ok := handler.(func(http.Handler) http.HandlerFunc); ok {
return wrapHandlerProvider(hp, funcInfo)
}
provider := func(next http.Handler) http.Handler {

View File

@ -9,6 +9,7 @@ import (
"net/url"
"strings"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
)
@ -48,12 +49,12 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
// Previous versions would use a cookie path with a trailing /.
// These are more specific than cookies without a trailing /, so
// we need to delete these if they exist.
DeleteLegacySiteCookie(resp, name)
deleteLegacySiteCookie(resp, name)
}
// DeleteLegacySiteCookie deletes the cookie with the given name at the cookie
// deleteLegacySiteCookie deletes the cookie with the given name at the cookie
// path with a trailing /, which would unintentionally override the cookie.
func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
func deleteLegacySiteCookie(resp http.ResponseWriter, name string) {
if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") {
// If the cookie path ends with /, no legacy cookies will take
// precedence, so do nothing. The exception is that cookies with no
@ -74,3 +75,11 @@ func DeleteLegacySiteCookie(resp http.ResponseWriter, name string) {
}
resp.Header().Add("Set-Cookie", cookie.String())
}
func init() {
session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) {
// Ensure that a cookie with a trailing slash does not take precedence over
// the cookie written by the middleware.
deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName)
})
}

View File

@ -0,0 +1,10 @@
Copyright (C) 1985, 1990 Regents of the University of California.
Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies. The University of California
makes no representations about the suitability of this
software for any purpose. It is provided "as is" without
express or implied warranty. Export of this software outside
of the United States of America may require an export license.

32
options/license/NCL Normal file
View File

@ -0,0 +1,32 @@
Copyright (c) 2004 the University Corporation for Atmospheric
Research ("UCAR"). All rights reserved. Developed by NCAR's
Computational and Information Systems Laboratory, UCAR,
www.cisl.ucar.edu.
Redistribution and use of the Software in source and binary forms,
with or without modification, is permitted provided that the
following conditions are met:
- Neither the names of NCAR's Computational and Information Systems
Laboratory, the University Corporation for Atmospheric Research,
nor the names of its sponsors or contributors may be used to
endorse or promote products derived from this Software without
specific prior written permission.
- Redistributions of source code must retain the above copyright
notices, this list of conditions, and the disclaimer below.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer below in the
documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

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