Compare commits

..

956 Commits

Author SHA1 Message Date
Basavaraj S m
25c5b6ab37 fix(issues): hide commit references the viewer cannot read (#40216)
Pushing a commit whose message mentions `owner/repo#1` adds a
"referenced this issue from a commit" event to that issue, with the
commit subject and a link into the pushed repository. The event never
recorded that repository, so a commit in a private repository
referencing a public issue showed its subject and repository path to
anonymous visitors of the issue page and the timeline API.

- The event now records the pushed repository in `RefRepoID`, and is
only shown to viewers who can read that repository's code, including the
issue's own. Older events have no recorded source and stay as they are.
- As on GitHub, the event stays attributed to the repository the commit
was first pushed to, even after the commit reaches another repository,
and is removed when that repository is deleted.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-10 06:13:50 +00:00
Shivansh Garg
640cd615c4 feat: restrict pull/issue creation on access basis (#40069)
Adds a repository-level **"Restrict to collaborators"** option under the
Issues and Pull Request unit settings. When enabled, only users with
collaborator access to the base repository (owner, team members, or
explicit collaborators) can open issues or pull requests against it.

Public repositories often need to be visible/cloneable by everyone, but
owners may want to limit who can open pull requests (e.g. restrict to an
internal team while still keeping the code public)

* Fixes: https://github.com/go-gitea/gitea/issues/40015
* Fixes: https://github.com/go-gitea/gitea/issues/31890
* Fixes: https://github.com/go-gitea/gitea/issues/28021
* Fixes: https://github.com/go-gitea/gitea/issues/14801

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-10 00:53:49 +00:00
GiteaBot
c01f93b5a7 [skip ci] Updated translations via Crowdin 2026-11-10 00:32:42 +00:00
silverwind
3c41728017 test(e2e): assert server-rendered biography after profile update (#40176)
Since Playwright 1.56, a WebKit click on a native submit button can
return before the form navigation starts, so `toHaveValue` passed on the
old page with the value `fill()` just set, and the next step raced the
reload.

Assert the textarea's text instead, its server-rendered default, which
only the reloaded page has.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-11-09 21:50:27 +00:00
Trung Hehe
06b393e402 feat(ldap): support multiple comma-separated LDAP hosts for failover (#6898) (#40201)
Allows an LDAP auth source to list multiple comma-separated servers in
the Host field so logins continue working when one server is
unreachable. Entries can be plain hosts using the configured Port and
Security Protocol fields or `ldap://`, `ldaps://`, or `ldap+starttls://`
URLs with their own port and protocol (as proposed in
https://github.com/go-gitea/gitea/pull/31649#issuecomment-2505555133),
with TLS protocol consistency enforced so an entry cannot silently
downgrade encryption.

Hosts are tried in order with a 250 ms head start each so an
unresponsive server does not block logins for the full 60 s connect
timeout, and only connection failures fail over to the next host so
failed binds are not retried across servers. Existing single-host
configurations continue to work unchanged without a migration.

Docs: https://gitea.com/gitea/docs/pulls/635
Fixes https://github.com/go-gitea/gitea/issues/6898
/claim #6898

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-09 21:45:12 +00:00
Lunny Xiao
3561478109 fix(projects): move only the dragged card on project boards (#40182)
The board reported every card of the target column on each drag, and the
server wrote all of them into that column. A stale board in another
browser therefore pulled cards moved by someone else back to their old
column.

The client now sends only the dragged issue and its predecessor, and the
server computes the position in one transaction:

- Only the dragged issue changes column, the others are just renumbered.
- A predecessor that is no longer in the column puts the card at the
end.

Added an e2e test.

Fixes https://github.com/go-gitea/gitea/issues/21243
Fixes https://github.com/go-gitea/gitea/issues/32070
Fixes https://github.com/go-gitea/gitea/issues/13498

---
Assisted-by: Codet:claude

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-09 21:39:22 +00:00
Shudhanshu Singh
2d6dca9abb fix(ui): improve user filter dropdown when no remote search is configured (#40296)
On the dashboard issue and pull request pages, author and assignee
filters only accept direct username input since candidate lists across
all accessible repositories cannot be pre-fetched. However, the dropdown
inherited several confusing artifacts from the repository-level user
search:

- Displayed a misleading "Shows a maximum of 30 users" tooltip even
though no remote user list was fetched.
- Used "Search users" as placeholder rather than indicating direct
username input.
- Pressing Enter in the input had no effect, requiring mouse selection.
- Any active username filter was not reflected in the dropdown menu on
open.

This change:
- Only renders the 30-user tooltip when a remote search URL is provided.
- Uses "Filter by username…" as placeholder when no remote search URL
exists.
- Applies the filter on Enter keypress.
- Ensures the active username is displayed and selected when opening the
menu.

Fixes: https://github.com/go-gitea/gitea/issues/40275
Fixes: https://github.com/go-gitea/gitea/issues/33426

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-09 21:34:43 +00:00
Lunny Xiao
bccc90d654 fix(api): lock and unlock issues as the signed-in user, not the repository owner (#40291)
`PUT/DELETE /repos/{owner}/{repo}/issues/{index}/lock` passed
`ctx.ContextUser` as the doer of `LockIssue` / `UnlockIssue`. In the
repo routes that is the owner of the repository, so on a repository of
an organization the lock and unlock timeline comments were created by
the organization instead of the user who did it. The web handler already
uses the signed-in user; this uses `ctx.Doer` as well.

Adds a subtest `DoerOnOrganizationRepo` to the existing
`TestAPILockIssue`: a member of an organization locks and unlocks an
issue of an organization repository, and the comments must be posted by
that member.

Found while testing audit logging of the issue lock events downstream.

_Authored by Codet (Claude)._
2026-11-09 18:00:06 +00:00
wxiaoguang
772f365254 refactor: use RunWithStderr for git commands (#40295)
If the stderr output is not handled, then it needs to use RunWithStderr
to make sure the git error message is included in the returned error.
Otherwise, when the git command fails, the error log won't show useful
information.

Ref:

* #39795 : no useful information in log messages
*
https://github.com/go-gitea/gitea/pull/40160#pullrequestreview-5752720866
2026-11-09 17:59:52 +00:00
bircni
e0b2f3425e fix(actions): run pull_request workflows on the merge commit (#40076)
`pull_request` workflow runs checked out the PR head, so a PR could pass
CI and still break the base branch once merged.

Gitea now maintains `refs/pull/<index>/merge`, a test merge of the head
into the base branch, for open pull requests. It is updated on creation
and mergeability checks and removed on close or merge.

- `pull_request` runs read their workflows from the test merge and
expose it as `github.ref`, `github.sha` and
`github.event.pull_request.merge_commit_sha`. Commit statuses, the API's
`head_sha`, `workflow_run` payloads and `paths` filters stay on the
head.
- Runs fall back to the head when the merge conflicts, the pull request
is closed or git is older than 2.40.
- Hidden refs keep each run's test merge (`refs/gitea/run/<id>`) and
merged commits (`refs/gitea/pull/<index>/merged`) from `git gc`, for
reruns and after the base branch is deleted or force-pushed.

Fixes: https://github.com/go-gitea/gitea/issues/27039
Fixes: https://github.com/go-gitea/gitea/issues/13134
Fixes: https://github.com/go-gitea/gitea/issues/30972
Docs: https://gitea.com/gitea/docs/pulls/617

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-09 12:42:40 +00:00
wxiaoguang
0ab0bfc375 fix: don't log network error in console when loading issue popup (#40288)
* fix #40286

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-09 05:59:03 +00:00
Giteabot
f3354aa902 chore(deps): update dependencies (#40292)
Updates JS, Go, Python and Actions dependencies, including the stylelint
18 and stylelint-config-recommended 19 majors.

- Update clippie to 4.3.1, which fixes copy buttons in browsers without
`ClipboardItem` such as Pale Moon
- Exempt the markup `tt` selectors from the new `selector-no-deprecated`
rule, the sanitizer allows `<tt>`
- Replace the `stylelint-value-no-unknown-custom-properties` plugin with
stylelint's `referenceFiles` and the core `no-unknown-*` rules
- Remove dead dropdown loading icon styles that referenced deleted
keyframes

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

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-09 05:32:54 +00:00
Shudhanshu Singh
7b1cde99d0 perf(charset): skip plain ASCII text when escaping control characters (#39937)
Every line of file, blame, diff and code preview views goes through
`EscapeControlHTML`, which streamed it through a 32 KiB read buffer and
copied every tag and text part. Views render the `EscapeStatus` before
the content, so every caller holds the complete HTML and streaming can't
help. It is now escaped in one pass over the string and returned
unchanged when nothing needs escaping.

- Skip text of printable ASCII, tabs and newlines, such lines return
immediately
- Render markup and wiki pages into a buffer instead of a pipe and
goroutine, and remove `EscapeControlReader`
- Analyze long text parts in windows overlapping by the detection
context, which bounds memory without changing results
- Escape invalid bytes before a tag, they were moved into the tag part
unescaped and could hide a `<math>` tag

| | before | after |
|---|---|---|
| ASCII line | 5.1 µs, 59 allocs | 0.08 µs, 1 alloc |
| line with one `é` | 3.9 µs, 30 allocs | 0.29 µs, 3 allocs |
| 150 KB document | 2.5 ms, 5.6 MB | 0.27 ms, 0.6 KB |

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-09 03:42:30 +00:00
GiteaBot
2bbac68f45 [skip ci] Updated translations via Crowdin 2026-11-09 00:36:42 +00:00
silverwind
d715a5ae75 test(api): cover null milestone due_on (#40172)
Removing a milestone deadline with `"due_on": null` was untested, only
`""` was. On release/v28 the null case returned HTTP 500
(https://github.com/go-gitea/gitea/pull/40171). main already handles it,
this adds the case to the existing test so it stays that way.
2026-11-08 20:53:59 +00:00
Lunny Xiao
1554b6c939 feat!: remove repository adopt feature (#40181)
Remove the "adopt unadopted repositories" feature. Adopting assumes a
fixed repository storage path on disk, which makes the storage path hard
to change. It is mostly used once for migrations, and the [Gitea
importer](https://gitea.com/gitea/importer) covers that after the
repositories are moved out of the repository root. The docs are updated
in https://gitea.com/gitea/docs/pulls/639.

- Remove the admin and user adopt pages, the `/admin/unadopted` API
endpoints and the adopt notifier hook
- Remove the `ALLOW_ADOPTION_OF_UNADOPTED_REPOSITORIES` and
`ALLOW_DELETION_OF_UNADOPTED_REPOSITORIES` settings
- The user "Repositories" settings page only lists the user's
repositories
- Repository files without a database record must now be removed from
disk by the administrator

Assisted-by: Codet:claude

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-08 18:38:23 +00:00
wxiaoguang
3699309edd feat: support jpeg-xl (jxl) (#40283)
Major browsers (Chrome/Firefox/Safari) all support it now,
https://developer.chrome.com/blog/jpeg-xl-in-chrome

* Fix #16629
2026-11-08 15:24:46 +00:00
silverwind
8cb6fb2001 fix: add missing checks to more handlers and services (#40131)
Several code paths skipped checks that their siblings already perform.

- Code comment replies are rejected when they target a review of another
pull request or another user's pending review
- OAuth2 refuses tokens for users who can't sign in, and introspection
reports their tokens and already used refresh tokens as inactive
- AGit proc-receive runs the same pull request permission and wiki
checks as pre-receive
- Repository transfer drops the repository's owner-level scoped workflow
source registrations, and registrations whose source the owner no longer
owns are ignored
- The web editor's base branch copy into a fork and the fork's upstream
divergence info require read access to the base repository
- LFS downloads require sign-in under strict `REQUIRE_SIGNIN_VIEW`, like
git smart HTTP
- Unknown AGit target branches are reported as an unexpected ref again
2026-11-08 09:02:51 +00:00
silverwind
cd92c6a008 fix: paste copied code as text, not as a markdown table (#40053)
Copying lines from a diff, file view, blame, code search or code preview
and pasting them into a comment on Gitea or GitHub produced a markdown
table with escaped pipes and stripped indentation. Both use
`@github/paste-markdown`, which converts copied HTML tables unless they
carry `data-paste-markdown-skip`, as GitHub's own code tables do.
Gitea's code tables now set it too.

The test is a bit more complicated because of a upstream bug which
causes `paste-markdown` to insert pasted content twice, once
https://github.com/github/paste-markdown/pull/128 is merged and
released, the negative assertion could be removed.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-11-08 09:01:50 +00:00
GiteaBot
edebfaa125 [skip ci] Updated translations via Crowdin 2026-11-08 00:36:44 +00:00
silverwind
bd8fd71bc1 enhance(theme): derive theme colors with color-mix and unify shades (#40113)
Derive theme colors from a few base colors with `color-mix(in srgb, …)`
and make the colorful palette coherent, so custom themes that import a
Gitea theme can override a base color and get matching shades.

- Every `-light`, `-dark-1` and `-dark-2` shade follows one rule for all
hues, fixing dark orange shades copied from the light theme, black
shades that barely moved and neon tints
- Base colors move at most 4 OKLab units toward a common lightness, the
median in dark and red in light, yellow stays unchanged
- Text, secondary, console, message, surface and alpha colors derive
from their own base, alpha variants now match their names
- Added and removed diff colors share one lightness per level in all
themes, including colorblind ones
- Chart.js and the external render iframe resolve `color-mix()` values
to hex through `getComputedStyle`
- Remove unused `secondary-dark-12`, `-13`, `shadow-opaque` and
`workflow-edge-hover`

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

<img width="880" alt="Components with the largest color changes, dark
and light, before and after"
src="https://github.com/user-attachments/assets/a9d27636-e3bb-46fa-a3ac-3fec33fd9806"
/>
<img width="880" alt="All theme colors per family, before and after,
unchanged ones dimmed"
src="https://github.com/user-attachments/assets/9977dde3-0749-4926-b110-18c0dc31d632"
/>

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-11-07 23:39:15 +00:00
Sandiyo Christan
c54ec52f0f fix(auth): limit failed TOTP attempts per account (#39993)
Gitea accepted unlimited wrong TOTP passcodes, so anyone holding an
account's password could enumerate the 6-digit code in minutes. This
allows 5 failed attempts per account, then refuses TOTP until 15 minutes
after the last one.

- The count lives in the cache, and the global lock spans the check,
validation and counting, so new sessions and concurrent requests cannot
widen it. With Redis, both are shared across instances
- Applies to every TOTP check: the web 2FA page, account recovery and
Basic auth with `X-Gitea-OTP` (API, git, packages)
- Only wrong or replayed passcodes count, server errors do not
- Scratch codes and WebAuthn stay usable as the recovery path while
locked
- Failures log `Failed authentication attempt for <user> from <ip>`,
which the fail2ban filter already matches

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-11-07 23:01:49 +00:00
silverwind
51879941dc enhance: improve commit and compare link rendering (#40154)
Commit, compare and SHA links render the SHA in code with the rest of
the URL after it, and links to other repositories get an `owner/repo@`
prefix like references do.

- Abbreviated SHAs, branch compares and `<url>` autolinks now render as
commit links
- Pull request commit URLs no longer break into an issue reference plus
stray text
- Only routes that take a SHA are shortened, so file and API URLs stay
plain

Replaces https://github.com/go-gitea/gitea/pull/36725

<img width="620" alt="Commit links before and after"
src="https://github.com/user-attachments/assets/5919c8a0-94e3-4148-ad11-6944bf790572"
/>
2026-11-07 22:43:07 +00:00
William Desportes
87a703819b fix(queue): bump levelqueue to v0.5.0 and remove workarounds (#40219)
levelqueue v0.5.1 fixes this upstream
(https://gitea.com/lunny/levelqueue/pulls/8), so this now only bumps the
dependency.

A push wrote "high" before the payload, so an interrupted push left an
id counted with no key. `LPop` then returned `ErrNotFound` without
advancing "low" and the wrapper retried forever: nothing popped, no
worker started, later items unreachable. All were only fixable by
clearing the queue, losing the backlog:
- https://github.com/go-gitea/gitea/issues/32006 — mail, 0 workers
- https://github.com/go-gitea/gitea/issues/28919 — mirror, "already in
queue"
- https://github.com/go-gitea/gitea/issues/33862 — archives hanging
- https://github.com/go-gitea/gitea/issues/33882 — self-healing request

v0.5.1 writes counter and payload in one batch, moves the counters past
missing keys, and repairs leftover unique-queue set entries, so affected
instances recover on their next pop.

No test: the behaviour lives in the dependency, which covers it in its
own `recover_test.go`.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-11-07 22:26:53 +00:00
silverwind
e09813f472 docs: update AGENTS.md (#40270)
Some useful rules from my past experiences, feel free to suggest more.
Dark theme screenshots is subjective of course, but I prefer them.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-11-07 16:09:25 +00:00
Lazizbek Ergashev
884884308d fix(pull): don't add a timeline entry for pending API reviews (#40260)
Pending reviews created through the API went through `SubmitReview`,
which added a timeline entry and sent notifications before the review
was submitted, so submitting it later showed the review twice.

- Pending reviews skip the timeline entry and notifications until
submitted, like on GitHub
- `CreatePullReview` rejects comments without a `path` or a positive
line with 422, like GitHub
- Already duplicated reviews stay as they are

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

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 14:19:50 +00:00
Renox
462ab3801a fix(web): don't block README images with REQUIRE_SIGNIN_VIEW = expensive (#40264)
Relax expensive endpoint list, then README can render its images.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-07 13:34:01 +00:00
Mitrahsoft
3aca9c8fb7 enhance(actions): create commit status for workflow_dispatch runs (#39961)
Runs triggered by `workflow_dispatch` never created a commit status, so
the commit showed no sign that a workflow ran on it.

- Post the status on the run's commit, e.g. `ci / e2e
(workflow_dispatch)`. `schedule` runs stay unchanged.
- These statuses don't count for pull requests. They don't show in a
PR's checks and can't satisfy or fail its required checks, even through
broad globs like `ci / e2e*`.

Closes https://github.com/go-gitea/gitea/issues/39639

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 12:55:19 +00:00
Basavaraj S m
4f69599981 fix(repo): do not reuse pull mirror credentials for a new host (#39991)
Changing a pull mirror address with an empty password field carried the
stored credentials over to the new address. The password is write-only,
so a repo admin who did not set up the mirror could send the owner's
token to a host they control, or pull another repository on the same
host that the token can read.

Stored credentials are now reused only when the address is unchanged,
like a changed username already drops the stored password. The API
cannot change the address and push mirrors never reuse credentials, so
both are unaffected.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 12:35:10 +00:00
TheFox0x7
dd4672607b chore: add report requirements (#40262)
Explicitly require version, reproduction and settings

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 12:09:23 +00:00
wxiaoguang
a165fdb63c refactor: lazy init global regexp variables (#40269)
Gitea binary is an all-in-one program.

It doesn't make sense to compile dozens or even hundreds of regexps for
git hook sub commands.
2026-11-07 11:56:56 +00:00
Trung Hehe
6bc92f464a fix(org): enforce unique team names in the database (#40228)
Concurrent requests could create teams with the same name in one
organization, because only the service checked the name. Editing such a
team, or renaming a team to a taken name, then failed with a 500.

- Add a unique index on `team (org_id, lower_name)`. Migration 368
renames every duplicate except the oldest to a free `<name>-<n>` and
logs each rename. IDs, members, repositories and permissions are kept,
but lookups by the old name (CODEOWNERS, group-to-team sync, mentions,
URLs) now reach only the kept team.
- Team writes report a name rejected by the index as
`ErrTeamAlreadyExist`, so duplicates, concurrent ones included, return
409 from `POST /orgs/{org}/teams` (previously 422) and `PATCH
/teams/{id}`, and a form error in the web UI.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 11:16:11 +00:00
silverwind
baccc2d7cd fix: truncate strings on character boundaries (#40267)
Byte slicing like `t.Description[:255]` can split a multi-byte character
and store invalid UTF-8. Team descriptions were also cut to 255 bytes
although their `MaxSize(255)` validation allows 255 characters.

Team descriptions now truncate by runes. Signup user agents, Actions log
lines and access log request IDs use `util.TruncateStringBytes`, which
now returns short strings untouched.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-07 11:15:28 +00:00
Michael Lopez
5d9bfa1e14 feat(packages): add private Terraform module registry (#38040)
Adds a Terraform module registry package type implementing the [module
registry
protocol](https://developer.hashicorp.com/terraform/internals/module-registry-protocol),
so `terraform init` and Terragrunt `tfr://` can resolve modules from
Gitea:

```hcl
module "vpc" {
  source  = "gitea.example.com/owner/vpc/aws"
  version = "~> 1.0"
}
```

- Modules are uploaded with `PUT
/api/packages/-/terraform/modules/{owner}/{name}/{provider}/{version}`
as a `.tar.gz` with the module at its root, stored verbatim and
immutable.
- `X-Terraform-Get` is a short-lived signed URL because Terraform sends
no credentials when downloading the archive.
- The package page shows install snippets, README, the root module's
inputs, outputs and providers, and `modules/*` submodules.
- Metadata is read by a small in-tree HCL scanner instead of
`hashicorp/hcl/v2`, which would add 5 modules and ~1.4 MB to the binary.

Docs: https://gitea.com/gitea/docs/pulls/433

---------

Signed-off-by: Michael Lopez <mickael.lopez@gmail.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 10:45:05 +00:00
Wei-Lun Li
7efb5c055f fix(markup): recognize issue references next to CJK punctuation (#40097)
Issue references next to non-ASCII characters, common in Chinese and
Japanese text, are ignored: `修复#1`, `(#2)` or `Closes #3,` create no
link or cross reference.

Like on GitHub, any non-ASCII character may now precede `#123`, `!123`
or `owner/repo#123`, and anything but a word character may follow it.
Dead trailing-space handling in the reference and mention search is
removed.

I used Claude Code to write this change.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-07 10:14:47 +00:00
silverwind
b23c70d439 fix: add missing checks to several handlers and services (#40120)
Several code paths skipped checks their siblings perform, or cleaned up
more than they validated.

- Official team review requests now count, and removing one no longer
marks an unrelated review official
- Release edits only delete files of validated attachments
- Rejected container uploads no longer delete an existing blob
- Attachment removal only removes the uploader's own pending uploads
- OpenID sign-in checks the allow and block lists against the normalized
identifier
- HTTP git access is rejected while a repository is being created, like
SSH

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-07 09:43:39 +00:00
Mitrahsoft
b6c291f87a fix(api): honor branch protection force push and allowlist options on create and edit (#39990)
The branch protection API handled push and force push options
inconsistently. Create dropped force push unless push was enabled, and
edit ignored allowlist fields unless their parent was set to true in the
same request.

- Create treats force push independently of push, like edit and the web
UI. Enforcement is unchanged.
- Edit applies `enable_push_whitelist`, `enable_force_push_allowlist`
and both deploy key flags against the rule's resulting state, like the
deletion fields. `{"enable_push_whitelist": false}` now disables the
whitelist instead of being ignored.
- Documents that `enable_force_push_allowlist` requires
`enable_force_push` on create.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 09:34:21 +00:00
Zettat123
c74f2f9216 fix(repo): mark repository as non-empty before git push returns (#40102)
The post-receive hook syncs pushed branches to the database, but
`is_empty` and the default branch of an empty repository were only
updated later by the `push_update` queue. Until then the API treated the
repository as empty (branch list `null`, `GET /branches/{branch}` 500),
and the queue overwrote the default branch chosen by the hook or set via
the API.

`SyncBranchesToDB` now initializes an empty repository and repairs a
default branch missing in git in its locked transaction, so the state is
final when `git push` returns. The push queue and web file edits retry
it while the repository is still empty.

- The default branch is the configured one if it exists, else `main`,
else `master`, else the first branch by name, like GitLab. HEAD follows
it.
- A first push of several branches no longer depends on push order. The
push that initializes a repository still prints no pull request hints,
like GitHub.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-07 03:20:25 +00:00
GiteaBot
b8b270bac9 [skip ci] Updated translations via Crowdin 2026-11-07 00:31:59 +00:00
Alan Hsu
df4544a58b fix(web): handle TypeCastError when building form validation messages (#40212)
Fixes https://github.com/go-gitea/gitea/issues/40210

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-06 23:27:06 +00:00
silverwind
ede9ec3e10 fix: stop discarding file close and test setup errors (#39987)
- Fix
https://github.com/go-gitea/gitea/security/quality/rules/go%2Funhandled-writable-file-close,
ignored close errors hide failed flushes, which could lose rotated logs
and truncate authorized_keys
- Fix `TestProjects` fixture reset, from
https://github.com/go-gitea/gitea/security/quality/ai-findings,
`TruncateBeans` rejected the table names and the ignored error let the
test run against fixture data
2026-11-06 20:45:17 +00:00
silverwind
24700dca2a test(e2e): retry clipboard reads rejected by Firefox (#40155)
The Firefox mermaid e2e test fails intermittently with `Clipboard read
operation is not allowed.`, for example in
https://github.com/go-gitea/gitea/actions/runs/41281998298/job/124776072700.

Firefox rejects `readText()` when it overlaps the page's still pending
clipboard write, and `expect.poll` does not retry errors thrown by its
callback, so one overlapping read fails the test.
https://github.com/go-gitea/gitea/pull/39838 only removed the paste
prompt, not this rejection.

- A new `assertClipboardText` helper treats a rejected read as a
mismatch so it is polled again
- The bot token copy test had the same race and uses it too

_Written by Claude (Opus 5.5)._
2026-11-06 20:44:00 +00:00
silverwind
7e18e81c7d refactor: remove duplicated and dead Go code (#40244)
Pure refactors with no behavior change, mostly reusing existing helpers
and dropping duplicated or dead code. Net −222 lines.

- Reuse existing helpers instead of inlined copies: `getIssueIndex`,
`NewLabel`, `ToCommitUser`, `GetPackageFileDescriptors`,
`util.SplitTrimSpace`
- Merge copy-pasted blocks in review comment deletion, project deletion
and the pre/post-receive hook templates
- Drop dead code: an error `toStringKeys` never returned, impossible
`bytes.Buffer` write errors, redundant nil-map branches, a `base64`
wrapper
- Use stdlib where it fits: `strings.Cut`, `bytes.IndexAny`,
`cmp.Compare`, `min`/`max`, `slices.Concat`, map lookups instead of key
scans
2026-11-06 11:59:49 +00:00
Shivansh Garg
a5201dc427 feat(release): allow setting a release as the latest (#40214)
Gitea picks the latest release by commit date, while GitHub keeps an
explicit pointer, so repositories migrated from GitHub could show a
different latest release than their source. Fixes
https://github.com/go-gitea/gitea/issues/35273.

- Add a "Set as Latest Release" checkbox and GitHub's `make_latest` API
option. Without them, the newest stable release by commit date stays the
latest.
- Migrations from GitHub and Gitea carry over the source's latest
release.
- Migrated releases are dated by their commit like native ones, and
equally dated ones keep the source's order.

<img width="355" height="122" alt="Set as Latest Release checkbox"
src="https://github.com/user-attachments/assets/d35f5324-7ae0-4e9f-b73c-d3c90496fb00"
/>

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-06 08:23:13 +00:00
Lazizbek Ergashev
98c937170f fix(repo): reject unparsable required approvals in branch protection (#40213)
Saving a branch protection rule with a "Required approvals" value that
can't be parsed as an integer (like `99999999999999999999` or `abc`)
showed the success message and stored 0, because the binding error was
never checked. The form now uses `form-fetch-action` with
`GetFetchActionForm`, so invalid input is rejected and nothing is saved.
Dev mode still answers unparsable numbers with a 500 until
`TypeCastError` gets a message, see
https://github.com/go-gitea/gitea/pull/40212.

- Validation errors show on the page and highlight the offending field,
keeping the form input
- Negative approvals are rejected before any rule lookup, and the input
gets `min="0"`

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

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-06 08:08:20 +00:00
silverwind
09922e4ae1 ci: update giteabot to v1.5.1, label conflict-free backports (#40247)
Bump `giteabot` to
[`v1.5.1`](https://github.com/go-gitea/giteabot/releases/tag/v1.5.1) to
label PRs with a `backport/<version>` label that would cherry-pick
cleanly onto the latest release branch with `backport/conflict-free`.

The backport workflow's concurrency group is now per ref, so release
branch pushes can't cancel a pending backport run from `main`.

_Written by Claude (Opus 5.5)._
2026-11-06 07:55:55 +00:00
Basavaraj S m
8d14c9af20 fix(pull): enforce user blocks on pull request reviews (#40194)
Code review never checked user blocks, so a user blocked by the poster
or repository owner could still post reviews, code comments and replies
through the web, API and email.

- `CreateCodeComment` and `SubmitReview` now check blocks, with the repo
admin exemption issue comments have.
- Official reviewers blocked only by the author can still review, so an
author can't keep a maintainer from requesting changes.
- Review requests are handled in
https://github.com/go-gitea/gitea/pull/39296.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-06 07:31:58 +00:00
silverwind
87ebf3b1ba feat(auth)!: remove support for obsolete OpenID 2.0 draft (#40231)
Remove OpenID 2.0 authentication. The spec is an obsolete draft, major
providers dropped it years ago, and the code keeps needing security
fixes like https://github.com/go-gitea/gitea/pull/39642. OpenID Connect
and OAuth2 are unaffected. Docs: https://gitea.com/gitea/docs/pulls/633.

- The `[openid]` settings, OpenID sign-in, sign-up and account
association are gone
- A migration drops the `user_open_id` table with all user OpenID URIs
- Accounts registered via OpenID have a random password and regain
access via password reset

Fixes: https://github.com/go-gitea/gitea/issues/1310
Fixes: https://github.com/go-gitea/gitea/issues/3521
2026-11-06 07:21:19 +00:00
wxiaoguang
fd98ab5472 enhance(codeowners): support section / glob / file view display / skip review request (#40243)
Document: https://gitea.com/gitea/docs/pulls/634 ([doc PR
preview](955ae7d75c/docs/usage/repository/code-owners.md))

1. If the pattern starts with `*` or contains `/*`, use our glob package
to parse it as glob to regexp
* Make it easier to use. Otherwise it's really ugly to keep writing a
lot of backslash escapes
2. Add GitLab-style "section" support
   * Fix #28926

```codeowners
[Section] @user
*.js
```

3. Add our annotation support, at the moment only need to support
review-request=true/false
    * Fix #40241

```codeowners
## review-request=false # this is global

*.js $user ## review-request=true # this is line-level

[Section] @user ## review-request=true # this is section-level
*.md
```

4. Add GitHub/GitLab-style "show codeowner when viewing a file"
    * Fix #28137
2026-11-06 07:19:51 +00:00
silverwind
0d59b737aa fix: upgrade Alpine base packages in Docker images (#40252)
The `alpine:3.24` base image lags behind fixes published to the Alpine
repository.

Both images now run `apk upgrade` before `apk add`. Packages from `apk
add` are already current, so this only affects the preinstalled base
packages.

_Written by Claude (Opus 5.5)._
2026-11-06 07:19:07 +00:00
Giteabot
917badaae6 fix(deps): update module github.com/andybalholm/brotli to v1.2.7 [security] (#40246)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[github.com/andybalholm/brotli](https://redirect.github.com/andybalholm/brotli)
| `v1.2.1` → `v1.2.7` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fandybalholm%2fbrotli/v1.2.7?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fandybalholm%2fbrotli/v1.2.1/v1.2.7?slim=true)
|

---

### Excessive memory allocation when decompressing crafted streams in
github.com/andybalholm/brotli
BIT-brotli-2026-111497 / BIT-dotnet-2026-111497 /
BIT-dotnet-sdk-2026-111497 / BIT-node-2026-111497 /
BIT-node-min-2026-111497 / BIT-powershell-2026-111497 /
[CVE-2026-111497](https://nvd.nist.gov/vuln/detail/CVE-2026-111497) /
[GHSA-9v8h-qqgm-4m35](https://redirect.github.com/advisories/GHSA-9v8h-qqgm-4m35)
/
[GHSA-c9gr-gmg4-2vqv](https://redirect.github.com/advisories/GHSA-c9gr-gmg4-2vqv)
/ [GO-2026-6888](https://pkg.go.dev/vuln/GO-2026-6888) / PYSEC-2026-4322
/ PYSEC-2026-4323

<details>
<summary>More information</summary>

#### Details
In github.com/andybalholm/brotli before v1.2.7, the decompressor
allocates memory for Huffman tables and block-type trees on every
meta-block header without bounding the cumulative allocations relative
to the input size. A small crafted brotli stream containing many empty
meta-blocks with new Huffman trees can trigger hundreds of megabytes of
allocations per kilobyte of input, leading to denial of service.

#### Severity
Unknown

#### References
-
[https://github.com/andybalholm/brotli/security/advisories/GHSA-c9gr-gmg4-2vqv](https://redirect.github.com/andybalholm/brotli/security/advisories/GHSA-c9gr-gmg4-2vqv)
-
[33c2a98842)
-
[https://github.com/andybalholm/brotli/releases/tag/v1.2.7](https://redirect.github.com/andybalholm/brotli/releases/tag/v1.2.7)
-
[0ed4268a44)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6888) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### Release Notes

<details>
<summary>andybalholm/brotli (github.com/andybalholm/brotli)</summary>

###
[`v1.2.7`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.6...v1.2.7)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.6...v1.2.7)

###
[`v1.2.6`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.5...v1.2.6)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.5...v1.2.6)

###
[`v1.2.5`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.4...v1.2.5)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.4...v1.2.5)

###
[`v1.2.4`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.3...v1.2.4)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.3...v1.2.4)

###
[`v1.2.3`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.2...v1.2.3)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.2...v1.2.3)

###
[`v1.2.2`](https://redirect.github.com/andybalholm/brotli/compare/v1.2.1...v1.2.2)

[Compare
Source](https://redirect.github.com/andybalholm/brotli/compare/v1.2.1...v1.2.2)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend Renovate
CLI](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xOTAuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjE5MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
2026-11-06 01:55:15 +00:00
Lazizbek Ergashev
288d54951e fix(perm): keep read access for restricted collaborators on public repos (#40234)
Adding a restricted user as a read collaborator to a public repo gave
them no access when the owner isn't fully public, like a limited org.
Restricted users get no default read there, and `RecalculateUserAccess`
dropped their read access row.

- `RecalculateUserAccess` now keeps the same read rows as
`refreshAccesses`, on org-owned repos and for restricted users.
- Re-saving an unchanged collaborator permission recalculates access,
repairing collaborators affected before this fix.

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

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-05 23:48:55 +00:00
silverwind
b81b50be55 fix(build): pass # test patterns to go test -run unchanged (#39977)
The `test-backend#` and `test-integration#` targets rewrote every `.`
into `/`, because GNU make pattern rules never match goals containing
`/`. That broke regexes like `make 'test-backend#TestA.*'`, which
silently selected subtests instead.

Static pattern rules built from the goals accept `/` directly, so the
pattern reaches `go test -run` unchanged. Subtests are now selected like
`make 'test-backend#TestA/sub'`, on GNU make 3.81 and 4.x.
2026-11-05 23:44:33 +00:00
silverwind
c62db5b2a3 perf: reduce git processes and queries on pull request view (#40224)
Removes redundant work from the pull request conversation tab.

- Base and head refs resolve through the `cat-file` batch of the git
repos the callers already hold, instead of two `git rev-parse`
processes, reducing the number of git processes from 9 to 7 per page,
about 17 ms faster locally.
- Sidebar reviewers are batch-loaded and code-review comments reuse
their loaded poster, reducing the number of SQL queries from 387 to 358
on a PR with 169 timeline entries and 15 reviews.

Also fixes the commit list of merged pull requests, which resolved the
merge base as a branch name and returned 500.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-05 21:58:17 +00:00
Andrew Barnes
4a9d49bb46 fix(dump): --file extension is ignored when --type is not set (#40235)
`gitea dump -f backup.tar.gz` without `--type` wrote a zip archive under
that name. Detecting the type from the file name was requested in
https://github.com/go-gitea/gitea/issues/24622 and intended in
https://github.com/go-gitea/gitea/pull/30240, but the check read the
still-empty result instead of `--file`, so every name fell back to zip.

A supported extension now selects the type, other names like `backup` or
`gitea.bak` still default to zip. An explicit `--type` still wins, and
its help text now mentions the detection.

AI disclosure: written with Claude Code (claude-opus-5-5).

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-05 18:30:31 +00:00
silverwind
b7c1fb2a39 enhance: enable DEFAULT_DELETE_BRANCH_AFTER_MERGE by default for new repos (#39975)
Switch the `DEFAULT_DELETE_BRANCH_AFTER_MERGE` default from
https://github.com/go-gitea/gitea/pull/36917 to `true`, saving users a
click on merge for the most common action that deletes the PR branch on
merge.

Docs: https://gitea.com/gitea/docs/pulls/608
2026-11-05 18:26:32 +00:00
rafał przypek
3b9b8a8976 fix(packages): return empty Chef dependencies instead of null (#40237)
Fixes #40230

Cookbooks without dependencies are served with `"dependencies": null`
from the universe and version metadata endpoints. This happens because
`omitempty` drops the empty map when the metadata is stored. Chef
clients iterate over this value and Policyfile resolution crashes.

Supermarket returns `{}` for these cookbooks, Gitea now does too. The
fix checks whether dependencies are `nil` while building response,
returns an empty map if true. This way cookbooks that are already stored
are covered without a re-upload.

Tested with the Chef integration test and `chef-cli install`.

Assisted by Claude Code (claude-opus-5-5).
2026-11-05 15:19:09 +00:00
Lunny Xiao
565f1b7d52 fix(migrations): Do not fail Bitbucket Server migration when pull request activities cannot be loaded (#40205)
Bitbucket Server can answer 500 for `/pull-requests/{id}/activities` of
some pull requests. That error aborted the whole repository migration:

`bitbucket server API .../pull-requests/128/activities returned status
500`

Now a 500 is logged as a warning and that pull request's comments,
reviews and merge commit are skipped, cached so the endpoint is not hit
again. Other errors are still returned, so the migration retries them as
before.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-05 07:28:12 +00:00
silverwind
e87ac3a0f4 chore(deps): update go.yaml.in/yaml/v4 to v4.0.0 (#40222)
Update go-yaml from the rc.6 pseudo-version to the stable v4.0.0
release, and actionslib to v1.5.2, which does the same in
https://gitea.com/gitea/actionslib/pulls/32. The bump also brings in
actionslib v1.5.1.

v4 double-quotes the `on` key like yaml.v3 did, so one jobparser fixture
changes. Runner counterpart: https://gitea.com/gitea/runner/pulls/1381.

Fixes: https://github.com/go-gitea/gitea/issues/40145
2026-11-05 07:25:10 +00:00
GiteaBot
4477d47d07 [skip ci] Updated translations via Crowdin 2026-11-04 20:23:49 +00:00
Mark
bfbf8cd366 fix: fix missing accessible name on static Fomantic combobox dropdowns (#40163)
Fixes https://github.com/go-gitea/gitea/issues/40107

Static Fomantic dropdowns get `role="combobox"` or `role="menu"`, and
neither role takes its accessible name from child contents (`nameFrom:
author` only). Dropdowns like Filter and Sort on `/explore/repos`
without `data-tooltip-content`, `aria-label` or `aria-labelledby`
therefore had an empty accessible name.

If the focusable element still has no `aria-label` or `aria-labelledby`
after the tooltip check, link `aria-labelledby` to the dropdown's direct
`.text` or `span` child. Selection dropdowns are skipped because their
`.text` holds the current value, not a label.

---------

Co-authored-by: Mark <14371969+malfunctionAI@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 19:55:29 +00:00
silverwind
dfe6db6149 fix(web): build issue popup request from parsed link (#40207)
The issue reference popup built its `/info` request from the raw link
path, so a link ending in `/owner/repo/issues/N` queried an unrelated
location.

It now builds the request from the owner, repo, type and index parsed
from the link, with `parseIssueHref` anchored to the pathname under
`appSubUrl`.
2026-11-04 19:40:27 +00:00
Lazizbek Ergashev
d1c980cf8b fix(web): inline the site manifest (#40198)
Inline the web app manifest as a `data:` URL instead of serving it from
`/assets/site-manifest.json`. That endpoint has been linked through
`STATIC_URL_PREFIX` since https://github.com/go-gitea/gitea/pull/37405
and fails to load when:

1. a reverse proxy checks auth on every request, because browsers fetch
manifests without cookies
2. `STATIC_URL_PREFIX` serves the `public/` directory from disk, which
has no such file
3. `STATIC_URL_PREFIX` is cross-origin, because the endpoint sends no
CORS headers

The manifest is built per request so `start_url` follows
`PUBLIC_URL_DETECTION`. A reverse proxy that adds a stricter CSP must
allow `manifest-src data:`.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 18:11:25 +00:00
silverwind
1152bc0cf2 fix(actions): evaluate job if: against needs finished in the same pass (#40126)
The job emitter resolves a dependency chain in several rounds in memory,
but a job's `if:` reads its needs' results from the database, where a
need skipped or failed earlier in the same pass was still blocked. A job
with `if: always() && needs.build.result == 'skipped'` was therefore
skipped instead of started.

Finished statuses are now persisted at the end of each round, which also
replaces the separate handling of skipped and failed reusable workflow
callers.
2026-11-04 18:10:56 +00:00
silverwind
159e6d7e75 test(actions): run repo Actions API tests as a non-owner repo admin (#40183)
The repo-level runner, secret and variable API tests ran as the repo
owner, who bypasses the repo admin check. That's how the regression
fixed in https://github.com/go-gitea/gitea/pull/40174 slipped through.
As suggested in
https://github.com/go-gitea/gitea/pull/40174#issuecomment-6591991824,
they now run as an admin collaborator, which replaces the duplicate
subtest added there.

The tests are also trimmed to one case per status code, because
`services/secrets/validation_test.go` already unit tests name
validation.
2026-11-04 17:34:29 +00:00
Paul van der lei
f29eedad83 fix(pull): code owner team is requested again after a member reviewed (#40187)
When a member of a CODEOWNERS team approves or rejects a pull request,
`SubmitReview` removes the team's review request. On the next push,
`PullRequestCodeOwnersReview` finds no request for that team and adds it
again, so every team member except the author gets a new "Requested to
review" mail. This happens even when the push doesn't change the PR's
diff (for example, merging the base branch into the PR branch) and the
approval is still valid.

Individual code owners don't have this problem: they are skipped when
they already have a review. This PR applies the same rule to teams. A
team is not requested again while one of its members has an approve or
reject review on the PR that hasn't been dismissed. If that review is
dismissed (for example by "Dismiss stale approvals"), the team is
requested again as before.

Testing: `TestPullView_CodeOwnerTeamAnsweredByMember` creates an org
repo with `README.md @org3/team1`, opens a PR, and checks three things:
the team is requested; after a member approves, a code owner pass (what
a push runs) doesn't request the team again; after the approval is
dismissed, the team is requested again. The test fails without the
change. `make lint-backend` and the `TestPull*` integration tests pass
locally on SQLite.

AI disclosure: I used Claude to help trace the cause and draft the
change, test and description. I reviewed and tested it myself and can
explain every line.

Fixes #34884.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-04 17:34:13 +00:00
Basavaraj S m
e9c5804075 fix(issues): hide unreadable issue types in search and milestone views (#40192)
The repository issue search JSON route sits behind the issues reader
check only, and the milestone page is open to anyone who can read either
issues or pull requests, since the issue routes have to tolerate pull
request permissions. Neither handler narrows the issue type to what the
viewer may read: the search route takes `type=pulls` straight from the
query or lists both types when it is absent, and the milestone page
always lists both. A team member granted only the Issues unit on a
private repository can therefore fetch every pull request's title and
body as JSON and see pull requests on milestone pages, while a
pull-requests-only reader sees issues there. I noticed it while
comparing these routes with the API `ListIssues` handler and the web
issue suggestions endpoint, which both already narrow the type. The
narrowing now lives in one small helper used by all three handlers, with
the search route answering 404 for a requested type the viewer cannot
read, like the API does. A unit test covers both handlers with an
issues-only permission.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-04 17:31:24 +00:00
Shivansh Garg
be297b7e05 fix(webhook): fire repository event on repository transfer (#40200)
Transferring a repository fired no `repository` webhook. It now sends a
`transferred` action shaped like GitHub's, with the previous owner in
`changes.owner.from.user` or `changes.owner.from.organization`. Like
GitHub, it goes to the new owner's webhooks, plus the repository's own
and system webhooks.

- Chat webhooks render "Repository transferred from <owner>".
- Actions workflows are unaffected, no trigger matches `repository`
events.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 17:25:51 +00:00
Lunny Xiao
fca64e7330 perf: reuse request-scoped git repo in pull request conversion (#40208)
Reuse the request-scoped base git repository in `ToAPIPullRequest`,
`ToPayloadPullRequest` and `ToAPIPullRequests` instead of reopening it
for each conversion, so conversions share the request's `cat-file`
process.

- Same-repo PRs reuse the base repository for the head instead of
opening it a second time.
- Drop a redundant second open of the base repository.
- Fork head repositories stay opened per call. A request-cached fork
keeps its `cat-file` running until the request ends, which blocks
deleting the fork on Windows and keeps one process per fork alive in
batch status changes.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 17:24:49 +00:00
Myers Carpenter
58279e39fd fix(actions): match pull_request unlabeled on label removal (#40215)
Fixes https://github.com/go-gitea/gitea/issues/34317

Removing a label from a pull request still started workflows with
`types: [labeled]` and never matched `types: [unlabeled]`.
https://github.com/go-gitea/gitea/pull/34316 derived
`labeled`/`unlabeled` from the added and removed labels for issues only,
while pull requests kept mapping every `label_updated` to `labeled`.
Both events now share one conversion, so they cannot drift apart again.

Two other label changes also triggered the wrong workflows:

- Clearing all labels started no `unlabeled` workflow, because the
Actions notifier ignored it
- Replacing labels reported every old label as removed, so a replace
that kept a label also matched `unlabeled`

AI disclosure: written with Claude Code (claude-opus-5-5), session
https://claude.ai/code/session_01Kw9ySGNd4ynmyX9GcVdUfw

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 17:24:33 +00:00
Nico Schlömer
ab295e62e4 fix(markup): make heading anchors match GitHub for whitespace and combining marks (#40195)
Heading anchors now keep the same characters as GitHub: letters, marks,
decimal and letter numbers, connector punctuation and alphabetic
symbols, with ASCII spaces turned into `-`. The rule follows
[github-slugger](https://github.com/Flet/github-slugger), whose fixtures
are generated from real GitHub renders.

- Other whitespace, including newlines in multi-line headings, is
dropped instead of becoming `-`, so `1<em space>ab` gets `#1ab`
- Combining marks are kept, so `हिन्दी` no longer becomes `हनद`
- Other numbers like `²` and `①` are dropped

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

This PR was written with Claude Code.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 05:51:12 +00:00
wxiaoguang
d919301d36 refactor: remove UnmarshalHandleDoubleEncode patch and fix repo unit TODO (#39998)
Remove the `UnmarshalHandleDoubleEncode` runtime workaround for an old
xorm bug, and normalize legacy double-encoded JSON in `repo_unit.config`
and `login_source.cfg` with a migration instead.

- Config `FromDB` no longer fails on corrupted values. A broken auth
source is deactivated, so admins can still fix it in the admin panel
- Remove `ProjectsConfig.GetProjectsMode`, `FromDB` already defaults
`ProjectsMode`

Actually it doesn't need so many changes, just because AI thinks it
should handle "corrupted database value" for auth sources.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 05:28:49 +00:00
Alan Hsu
666e6e2d17 fix(web): allow clearing the full name in profile settings (#40186)
Clearing "Full Name" in the profile settings shows success but keeps the
old value: since #31959 it is only updated when non-empty, because the
input is disabled (not submitted) when `change_full_name` is disabled.

Use `*string` for the field, so a missing field keeps the value and an
empty one clears it. With `change_full_name` disabled, any submitted
value (including empty) is now rejected. Not `optional.Option[string]`,
because `MaxSize` would count slice elements instead of characters.

Fixes #40185

---
AI disclosure: written with Claude Code (claude-opus-5-5) and tested
locally.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-04 01:42:41 +00:00
Jon Fuller
4d00ed2a1e fix(notifications): avoid deadlock when creating issue notifications concurrently (#40166)
Concurrent `notification-service` workers can deadlock in the
transaction for-loop.

The transaction is useless, so it is removed to avoid deadlock.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-03 16:33:39 +00:00
silverwind
ce0da8a378 build: cross-compile in docker (#40136)
Building the backend under QEMU takes 46 to 54 minutes per foreign
platform in the nightly image build, against 7 minutes natively. The
backend stage now runs on the build platform and cross-compiles with
`GOOS`/`GOARCH`/`GOARM`, so every platform builds at native speed.

- `go generate` runs once instead of once per platform.
- The PR docker dry run builds all three platforms in a single job, on
any docker-related change.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-11-03 16:20:48 +00:00
Gaurav Dubey
f26a314636 fix(api): keep wiki page name when editing with an empty title (#40153)
Match `PATCH /repos/{owner}/{repo}/wiki/page/{pageName}` document "leave
empty to keep unchanged"

Fixes #39179

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-03 16:20:39 +00:00
Jon Fuller
5c68ba3e0a fix(repo): avoid deadlock when updating language stats concurrently (#40167)
The `repo_stats_update` queue only deduplicates waiting items, so
several workers can index the same repository at once, then
`UpdateLanguageStats` can lead to transaction deadlock.

Use a global lock to avoid concurrent updates, and also helps
https://github.com/go-gitea/gitea/pull/39342 to avoid duplicate updates.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-03 16:18:27 +00:00
silverwind
78eb95ab19 fix: enable more djlint rules and improve keyboard navigation (#40081)
Enable djlint's [H018](https://djlint.com/docs/linter/#h018),
[H046](https://djlint.com/docs/linter/#h046) and
[H054](https://djlint.com/docs/linter/#h054) rules, fix their findings
and the keyboard navigation problems around them. H054 found links
nested in `<button>` dropdowns and radio inputs nested in links, both
invalid HTML.

- "Add File" and "More Operations" in the repo file list become `div`
dropdowns, the disabled state stays unfocusable
- The diff whitespace menu marks the current option with a check and
`aria-current` instead of radios, which made our dropdown aria patch
announce it as a combobox
- Sign-in moves "Forgot password?" below "Sign In" and drops the
positive tabindexes from https://github.com/go-gitea/gitea/pull/31689,
so the tab order follows the DOM: username, password, remember, sign in,
forgot
- The focus ring on primary, red and green buttons is near-white on dark
themes and near-black on light ones, the primary-colored ring was
invisible on primary buttons

<img width="500" alt="Whitespace menu, before and after"
src="https://github.com/user-attachments/assets/d22a423d-95e5-434c-aec4-59264c7132b7"
/>

<img width="600" alt="Sign-in form, main and this PR"
src="https://github.com/user-attachments/assets/d6780690-d906-4791-8a2b-8d80fb03067a"
/>

<img width="215" height="132" alt="Keyboard-focused primary and red
buttons, dark and light theme"
src="https://github.com/user-attachments/assets/a2400b18-4802-4383-8560-7e8267a7f02f"
/>

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-03 15:30:59 +00:00
silverwind
5c723ee0fd refactor(markdown): remove goldmark-highlighting (#39751)
Render fenced code blocks with Gitea's own code block renderer instead
of `goldmark-highlighting`, using the managed lexer lookup from
`modules/highlight`. Indented and fenced blocks now share one rendering
path.

Rendered output is unchanged. The trailing newline is stripped on copy
in the frontend since https://github.com/go-gitea/gitea/pull/39755.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-03 12:25:31 +00:00
Giteabot
12b20c7c99 chore(deps): update dependencies (#40168)
> [!NOTE]
> This PR body was truncated due to platform limits.

This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) | Type |
Update | Pending |
|---|---|---|---|---|---|---|
| [@primer/octicons](https://primer.style/octicons)
([source](https://redirect.github.com/primer/octicons)) | [`19.41.0` →
`19.42.0`](https://renovatebot.com/diffs/npm/@primer%2focticons/19.41.0/19.42.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@primer%2focticons/19.42.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@primer%2focticons/19.41.0/19.42.0?slim=true)
| dependencies | minor | |
|
[@typescript-eslint/parser](https://typescript-eslint.io/packages/parser)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser))
| [`8.72.0` →
`8.72.1`](https://renovatebot.com/diffs/npm/@typescript-eslint%2fparser/8.72.0/8.72.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@typescript-eslint%2fparser/8.72.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@typescript-eslint%2fparser/8.72.0/8.72.1?slim=true)
| devDependencies | patch | |
| @&#8203;vitest/eslint-plugin | [`1.6.27` →
`1.6.28`](https://renovatebot.com/diffs/npm/@vitest%2feslint-plugin/1.6.27/1.6.28)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/@vitest%2feslint-plugin/1.6.28?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vitest%2feslint-plugin/1.6.27/1.6.28?slim=true)
| devDependencies | patch | |
| [astral-sh/setup-uv](https://redirect.github.com/astral-sh/setup-uv) |
`v10.3.0` → `v10.3.2` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/astral-sh%2fsetup-uv/v10.3.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/astral-sh%2fsetup-uv/v10.3.0/v10.3.2?slim=true)
| action | patch | `v10.3.3` |
| [chart.js](https://www.chartjs.org)
([source](https://redirect.github.com/chartjs/Chart.js)) | [`4.5.1` →
`4.6.1`](https://renovatebot.com/diffs/npm/chart.js/4.5.1/4.6.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/chart.js/4.6.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/chart.js/4.5.1/4.6.1?slim=true)
| dependencies | minor | |
| [cli/cli](https://redirect.github.com/cli/cli) | `2.103.2` → `2.103.3`
|
![age](https://developer.mend.io/api/mc/badges/age/github-releases/cli%2fcli/2.103.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-releases/cli%2fcli/2.103.2/2.103.3?slim=true)
| | patch | |
|
[connectrpc.com/connect/v2](https://redirect.github.com/connectrpc/connect-go)
| `v2.0.1` → `v2.0.2` |
![age](https://developer.mend.io/api/mc/badges/age/go/connectrpc.com%2fconnect%2fv2/v2.0.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/connectrpc.com%2fconnect%2fv2/v2.0.1/v2.0.2?slim=true)
| require | patch | |
|
[crowdin/github-action](https://redirect.github.com/crowdin/github-action)
| `v3.4.0` → `v3.5.0` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/crowdin%2fgithub-action/v3.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/crowdin%2fgithub-action/v3.4.0/v3.5.0?slim=true)
| action | minor | |
| [djlint](https://redirect.github.com/djlint/djLint) | `==1.46.6` →
`==1.47.1` |
![age](https://developer.mend.io/api/mc/badges/age/pypi/djlint/1.47.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/djlint/1.46.6/1.47.1?slim=true)
| dependency-groups | minor | `1.49.2` (+4) |
| [docker/login-action](https://redirect.github.com/docker/login-action)
| `v4.6.0` → `v4.7.0` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/docker%2flogin-action/v4.7.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/docker%2flogin-action/v4.6.0/v4.7.0?slim=true)
| action | minor | |
| [elasticsearch](https://hub.docker.com/_/elasticsearch)
([source](https://redirect.github.com/elastic/dockerfiles)) | `3f4d63c`
→ `ef5cafd` | | | service | digest | |
|
[eslint-plugin-unicorn](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn)
| [`78.0.0` →
`79.0.0`](https://renovatebot.com/diffs/npm/eslint-plugin-unicorn/78.0.0/79.0.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-unicorn/79.0.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-unicorn/78.0.0/79.0.0?slim=true)
| devDependencies | major | |
| [eslint-plugin-vue](https://eslint.vuejs.org)
([source](https://redirect.github.com/vuejs/eslint-plugin-vue)) |
[`10.11.1` →
`10.12.0`](https://renovatebot.com/diffs/npm/eslint-plugin-vue/10.11.1/10.12.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-vue/10.12.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-vue/10.11.1/10.12.0?slim=true)
| devDependencies | minor | |
|
[getmeili/meilisearch](https://redirect.github.com/meilisearch/meilisearch)
| `c94e58c` → `1f578fa` | | | service | digest | |
| [gitea.dev/sdk](https://gitea.com/gitea/go-sdk) | `v1.3.0` → `v1.5.0`
|
![age](https://developer.mend.io/api/mc/badges/age/go/gitea.dev%2fsdk/v1.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/gitea.dev%2fsdk/v1.3.0/v1.5.0?slim=true)
| require | minor | `v1.5.1` |
|
[github.com/buildkite/terminal-to-html/v3](https://redirect.github.com/buildkite/terminal-to-html)
| `v3.17.1` → `v3.17.3` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fbuildkite%2fterminal-to-html%2fv3/v3.17.3?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fbuildkite%2fterminal-to-html%2fv3/v3.17.1/v3.17.3?slim=true)
| require | patch | |
|
[github.com/dlclark/regexp2/v2](https://redirect.github.com/dlclark/regexp2)
| `v2.9.1` → `v2.11.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdlclark%2fregexp2%2fv2/v2.11.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdlclark%2fregexp2%2fv2/v2.9.1/v2.11.0?slim=true)
| require | minor | `v2.12.0` |
|
[github.com/getkin/kin-openapi](https://redirect.github.com/getkin/kin-openapi)
| `v0.151.0` → `v0.152.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgetkin%2fkin-openapi/v0.152.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgetkin%2fkin-openapi/v0.151.0/v0.152.0?slim=true)
| require | minor | |
|
[github.com/go-co-op/gocron/v2](https://redirect.github.com/go-co-op/gocron)
| `v2.22.0` → `v2.23.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-co-op%2fgocron%2fv2/v2.23.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-co-op%2fgocron%2fv2/v2.22.0/v2.23.0?slim=true)
| require | minor | |
|
[github.com/go-redsync/redsync/v4](https://redirect.github.com/go-redsync/redsync)
| `v4.18.0` → `v4.18.1` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-redsync%2fredsync%2fv4/v4.18.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-redsync%2fredsync%2fv4/v4.18.0/v4.18.1?slim=true)
| require | patch | |
| [github.com/google/pprof](https://redirect.github.com/google/pprof) |
`1facf8f` → `dbd0ed3` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgoogle%2fpprof/v0.0.0-20261026192309-dbd0ed30aa88?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgoogle%2fpprof/v0.0.0-20261016150608-1facf8f91b50/v0.0.0-20261026192309-dbd0ed30aa88?slim=true)
| require | digest | |
|
[gitlab.com/gitlab-org/api/client-go/v3](https://gitlab.com/gitlab-org/api/client-go)
| `v3.29.0` → `v3.39.1` |
![age](https://developer.mend.io/api/mc/badges/age/go/gitlab.com%2fgitlab-org%2fapi%2fclient-go%2fv3/v3.39.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/gitlab.com%2fgitlab-org%2fapi%2fclient-go%2fv3/v3.29.0/v3.39.1?slim=true)
| require | minor | `v3.40.2` (+3) |
| [google.golang.org/grpc](https://redirect.github.com/grpc/grpc-go) |
`v1.84.0` → `v1.85.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/google.golang.org%2fgrpc/v1.85.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/google.golang.org%2fgrpc/v1.84.0/v1.85.0?slim=true)
| require | minor | |
| [katex](https://katex.org)
([source](https://redirect.github.com/KaTeX/KaTeX)) | [`0.19.0` →
`0.19.1`](https://renovatebot.com/diffs/npm/katex/0.19.0/0.19.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/katex/0.19.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/katex/0.19.0/0.19.1?slim=true)
| dependencies | patch | |
|
[markdownlint-cli](https://redirect.github.com/igorshubovych/markdownlint-cli)
| [`0.49.1` →
`0.50.0`](https://renovatebot.com/diffs/npm/markdownlint-cli/0.49.1/0.50.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/markdownlint-cli/0.50.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/markdownlint-cli/0.49.1/0.50.0?slim=true)
| devDependencies | minor | |
|
[material-icon-theme](https://redirect.github.com/material-extensions/vscode-material-icon-theme/blob/main/README.md)
([source](https://redirect.github.com/material-extensions/vscode-material-icon-theme))
| [`5.39.0` →
`5.40.0`](https://renovatebot.com/diffs/npm/material-icon-theme/5.39.0/5.40.0)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/material-icon-theme/5.40.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/material-icon-theme/5.39.0/5.40.0?slim=true)
| devDependencies | minor | |
| [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) | `v1.60.1` →
`v1.61.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/modernc.org%2fsqlite/v1.61.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/modernc.org%2fsqlite/v1.60.1/v1.61.0?slim=true)
| require | minor | `v1.61.1` |
| [pnpm](https://pnpm.io)
([source](https://redirect.github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm))
| [`12.14.0` →
`12.16.1`](https://renovatebot.com/diffs/npm/pnpm/12.14.0/12.16.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.16.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.14.0/12.16.1?slim=true)
| packageManager | minor | |
| [pnpm](https://pnpm.io)
([source](https://redirect.github.com/pnpm/pnpm/tree/HEAD/pnpm/npm/pnpm))
| [`>= 12.14.0` → `>=
12.16.1`](https://renovatebot.com/diffs/npm/pnpm/12.14.0/12.16.1) |
![age](https://developer.mend.io/api/mc/badges/age/npm/pnpm/12.16.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/pnpm/12.14.0/12.16.1?slim=true)
| engines | minor | |
| [postcss](https://postcss.org/)
([source](https://redirect.github.com/postcss/postcss)) | [`8.5.31` →
`8.5.32`](https://renovatebot.com/diffs/npm/postcss/8.5.31/8.5.32) |
![age](https://developer.mend.io/api/mc/badges/age/npm/postcss/8.5.32?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/postcss/8.5.31/8.5.32?slim=true)
| dependencies | patch | |
| [renovate](https://renovatebot.com)
([source](https://redirect.github.com/renovatebot/renovate)) |
[`44.172.2` →
`44.190.1`](https://octochangelog.com/compare?repo=renovatebot%2Frenovate&from=44.172.2&to=44.190.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/renovate/44.190.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/renovate/44.172.2/44.190.1?slim=true)
| | minor | `44.200.0` (+21) |
|
[renovatebot/github-action](https://redirect.github.com/renovatebot/github-action)
| `v46.3.9` → `v46.3.10` |
![age](https://developer.mend.io/api/mc/badges/age/github-tags/renovatebot%2fgithub-action/v46.3.10?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/github-tags/renovatebot%2fgithub-action/v46.3.9/v46.3.10?slim=true)
| action | patch | `v46.3.12` (+1) |
|
[typescript-eslint](https://typescript-eslint.io/packages/typescript-eslint)
([source](https://redirect.github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint))
| [`8.72.0` →
`8.72.1`](https://renovatebot.com/diffs/npm/typescript-eslint/8.72.0/8.72.1)
|
![age](https://developer.mend.io/api/mc/badges/age/npm/typescript-eslint/8.72.1?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/typescript-eslint/8.72.0/8.72.1?slim=true)
| devDependencies | patch | |
| [vite](https://vite.dev)
([source](https://redirect.github.com/vitejs/vite/tree/HEAD/packages/vite))
| [`8.3.4` →
`8.3.5`](https://renovatebot.com/diffs/npm/vite/8.3.4/8.3.5) |
![age](https://developer.mend.io/api/mc/badges/age/npm/vite/8.3.5?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/8.3.4/8.3.5?slim=true)
| dependencies | patch | |

---

### Release Notes

<details>
<summary>primer/octicons (@&#8203;primer/octicons)</summary>

###
[`v19.42.0`](https://redirect.github.com/primer/octicons/releases/tag/%40primer/octicons%4019.42.0)

[Compare
Source](https://redirect.github.com/primer/octicons/compare/@primer/octicons@19.41.0...@primer/octicons@19.42.0)

##### Minor Changes

- [#&#8203;1381](https://redirect.github.com/primer/octicons/pull/1381)
[`d25ce68df9d08abd65431a8a48b45446432f1b45`](d25ce68df9)
Thanks [@&#8203;edokoa](https://redirect.github.com/edokoa)! - Add the
16px `git-draft-agent`, `git-pull-request-agent`, `check-agent`,
`git-que-merge-agent`, `git-closed-agent`, and `git-merge-agent` icons
for agent-assisted draft, open, ready-to-merge, queued, closed, and
merged pull request states.

</details>

<details>
<summary>typescript-eslint/typescript-eslint
(@&#8203;typescript-eslint/parser)</summary>

###
[`v8.72.1`](https://redirect.github.com/typescript-eslint/typescript-eslint/blob/HEAD/packages/parser/CHANGELOG.md#8721-2026-10-26)

[Compare
Source](https://redirect.github.com/typescript-eslint/typescript-eslint/compare/v8.72.0...v8.72.1)

This was a version bump only for parser to align it with other projects,
there were no code changes.

See [GitHub
Releases](https://redirect.github.com/typescript-eslint/typescript-eslint/releases/tag/v8.72.1)
for more information.

You can read about our [versioning
strategy](https://typescript-eslint.io/users/versioning) and
[releases](https://typescript-eslint.io/users/releases) on our website.

</details>

<details>
<summary>astral-sh/setup-uv</summary>

###
[`v10.3.2`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v10.3.2):
🌈 update known checksums for 0.13.5

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v10.3.1...v10.3.2)

#### Changes

#### 🧰 Maintenance

- chore: update known checksums for 0.13.5
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1103](https://redirect.github.com/astral-sh/setup-uv/issues/1103))

#### 📚 Documentation

- docs: update version references to v10.3.1
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1102](https://redirect.github.com/astral-sh/setup-uv/issues/1102))

###
[`v10.3.1`](https://redirect.github.com/astral-sh/setup-uv/releases/tag/v10.3.1):
🌈 preserve cache error class names in save-cache bundle

[Compare
Source](https://redirect.github.com/astral-sh/setup-uv/compare/v10.3.0...v10.3.1)

#### Changes

This release includes a fix so that multiple concurrent workflows trying
to upload the same cache no longer warn

`##[warning]Failed to save: Unable to reserve cache with key ...,
another job may be creating this cache.`

#### 🐛 Bug fixes

- fix: preserve cache error class names in save-cache bundle
[@&#8203;dannyward630](https://redirect.github.com/dannyward630)
([#&#8203;1093](https://redirect.github.com/astral-sh/setup-uv/issues/1093))

#### 🧰 Maintenance

- chore: update known checksums for 0.13.4
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1099](https://redirect.github.com/astral-sh/setup-uv/issues/1099))
- chore: update known checksums for 0.13.2/0.13.3
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1098](https://redirect.github.com/astral-sh/setup-uv/issues/1098))
- chore: update known checksums for 0.13.1
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1094](https://redirect.github.com/astral-sh/setup-uv/issues/1094))
- Verify npm dependency signatures before builds
[@&#8203;zaniebot](https://redirect.github.com/zaniebot)
([#&#8203;1027](https://redirect.github.com/astral-sh/setup-uv/issues/1027))

#### 📚 Documentation

- docs: cache-dependency-glob matches relative to the working directory
[@&#8203;flange-ipb](https://redirect.github.com/flange-ipb)
([#&#8203;1092](https://redirect.github.com/astral-sh/setup-uv/issues/1092))
- docs: update version references to v10.3.0
@&#8203;[github-actions\[bot\]](https://redirect.github.com/apps/github-actions)
([#&#8203;1090](https://redirect.github.com/astral-sh/setup-uv/issues/1090))

#### ⬆️ Dependency updates

- chore(deps): roll up Dependabot updates
[@&#8203;eifinger](https://redirect.github.com/eifinger)
([#&#8203;1101](https://redirect.github.com/astral-sh/setup-uv/issues/1101))

</details>

<details>
<summary>chartjs/Chart.js (chart.js)</summary>

###
[`v4.6.1`](https://redirect.github.com/chartjs/Chart.js/releases/tag/v4.6.1)

[Compare
Source](https://redirect.github.com/chartjs/Chart.js/compare/v4.5.1...v4.6.1)

### Essential Links

- [npm](https://www.npmjs.com/package/chart.js)
- [Migration
guide](https://www.chartjs.org/docs/4.6.1/migration/v4-migration.html)
- [Docs](https://www.chartjs.org/docs/4.6.1/)
- [API](https://www.chartjs.org/docs/4.6.1/api/)
- [Samples](https://www.chartjs.org/docs/4.6.1/samples/information.html)

*
[#&#8203;12355](https://redirect.github.com/chartjs/Chart.js/issues/12355)
fix: trusted publishing fix + version 4.6.1 bump

Thanks to [@&#8203;etimberg](https://redirect.github.com/etimberg)

</details>

<details>
<summary>cli/cli</summary>

###
[`v2.103.3`](https://redirect.github.com/cli/cli/releases/tag/v2.103.3):
GitHub CLI 2.103.3

[Compare
Source](https://redirect.github.com/cli/cli/compare/v2.103.2...v2.103.3)

##### What's Changed

##### 🐛 Fixes

- fix(gh auth status): explain expired expiring tokens with
`--no-refresh` by [@&#8203;babakks](https://redirect.github.com/babakks)
in [#&#8203;14815](https://redirect.github.com/cli/cli/pull/14815)

**Full Changelog**:
<https://github.com/cli/cli/compare/v2.103.2...v2.103.3>

</details>

<details>
<summary>connectrpc/connect-go (connectrpc.com/connect/v2)</summary>

###
[`v2.0.2`](https://redirect.github.com/connectrpc/connect-go/releases/tag/v2.0.2)

[Compare
Source](https://redirect.github.com/connectrpc/connect-go/compare/v2.0.1...v2.0.2)

#### What's Changed

##### Bugfixes

- Fix data race reading ClientInfo after a cancelled call by
[@&#8203;emcfarlane](https://redirect.github.com/emcfarlane) in
[#&#8203;1036](https://redirect.github.com/connectrpc/connect-go/pull/1036)
- List JSON charset variants in Accept-Post by
[@&#8203;emcfarlane](https://redirect.github.com/emcfarlane) in
[#&#8203;1037](https://redirect.github.com/connectrpc/connect-go/pull/1037)
- Flush response headers in ServerStream.SendHeaders over HTTP by
[@&#8203;raveesh-me](https://redirect.github.com/raveesh-me) in
[#&#8203;1035](https://redirect.github.com/connectrpc/connect-go/pull/1035)
- Stop reading oversized messages after rejecting them by
[@&#8203;stefanvanburen](https://redirect.github.com/stefanvanburen) in
[#&#8203;1039](https://redirect.github.com/connectrpc/connect-go/pull/1039)

#### New Contributors

- [@&#8203;raveesh-me](https://redirect.github.com/raveesh-me) made
their first contribution in
[#&#8203;1035](https://redirect.github.com/connectrpc/connect-go/pull/1035)

**Full Changelog**:
<https://github.com/connectrpc/connect-go/compare/v2.0.1...v2.0.2>

</details>

<details>
<summary>crowdin/github-action</summary>

###
[`v3.5.0`](https://redirect.github.com/crowdin/github-action/releases/tag/v3.5.0)

[Compare
Source](https://redirect.github.com/crowdin/github-action/compare/v3.4.0...v3.5.0)

#### What's Changed

- CLI
[5.5.0](https://redirect.github.com/crowdin/crowdin-cli/releases/tag/5.5.0)
by [@&#8203;andrii-bodnar](https://redirect.github.com/andrii-bodnar)

**Full Changelog**:
<https://github.com/crowdin/github-action/compare/v3.4.0...v3.5.0>

</details>

<details>
<summary>djlint/djLint (djlint)</summary>

###
[`v1.47.1`](https://redirect.github.com/djlint/djLint/blob/HEAD/CHANGELOG.md#1471---2026-10-26)

[Compare
Source](https://redirect.github.com/djlint/djLint/compare/v1.47.0...v1.47.1)

##### Fix

- `preserve-inline` in the `js` setting `brace_style` takes effect, so
`{ bubbles: true }` stays on one line in a `<script>` or in an attribute
formatted with `--format-attribute-js-json`. It used to be dropped
before the code was formatted.
- The json schema allows any option under `css` and `js`, so a strict
validator such as Tombi no longer reports `indent_size` there.

###
[`v1.47.0`](https://redirect.github.com/djlint/djLint/blob/HEAD/CHANGELOG.md#1470---2026-10-22)

[Compare
Source](https://redirect.github.com/djlint/djLint/compare/v1.46.6...v1.47.0)

##### Feature

- New option `--sort-load-tags` / `sort_load_tags` sorts the libraries
in a `{% load %}` tag and drops the ones written twice, so `{% load
static i18n %}` becomes `{% load i18n static %}`. In `{% load a b from
library %}` the names before `from` are sorted. It is off by default:
where two libraries define a tag or filter of the same name, the one
loaded last wins, so sorting can change which one a template uses.
- The formatter drops the spaces `H012` reports around an attribute's
`=`, so `<div class = "a">` becomes `<div class="a">`.

##### Changed

- `H010` reports the names the formatter lowercases, in any mix of case,
so `Width="3"` is now reported. A framework binding such as `:itemID` or
`@ID` and a longer name such as `data-ID` no longer are, since the
formatter leaves their case alone.
- `H018` is not reported under `--close-void-tags`, since the formatter
then writes the `/>` ending the rule reports. `--ignore-case` already
worked this way for `H009` and `H010`.

##### Fix

- `H020` reports an empty pair whose end tag has whitespace before its
`>`, such as `<div></div\n>`.
- `H026` reports a `class` or `id` holding only whitespace, such as
`class=" "`, and one written with spaces around the `=`.
- `H033` reports an action padded at both ends once. It used to report
it twice, unless an attribute before `action` happened to make the two
reports look the same.
- Markup written inside an attribute value, as in `<p title="<img
src='a'>">`, is text, and `H005`, `H008`, `H010`, `H012`, `D018`,
`J018`, `H024`, `H026`, `H029` and `H033` no longer report it. `H046`
and `H051` read `tabindex` and `role` from the tag's own attributes, so
`tabindex=1` written in a value or in the page text is not reported
either.
- `T001` and `T027` read a tag holding a dict, such as `{{ f({"a": {"b":
1}}) }}`, up to the `}}` that closes it rather than the first `}}`
inside it.
- `T032` steps over an escaped quote in a string, so the spaces in `{{
x|default:"a \"b c\"" }}` are not reported.
- `H015` reports a heading followed by a tag on the same line anywhere
in the file. It used to look only at the last line.
- The rules that read a tag's attributes step over a template tag
written in it, including a handlebars `{{{attrs}}}` and a dict as in `{{
{'class': 'x'}|xmlattr }}`, so `<img {{ attrs }} src='a'>` is reported
by `H008`, which used to stop reading at the `{{`.
- `T038` steps over a string in a block tag, so the `{%` in `{% with
a="{%" %}` does not cut the tag short.

##### Performance

- The rules that read a tag's attributes stop at a stray `<`, and
`H015`, `H030`, `H036`, `T001`, `T027`, `T038` and `T039` no longer read
the rest of the document again for each tag. A file holding thousands of
unclosed tags or headings on one line is linted in a fraction of a
second, where it used to take minutes.

</details>

<details>
<summary>docker/login-action</summary>

###
[`v4.7.0`](https://redirect.github.com/docker/login-action/releases/tag/v4.7.0)

[Compare
Source](https://redirect.github.com/docker/login-action/compare/v4.6.0...v4.7.0)

- Preserve explicit ECR settings in `registry-auth` by
[@&#8203;crazy-max](https://redirect.github.com/crazy-max) in
[#&#8203;1089](https://redirect.github.com/docker/login-action/pull/1089)
- Use registry hosts for scoped credentials by
[@&#8203;crazy-max](https://redirect.github.com/crazy-max) in
[#&#8203;1088](https://redirect.github.com/docker/login-action/pull/1088)
- Raise Docker Hub OIDC max expiry to 6 hours by
[@&#8203;crazy-max](https://redirect.github.com/crazy-max) in
[#&#8203;1068](https://redirect.github.com/docker/login-action/pull/1068)
- Log Docker Hub OIDC token exchange progress by
[@&#8203;crazy-max](https://redirect.github.com/crazy-max) in
[#&#8203;1061](https://redirect.github.com/docker/login-action/pull/1061)
- Bump
[@&#8203;aws-sdk/client-ecr](https://redirect.github.com/aws-sdk/client-ecr)
and
[@&#8203;aws-sdk/client-ecr-public](https://redirect.github.com/aws-sdk/client-ecr-public)
to 3.1154.0 in
[#&#8203;1093](https://redirect.github.com/docker/login-action/pull/1093)
- Bump
[@&#8203;docker/actions-toolkit](https://redirect.github.com/docker/actions-toolkit)
from 0.94.0 to 0.107.0 in
[#&#8203;1100](https://redirect.github.com/docker/login-action/pull/1100)
- Bump [@&#8203;humanfs/node](https://redirect.github.com/humanfs/node)
from 0.16.7 to 0.16.8 in
[#&#8203;1077](https://redirect.github.com/docker/login-action/pull/1077)
- Bump csv-parse from 7.0.1 to 7.0.3 in
[#&#8203;1078](https://redirect.github.com/docker/login-action/pull/1078)
- Bump handlebars from 4.7.9 to 4.7.10 in
[#&#8203;1091](https://redirect.github.com/docker/login-action/pull/1091)
- Bump js-yaml from 5.2.2 to 5.5.0 in
[#&#8203;1066](https://redirect.github.com/docker/login-action/pull/1066)
- Bump postcss from 8.5.22 to 8.5.26 in
[#&#8203;1071](https://redirect.github.com/docker/login-action/pull/1071)
- Bump postcss-selector-parser from 7.1.1 to 7.1.6 in
[#&#8203;1092](https://redirect.github.com/docker/login-action/pull/1092)
- Bump source-map-js from 1.2.1 to 1.2.2 in
[#&#8203;1090](https://redirect.github.com/docker/login-action/pull/1090)
- Bump undici from 6.27.0 to 6.29.1 in
[#&#8203;1085](https://redirect.github.com/docker/login-action/pull/1085)
- Bump uuid from 14.0.1 to 14.0.3 in
[#&#8203;1096](https://redirect.github.com/docker/login-action/pull/1096)

**Full Changelog**:
<https://github.com/docker/login-action/compare/v4.6.0...v4.7.0>

</details>

<details>
<summary>sindresorhus/eslint-plugin-unicorn
(eslint-plugin-unicorn)</summary>

###
[`v79.0.0`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/releases/tag/v79.0.0)

[Compare
Source](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/compare/v78.0.0...v79.0.0)

#### Breaking

- `no-top-level-side-effects`: Focus on mutations of globals, and remove
it from the `unopinionated` preset
([#&#8203;3915](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3915))
[`24abac4`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/24abac48)
- Unknown calls are no longer reported by default. Enable the new
[`checkUnknownCalls`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-top-level-side-effects.md#checkunknowncalls)
option for stricter checking.
- Mutations of globals, like `document.title = 'x'` and
`Object.assign(globalThis, …)`, are now reported, also in declarations
and default exports. You may see new reports.

#### Improvements

- Infer types from JSDoc `@type`, `@param`, and `@returns` annotations
in rules that use type information
([#&#8203;3941](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3941))
[`2b4bfab`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/2b4bfab0)
- Handle template literals without expressions like strings in more
rules
([#&#8203;3943](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3943))
[`8bd8c06`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/8bd8c063)
- Autofix more code that contains comments, while keeping the comments
([#&#8203;3939](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3939))
[`87baf82`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/87baf82f)
- Fix autofixes in many rules that could produce broken code or wrong
parentheses
([#&#8203;3917](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3917))
[`fed7eac`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/fed7eac2)
- Make autofixes safer in many rules
- Fixes that can change behavior in rare cases are now suggestions, for
example in `prefer-spread`, `prefer-math-min-max`,
`prefer-modern-dom-apis`, and `prefer-switch`.
- Improve performance
[`175efaf`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/175efaf1)
[`3dd8fd2`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/3dd8fd28)
- `comment-content`: Add
[`ignore`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/comment-content.md#ignore)
option
([#&#8203;3950](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3950))
[`3fe046e`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/3fe046e6)
- `consistent-function-scoping`: Ignore functions in Vitest hoisted
factories
([#&#8203;3905](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3905))
[`98912a9`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/98912a91)
- `custom-error-definition`: Ignore abstract TypeScript classes
([#&#8203;3934](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3934))
[`ee3501f`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/ee3501fb)
- `no-non-function-verb-prefix`: Ignore shorthand destructuring
([#&#8203;3926](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3926))
[`76e5760`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/76e5760b)
- `no-top-level-side-effects`: Recognize more config helpers
([#&#8203;3916](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3916))
[`d459c51`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/d459c511)
- `no-useless-coercion`: Preserve boolean return types in TypeScript
([#&#8203;3904](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3904))
[`bef1571`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/bef15714)
- `prefer-array-some`: Check `.find()` results used as booleans
([#&#8203;3913](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3913))
[`c957bf3`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/c957bf3d)
- `prefer-default-parameters`: Skip unsafe logical OR fallbacks
([#&#8203;3951](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3951))
[`bcec97a`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/bcec97a4)
- `prefer-includes`: Skip incompatible TypeScript search values
([#&#8203;3927](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3927))
[`d66441a`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/d66441aa)
- `prefer-includes-over-repeated-comparisons`: Report negated and typed
loose comparisons
([#&#8203;3935](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3935))
[`6e21cb1`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/6e21cb1b)
- `prefer-includes-over-repeated-comparisons`: Preserve TypeScript union
narrowing
([#&#8203;3910](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3910))
[`b5c479d`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/b5c479d8)
- `prefer-literal-ascii`: Preserve escaped trailing spaces in templates
([#&#8203;3911](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3911))
[`360094b`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/360094bb)
- `prefer-minimal-ternary`: Skip minimizations that break types
([#&#8203;3892](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3892))
[`f022d6d`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/f022d6d1)
- `prefer-modern-math-apis`: Use suggestions for changes that can affect
numeric results
([#&#8203;3914](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3914))
[`8af7c17`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/8af7c172)
- `prefer-simple-condition-first`: Improve `typeof` comparisons and
guards
([#&#8203;3948](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3948))
[`ee3edf0`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/ee3edf06)
- `prefer-simplified-conditions`: Handle equivalent logical expressions
([#&#8203;3938](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3938))
[`a0b2788`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/a0b2788b)
- `prefer-simplified-conditions`: Factor common terms in longer chains
and static templates
([#&#8203;3942](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3942))
[`0d8c8bc`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/0d8c8bcb)
- `prefer-smaller-scope`: Check TypeScript type aliases
([#&#8203;3928](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3928))
[`290d922`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/290d922c)
- `prefer-string-raw`: Preserve TypeScript literal types in autofixes
([#&#8203;3912](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3912))
[`0611546`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/06115464)
- `prefer-ternary`: Preserve consecutive early-return guards
([#&#8203;3945](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3945))
[`3fa840f`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/3fa840fe)
- `require-post-message-target-origin`: Skip incompatible typed methods
([#&#8203;3925](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3925))
[`387194b`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/387194bd)
- `throw-new-error`: Ignore calls that return functions
([#&#8203;3894](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/issues/3894))
[`5e35def`](https://redirect.github.com/sindresorhus/eslint-plugin-unicorn/commit/5e35def7)

***

</details>

<details>
<summary>vuejs/eslint-plugin-vue (eslint-plugin-vue)</summary>

###
[`v10.12.0`](https://redirect.github.com/vuejs/eslint-plugin-vue/blob/HEAD/CHANGELOG.md#10120)

[Compare
Source](https://redirect.github.com/vuejs/eslint-plugin-vue/compare/v10.11.1...v10.12.0)

##### New rules

- Added new
[`vue/no-content-outside-block`](https://eslint.vuejs.org/rules/no-content-outside-block.html)
rule
([#&#8203;3119](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3119))
- Added new
[`vue/no-define-model-default`](https://eslint.vuejs.org/rules/no-define-model-default.html)
rule
([#&#8203;3146](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3146))
- Added new
[`vue/no-props-shadow`](https://eslint.vuejs.org/rules/no-props-shadow.html)
rule
([#&#8203;3118](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3118))

##### New rule options

- Added `required` option to
[`vue/block-lang`](https://eslint.vuejs.org/rules/block-lang.html) to
report components that omit a required block entirely
([#&#8203;3125](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3125))
- Added `ignoreMixins` option to
[`vue/require-explicit-emits`](https://eslint.vuejs.org/rules/require-explicit-emits.html)
rule
([#&#8203;3141](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3141))

##### Fixed false negatives (may result in more lint issues)

- Added support for refs detected by type information, such as refs
returned from composables, to
[`vue/no-ref-as-operand`](https://eslint.vuejs.org/rules/no-ref-as-operand.html)
rule
([#&#8203;3144](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3144))
- Fixed
[`vue/no-dupe-keys`](https://eslint.vuejs.org/rules/no-dupe-keys.html)
not reporting `defineModel()` names that collide with a prop, another
model, or a `<script setup>` variable
([#&#8203;3163](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3163))
- Fixed
[`vue/no-dupe-keys`](https://eslint.vuejs.org/rules/no-dupe-keys.html)
not reporting `<script setup>` variables that shadow a prop while
aliasing a different one, such as `const foo = toRef(props, 'bar')`
([#&#8203;3124](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3124))
- Fixed
[`vue/no-multi-spaces`](https://eslint.vuejs.org/rules/no-multi-spaces.html)
not reporting multiple spaces in the tags of some top-level blocks, e.g.
`<script setup>`
([#&#8203;3162](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3162))
- Fixed
[`vue/no-ref-as-operand`](https://eslint.vuejs.org/rules/no-ref-as-operand.html)
and
[`vue/no-ref-object-reactivity-loss`](https://eslint.vuejs.org/rules/no-ref-object-reactivity-loss.html)
to also check refs created with `useTemplateRef()`
([#&#8203;3154](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3154))
- Fixed
[`vue/no-unused-properties`](https://eslint.vuejs.org/rules/no-unused-properties.html)
not reporting unused non-props properties in components that use
`$props` as a whole (e.g. `v-bind="$props"`)
([#&#8203;3161](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3161))
- Fixed
[`vue/padding-lines-in-component-definition`](https://eslint.vuejs.org/rules/padding-lines-in-component-definition.html)
to check every pair of adjacent properties, not only the first, when
`withinOption` or `withinEach` is not set
([#&#8203;3155](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3155))
- Fixed
[`vue/require-default-prop`](https://eslint.vuejs.org/rules/require-default-prop.html),
[`vue/no-boolean-default`](https://eslint.vuejs.org/rules/no-boolean-default.html),
[`vue/no-required-prop-with-default`](https://eslint.vuejs.org/rules/no-required-prop-with-default.html)
and
[`vue/require-valid-default-prop`](https://eslint.vuejs.org/rules/require-valid-default-prop.html)
not handling defaults that are passed to `withDefaults()` by reference
or spread instead of an inline object literal
([#&#8203;3114](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3114))

##### Fixed false positives (may result in fewer lint issues)

- Changed
[`vue/singleline-html-element-content-newline`](https://eslint.vuejs.org/rules/singleline-html-element-content-newline.html)
and
[`vue/multiline-html-element-content-newline`](https://eslint.vuejs.org/rules/multiline-html-element-content-newline.html)
rules to ignore `router-link` and `nuxt-link` elements by default
([#&#8203;3165](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3165))
- Fixed
[`vue/no-undef-properties`](https://eslint.vuejs.org/rules/no-undef-properties.html)
false positives for properties returned via `...toRefs(reactive({ ...
}))` from `setup()`
([#&#8203;3134](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3134))
- Fixed
[`vue/require-typed-ref`](https://eslint.vuejs.org/rules/require-typed-ref.html)
false positives when an explicit type annotation already applies to the
`ref()` call
([#&#8203;3123](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3123))
- Fixed
[`vue/v-if-else-key`](https://eslint.vuejs.org/rules/v-if-else-key.html)
false positive for components with a bound `:key`
([#&#8203;3150](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3150))

##### Other changes

- Removed Reactivity Transform support from
[`vue/no-ref-object-reactivity-loss`](https://eslint.vuejs.org/rules/no-ref-object-reactivity-loss.html),
since the Reactivity Transform proposal was dropped from Vue
([#&#8203;3126](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3126))
- Changed
[`vue/html-self-closing`](https://eslint.vuejs.org/rules/html-self-closing.html)
to treat Vue's special `<slot>` and `<template>` elements as components
(`html.component` option) instead of normal HTML elements
([#&#8203;3164](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3164))
- Changed
[`vue/attributes-order`](https://eslint.vuejs.org/rules/attributes-order.html)
rule to provide a suggestion instead of an autofix when the fix would
move a binding across `v-bind="object"`
([#&#8203;3145](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3145))
- Improved performance of
[`vue/no-reserved-component-names`](https://eslint.vuejs.org/rules/no-reserved-component-names.html)
by no longer rebuilding its reserved name sets for every file
([#&#8203;3147](https://redirect.github.com/vuejs/eslint-plugin-vue/pull/3147))

</details>

<details>
<summary>gitea/go-sdk (gitea.dev/sdk)</summary>

### [`v1.5.0`](https://gitea.com/gitea/go-sdk/releases/tag/v1.5.0)

[Compare Source](https://gitea.com/gitea/go-sdk/compare/v1.4.0...v1.5.0)

#### What's Changed

- feat: allow PAT-authenticated clients to manage access tokens in
[#&#8203;840](https://gitea.com/gitea/go-sdk/pulls/840)
- fix(pull): surface the server's error when a merge is refused in
[#&#8203;870](https://gitea.com/gitea/go-sdk/pulls/870)
- feat(hook): add name option to webhooks in
[#&#8203;867](https://gitea.com/gitea/go-sdk/pulls/867)
- feat: cover the API options gitea-cli still calls raw in
[#&#8203;862](https://gitea.com/gitea/go-sdk/pulls/862)
- ci: implement gitea's cache-seeder in
[#&#8203;866](https://gitea.com/gitea/go-sdk/pulls/866)
- Add bitbucketserver and forgejo migration service types in
[#&#8203;868](https://gitea.com/gitea/go-sdk/pulls/868)
- chore(deps): update dependencies in
[#&#8203;869](https://gitea.com/gitea/go-sdk/pulls/869)
- chore: remove the Client wrapper generator in
[#&#8203;865](https://gitea.com/gitea/go-sdk/pulls/865)
- feat: add force push and stale approval fields to branch protections
in [#&#8203;864](https://gitea.com/gitea/go-sdk/pulls/864)

#### Contributors

- [@&#8203;sedlund](https://redirect.github.com/sedlund)
- [@&#8203;Svtter](https://redirect.github.com/Svtter)
- [@&#8203;SudhanshuSingh](https://redirect.github.com/SudhanshuSingh)
- [@&#8203;bircni](https://redirect.github.com/bircni)
- [@&#8203;silverwind](https://redirect.github.com/silverwind)
- [@&#8203;lunny](https://redirect.github.com/lunny)
- [@&#8203;renovate-bot](https://redirect.github.com/renovate-bot)

#### New Contributors

- [@&#8203;Svtter](https://redirect.github.com/Svtter) made their first
contribution in [#&#8203;870](https://gitea.com/gitea/go-sdk/pulls/870)
- [@&#8203;bircni](https://redirect.github.com/bircni) made their first
contribution in [#&#8203;862](https://gitea.com/gitea/go-sdk/pulls/862)
- [@&#8203;SudhanshuSingh](https://redirect.github.com/SudhanshuSingh)
made their first contribution in
[#&#8203;864](https://gitea.com/gitea/go-sdk/pulls/864)

**Full Changelog**:
[v1.4.0...v1.5.0](https://gitea.com/gitea/go-sdk/compare/v1.4.0...v1.5.0)

### [`v1.4.0`](https://gitea.com/gitea/go-sdk/releases/tag/v1.4.0)

[Compare Source](https://gitea.com/gitea/go-sdk/compare/v1.3.0...v1.4.0)

#### What's Changed

- fix: retry server version lookup after failure in
[#&#8203;829](https://gitea.com/gitea/go-sdk/pulls/829)
- chore(deps): update dependencies, adapt to Gitea 28 in
[#&#8203;863](https://gitea.com/gitea/go-sdk/pulls/863)
- chore: update golangci-lint for Go 1.27.2, exclude dagger from
Renovate in [#&#8203;861](https://gitea.com/gitea/go-sdk/pulls/861)

**Note**: Gitea skipped 1.28, so the project and workflow run
approve/cancel/logs APIs are now gated on Gitea 28.0.0. Clients pinning
`SetGiteaVersion("1.28.0")` must switch to `"28.0.0"`.

#### Contributors

- [@&#8203;noeljackson](https://redirect.github.com/noeljackson)
- [@&#8203;silverwind](https://redirect.github.com/silverwind)
- [@&#8203;renovate-bot](https://redirect.github.com/renovate-bot)

**Full Changelog**:
[v1.3.0...v1.4.0](https://gitea.com/gitea/go-sdk/compare/v1.3.0...v1.4.0)

Written by Claude.

</details>

<details>
<summary>buildkite/terminal-to-html
(github.com/buildkite/terminal-to-html/v3)</summary>

###
[`v3.17.3`](https://redirect.github.com/buildkite/terminal-to-html/releases/tag/v3.17.3)

[Compare
Source](https://redirect.github.com/buildkite/terminal-to-html/compare/v3.17.2...v3.17.3)

####
[v3.17.3](https://redirect.github.com/buildkite/terminal-to-html/tree/v3.17.3)

[Full
Changelog](https://redirect.github.com/buildkite/terminal-to-html/compare/v3.17.2...v3.17.3)

This release provides the Linux, macOS, and Windows binaries missing
from v3.17.2. Apart from the version number, application code is
unchanged from v3.17.2.

##### Changed

- Prepare versioned binaries for v3.17.3.
[#&#8203;293](https://redirect.github.com/buildkite/terminal-to-html/pull/293)
([@&#8203;wolfeidau](https://redirect.github.com/wolfeidau))
- Document the draft-first binary release process, including artifact
verification and immutable release restrictions.
[#&#8203;292](https://redirect.github.com/buildkite/terminal-to-html/pull/292)
([@&#8203;wolfeidau](https://redirect.github.com/wolfeidau))

##### Included from v3.17.2

- Remove `html/template` from the rendering path to avoid a runtime trap
when rendering with TinyGo, while preserving the generated HTML.
[#&#8203;282](https://redirect.github.com/buildkite/terminal-to-html/pull/282)
([@&#8203;dannymidnight](https://redirect.github.com/dannymidnight))
- Raise the minimum supported Go version to **1.26.10** and update the
development/CI Go Docker image.
[#&#8203;290](https://redirect.github.com/buildkite/terminal-to-html/pull/290)
([@&#8203;wolfeidau](https://redirect.github.com/wolfeidau))
- Bump `golang.org/x/sys` to 0.47.0.
[#&#8203;280](https://redirect.github.com/buildkite/terminal-to-html/pull/280)
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

###
[`v3.17.2`](https://redirect.github.com/buildkite/terminal-to-html/releases/tag/v3.17.2)

[Compare
Source](https://redirect.github.com/buildkite/terminal-to-html/compare/v3.17.1...v3.17.2)

####
[v3.17.2](https://redirect.github.com/buildkite/terminal-to-html/tree/v3.17.2)

[Full
Changelog](https://redirect.github.com/buildkite/terminal-to-html/compare/v3.17.1...v3.17.2)

##### Fixed

- Remove `html/template` from the rendering path to avoid a runtime trap
when rendering with TinyGo, while preserving the generated HTML.
[#&#8203;282](https://redirect.github.com/buildkite/terminal-to-html/pull/282)
([@&#8203;dannymidnight](https://redirect.github.com/dannymidnight))

##### Changed

- Raise the minimum supported Go version from 1.25.0 to 1.26.10.
[#&#8203;290](https://redirect.github.com/buildkite/terminal-to-html/pull/290)
([@&#8203;wolfeidau](https://redirect.github.com/wolfeidau))

##### Dependency updates

- Update the development/CI Go Docker image from 1.26.4 to 1.26.10.
[#&#8203;281](https://redirect.github.com/buildkite/terminal-to-html/pull/281),
[#&#8203;290](https://redirect.github.com/buildkite/terminal-to-html/pull/290)
- Bump `golang.org/x/sys` from 0.46.0 to 0.47.0.
[#&#8203;280](https://redirect.github.com/buildkite/terminal-to-html/pull/280)
([@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot])

</details>

<details>
<summary>dlclark/regexp2 (github.com/dlclark/regexp2/v2)</summary>

###
[`v2.11.0`](https://redirect.github.com/dlclark/regexp2/compare/v2.10.1...v2.11.0)

[Compare
Source](https://redirect.github.com/dlclark/regexp2/compare/v2.10.1...v2.11.0)

###
[`v2.10.1`](https://redirect.github.com/dlclark/regexp2/compare/v2.10.0...v2.10.1)

[Compare
Source](https://redirect.github.com/dlclark/regexp2/compare/v2.10.0...v2.10.1)

###
[`v2.10.0`](https://redirect.github.com/dlclark/regexp2/compare/v2.9.1...v2.10.0)

[Compare
Source](https://redirect.github.com/dlclark/regexp2/compare/v2.9.1...v2.10.0)

</details>

<details>
<summary>getkin/kin-openapi (github.com/getkin/kin-openapi)</summary>

###
[`v0.152.0`](https://redirect.github.com/getkin/kin-openapi/releases/tag/v0.152.0)

[Compare
Source](https://redirect.github.com/getkin/kin-openapi/compare/v0.151.0...v0.152.0)

#### What's Changed

- openapi2conv: push path-level body/formData parameters into operations
by [@&#8203;Yi-111-a](https://redirect.github.com/Yi-111-a) in
[#&#8203;1340](https://redirect.github.com/getkin/kin-openapi/pull/1340)
- fix(openapi3): resolve transitive component refs in JSON Schema
2020-12 validation by
[@&#8203;Yi-111-a](https://redirect.github.com/Yi-111-a) in
[#&#8203;1342](https://redirect.github.com/getkin/kin-openapi/pull/1342)
- openapi3: honor readOnly/writeOnly from allOf schemas in required
checks by [@&#8203;ybai08](https://redirect.github.com/ybai08) in
[#&#8203;1343](https://redirect.github.com/getkin/kin-openapi/pull/1343)
- openapi3: resolve $refs of Components.Links by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1344](https://redirect.github.com/getkin/kin-openapi/pull/1344)
- openapi3: marshal a schema's empty type list as absent rather than
null by [@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1345](https://redirect.github.com/getkin/kin-openapi/pull/1345)
- openapi3: drop the Header dump from its content/schema error by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1346](https://redirect.github.com/getkin/kin-openapi/pull/1346)
- openapi3gen: always propagate field tag by
[@&#8203;phooijenga](https://redirect.github.com/phooijenga) in
[#&#8203;1347](https://redirect.github.com/getkin/kin-openapi/pull/1347)
- routers/gorillamux: return an error instead of panicking when
registering routes by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1348](https://redirect.github.com/getkin/kin-openapi/pull/1348)
- openapi3: report $refs to null nodes as unresolved instead of
panicking by [@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1349](https://redirect.github.com/getkin/kin-openapi/pull/1349)
- openapi3: validate null servers, server variables, tags and components
without panicking by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1351](https://redirect.github.com/getkin/kin-openapi/pull/1351)
- openapi2conv: fix FromV3Parameter panics on binary schema refs and
value-less schemas by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1352](https://redirect.github.com/getkin/kin-openapi/pull/1352)
- openapi3filter: parse integer values as base 10 by
[@&#8203;LuisFigueroaG](https://redirect.github.com/LuisFigueroaG) in
[#&#8203;1353](https://redirect.github.com/getkin/kin-openapi/pull/1353)
- openapi3filter: decode spaceDelimited and pipeDelimited query objects
by [@&#8203;ybai08](https://redirect.github.com/ybai08) in
[#&#8203;1350](https://redirect.github.com/getkin/kin-openapi/pull/1350)
- Case-sensitive usmarshalling with json/v2 by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1355](https://redirect.github.com/getkin/kin-openapi/pull/1355)
- openapi3filter: decode deepObject and non-exploded form object
properties of urlencoded bodies by
[@&#8203;ybai08](https://redirect.github.com/ybai08) in
[#&#8203;1354](https://redirect.github.com/getkin/kin-openapi/pull/1354)
- openapi3: keep $ref siblings on a schema reached through a cycle by
[@&#8203;Yi-111-a](https://redirect.github.com/Yi-111-a) in
[#&#8203;1357](https://redirect.github.com/getkin/kin-openapi/pull/1357)
- tests: go around encoding/json/v2's randomness in error message by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1361](https://redirect.github.com/getkin/kin-openapi/pull/1361)
- tests: remove actual network calls from tests by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1362](https://redirect.github.com/getkin/kin-openapi/pull/1362)
- tests: run more in parallel by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1363](https://redirect.github.com/getkin/kin-openapi/pull/1363)
- openapi3: ignore reference siblings in OpenAPI 3.0 by
[@&#8203;lucky798213](https://redirect.github.com/lucky798213) in
[#&#8203;1268](https://redirect.github.com/getkin/kin-openapi/pull/1268)
- openapi3: WalkSchemas and WalkParameters stop on a callback cycle by
[@&#8203;reuvenharrison](https://redirect.github.com/reuvenharrison) in
[#&#8203;1364](https://redirect.github.com/getkin/kin-openapi/pull/1364)
- openapi3filter: validate response headers described with content by
[@&#8203;LuisFigueroaG](https://redirect.github.com/LuisFigueroaG) in
[#&#8203;1358](https://redirect.github.com/getkin/kin-openapi/pull/1358)
- openapi3filter: honor ExcludeRequestQueryParams for path item
parameters by
[@&#8203;LuisFigueroaG](https://redirect.github.com/LuisFigueroaG) in
[#&#8203;1365](https://redirect.github.com/getkin/kin-openapi/pull/1365)
- openapi3: handle fields next to $ref as OpenAPI 3.0, 3.1 and 3.2
specify by [@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1371](https://redirect.github.com/getkin/kin-openapi/pull/1371)
- changelog: fixup rebased messes by
[@&#8203;fenollp](https://redirect.github.com/fenollp) in
[#&#8203;1372](https://redirect.github.com/getkin/kin-openapi/pull/1372)

#### New Contributors

- [@&#8203;ybai08](https://redirect.github.com/ybai08) made their first
contribution in
[#&#8203;1343](https://redirect.github.com/getkin/kin-openapi/pull/1343)
- [@&#8203;phooijenga](https://redirect.github.com/phooijenga) made
their first contribution in
[#&#8203;1347](https://redirect.github.com/getkin/kin-openapi/pull/1347)

**Full Changelog**:
<https://github.com/getkin/kin-openapi/compare/v0.151.0...v0.152.0>

</details>

<details>
<summary>go-co-op/gocron (github.com/go-co-op/gocron/v2)</summary>

###
[`v2.23.0`](https://redirect.github.com/go-co-op/gocron/releases/tag/v2.23.0)

[Compare
Source](https://redirect.github.com/go-co-op/gocron/compare/v2.22.0...v2.23.0)

#### What's Changed

- feat(scheduler): WithStartAtGrace opt-in tolerance for late first-run
dispatch by
[@&#8203;JohnRoesler](https://redirect.github.com/JohnRoesler) in
[#&#8203;945](https://redirect.github.com/go-co-op/gocron/pull/945)
- fix(executor,scheduler): use injected clock for job timing and guard
reschedule loop by
[@&#8203;JohnRoesler](https://redirect.github.com/JohnRoesler) in
[#&#8203;946](https://redirect.github.com/go-co-op/gocron/pull/946)
- fix(executor): attribute scheduling delay to the tick that actually
fired by [@&#8203;JohnRoesler](https://redirect.github.com/JohnRoesler)
in [#&#8203;948](https://redirect.github.com/go-co-op/gocron/pull/948)
- refactor(scheduler): extract shared first-run logic from
selectNewJob/selectStart by
[@&#8203;JohnRoesler](https://redirect.github.com/JohnRoesler) in
[#&#8203;947](https://redirect.github.com/go-co-op/gocron/pull/947)
- build(deps): bump actions/setup-go from 6 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;949](https://redirect.github.com/go-co-op/gocron/pull/949)
- build(deps): bump actions/setup-python from 6 to 7 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203;950](https://redirect.github.com/go-co-op/gocron/pull/950)
- ci: updating golangci-lint to 2.13.1 to support go 1.27 by
[@&#8203;manuelarte](https://redirect.github.com/manuelarte) in
[#&#8203;954](https://redirect.github.com/go-co-op/gocron/pull/954)
- Exclude stable Go version in CI workflow by
[@&#8203;JohnRoesler](https://redirect.github.com/JohnRoesler) in
[#&#8203;968](https://redirect.github.com/go-co-op/gocron/pull/968)
- docs: removing retired go report badge by
[@&#8203;manuelarte](https://redirect.github.com/manuelarte) in
[#&#8203;952](https://redirect.github.com/go-co-op/gocron/pull/952)
- build(deps): bump github.com/stretchr/testify from 1.11.1 to 1.12.1 by
[@&#8203;dependabot](https://redirect.github.com/dependabot)\[bot] in
[#&#8203

> [!IMPORTANT]
> ✂ PR body was truncated to here.


</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Between 12:00 AM and 03:59 AM, only on Monday (`* 0-3 * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend Renovate
CLI](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNzIuMiIsInVwZGF0ZWRJblZlciI6IjQ0LjE3Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-02 19:30:55 +00:00
wxiaoguang
cdadd0c788 fix(actions): make Actions API work with repo admin permission (#40174)
The web route already uses "repo admin" for repo-level Actions
management.

* Fix #40173

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-02 19:29:49 +00:00
Abdellatif Anaflous
2ed6a6f61b fix(web): read the wiki blob excerpt from the wiki repository (#40165)
The "load more lines" button in wiki diffs returned 404. `ExcerptBlob`
loads the commit from the wiki repo but read the blob from
`ctx.Repo.GitRepo`, a regression from
https://github.com/go-gitea/gitea/pull/38464.

Read the blob from the repo the commit was loaded from.

---------

Co-authored-by: Abdel <hktitof@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-02 14:30:41 +00:00
silverwind
1f51d2c318 test: run e2e tests in WebKit on Linux (#40152)
WebKit was removed from the e2e tests in
https://github.com/go-gitea/gitea/pull/37315 because Linux WebKit kept
crashing. The crashes came from a use-after-free in the libsoup bundled
with WebKit, which killed the network process and surfaced as `WebKit
encountered an internal error`. macOS WebKit never had them because it
does not use libsoup. Playwright 1.64 bundles libsoup 3.6.6 with the fix
(https://github.com/microsoft/playwright/issues/42803), so WebKit now
runs on all platforms. Builds on
https://github.com/go-gitea/gitea/pull/40140.

The e2e job ran 50 times on `ubuntu-latest` with WebKit enabled, and all
runs passed without WebKit errors. The third browser adds about 1.5 to 2
minutes per run.

1. Playwright installs its system dependencies on GitHub runners too,
which only come with the Chromium and Firefox libraries
1. The passkey test skips Linux WebKit, which has no WebAuthn,
Playwright only stubs `PublicKeyCredential` there

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-11-01 15:24:41 +00:00
bircni
d3021b69ee enhance: replace tea CLI with gt (#40036)
tea has been replaced by [gitea-cli](https://gitea.com/gitea/cli)
(`gt`), which reuses tea's OAuth2 client ID.

- The repository clone panel now offers `gt repo clone owner/repo` under
a "Gitea CLI" tab.
- The built-in OAuth2 application is renamed from `tea` to `gitea-cli`.
`tea` in `[oauth2] DEFAULT_APPLICATIONS` is still accepted as an alias,
so existing configs keep working.
- Instances that already registered the app keep its stored display name
"tea".
2026-11-01 12:47:10 +00:00
silverwind
c62e07f18a fix(projects): only require project write to move board cards (#40161)
Follow-up to https://github.com/go-gitea/gitea/pull/30205, per
https://github.com/go-gitea/gitea/pull/30205#discussion_r4435046605.
Moving a card on a project board is a project operation, so project
write is enough again. Repository boards already show every card to
project writers, who then got a 404 when moving one.

The per-card archived check is removed too. The frontend sends every
card of the target column, so one card from an archived repository
rejected all drops and reorders into that column, silently. Project
workflows already skip archived repositories, and an archived
repository's own board is read-only.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-01 10:46:53 +00:00
bircni
51368e1997 ci: use GitHub merge queue (#40130)
Requiring branches to be up to date reruns the full CI matrix on every
waiting PR after each merge, which saturates our 20 concurrent jobs. A
merge queue still tests against the latest `main`, but once per batch.

This runs the PR workflows on `merge_group` and reports `giteabot/lgtm`
on queue commits, since GiteaBot only sets it on PR heads. Mergers queue
PRs with "Merge when ready" instead of `reviewed/wait-merge`, so
giteabot no longer updates branches. Nothing changes until the
`merge-queue` ruleset is enabled and "Require branches to be up to date"
is turned off. Backport to `release/v28` before the ruleset covers it.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-01 11:23:51 +01:00
silverwind
eba3a81c9a ci: build snap nightlies every 12 hours instead of on every push (#40150)
The snap nightly ran on every push to `main`, about 58 runs and 520 job
minutes a week on GitHub-hosted runners, which share the concurrent job
limit with PR CI.

It now runs as a job in `release-nightly.yml`, reusing its 12-hourly
check for new commits on `main`, like
https://github.com/go-gitea/gitea/pull/40138 did for the other
nightlies. A new release tag is still published by the first build after
`main` changes.
2026-11-01 08:44:50 +00:00
silverwind
2a5c34c379 fix(pull): reject pull requests without common history (#39872)
Pull requests between branches without a common history show no commits
and can never be merged. The web form already rejects them, but the API
and crafted `..` compare POSTs did not.

The API compared with `..` since
https://github.com/go-gitea/gitea/pull/36105, it now uses `...` again
and returns 422 when there is no merge base.
2026-11-01 07:53:22 +00:00
silverwind
91a49fadc1 fix(repo): don't get stuck generating contributor stats (#40125)
A failed contributor stats generation never released its lock, so after
the cached error expired, the contributors and code frequency pages kept
saying stats are being generated until a restart. A generation
outlasting the 5 second wait also leaked its goroutine on an unread
channel.

The lock is now claimed atomically and released together with the
completion signal when generation ends, and a cache miss after the wait
counts as still generating.
2026-11-01 07:36:07 +00:00
silverwind
db9d2944d8 fix(api): return language of starred and watched repositories (#40127)
The starred and watched repository API lists converted repositories
without loading their attributes, so `language` was always empty. They
are now loaded in one batch, owners included.

Three of these handlers also continued after an internal error and wrote
a second response body, they now return.
2026-11-01 06:57:30 +00:00
Norbert Szulc
da7b81d407 feat: add commitstatus access token scope (#40144)
Add `read:commitstatus` and `write:commitstatus` token scopes, so CI and
other integrations can report commit statuses without a token that
grants full repository access.

- The commit status endpoints (`/statuses/{sha}`,
`/commits/{ref}/status` and `/commits/{ref}/statuses`) accept the new
scopes, and the repository scopes keep working as they include them
- Creating a status still requires code write permission on the
repository, the scope only narrows what the token may do
- `public-only` tokens still cannot reach private repositories
- `PathGroup` middlewares now run only for a matched path, so unrelated
methods and paths that fall back to the group are not rejected by its
scope check

Fixes: https://github.com/go-gitea/gitea/issues/32625
Ref: https://github.com/go-gitea/gitea/pull/35383
Docs: https://gitea.com/gitea/docs/pulls/622
Signed-off-by: Norbert Szulc <norbert@not7cd.net>
Co-authored-by: Aiden Scandella <git@sca.ndella.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-01 06:27:12 +00:00
GiteaBot
b46b820cec [skip ci] Updated translations via Crowdin 2026-11-01 00:40:38 +00:00
silverwind
5d84bbd525 test: run e2e tests in WebKit on macOS (#40140)
WebKit was removed from the e2e tests in
https://github.com/go-gitea/gitea/pull/37315 because Linux WebKit kept
crashing. `make test-e2e` on macOS now also runs WebKit, which does not
show these crashes there. CI stays on Linux, because macOS runners would
need about an extra GB of cache storage.

1. The bot account test clicks the user type dropdown, Safari only moves
Tab focus between text fields
1. On macOS, Playwright runs with a single libuv thread, concurrent
`getaddrinfo` lookups of `localhost` there sporadically stall for 5s

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-10-31 22:44:39 +00:00
silverwind
59cbacc924 ci: build nightlies every 12 hours instead of on every push (#40138)
Building nightlies on every push to `main` and `release/v*` costs about
1000 runner minutes a day, a third of it in runs cancelled by the next
push. Nightlies now run every 12 hours and only build branches with
commits since the last successful run.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-10-31 22:25:21 +00:00
sarankumar
a4d1711777 fix(api): remove git remote when deleting a push mirror (#40049)
Fixes https://github.com/go-gitea/gitea/issues/40037

`DELETE /repos/{owner}/{repo}/push_mirrors/{name}` only deleted the
`push_mirror` row and left the `remote_mirror_*` git remotes, which
embed the mirror credentials, in the repository and wiki config. Each
create/delete cycle through the API left more credentials behind.

- Remove the git remotes before deleting the row and record it in the
audit log, like the web settings handler.
- Return 404 for an unknown name, as documented, instead of 204.
- Ignore an already missing code remote, so the wiki remote is still
removed and such mirrors can be deleted through both the API and the web
settings.

---

AI disclosure: I used an AI coding assistant to help write this change
and the PR description. I reviewed the code and ran the tests myself.

---------

Signed-off-by: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-31 17:47:36 +00:00
bircni
4ed6f89b60 fix(auth): only auto-link the account the external identity points to (#40121)
With `[oauth2_client].ACCOUNT_LINKING = auto`, submitting the link
account sign-up form with a user name or email that already exists
linked the external identity to that existing account and signed in as
it, without checking that the external identity belongs to that account.

Automatic linking now only happens when the existing user matches the
external identity: its email equals the provider email, or its name
equals the user name derived from the provider (per
`[oauth2_client].USERNAME`, same as auto registration). Otherwise the
linking login page is shown, which requires the existing account's
password.

Auto registration is unchanged, since the name and email there already
come from the provider.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 18:20:56 +01:00
wxiaoguang
723095e66d docs: update contributing.md (#40129) 2026-10-31 17:46:04 +01:00
Lunny Xiao
40507491f7 perf(pull): load the repository once for all commit statuses (#39930)
Pages showing commit statuses loaded the repository of every status
separately, so a PR with ~75 statuses ran ~75 identical repository
queries per merge box render (about 3.4s in a production trace).
`statusRepoCanReadActions` only loaded the repository inside the
permission cache loader, which never runs once `RepoAssignment` has
published the permission.

- `CommitStatus.loadRepository` resolves through the request context
cache
- `statusRepoCanReadActions` loads the repository before the permission
lookup
- `RepoAssignment` publishes its repository to the context cache next to
the permission
- `GetCommitActionsStatusMap` drops its own repository map

Queries per request with 50 statuses (45 Actions, 5 external) on the
head commit, SQLite:

| Page | Repository queries | Total queries |
|---|---|---|
| PR view | 61 → 11 | 157 → 107 |
| Commit | 6 → 5 | 24 → 23 |
| Branches | 6 → 5 | 33 → 32 |
| Commits | 6 → 5 | 23 → 22 |
| Repo home | 8 → 7 | 35 → 34 |
| `/pulls` | 15 → 15 | 37 → 37 |

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 17:32:03 +01:00
Shreyansh Jain
bbb8ef8244 fix(actions): default-branch push discards a due schedule occurrence (#40073)
Default-branch pushes and Actions unit saves recreated the repository's
schedules with `Next` recomputed from that moment, so a push between a
cron's due minute and the scheduler's pass skipped the occurrence, and
`@every` timers restarted.

Re-registration now updates schedule rows in place and keeps the spec
rows of unchanged crons, so their cursor survives and an in-flight
scheduler pass still finds its schedule. Re-detection also clears
schedules when no scheduled workflow remains.

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

Written with AI assistance.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 5.5) <noreply@anthropic.com>
2026-10-31 17:31:59 +01:00
leo
7df657116c fix(api): honor label archive options (#40008)
Updating a label through the API without `is_archived`, for example to
rename or recolor it, silently unarchived it. Creating an organization
label also ignored `is_archived`.

- Keep a label's archive state when a PATCH omits `is_archived`, for
repository and organization labels
- Honor `is_archived` when creating organization labels, as repository
labels already do

AI-assisted by Codex (GPT-6); prepared and tested by the assistant.

---------

Co-authored-by: Hello-Liar <80651389+Hello-Liar@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:55 +01:00
Phoenix
9cd06bf57f fix(migrations): wait out GitHub rate limits and tolerate failed patch downloads (#40012)
Large GitHub migrations failed once the API budget ran out. A rejected
request never updated the downloader's rate state, so go-github refused
every retry locally.

- Record the rate limit from error responses too, so the next request
waits for the reset
- Retry repository-wide comment pages like the other downloader calls
- Stop `waitAndPickClient` spinning when the rate refresh fails
- Skip reaction requests when the reaction rollup reports none
- Log a failed patch download instead of failing the migration

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:51 +01:00
silverwind
680f2a2598 fix: don't roll back branches on out-of-order post-receive hooks (#40110)
The post-receive hook wrote each push's new commit into the branch
table. Each push runs its own hook after the ref is updated, so with
pushes in close succession, like CI pushing back or a quick force-push,
a slower first hook can finish last. The table then keeps the older
commit, or marks a re-created branch as deleted.

- The sync reads each pushed branch's state from git under a repo-scoped
lock
- Deletions go through the same sync, so a re-created branch stays
active
- A branch that fails to sync is logged and skipped instead of aborting
the batch

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:47 +01:00
silverwind
70d0f190a1 perf: speed up repository issue and pull request lists (#40114)
The repository issue and pull request lists filter `access` by
`repo_id`, which no index covers, so every load scans the whole table.
Issue counts on larger repositories merge three single-column indices,
and the API pull request list loads the latest reviews with `id IN
(SELECT max(id) ... GROUP BY ...)`, which MySQL runs as a scan of every
review, about 1.1s on
https://gitea.com/api/v1/repos/gitea/gitea-mcp/pulls?limit=1.

- Index `access.repo_id`
- Index `team_repo.repo_id`
- Replace the single `repo_id`, `is_closed` and `is_pull` indices with
one `issue` index on `(repo_id, is_closed, is_pull)`
- Join the latest review ids instead of `id IN (subquery)`

Median times in ms on MySQL 8.4 with tables sized like gitea.com (1M
access rows, 500k issues, 200k reviews), for a copy of
https://gitea.com/gitea/gitea-mcp and a repository with 9.7k issues:

| Page | Before | After |
|---|---|---|
| `/{owner}/{repo}/issues` | 107 | 40 |
| `/{owner}/{repo}/pulls` | 106 | 32 |
| `/{owner}/{repo}/pulls?state=closed` | 153 | 35 |
| `/{owner}/{repo}/issues/{index}` | 141 | 60 |
| `/{owner}/{repo}/issues`, 9.7k issues | 303 | 53 |
| `/api/v1/repos/{owner}/{repo}/pulls?limit=50` | 180 | 46 |
| `/api/v1/repos/{owner}/{repo}/pulls?limit=1` | 137 | 16 |
| `/api/v1/repos/{owner}/{repo}/issues`, 9.7k issues | 248 | 43 |

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:44 +01:00
silverwind
d58be0bcff fix(pull): keep review body in sync when its comment is edited (#40058)
A review's text is stored on both the review and its timeline comment,
but editing it in the web UI or through the issue comment API only
updated the comment. The reviews API kept returning the old body
afterwards.

Extracted out of https://github.com/go-gitea/gitea/pull/39865.

*Written by Claude.*

Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:39 +01:00
Basavaraj S m
36ea65a1df fix(projects): hide linked pull requests the viewer cannot read (#40016)
Project board cards list the pull requests that reference their issue,
but loaded them without checking the viewer's access. A private pull
request mentioning a public issue showed its title, number and state on
that issue's board to anyone who can view the board, anonymous visitors
included.

Repository and organization boards now skip linked pull requests in
repositories where the viewer cannot read pull requests, matching how
the issue page already hides such references.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:31:34 +01:00
TheFox0x7
4144d4ed5b docs: update contributing.md (#39988)
Given recent weeks of fully LLM authored closures and mass replies in
PRs with LLMs I find this entry hypocritical.
2026-10-31 15:33:01 +00:00
silverwind
fc4d490b35 fix(ssh): let pushes finish when the client disconnects early (#40105)
With the built-in SSH server, closing the client connection cancelled
the `gitea serv` process tree. go-git based clients close it right after
the push report, so post-receive was killed and branches, pull requests,
webhooks and Actions were not updated.

The command now runs under the server's hammer context like with
OpenSSH, where it ends once its pipes close or at shutdown.
2026-10-31 15:24:13 +00:00
Lunny Xiao
bd5fbcfb29 feat(migrations): add repository migration from bitbucket.org (#38607)
Adds migration from bitbucket.org as the Cloud variant of the Bitbucket
entry, in the web UI and via `POST /repos/migrate` with
`service=bitbucket`. It migrates the repository and pull requests with
their comments and reviews. Bitbucket Cloud has no releases, and
Atlassian removed its issues and wikis on 2026-08-20
(https://community.atlassian.com/forums/Bitbucket-articles/Announcing-sunset-of-Bitbucket-Issues-and-Wikis/ba-p/3193882).

- Private repositories need an API or access token, which also raises
the rate limit.
- Rate-limited requests back off and retry, Bitbucket sends no
`Retry-After`.
- Tests replay recorded responses, set `BITBUCKET_READ_TOKEN` to
re-record.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 14:51:43 +00:00
silverwind
cc20c4c11b fix(actions): don't pick up jobs of repos with Actions disabled (#40106)
Repository-level and instance-level runners still picked up waiting jobs
of repositories with Actions disabled.
https://github.com/go-gitea/gitea/pull/30331 only covered owner-level
runners, by filtering them out in SQL.

Pickup now checks the Actions unit of each job it is about to claim, for
every runner scope, and cancels the job instead when Actions is
disabled, also honoring `DISABLED_REPO_UNITS`. Disabling Actions
therefore cancels its queued jobs once a matching runner polls, instead
of leaving them waiting until `ABANDONED_JOB_TIMEOUT`.
2026-10-31 14:28:58 +00:00
Lazizbek Ergashev
8465bed994 fix(pull): respect max approvers in default merge message (#40118)
The approver limit was checked before writing each `Reviewed-by:` line
but compared with `>`, so the default merge message got one more trailer
than `DEFAULT_MERGE_MESSAGE_MAX_APPROVERS` allows (2 with a limit of 1,
11 with the default 10). It now stops once the limit is reached.

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

Assisted-by: Claude Code:claude-opus-5-5
2026-10-31 07:00:47 -07:00
silverwind
b89fcf7496 fix(actions): run review-triggered workflows on the pull request head (#40099)
Runs triggered by pull request reviews took their ref and commit from
the review's `commit_id` instead of the pull request. They now use the
pull request head like other pull request events, and their commit
status is set on the commit that ran.
2026-10-31 12:48:26 +00:00
soujasK
405ba258a8 fix: list bot users in gitea admin user list and admin users API (#40060)
Fixes https://github.com/go-gitea/gitea/issues/40056

Signed-off-by: Soujas K <kudchadkarsoujas@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-31 12:30:07 +00:00
bircni
6782d0a37d fix: harden context user access scope (#39945)
API tokens and Actions task tokens could see data beyond their scope
through user-scoped API routes and web profile pages.

- Actions task tokens authenticate as a shared system user with a
negative ID, which org, project and SSH/GPG key filters treated as "no
owner filter". These filters now check `!= 0`, so system users own
nothing. The `/api/v1/user` routes except `GET /api/v1/user` only accept
individual and bot accounts, and repo search no longer skips access
checks for the owner.
- `/{username}` only accepts tokens for RSS/Atom feeds. HTML profile and
org pages render as anonymous for token requests.
- User and org feeds require `read:user` or `read:organization` from
tokens, the same way repo feeds require `read:repository`.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-31 13:03:59 +01:00
Shivansh Garg
365cb2bd5e enhance(admin): allow regenerating access tokens for bot accounts (#40109)
Bot accounts cannot sign in, so admins manage their access tokens, but
the regenerate button was hidden for them. This adds an admin route to
regenerate a bot's token.

Token regeneration is now also recorded in the audit log.

Fixes: https://github.com/go-gitea/gitea/issues/40074
Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 09:31:56 +00:00
Jacky
08c5fcafd3 fix(mail): preserve outer spacing in notification emails (#40104)
Remove the `body {margin: 0; padding: 0}` reset from
https://github.com/go-gitea/gitea/pull/38396, which left HTML mails
flush against the edge in Apple Mail and Thunderbird. The devtest
preview stops injecting its own padding, so it matches delivered mails.

These clients render the mail as a document and apply the default 8px
body margin again, as with GitHub and GitLab notifications. Webmail
clients replace `<body>` and keep their own inset.

Fixes https://github.com/go-gitea/gitea/issues/40103
Assisted-by: Codex:GPT-6.1, Claude:Opus 5.5
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 10:02:08 +01:00
GiteaBot
ae0136acd2 [skip ci] Updated translations via Crowdin 2026-10-31 00:33:25 +00:00
Mitrahsoft
ab434f4b38 enhance(actions): list workflow_call-only workflows in their own sidebar group (#40030)
A workflow whose only trigger is `workflow_call` never runs on its own,
so its sidebar entry always showed an empty run list and a "Disable
workflow" action that has no effect.

Following the discussion with @bircni and @Zettat123, these workflows
are now listed in a separate collapsed "Reusable workflows" group. Their
page shows "This workflow can only be called by other workflows."
instead of "no runs", and "Disable workflow" and "Create status badge"
are hidden. A workflow disabled before it became `workflow_call`-only
keeps its label and "Enable workflow". "Other workflows" again only
lists workflows whose file is gone.

Fixes: https://github.com/go-gitea/gitea/issues/39385
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-30 21:41:59 +00:00
Abdellatif Anaflous
1aff9b8cd6 fix(api): answer 409 when repository files already exist on disk (#40095)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 20:37:37 +00:00
Tim
fa41fc94d4 feat(pulls): support per-commit review (#39326)
Opening a single commit of a PR let you look, but not review. The Review
button was disabled and there was no way to reach the next or previous
commit. This finishes https://github.com/go-gitea/gitea/pull/37475,
which only reached the head commit.

- Reviews can be submitted from a single commit, a commit range stays
disabled
- A bar above a single-commit diff shows the commit, its position in the
PR and prev/next buttons
- The commit dropdown marks the commits on screen
- Timeline commits open the PR's commit view, commits force-pushed out
of the PR redirect to the repository commit page

Assisted-by: Claude Code (Fable 5.1)
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-30 20:18:13 +00:00
leo
cbb563b080 fix(api): edit and delete repository labels by name (#39974)
Repo and org label endpoints accepted a label name for GET but only a
numeric ID for PATCH and DELETE, which breaks label-sync style tools.

- PATCH and DELETE on `/repos/{owner}/{repo}/labels/{id}` and
`/orgs/{org}/labels/{id}` accept a label name when the value is not
numeric, numeric values stay IDs
- A name shared by several labels returns 400 instead of acting on an
arbitrary one
- Deleting a missing label returns 404 instead of 204
- The swagger `id` parameter of these endpoints is now a string

Fixes: https://github.com/go-gitea/gitea/issues/34937
Co-authored-by: Hello-Liar <80651389+Hello-Liar@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-30 19:48:56 +00:00
silverwind
2a2fb86ca0 ci: update toolchains and let renovate update action inputs (#40092)
- Let renovate update hyphenated action inputs like `python-version`
- Python 3.14 → 3.15 in CI
- Download gh 2.103.2 directly in the release workflow instead of using
`install-gh-cli-action` with 2.39.1
- Devcontainer Go image 1.26 → 1.27
- Snap build toolchains Go 1.26 → 1.27 and Node 24 → 26
2026-10-30 19:24:32 +00:00
Mitrahsoft
7d12394561 fix(actions): clear commit status links when a run is deleted (#40088)
Deleting an Actions run (UI, API or `RUN_RETENTION_DAYS`) left its
commit statuses linking to the deleted run, so "Details" gave a 404.
`DeleteRun` now clears the `target_url` of the run's statuses and of the
commit's status summary on the run's commit, in the same transaction.
States are kept, so required status checks and merging are unaffected.
This differs from GitHub, which deletes the checks together with the
run.

Fixes https://github.com/go-gitea/gitea/issues/40079
Co-authored-by: silverwind <me@silverwind.io>
2026-10-30 18:54:07 +00:00
silverwind
6a8eec1cc3 perf: speed up the issue and pull request dashboards and fix table indices (#40001)
The dashboard counts ran queries whose cost grew with the whole instance
instead of the viewing user. On https://gitea.com/pulls the two review
counts alone take about 1.4s per page load.

- Check the owner of the already joined repository with `EXISTS` instead
of listing every public repository and public user
- Hide superseded review requests with `NOT EXISTS` instead of grouping
every review on the instance
- Skip the review counts on `/issues`, which never shows them
- Index `team_user.uid` for permission and team lookups by user
- Fix indices for table `watch`, `package_property` and `action_run`,
fix #40090

Median page times in ms, before → after, on a synthetic dataset sized
like gitea.com (175k users, 40k repos, 120k issues, 1M comments):

| Page | SQLite | PostgreSQL 17 | MySQL 8.4 | MariaDB 13 |
|---|---|---|---|---|
| `/pulls` | 901 → 56 | 330 → 156 | 189 → 124 | 192 → 218 |
| `/pulls?type=review_requested` | 1797 → 66 | 557 → 254 | 307 → 245 |
604 → 499 |
| `/pulls?type=reviewed_by` | 1646 → 118 | 537 → 316 | 515 → 265 | 528 →
682 |
| `/pulls?type=mentioned` | 1403 → 46 | 426 → 119 | 165 → 116 | 179 →
179 |
| `/issues` | 923 → 33 | 293 → 39 | 178 → 65 | 140 → 41 |
| `/api/v1/repos/issues/search` (anonymous) | 870 → 110 | 144 → 104 |
123 → 87 | 170 → 193 |

MariaDB checks the owner per row on long scans across all public
repositories, which makes a few pages slower there.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 18:16:22 +00:00
somaz
140fac63e2 fix(repo): honor ALLOW_FORK_INTO_SAME_OWNER for organizations (#40025)
With `[repository] ALLOW_FORK_INTO_SAME_OWNER = true` a user can fork
their own repository, but an organization still cannot fork a repository
it owns. The fork button check and the owner list on the fork page
compared the organization with the repository owner directly instead of
asking `CanUserForkBetweenOwners`, and the walk over parent repositories
dropped the owning organization whenever the setting was on.
Organizations now follow the setting the same way users already do, and
nothing changes when it is off.

Fixes https://github.com/go-gitea/gitea/issues/40021
Assisted-by: Claude Code:claude-opus-5-5
2026-10-30 17:28:47 +00:00
Shivansh Garg
eb3bc196ec chore(maintainers): add Shivansh Garg as maintainer (#40072) 2026-10-30 12:33:16 +00:00
Jorge
2660b4d2c4 fix(api)!: list only open pull requests when no state is given (#40078)
`GET /repos/{owner}/{repo}/pulls` is documented with `state` defaulting
to `open`, but without `state` it returned every pull request, closed
and merged ones included. Now it parses `state` with
`ParseIssueFilterStateIsClosed`, like the issues and milestones
endpoints, so no state means open, as documented. `closed` and `all`
work as before. GitHub's pulls endpoint also defaults to open.
2026-10-30 08:42:49 +00:00
Basavaraj S m
467963f0dd fix(issues): require per-item write access for batch issue actions (#40047)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 08:22:37 +00:00
GiteaBot
b2ce7efd3b [skip ci] Updated translations via Crowdin 2026-10-30 00:40:31 +00:00
Basavaraj S m
5ee660e437 fix(pull): refuse review comments on locked pull requests (#39960)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-29 23:57:51 +00:00
Lunny Xiao
7361ade2e5 fix(user): answer the follow of a bot user with the profile card (#39980)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 06:35:11 +08:00
silverwind
ac11887b02 ci(renovate): fix docker image updates with over 1000 tags (#40077)
Docker Hub refuses anonymous tag pages past 1000 tags, and Renovate then
drops every release date it fetched
(https://github.com/renovatebot/renovate/issues/46925). The 5-day
`minimumReleaseAge` then holds digest updates of `postgres`, `redis` and
`getmeili/meilisearch` back forever. Stopping at 10 pages keeps the
dates of the newest 1000 tags.
2026-10-29 18:49:54 +00:00
Lunny Xiao
b0b31bc593 feat(packages): expose package file properties in the package files API (#40066)
Package files carry type specific properties like `debian.distribution`,
`debian.component`, `rpm.group` or `alpine.branch` that decide where a
file lives in a registry, but the API never returned them. A client
listing `GET /packages/{owner}/{type}/{name}/{version}/files` could not
tell which distribution or component a file belongs to, or tell apart
same-named files in different ones. Tools copying packages between
instances, like https://gitea.com/gitea/importer, need them to re-upload
a file to the same place.

This adds a `properties` map to `PackageFile`, limited to the properties
that locate a file. Internal ones like the rpm, alpine, arch and conda
metadata or the Debian control file stay hidden, as they can be many MiB
per file.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-29 18:10:36 +00:00
silverwind
eb9267365d enhance(markup): align markdown rendering details with GitHub (#39955)
Fixes markdown rendering details where Gitea deviated from GitHub
unintentionally, and adds a `/devtest/markdown` page to review markdown
rendering.

- Paragraphs get the intended 1.5 line height
- Blockquotes, alerts and definition lists no longer inherit browser
default margins
- Loose task lists keep their spacing, the checkbox renders inside the
paragraph like on GitHub
- Footnote references render as `[1]` so adjacent ones stay apart
without CSS
- Code in headings, headings in summaries, nested ordered lists, tables,
`<mark>`, alert icons and `<hr>` follow GitHub

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-29 11:18:04 +00:00
silverwind
7308c5fb97 enhance(websocket): improve client synchronization (#38847)
Keep notification counts and stopwatches in sync when tabs open or
reconnect, closing the gap behind [this
flake](https://github.com/go-gitea/gitea/actions/runs/31312525323/job/93242429394).

1. Send current notification counts and stopwatch state on each
WebSocket connection, replacing the separate state fetches tabs made on
every connect.
2. Tabs joining an already open connection fetch current state once,
since its snapshot went out before they attached.
3. Send events queued during the handshake before the snapshot, so older
events can't roll it back.
4. Wait for the Redis SUBSCRIBE acknowledgement, so the snapshot read
after subscribing can't miss an event.
2026-10-29 05:18:17 +00:00
Myers Carpenter
965d7522e0 perf(actions): is_reusable_caller index makes SQLite read nearly every job row (#40055)
On SQLite, the `is_reusable_caller = false` filters (e.g. the admin job
queue page) are planned from `IDX_action_run_job_is_reusable_caller`,
which matches almost every row, so nearly the whole `action_run_job`
table is read. `ANALYZE` does not help: `sqlite_stat1` only keeps
average rows per key.

No query needs that index. `is_reusable_caller = true` is only used by
`expandedCallerCond`, always together with `repo_id` and
`concurrency_group`. Their plans use other indexes on SQLite, PostgreSQL
17 and MySQL 8.4 (PostgreSQL is slightly slower with it).

This removes the index tag. `SyncAllTables` drops the existing index on
the next start, so no migration is needed.

Job queue count on SQLite, before: a search on the `is_reusable_caller`
index; after: a `MULTI-INDEX OR` on the `status` and `pickup` indexes.
On an instance with about 7,000 jobs the admin job queue page went from
`Page: 784ms` to `39ms` (measured with an earlier revision of this PR
that gave the same plan).

---------

Signed-off-by: Myers Carpenter <myers@maski.org>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-28 19:25:58 +00:00
Danny Ward
1ebeade2c1 fix(indexer): search code as case-insensitive literal substrings (#40028)
Code search now matches case-insensitive literal substrings like GitHub,
in every script. Word tokenizers can't do this: the ES pattern dropped
every non-ASCII word, and the `standard` tokenizer keeps `console.log`
as one token.

- Elasticsearch indexes positional trigrams, plus 1-2 grams for short
terms
- Bleve indexes distinct 1-3 grams as a candidate filter and verifies
candidates against the content before paging, because phrase queries
over trigrams are quadratic in repeated grams
- Snippet offsets come from the original content, so `<em>` in sources
and width-changing lowercase no longer misplace them
- `search_mode=fuzzy` requests behave like Words, the UI never offered
fuzzy for code search
- The ES code indexer test now runs in CI, it was skipped before

There are no UI changes. Both code indexes rebuild after upgrade.

Fixes: https://github.com/go-gitea/gitea/issues/40027
Fixes: https://github.com/go-gitea/gitea/issues/16158
Fixes: https://github.com/go-gitea/gitea/issues/21359

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-28 20:38:45 +08:00
Jorge
02c03d69d3 enhance(repo): allow system users in protected tag allowlist (#40038)
Allowlisting gitea-actions lets any workflow of the repository with write access push matching tags, the same trade-off branches already have. Allowing gitea:deploy-key is new for tags.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-28 09:19:35 +00:00
wxiaoguang
5671c5192d refactor: use default error response for swagger (#40048)
ref: https://github.com/go-gitea/gitea/pull/39942#discussion_r4393487959
2026-10-28 09:05:00 +00:00
Vu Linh Truong
30e5ef4228 fix(ui): org worktime page overflows the screen on mobile (#40033)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-28 08:49:17 +00:00
silverwind
643bd38051 fix: size mermaid iframe by the diagram's aspect ratio (#40046)
Mermaid scales diagrams wider than the markup column down to its width,
but the iframe kept the unscaled viewBox height, leaving a large blank
area below them.

The iframe now uses a CSS `aspect-ratio` capped at the viewBox height,
so it follows the rendered diagram on any width and resize without
measuring, which keeps diagrams in collapsed `<details>` working.
Diagrams that don't scale (`useMaxWidth: false`) keep the fixed height.

Fixes: https://github.com/go-gitea/gitea/issues/40045
2026-10-28 03:54:41 +00:00
Giteabot
9cacca6f65 fix(deps): update module golang.org/x/net to v0.62.0 [security] (#40050)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) | [`v0.61.0` →
`v0.62.0`](https://cs.opensource.google/go/x/net/+/refs/tags/v0.61.0...refs/tags/v0.62.0)
|
![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fnet/v0.62.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fnet/v0.61.0/v0.62.0?slim=true)
|

---

### Avoid quadratic complexity when coalescing text nodes in html in
golang.org/x/net
[CVE-2026-78661](https://nvd.nist.gov/vuln/detail/CVE-2026-78661) /
[GO-2026-6860](https://pkg.go.dev/vuln/GO-2026-6860)

<details>
<summary>More information</summary>

#### Details
When coalescing adjacent text nodes in addText and fosterParent,
appending to an existing TextNode's Data via string concatenation
(n.Data += text) reallocated and copied the accumulated string on every
token. Inputs containing many short text runs separated by ignored end
tags (such as "a</x>") caused O(n^2) allocations and byte copies.

Accumulate coalesced text in per-node byte slices during parsing and
materialize Data strings once at the end of parse.

#### References
- [https://go.dev/cl/849365](https://go.dev/cl/849365)
- [https://go.dev/issue/82224](https://go.dev/issue/82224)
-
[https://groups.google.com/g/golang-announce/c/sqrs2vTx_Xs](https://groups.google.com/g/golang-announce/c/sqrs2vTx_Xs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6860) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### Check all ancestor namespace transitions when rendering raw text in
html in golang.org/x/net
[CVE-2026-97045](https://nvd.nist.gov/vuln/detail/CVE-2026-97045) /
[GO-2026-6866](https://pkg.go.dev/vuln/GO-2026-6866)

<details>
<summary>More information</summary>

#### Details
In childTextNodesAreLiteral, walking up the tree previously stopped at
the nearest foreign ancestor once an HTML integration point was found.
However, that integration point may itself be a descendant of an element
that was foster-parented into a foreign namespace (such as <mglyph> or
<malignmark> fostered under <mtext>), causing a parser to resolve the
subtree in a different foreign namespace upon re-parsing the rendered
output.

Continue walking the full ancestor chain and verify that every
transition from a non-empty namespace occurs at an HTML integration
point before emitting literal text.

#### References
- [https://go.dev/cl/855121](https://go.dev/cl/855121)
- [https://go.dev/issue/82622](https://go.dev/issue/82622)
-
[https://groups.google.com/g/golang-announce/c/sqrs2vTx_Xs](https://groups.google.com/g/golang-announce/c/sqrs2vTx_Xs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6866) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>
2026-10-27 20:29:02 -07:00
GiteaBot
a4ff1dcac7 [skip ci] Updated translations via Crowdin 2026-10-28 00:40:15 +00:00
Basavaraj S m
92ad81bb14 fix(feed): require code read access for the repo home branch feed (#39964)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-28 00:33:35 +00:00
bircni
178d33ed4d fix: add missing access checks and size limits (#39942)
Several handlers and parsers skipped checks that their siblings already
perform.

- Reject issue, comment, label, milestone and release mutations in
archived repositories via the API with one guard per route group,
matching the web UI
- Hide timeline references and dependencies pointing at issues the doer
cannot read, in the web UI and the API, including pull requests of the
same repository without pull request access
- Reject workflow runs with more than `MaxJobNumPerRun` jobs, including
inline reusable workflow jobs, and show rejected pushes as invalid
workflow runs
- Bound how much of a Helm chart archive is inflated while looking for
`Chart.yaml`

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-27 21:40:42 +01:00
GiteaBot
f35c67bd79 [skip ci] Updated translations via Crowdin 2026-10-27 00:52:41 +00:00
leo
9e27bce945 fix(markup): keep commit title emoji inside links (#39986)
Render emoji inside the default commit-title link so the whole title
remains clickable, including emoji-only titles. Explicit links and
mentions keep their own destinations.

Fixes https://github.com/go-gitea/gitea/issues/31873
2026-10-26 15:27:42 +00:00
petervavrinecz
a397e92c80 fix(issue): prevent redirect to external tracker when built-in tracker is enabled (#39997)
PR #39354 added support for enabling both internal and external trackers
concurrently and introduced separate navigation buttons.

However, as originally pointed out during code review on #39354
([comment](https://github.com/go-gitea/gitea/pull/39354#discussion_r4047774417)):
> *"With both trackers enabled and the built-in issue unit readable,
this redirect still runs whenever the external URL is nonempty, so
`/issues` never reaches the internal issue list in the new combined
configuration. Only redirect to the external tracker when the caller
cannot read the internal issue unit."*

`MustEnableIssues` in `routers/web/repo/issue.go` was still
unconditionally redirecting whenever `unit.TypeExternalTracker` is
enabled with a non-empty `ExternalTrackerURL`.

### Fix

In `routers/web/repo/issue.go`, only redirect to the external tracker
URL when the caller cannot read the built-in issue unit
(`!ctx.Repo.Permission.CanRead(unit.TypeIssues)`).

Signed-off-by: Vavrinecz Péter <98645804+petervavrinecz@users.noreply.github.com>
2026-10-26 08:12:14 -07:00
wxiaoguang
879ebfb3d8 fix: replace null char with space for issue/comment content (#40014)
A simple workaround to fix #40003
2026-10-26 14:55:06 +00:00
Giteabot
f0bb283a17 chore(deps): update dependencies (#40020)
Co-authored-by: silverwind <me@silverwind.io>
2026-10-26 14:36:25 +00:00
silverwind
8321b09912 fix(git): default to the gc maintenance strategy (#40022)
Git 2.54 made "geometric" the default auto-maintenance strategy. Pushes
wait for its foreground reflog expiry
(https://github.com/go-gitea/gitea/issues/39693), and its detached
repacks race with filesystem operations, as in
https://github.com/go-gitea/gitea/actions/runs/40236681252/job/121299686933.
Pin the pre-2.54 "gc" strategy, older git ignores it.

https://github.com/go-gitea/gitea/pull/39745 is the more complete fix
which disables the auto-maintenance.
2026-10-26 14:05:34 +00:00
silverwind
85211296dd test: speed up go tests (#39914)
- Skip race instrumentation of the transpiled SQLite engine
`modernc.org/sqlite/lib` when `GOTEST_FLAGS` contains `-race`, Gitea
code and the driver boundary stay instrumented. `models/issues` drops
from ~10s to ~5s.
- Hash OAuth2 client secrets with `bcrypt.MinCost` in tests, like user
passwords already use the `dummy` hasher. `TestOAuth2` drops from ~22s
to ~2s on the PostgreSQL job.
- Stop the Redis locker's extension loop directly on `Close` instead of
waiting for its next tick, and run the memory locker tests under
`testing/synctest`. `modules/globallock` drops from ~12s to ~4s.

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-26 11:22:44 +00:00
Lunny Xiao
e221551c7b fix: Fix OneDev migration with OneDev 16.8 and newer (#40017)
OneDev 16.8 moved the server version endpoint from
`/~api/version/server` to `/~api/server/version`. Both are tried, the
new one first, and both errors are reported when neither works.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-26 09:48:18 +00:00
GiteaBot
1657036ba6 [skip ci] Updated translations via Crowdin 2026-10-26 00:54:21 +00:00
Hello-Liar
257c34e003 fix(repo): use the viewed ref's editorconfig in file view, blame and editor (#39973)
The file view, blame and web editor read `.editorconfig` from the
default branch, so branch-specific settings like tab width were ignored.
The file view's middleware ran before the ref was resolved, and blame
never loaded it.

- `GetEditorconfig` reads the current commit, falling back to the
default branch only where no ref is resolved
- `Home` and `RefBlame` load the editorconfig after ref resolution

Fixes: https://github.com/go-gitea/gitea/issues/27413
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-25 21:41:06 +00:00
Lunny Xiao
f10f7e7f91 fix(migrations): only allow local paths for plain git migration (#40006)
Only the plain git migration can clone from a local server path. Other
services (Gitea, GitLab, Gogs, OneDev, GitBucket, Codebase, CodeCommit,
Bitbucket Server) are accessed through their remote API, so a local path
never works, yet their migrate forms advertised "or a local server
path".

- Remove the local path hint from those templates.
- Add `IsMigrateURLAllowedForService` and use it in the web and API
migrate handlers and in `MigrateRepository`, rejecting local paths for
any service other than plain git.
- Add a unit test.

Raised in review of #39913.

_Authored with Codet (AI assistant) on behalf of @lunny._

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-25 21:21:19 +00:00
silverwind
ff68b39c93 docs: update AGENTS.md and dev docs (#39976)
Agents rarely read the `docs` guides when AGENTS.md only asks for
relevant ones. AGENTS.md now names the guide for each area, and rules
agents kept missing go into those guides.

- AGENTS.md maps Go, TS, CSS, Vue, templates, tests, config options,
migrations and refactors to their guide
- The backend guide lists XORM pitfalls behind recent bugs
(https://gitea.com/xorm/xorm/issues/2615): zero-valued struct
conditions, `Cols` and `updated` columns on updates, soft-delete values,
index drops by `SyncAllTables`, MSSQL limits and pagination order
- The development guide requires a `WIP:` companion docs PR copying the
ini text verbatim for `app.example.ini` changes, and says to avoid
migrations unless the alternative is a workaround
2026-10-24 22:36:27 +00:00
silverwind
8abda62312 test: consolidate Go tests (#39962)
Consolidate all Go tests. Coverage is identical to before. Go tests are
around 5% faster while being 7% smaller. No non-test code is touched.
2026-10-24 13:48:19 +00:00
fixfon
ce7364e215 fix: get valid UTF-8 for diff_hunk and composer readme (#39932)
* Fixes #39929
* Fixes #39994

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-24 15:17:08 +02:00
Basavaraj S m
4f744d15be fix(actions): drop untrusted Content-Encoding from v1 artifact uploads (#39855)
Legacy (v1 to v3) artifact uploads stored the request's
`Content-Encoding` header in the column that v4 artifacts read as their
`Content-Type`.

- Store only the `gzip` marker on legacy uploads, the only encoding
their clients send
- Pick the PDF and audio/video serve headers only for content types that
`mime.ParseMediaType` accepts

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-24 11:48:07 +00:00
schop-rob
497e169398 fix(diff): load both blobs of renamed files for diff rendering (#39969)
When opening a pull request that renamed a CSV file:
the compare page fails with a 500.

Fix: load the left blob for every file that was not added, and the right blob
(from `Name`) for every file that was not deleted. That matches what the
templates did before #38517.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-24 08:44:16 +00:00
wxiaoguang
1d8474ee1f refactor: username sub route (#39967)
Remove the workaround for chi router, do as much as possible in
middlewares

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-24 16:27:56 +08:00
silverwind
fc49368845 feat: Improve Gitea-compatible forge migrations, fix many migration bugs (#39424)
Supports migrating from Gitea-compatible forges like Forgejo and fixes
many migration bugs.

1. Pre-release and `+gitea-` suffixed server versions no longer drop
reviews, reactions and pagination.
2. Review states, dismissals, scoped and archived labels and merge bases
are migrated.
3. Disabled units, external trackers, unavailable release assets, and
GitLab confidential issues and internal notes are skipped.
4. Credentials are only sent to the source host, and `Retry-After` rate
limits are waited out.
5. GitHub migrations of only issues or pull requests, basic auth clones
and dismissed reviews work again.
6. The API returns 404 for releases of a disabled unit and shows
migrated authors as mannequins, replacing the never populated
`original_author` and `original_author_id` fields.

Fixes: https://github.com/go-gitea/gitea/issues/19533
Fixes: https://github.com/go-gitea/gitea/issues/35885
Fixes: https://github.com/go-gitea/gitea/issues/18275
Fixes: https://github.com/go-gitea/gitea/issues/35069
Fixes: https://github.com/go-gitea/gitea/issues/17763
Fixes: https://github.com/go-gitea/gitea/issues/39548
Co-authored-by: bircni <bircni@icloud.com>
2026-10-24 01:15:31 +00:00
GiteaBot
7c91252be3 [skip ci] Updated translations via Crowdin 2026-10-24 00:51:27 +00:00
Mitrahsoft
96bee6c5a5 feat(api): add REST API endpoints for repository webhook delivery history (#38775)
Webhook delivery history (request/response, status, timestamp) was only
reachable through the repo settings UI, which shows the last ~10
deliveries with no filtering. There was no way to fetch this data
programmatically for debugging integrations or auditing webhook
activity.

This adds two read endpoints at the repository level:

- `GET /repos/{owner}/{repo}/hooks/{id}/deliveries` — paginated list of
  a hook's deliveries, most recent first
- `GET /repos/{owner}/{repo}/hooks/{id}/deliveries/{uuid}` — a single
  delivery, including request/response headers, body, and status

Both reuse the existing `hook_task` storage and retention policy (the
`CleanupHookTaskTable` cron job) that already back the "Recent
Deliveries" web UI, and are gated by the same `reqAdmin()` +
`reqWebhooksEnabled()` guard as the existing hook endpoints.

Org, user, and admin/system-level webhooks are intentionally out of
scope for this PR to keep it reviewable; they can follow as separate
PRs using the same pattern.

## Usage

```bash
curl -H "Authorization: token $TOKEN" \
  https://gitea.example.com/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries

curl -H "Authorization: token $TOKEN" \
  https://gitea.example.com/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{uuid}
```

## Testing

- Unit tests for the new model-layer query functions
  (`ListHookTasks`, `GetHookTaskByUUID`)
- Integration tests covering: list + get, an undelivered task's response
  is `null`, a hook/delivery UUID from another repository 404s instead
  of leaking across repos, and pagination (`page`/`limit`, `Link` and
  `X-Total-Count` headers, correct ordering/no overlap across pages)
- `make lint-go`, `make generate-swagger`

Fixes: https://github.com/go-gitea/gitea/issues/38463.
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-24 00:18:51 +00:00
Shivansh Garg
bcb4559c10 enhance(project): filter project milestones by linked issues (#39958)
The project milestone filter listed every milestone of the repo, or of
all accessible org repos for org projects, including ones with no issue
in the project. That made the dropdown noisy and unhelpful.

Both repo and org projects now list only milestones that have at least
one issue linked to the project, via a new `ProjectID` option on
`FindMilestoneOptions`.

Fixes: https://github.com/go-gitea/gitea/issues/39150
Co-authored-by: silverwind <me@silverwind.io>
2026-10-23 23:00:09 +00:00
Léon Simmons
efd69ab1ff docs: fix typos in code comments (#39968)
- `models/asymkey/error.go`: `IsErrGPGKeyIDAlreadyUsed` doc named the
wrong type (`ErrKeyNameAlreadyUsed` → `ErrGPGKeyIDAlreadyUsed`)
- `modules/lfs/http_client.go`: `batchs` → `batches`
- `routers/web/repo/milestone.go`: `edting` → `editing` (x2), `for
change` → `for changing`

Signed-off-by: Léon Avic Simmons <Avicennasis@gmail.com>
2026-10-23 22:41:08 +00:00
Lunny Xiao
a2da5cd7b5 feat(migrations): add migration from Bitbucket Server (Data Center) (#39913)
Add migration from Bitbucket Server (Data Center). It has no issues,
wiki, milestones or releases, so those are not offered.

Migrated: git data with tags and LFS, and pull requests with state,
draft flag, merge commit/base, comments, approvals and inline code
comments including resolved threads. Multi-line ranges become a `Lines
a-b:` text prefix, as Gitea anchors code comments to one line. Auth is
an HTTP access token (personal, project or repository) or
username/password; 429 responses are retried.

Changes outside the downloader:
- `ReviewComment.Resolved`, applied by the uploader.
- The uploader creates a fork PR's head branch from its head commit when
already in the repository, instead of fetching the fork (Bitbucket keeps
it in `refs/pull-requests/<id>/from`; private forks need credentials).

Tested with unit tests, responses recorded from Bitbucket 10.5.1, and an
end-to-end migration. Independent of
https://github.com/go-gitea/gitea/pull/38607 (Bitbucket Cloud); the
service type enum value needs adjusting for whichever lands second.

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-10-23 19:25:29 +00:00
silverwind
8cd7721deb ci(release): stop creating deployments for release tags (#39966)
Each `release-create-tag` run created a deployment record, which makes
GitHub show a useless "This branch has not been deployed" box on every
PR:

<img width="819" height="113" alt="image"
src="https://github.com/user-attachments/assets/129c8348-1d70-46b9-8996-8872c84a11f2"
/>

With `deployment: false`, the `release-signing` approval and branch
policy still apply.
2026-10-23 11:17:53 -07:00
silverwind
5e4ed4d508 fix: correctly parse and validate forwarded client IPs and remove unmaintained chi-middleware/proxy (#39190)
Replace the unmaintained `chi-middleware/proxy` with an in-tree handler
that detects client IPs correctly behind common reverse proxies.

1. Handle repeated header lines and comma-separated proxy hops.
2. Accept and normalize IPs, including `ip:port`.
3. Keep the client port from the header value or `X-Real-Port` instead
of logging port 0.
4. Prefer the last `X-Real-IP` line.
5. Keep the proxy's address when the selected value is invalid.
6. Read `Forwarded` only when `X-Forwarded-For` is absent.

Docs PR: https://gitea.com/gitea/docs/pulls/560
Fixes: https://github.com/go-gitea/gitea/issues/35682
Fixes: https://github.com/go-gitea/gitea/issues/36814
Co-authored-by: bircni <bircni@icloud.com>
2026-10-23 09:25:04 +00:00
Lunny Xiao
5129ad5439 perf(actions): cache successful runner token verification (#39934)
Every Actions runner RPC (`FetchTask` polling, `UpdateTask`,
`UpdateLog`, ...) goes through the `withRunner` interceptor, which
verifies the runner token with PBKDF2 (`auth_model.HashToken`, ~2 ms).
In a 60s CPU profile of a production instance with many runners, this
accounted for ~49% of all CPU samples.

- Cache successful verifications in a dedicated LRU of up to 10000
runners, keyed by the stored salt and hash and holding only a SHA-256
digest of the token. A hit costs ~100 ns.
- The runner is still loaded from the DB on every RPC and failures are
never cached, so deleted runners and wrong tokens never pass.
- The shared `auth_model.TokenCache` is not used because its default 20
entries miss on every poll once more runners poll in turn.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-23 03:59:11 +00:00
David Lovas
6624d96689 feat(migrations): GraphQL fast path for GitHub migration + rate-limit resilience (#38694)
Adds a batched GraphQL path for GitHub migrations that fetches each
issue or pull request together with its comments, reviews, reactions and
timeline events instead of the REST N+1 pattern, sharply reducing
rate-limit pressure on large repositories.

- GraphQL is used for authenticated migrations with issues or pull
requests and falls back to REST automatically when it fails
- timeline events (close/reopen, lock, rename, label, milestone, merge,
branch deletion, pin, auto-merge) are imported as typed comments showing
their GitHub author
- GitBucket issue migrations no longer stop after the first page,
GitBucket sends no Link header

Fixes: https://github.com/go-gitea/gitea/issues/13243
Fixes: https://github.com/go-gitea/gitea/issues/31856
Signed-off-by: David E. Lovas <david@rexovas.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-23 02:23:28 +00:00
Lunny Xiao
c45af635e4 feat: Allow automatic project actions on events (#30205)
This PR introduces project workflows, allowing users to configure
event-based automations directly from the UI.
Users can define filters and actions for each workflow event, and
duplicate existing events when needed.

### Concepts

Every workflow is composed of `event`, `filters`, and `actions`.

- **Event** - An Event defines when a workflow should be triggered. It
represents specific activities such as an issue being added to the
project, a pull request being merged, or a project column being changed.
- **Filter** - A Filter refines when an event should take effect. It
allows users to specify conditions like issue type, labels, or project
columns to ensure actions only run in targeted scenarios.
- **Action** - An Action defines what happens when the event and filters
match. It performs operations such as adding labels, moving issues
between columns, or changing the issue or pull request state.

### Available Events

Currently, the following eight events are supported:

- On issues/pull requests closed
- On issues/pull requests reopened
- On issues/pull requests was added to the project, including when
created in it
- On issues/pull requests was removed from the project
- On issues/pull requests' column was changed
- On code changes requested
- On code review approved
- On pull request merged

Each event type supports its own set of filters and actions.

### Available Filters

There are four types of filters that can be configured for an event:

- **Apply to** - Specify the target type: `issues only`, `pull requests
only`, or `issues and pull requests`.
- **Labels** - Trigger the event only if the issue or pull request
contains one or more specified labels.
- **Move from column** - Filter events based on the original project
column.
- **Move to column** - Filter events based on the destination project
column.

### Available Actions

There are four types of actions that can be executed when an event is
triggered:

- **Add labels** - Automatically add one or more labels to the issue or
pull request.
- **Remove labels** - Remove one or more specified labels.
- **Move to column** - Move the issue or pull request to a designated
project column.
- **Issue state** - Close or reopen the issue. Pull requests can only be
closed.

### Enable/Disable an event workflow

Each event workflow can be individually enabled or disabled. When
disabled, the workflow will be ignored even if the event occurs. This
feature is useful when you want to temporarily stop certain automated
actions without deleting the workflow configuration. Workflows of closed
projects don't run either.

### Clone an event workflow

When you need multiple workflows for the same event, you can clone an
existing event to create a new one with different filters and actions.
This allows you to customize how the same event behaves under different
conditions, making workflows more flexible and powerful.

### Permissions

Permissions are checked when workflows are managed, not when they run:

- Viewing a project's workflows needs read access to the project
- Managing them needs write access to the project plus write access to
both issues and pull requests of a repository project's repository,
being an owner of an organization project's organization or a site
administrator, or being the user of a user project. Tokens limited to
public repositories can't manage owner-level projects' workflows
- A workflow runs when its event happens to an item of an open project
that can hold the item's repository, unless that repository is archived,
no matter who caused the event, and applies its actions without further
permission checks. Pull requests are never reopened
- Changes show as made by "Project workflow" on behalf of the user who
triggered the event and don't trigger other workflows
- Only users who can read an item can put it on a board or move its card

The management rule is also documented on `CanManageWorkflows` in
`services/projects/workflow.go`.

Fixes:  #14359
Fixes: #26704
Fixes: #27990
Replaces:  #28745
Documentation PR: https://gitea.com/gitea/docs/pulls/308
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-23 01:53:45 +00:00
GiteaBot
a17f326c17 [skip ci] Updated translations via Crowdin 2026-10-23 00:53:15 +00:00
silverwind
f41cc4cd8d docs: clarify that the team packages permission covers all org packages (#39953)
The Packages row in the team settings read "Manage repository
packages.", but packages belong to the owner, so a team's Packages
permission applies to every package of the organization, regardless of
the team's repositories. Reword it to say so. The docs get the same
clarification in https://gitea.com/gitea/docs/pulls/605.
2026-10-22 23:32:24 +00:00
silverwind
5f85a5157d ci: update giteabot to v1.3.1 (#39947)
Bump `giteabot` to
[`v1.3.1`](https://github.com/go-gitea/giteabot/releases/tag/v1.3.1) in
both workflows. It fixes `reviewed/wait-merge` PRs that the merge queue
left behind their base, like
https://github.com/go-gitea/gitea/pull/39854 and
https://github.com/go-gitea/gitea/pull/39928, and stops the lgtm sweep
from re-posting unchanged statuses
(https://github.com/go-gitea/giteabot/pull/30).
2026-10-22 23:05:02 +00:00
silverwind
ad37b88631 chore: sync pnpm engines version with packageManager (#39950)
The pnpm `engines` range was still `>= 11.0.0` while `packageManager` is
on pnpm 12. Raise it to match, and set Renovate's range strategy for it
to `bump`, because the default strategy leaves an already satisfied `>=`
range untouched.
2026-10-22 22:45:59 +00:00
Josway
7ee96b7faa fix(ui): commit hashes in the activity feed are misaligned (#39883)
Fixes #38646

### The problem

Each commit line of a push in the activity feed starts with the avatar
stack of its co-authors. The width of that stack grows with the number
of co-authors, so the hash of every commit started at a different
horizontal offset and the column looked ragged.

---------

Signed-off-by: Josway <jxh98@foxmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 21:17:14 +00:00
Nazem
7448b49aac fix(api): adding issue labels by name attaches both the repo and the org label (#39873)
Fixes #39651

When an org-owned repo has a repo label and an org label with the same
name, `POST`/`PUT /repos/{owner}/{repo}/issues/{index}/labels` with
label names attached both of them.

---------

Signed-off-by: Nazem <214302554+nazem-droubi@users.noreply.github.com>
Co-authored-by: Nazem <214302554+nazem-droubi@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 20:54:33 +00:00
Basavaraj S m
269b116b1b fix(feed): hide release notes in tags feed without releases access (#39944)
The tags feed (`tags.rss` / `tags.atom`) lives in the tags route group,
which only requires code read access, yet it lists every published
release next to the plain tags and includes each release's title and
rendered note. The releases page and `releases.rss` require releases
read access, and the tags page only shows tag names to users without it,
so the feed is the one place that leaks. A team member granted Code but
not Releases on a private repo, or anyone on a public repo whose
Releases unit is disabled, can read all release notes this way. I
noticed it while comparing the feed routes with the tags template. When
the doer cannot read releases, the feed now lists releases as plain
tags, linking to `/src/tag/` like the tags page does.
2026-10-22 20:25:44 +00:00
Chris Eastwood
4bbe1b918a build(tools): install playwright deps only when that needs no prompt (#39905)
On Debian/Ubuntu outside GitHub Actions, `tools/playwright.sh` always
ran `pnpm exec playwright install --with-deps`. Installing system
packages needs root: when it isn't root, Playwright tries `sudo` and
falls back to `su`, so a non-root user without passwordless sudo sees
`make playwright` — and the targets depending on it, `make test-e2e` and
`make test-frontend` — either stop and ask for the root password
mid-build or fail outright with `su: System error`.

Related: #38860, which added this script.
---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 19:59:11 +00:00
bircni
3a70b85220 fix(actions): bound requested artifact retention (#39894) 2026-10-22 19:52:35 +00:00
bircni
c11302233b docs: clarify reverse proxy authentication requirements (#39940)
Document that reverse proxy authentication requires Gitea to be
reachable only through the authenticating proxy, and which routes each
setting covers.

Docs: https://gitea.com/gitea/docs/pulls/604
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 19:35:34 +00:00
silverwind
04c65f26f1 enhance: remove box-shadow from kbd elements (#39943)
Remove the inset bottom shadow from `<kbd>` elements in markup and on
keyboard shortcut hints. The skeuomorphic key cap look is not needed,
the border alone marks the element as a key.
2026-10-22 21:33:50 +02:00
silverwind
facc0f2b15 refactor: prevent out-of-bounds read (#39936)
`IsReadmeFileExtension("readme", exts...)` panicked with `index out of
range [6] with length 6` when `exts` had no empty extension. The only
caller always passes one, so this was not reachable in practice.

Fixes:
https://github.com/go-gitea/gitea/security/quality/rules/go%2Findex-out-of-bounds

Written by Claude.
2026-10-22 13:06:01 +00:00
Phoenix
5f8f625ff7 fix(migrations): use cursor pagination for GitHub issue migration too (#39888)
For repos with many issues, the GitHub API only allows cursor-based
pagination instead of page-based pagination after a certain page.
A full migration of such repos failed with a 422 response.
Now, we dynamically switch how we migrate based on the response from the GitHub API.

Fixes #39882

---------

Signed-off-by: Phoenix <44333391+yuhongwei380@users.noreply.github.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 12:00:12 +00:00
Aradhya Jain
bc5e896d35 enhance(webhook): show sender avatar on Slack notifications (#39656)
Closes #39655
2026-10-22 10:56:48 +00:00
Basavaraj S m
d4ecd5eadf fix(api): check unit read permission in issue sub-resource endpoints (#39885)
Several `/repos/{owner}/{repo}/issues/{index}/...` API endpoints load
the issue by index without checking
`CanReadIssuesOrPulls(issue.IsPull)`, unlike `GetIssue`,
`ListIssueComments` and `GetIssueReactions`. The route group's
`mustEnableIssuesOrPulls` passes when the caller can read either unit,
so someone who can read issues but not pull requests (a team with only
the Issues unit, or per-unit anonymous access on a public repo) can read
a pull request's whole conversation through `/timeline`, plus its
labels, subscribers, attachments and tracked times, and can subscribe to
it. The reverse holds for issues.

These handlers now return 404 in that case, like their siblings.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-22 12:04:20 +02:00
fixfon
aaeb502b30 fix(oauth2): decode form-encoded client credentials in HTTP Basic auth (#39926)
Fixes https://github.com/go-gitea/gitea/issues/39925

RFC 6749 §2.3.1 has clients form-urlencode the client id and secret
before building the HTTP Basic header. `openid-client` with
`client_secret_basic` does this and sends `-` as `%2D` and `_` as `%5F`.
The token and introspection endpoints used the raw header values, so
these clients failed with `invalid_client`. Both endpoints now decode
the values with `url.QueryUnescape` before the client lookup, the secret
check and the body consistency check, and reject a value that does not
decode. Plain HTTP Basic parsing in `httpauth` is unchanged, since the
encoding only applies to OAuth client authentication.

Clients that don't encode are unaffected, since no client id or secret
Gitea has ever issued contains `+` or `%`.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 04:18:43 +00:00
Jeremy Schoemaker
cf7050b91d fix(issues): keep one row per issue when sorting by exclusive label order (#39364)
Sorting by an exclusive label scope joined `issue_label`, returning one
row per label. Issues with several labels were listed repeatedly and
pushed others off the page, and `IssueIDs` returned inflated totals.

The sort key now comes from a correlated subquery, so each issue yields
one row.

Fixes: https://github.com/go-gitea/gitea/issues/35711
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 03:40:31 +00:00
silverwind
c1c6bdfedd test(e2e): enable async clipboard testing pref in Firefox (#39838)
When `navigator.clipboard.readText()` runs before the page's copy has
landed, Firefox shows its "Paste" prompt, which cannot be interacted
with when headless. Avoid that and and use
`dom.events.testing.asyncClipboard` which is a pref meant for testing
that disabes this prompt.

Fixes: https://github.com/go-gitea/gitea/actions/runs/39113819632/job/117635434106
Signed-off-by: silverwind <me@silverwind.io>
2026-10-22 03:16:54 +00:00
Giteabot
7e164fe61a chore(deps): update dependencies, migrate to connectrpc.com/connect v2 (#39854)
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 01:48:49 +00:00
silverwind
98c613d943 test: fix unit tests failing on repeated runs (#39915)
`go test -count=2` failed in 12 packages because tests leaked state into
later runs.

- Reset SQLite sequences after each fixture load, like PostgreSQL
already does
- Prepare the database in tests that relied on earlier tests doing it
- Restore mutated globals with `test.MockVariableValue`
2026-10-22 01:27:02 +00:00
GiteaBot
ef55965d98 [skip ci] Updated translations via Crowdin 2026-10-22 00:50:21 +00:00
Shivansh Garg
0948aed411 refactor(api): load assignees list correctly (#39917)
Correctly handle empty names, upper&lower cases, duplicate names.

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-21 16:15:43 +00:00
Shudhanshu Singh
33610142c3 chore(maintainers): add SudhanshuMatrix as maintainer (#39922) 2026-10-21 14:54:09 +02:00
Shudhanshu Singh
a8e1057f5e fix(api): keep milestone due date when it is omitted in edit request (#39869)
`PATCH /repos/{owner}/{repo}/milestones/{id}` always overwrote the due
date with `due_on`, so a request that only changed other fields, such as
`{"state": "closed"}`, silently removed it.

The due date is now kept when `due_on` is omitted, and `"due_on": null`
or `"due_on": ""` remove it.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-21 18:52:33 +08:00
Shudhanshu Singh
b5e53210c2 fix(lfs): send the default branch as ref in batch download requests (#39891)
Fixes https://github.com/go-gitea/gitea/issues/38085

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-21 03:12:41 -07:00
Ferdinand Maulana Za Fauzi
a6bc03cce1 fix(api): return 4xx for invalid fork repository names (#39909)
Fixes #39908

---------

Signed-off-by: Ferdinand-MZ <ferdinandmaulanazafauzi@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-21 17:55:01 +08:00
silverwind
3648598d7d chore(build): run golangci-lint via go run (#39907)
`make lint-go` ran `go install` and then a bare `golangci-lint` from
`PATH`, which fails when `GOBIN` is not in `PATH` and overwrites any
`golangci-lint` installed there. It now uses `go run -exec 'env
GOOS=<target>'`, which builds golangci-lint for the host and passes the
lint target `GOOS` only to golangci-lint.

`deps-tools` prebuilds the tools with `go run -exec true` instead of `go
install`, because `go install` does not fill the executable cache that
`go run` uses.

Also drops the unused `TAGS` env and the redundant OS name in
`--build-tags`.
2026-10-21 05:30:19 +00:00
silverwind
ecae91132b ci: update giteabot to v1.3.0 (#39910) 2026-10-21 04:59:39 +00:00
Artem Lytkin
29a59f6dcf fix(compare): show the diff when a comparison has no commits (#39247)
The compare page hid the diff when a comparison had no commits, even if
the trees differ. A direct comparison whose head is an ancestor of its
base hits this, which is where the Compare link on a force-push comment
leads after commits were dropped.

The diff box now shows whenever there are commits or changed files, with
the header reading "0 Commits".

Fixes: https://github.com/go-gitea/gitea/issues/36390
Co-authored-by: silverwind <me@silverwind.io>
2026-10-21 04:12:18 +00:00
Aradhya Jain
1244e06d76 enhance(webhook): mention release assets in notifications (#39631)
Release webhook notifications never mention attached files, although the
payload carries them.

The summary shared by all webhook providers now appends an asset count
like `(assets: 2)` for created and updated releases.

Closes https://github.com/go-gitea/gitea/issues/39630
Co-authored-by: silverwind <me@silverwind.io>
2026-10-21 01:24:03 +00:00
GiteaBot
bb58160aed [skip ci] Updated translations via Crowdin 2026-10-21 00:50:52 +00:00
wxiaoguang
76e88b30c0 fix: use clear error message when failed to parse git version (#39898)
fix #39892
2026-10-20 23:26:56 +00:00
silverwind
82536a3ede fix: off-by-one errors in code comment patches (#39887)
Code comments next to a diff hunk could show one line too high in the
conversation view vs. in the code view that showed them on the correct
line.

- Treat the hunk end in `CutDiffAroundLine` as exclusive, so the line
after a hunk falls back to the unchanged-line context
- Don't count `\ No newline at end of file` markers as lines when
cutting the hunk
- Share the unchanged-line fallback with migrated review comments
2026-10-20 22:10:04 +00:00
Basavaraj S m
c52e7c0cd2 fix(packages): reject newline-bearing rubygems dependency metadata (#39782)
The RubyGems compact index (the shared `/versions` file and per-package
`info/<name>` files) is line-based.
https://github.com/go-gitea/gitea/pull/38061 rejected line breaks in the
gem name, but the platform, dependency names and version requirements
from the gemspec still accepted them, letting an uploader forge index
lines for bundler clients.

The parser now rejects a line break in any of those fields. The Marshal
based endpoints are length prefixed and unaffected.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-20 21:18:44 +00:00
silverwind
738ff1d65a refactor(git): replace git stderr matching with exit codes (#39866)
Matching git's stderr text is fragile because it breaks silently when
git rewords a message. Where git offers an exit code or a query with a
defined answer, use that instead, and drop matches that could never
trigger. Follow-up to the discussion in https://github.com/go-gitea/gitea/pull/39857.

- Detect a missing remote by `git remote` exit code 2
- Resolve annotated tags to their tree with cat-file `^{tree}` instead
of spawning `ls-tree` when listing tree entries
- Skip the wiki page lookup when the default branch doesn't exist yet
- Remove unreachable "no merge base" and "unknown revision" checks
2026-10-20 21:00:32 +00:00
silverwind
52eb515751 enhance: improve hover border of colored button groups (#39861)
Hovering a button in a primary or red button group showed a grey frame,
because the generic group hover border applied to colored groups too.
Green groups had no colors at all.

Colored groups now let the hover background fill the border, and green
groups get the same colors as primary and red. The devtest page shows
all three.

<img width="569" height="304" alt="Hovered buttons in uncolored,
primary, red and green button groups, before and after"
src="https://github.com/user-attachments/assets/e0fb6b16-f1f1-459b-9848-9386cd9453c9"
/>

Extracted from https://github.com/go-gitea/gitea/pull/38404
2026-10-20 19:29:38 +00:00
wxiaoguang
d7d658a8e9 fix: prevent system users from watching a repository (#39886)
fix #39884
2026-10-20 19:07:20 +00:00
Shivansh Garg
5265238726 fix(api): skip label lookup in ListIssues when no labels filter given (#39890)
strings.Split("", ",") returns []string{""} (length 1), not an empty
slice, so the previous len(splitted) > 0 guard was always true and never
skipped the label ID lookup, even when the labels query param was
absent. Check the raw trimmed param for emptiness before splitting,
mirroring the pattern already used elsewhere in this file.

Fixes: https://github.com/go-gitea/gitea/issues/39889
2026-10-20 18:46:14 +00:00
fixfon
35fa0400e9 fix(pulls): tolerate invalid stored review commit_id (#39849)
Fixes #39848

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-20 11:03:34 -07:00
Shudhanshu Singh
edd0c0e2ce feat: add release reactions and a GitHub-like reaction bar (#38415)
Adds emoji reactions to releases and moves the reaction picker of
issues, comments and releases into a GitHub-like reaction bar.

- Releases omit 👎 or 😕
- Drafts and tags can't be reacted to
- Moved menu to tippy
- Added keyboard navigation

Fixes: https://github.com/go-gitea/gitea/issues/34268
Fixes: https://github.com/go-gitea/gitea/issues/16676
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-20 15:04:10 +02:00
silverwind
e645afbb3f refactor: replace Fomantic modal with native dialog (#39846)
Replace Fomantic's hard-forked jQuery modal with native `<dialog>`
elements, advancing https://github.com/go-gitea/gitea/issues/29849. The
UI is visually unchanged. Dialogs open with `show()` over an `inert`
page, so tooltips, popups and toasts mounted on `<body>` stay usable
above them, unlike with `showModal()`
(https://github.com/whatwg/html/issues/9936).

- Remove the Fomantic modal and dimmer, add `showModal` and `hideModal`
- Declare modals as `<dialog class="modal-dialog">`, shown inside a
`.modal-backdrop`, instead of `ui modal` with its unused size classes
- Remove two modals that could never be opened
- Block the page while a modal is open, close the topmost one on Escape
and restore focus to its opener
- Allow stacked modals, use a confirm modal instead of `window.confirm`
for deleting content history
- Hide popups and toasts when a modal opens or closes
- Keep the release notes modal open when generating notes fails

Fixes: https://github.com/go-gitea/gitea/issues/39643
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-20 12:32:27 +02:00
Giteabot
7610336d74 chore(deps): update go toolchain directive to v1.27.3 (#39881) 2026-10-20 05:30:28 +02:00
Danny Ward
3701fcec84 fix(issues): use unified user display name pattern for more places (#39852)
* Fix #39761

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-20 01:13:17 +00:00
GiteaBot
f8dd2dd4cc [skip ci] Updated translations via Crowdin 2026-10-20 00:51:42 +00:00
bircni
ba4f8fa246 enhance(pull)!: add per-repository default squash merge commit message (#39816)
Adds a repository setting for the default squash merge commit message
body with GitHub's options `PR_BODY`, `COMMIT_MESSAGES` and `BLANK`,
also settable via `squash_merge_commit_message` in the API. The commit
title stays the pull request title.

1. Breaking: API squash merges without `merge_message_field` now get the
same default message as the web merge box instead of a title-only
commit, which affects clients sending only `Do` like Renovate. An empty
value keeps title-only.
2. Breaking: `[repository.pull-request] DEFAULT_SQUASH_MERGE_MESSAGE`
replaces `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`, which still
works as a deprecated fallback. Repositories store the default when
created or next saved.
3. Deprecated app.ini keys now migrate through one table instead of
per-loader fallbacks, which also fixes the ignored `[mailer] CERT_FILE`
and `KEY_FILE` fallbacks. Deprecation warnings no longer claim a removal
version.

<img width="1891" height="571" alt="grafik"
src="https://github.com/user-attachments/assets/0fdcde13-1f11-4156-b598-d80eb0d04cbe"
/>

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-19 09:15:41 +00:00
GiteaBot
458eda0f1d [skip ci] Updated translations via Crowdin 2026-10-19 00:56:21 +00:00
silverwind
5264304a34 refactor: carry the API token on the doer (#39690)
The token a request authenticated with now lives on the doer, so
permission and visibility checks apply its scope from the doer alone.
Replaces https://github.com/go-gitea/gitea/pull/39167, following
https://github.com/go-gitea/gitea/pull/39167#issuecomment-6063839619.

1. Public-only tokens get 404 instead of 403 for hidden users, orgs and
repos, like GitHub.
2. Org ownership, site admin, repo and org creation, generation and
transfer endpoints reject public-only tokens.
3. API pull request responses set `head.repo` to `null` and
`head.repo_id` to `-1` when the caller cannot see the head repository.
4. Public-only tokens see org memberships like anonymous callers, so
concealed members stay hidden.
5. Site admin access in activity feeds, follower lists, org members,
Actions run lists and packages needs a session or an `all`-scoped token.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-19 00:34:01 +00:00
silverwind
7e62dda791 chore(actions): update stale go-yaml wrapping comment (#39844)
https://github.com/yaml/go-yaml/pull/441 fixed the wrapping bug this
comment waited for, so reword it. `WithLineWidth(-1)` stays, wrapping
the machine-read payload helps no reader and only adds risk.

Runner: https://gitea.com/gitea/runner/pulls/1324
2026-10-18 18:04:39 +00:00
wxiaoguang
f690ea8efd chore: refactor legacy JS init code to use "data-global-init" (#39836)
Move the remaining legacy init code, which probed page classes with
`querySelector`, to `data-global-init`, so each feature initializes from
its own element. `hashchange` listeners now go through one module that
runs them in priority order, because independent listeners broke when
mount order changed.

- Convert issue list, diff, repo create, settings and org team inits to
`data-global-init`
- Run `hashchange` listeners by priority, the diff scroll handler last
- Make the devtest mock's failing log requests deterministic
- Add e2e tests for diff tree navigation to folded files and job log
permalinks

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-18 13:59:51 +00:00
silverwind
ae17c14cff fix: add missing checks to several handlers and models (#39839)
Several handlers and model helpers skipped checks that their siblings
already perform.

- Only match principal keys when looking up SSH certificate principals
- Return no attachments for an unsaved issue instead of every issue-less
one
- Reject issue and comment attachments when adding release attachments
- Share one permission check for editing, deleting and reacting to issue
content and for comment attachments between the web UI and the API,
refusing them on locked issues for users who can't write
- Reject issue edits, deletion and label changes in archived
repositories via the API
- Apply cross-origin protection to the repository topics endpoint
- Return 404 for the repositories tab of individual users and respect
profile visibility for the heatmap

Fixes: https://github.com/go-gitea/gitea/issues/39226
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-18 06:29:49 -07:00
silverwind
1031e34987 test: run integration tests on random ports and speed up slow tests (#39842)
Integration test processes now get their own work directory, config file
and random ports, so several runs can execute in parallel on one
machine. The slowest tests also lose fixed sleeps and needlessly
expensive setup, with their assertions kept.

- Batch PostgreSQL sequence resets, the main per-test fixture cost there
- Cheap hashing params in hash and OAuth2 fixture tests, full-cost
vectors stay
- Single fetch for negative Actions runner assertions instead of 1s
polls
- Queue flushes and backdated timestamps instead of sleeps
- In-memory SQLite test journal, tmpfs for PostgreSQL in CI
- E2E instance writes SSH keys into its work directory instead of
`~/.ssh/authorized_keys`
2026-10-18 12:56:04 +00:00
Lunny Xiao
d0c35bcd58 refactor(git): remove unused TreeCommon embedded struct (#39830)
`TreeCommon` only holds `ID` and is embedded solely by `Tree`, with no
other users. Inline the field into `Tree`; `t.ID` access is unchanged.

Assisted-by: Codet:codet
2026-10-18 12:33:19 +00:00
wxiaoguang
64846db075 fix: use correct template variable on milestone page (#39834)
* fix #39828
2026-10-18 10:42:43 +00:00
Basavaraj S m
18d0ee174d fix(mailer): reject incoming email tokens of users who cannot sign in (#39833)
Incoming email reply tokens are embedded in every notification email and
stay valid for a year. `DecodeToken` verifies them with `user.Rands`,
which is not rotated when an admin disables sign-in or the account is
deactivated, and `ReplyHandler` only checks repo read permission. A
suspended user could therefore keep posting comments, review comments
and attachments on any public repo by replying to old notifications,
while the web, API, git and package paths all refuse them. `DecodeToken`
now rejects tokens of users who are inactive or prohibited from signing
in, which covers every incoming mail handler.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-18 10:20:00 +00:00
Lunny Xiao
52b18b4903 fix(pull): recover PRs whose recorded merge commit never reached the base repo (#39831)
When pushing the merge commit to the base repo fails (e.g.
non-fast-forward), the merge intent recorded by
https://github.com/go-gitea/gitea/pull/39442 is left behind.
`merged_commit_id` then points to a commit that does not exist in the
base repo, and `git branch --contains` fails with `no such commit`, so
the PR can never be checked or merged again.

- Clear the recorded merge intent if the push fails and the commit is
not on the base branch
- `IsCommitInBranch` now returns false for a missing commit, so
already-stuck PRs recover on the next check

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

_Written with Codet assistance._

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-18 09:52:44 +00:00
Ubayed Bin Sufian
13ddad6e07 enhance: allow custom watches to bypass on-mention email restriction (#39652)
Users with "Only Email on Mention" can now still receive email for the
events they picked in a custom repository watch, covering issues, pull
requests and releases. The setting is renamed to "Only Email on Mention
and Custom Watches" to make this explicit, matching GitHub's
"Participating, @mentions and custom".

"All activity" watches don't bypass the setting, because they are also
created automatically by `AUTO_WATCH_NEW_REPOS` for repository creators
and team members. To keep picking every event distinct from "All
activity", the custom watch dialog now stores a new `WatchModeCustom`.
Existing custom watches keep working without a migration.

## Screenshots
### Before
<img width="1650" height="675" alt="grafik"
src="https://github.com/user-attachments/assets/ecc56e20-cd05-41e9-a840-607b352c50a8"
/>

### After
<img width="1657" height="671" alt="grafik"
src="https://github.com/user-attachments/assets/a406db94-3ac4-4587-b840-0cd472b135c5"
/>


Addresses https://github.com/openSUSE/openSUSE-git/issues/183
Ref: https://github.com/go-gitea/gitea/issues/35492

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-18 09:23:12 +00:00
Mitrahsoft
b0df5e4abe fix(markup): prefer sturdier math fonts over Latin Modern Math (#39811)
In Firefox and in Chromium on Linux, the `math` font family resolves to
Latin Modern Math when it is installed. Its hairline strokes are too
thin for screens, so MathML looks noticeably weaker than KaTeX on the
same page.

List sturdier math fonts before `math`: Cambria Math, STIX Two Math, New
Computer Modern Math Book (aliased via `local()` because it shares its
family name with the thinner Regular), Libertinus Math and DejaVu Math
TeX Gyre. `math` stays as the fallback before Noto Sans Math. No fonts
are shipped, so systems with only Latin Modern Math, or no math font at
all, are unchanged.

Fixes: https://github.com/go-gitea/gitea/issues/39665
Co-authored-by: silverwind <me@silverwind.io>
2026-10-18 02:02:51 -07:00
Shivansh Garg
7f420f8d82 fix: avoid index out-of-range panic in webhook assignee formatting (#39827)
getPullRequestInfo and getIssuesInfo indexed into the last element of an
empty Assignees slice when Action was HookIssueAssigned, causing a panic
under a race where the assignee list is empty by the time the webhook
payload is built.

Fixes https://github.com/go-gitea/gitea/issues/39826
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-18 08:41:39 +00:00
silverwind
b0ae5890cf chore: remove misspell (#39829)
Remove the misspell spell checker along with its `lint-spell` and
`lint-spell-fix` make targets, its CI step and its custom dictionary.

`make lint` and `make lint-fix` no longer run a spell check.

Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-18 08:00:56 +00:00
silverwind
428d72d26f chore: re-enable and extend unescaped-html-literal eslint rule (#39832)
https://github.com/go-gitea/gitea/pull/38046 accidentally dropped
`gitea/unescaped-html-literal` from the eslint config, so it has been
off since. Raised in
https://github.com/go-gitea/gitea/pull/39829#issuecomment-6246720235.

- Re-enable the rule, exempting e2e tests like unit tests
- Detect tags anywhere in strings and template chunks, not only at the
start
- Allow `htmlRaw` templates, they escape like `html`
- Fix the existing violations, which escapes the heatmap tooltip and
shortcut keys
2026-10-18 07:37:17 +00:00
Max P.
a4908d4af4 enhance(actions): add OIDC workload identity support (#39052)
Adds OpenID Connect workload identity to Gitea Actions: jobs declaring
`id-token: write` can request short-lived tokens to authenticate to
cloud providers and other services without stored secrets. Tokens follow
GitHub's format, so clients like `@actions/core` and existing trust
policies carry over.

- Issuer at `/api/actions/oidc` with discovery and JWKS, signed with a
dedicated RS256 key
- GitHub's claims, including `workflow_ref`, `job_workflow_ref` and the
immutable `repo:owner@id/repo@id:ref:...` subject
- `id-token: write` or `write-all` grant it, default permissions and
fork pull requests never get it, owner and repository maximum
permissions can disable it
- Reusable workflows record the exact ref they were loaded from, and
dispatch rejects refs that only resolve through a same-named tag
- Generated signing keys are written atomically, and generated ES384 and
ES512 OAuth2 keys use their own curves instead of P-256
- `github.ref_type` is `branch` for pull request runs, like on GitHub

Fixes https://github.com/go-gitea/gitea/issues/26383
Fixes https://github.com/go-gitea/gitea/issues/33681
Docs: https://gitea.com/gitea/docs/pulls/526
Assisted-by: ChatGPT:gpt-5.6-sol, Claude Code:claude-opus-5-5
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-18 07:08:55 +00:00
Jakub Vrána
31626de2a6 enhance(repo): prefill new issue and pull request sidebars from query parameters (#39804)
Links to the new issue and new pull request pages can preselect sidebar
values with the same query parameters as the issue list filters:
`labels`, `milestone`, `project` and `assignee`, each taking
comma-separated IDs, e.g. `compare/main...branch?labels=1,2&assignee=3`.
Previously, pull requests supported none of them and issues only
`milestone` and `project`. On the compare page they also expand the new
pull request form, like `title` and `body`. Query values take precedence
over issue template labels and assignees.

* Fix https://github.com/go-gitea/gitea/issues/27341

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-17 10:58:33 +00:00
Shivansh Garg
0081bd8430 fix(actions): bound request body size in artifacts v4 twirp handlers (#39818)
parseProtobufBody read the full Twirp request body via io.ReadAll with
no size limit, allowing an authenticated runner token holder to force
unbounded memory allocation. Cap it at 1 MiB via io.LimitReader,
matching the existing pattern in job_summary.go.

Fixes https://github.com/go-gitea/gitea/issues/39814
2026-10-17 10:40:04 +00:00
bircni
3058f5ddf9 perf(actions): batch per-row queries in run, job and cancellation paths (#39723)
Several Actions code paths issued one or more queries per listed row.
They now load in batch, with unchanged output.

- API job list: tasks, steps and runners load in batch
- API run list: latest attempts and their trigger users load in batch
- Repository runs page: the jobs of all active runs load in one query
- Cancelling previous runs and replacing concurrency group entries fetch
their jobs in one query

Per call with 50 rows on SQLite, base vs head:

| Path | Queries | Time |
| --- | --- | --- |
| API job list | 254 → 6 | 12.9 → 1.9 ms |
| API run list | 305 → 207 | 11.7 → 6.2 ms |
| Runs page job loading | 100 → 1 | 6.5 → 0.47 ms |
| `CancelPreviousJobs` | 546 → 496 | ~120 ms, per-job writes dominate |

The remaining run list queries come from per-run repository conversion,
which this PR doesn't change.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-17 09:47:17 +02:00
Mitrahsoft
efb0b34395 fix(packages): list highest CRAN version in PACKAGES index (#39793)
Fixes https://github.com/go-gitea/gitea/issues/35728

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-17 06:48:48 +00:00
afishcalledwander
42b1f95261 fix: optimize packages upload memory (#39366)
Bound the memory used when parsing uploaded packages, so instances with
low memory limits survive non-standard packages.

1. Metadata files are read through a size-limited reader, archives
themselves stay unbounded so large payloads keep working
2. zstd decoding caps the window at 128 MiB, the zstd CLI default
3. RPM headers go through a first-party reader with rpmlib's size and
array count limits that allocates only what the upload contains, until
https://github.com/sassoftware/go-rpmutils/pull/44 is released. Signing
still verifies the payload digest, and its signature header matches
rpmsign's byte for byte
4. Swift and NuGet read the zip entry they size-checked, a duplicate
name could stand in for it before
5. Terraform states check `LIMIT_SIZE_TERRAFORM_STATE` before parsing,
NuGet symbol PDBs share a 256 MiB budget
6. Oversized container configs are rejected instead of stored without
metadata, invalid Vagrant boxes answer 400 instead of 500

Signed-off-by: techknowlogick <git@tklk.dev>
Co-authored-by: techknowlogick <git@tklk.dev>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2026-10-17 06:10:04 +00:00
Lunny Xiao
c1ae4ec29e fix(actions): keep folded block scalars intact across serialization (#39787)
Folded (`>`) scalars in workflows gained blank lines and had spaces
turned into newlines every time Gitea re-serialized a job. This bumps
go-yaml to its main branch and drops our workarounds. The runner gets
the same change in https://gitea.com/gitea/runner/pulls/1321.

- go-yaml main fixes the accumulating blank lines
(https://github.com/yaml/go-yaml/pull/366) and the block scalar
indentation indicators (https://github.com/yaml/go-yaml/pull/414), so
`BlockSafeString` and the folded scalar requoting go away
- The job encoder no longer wraps lines, because go-yaml still wraps
more-indented lines of folded scalars and turns a space into a newline
- The fix applies to folded scalars anywhere in a workflow, including
workflow-level `env`

Fixes https://github.com/go-gitea/gitea/issues/39775
Co-authored-by: silverwind <me@silverwind.io>
2026-10-16 22:53:37 +00:00
silverwind
078198d38d ci: update giteabot to v1.2.1 (#39815)
Bump `giteabot` from `v1.1.0` to
[`v1.2.1`](https://github.com/go-gitea/giteabot/releases/tag/v1.2.1). It
now runs on the `node24` action runtime instead of setting up Deno
(https://github.com/go-gitea/giteabot/pull/23), and a reviewer who
requested changes can lift `lgtm/blocked` by re-requesting their own
review (https://github.com/go-gitea/giteabot/pull/24).
2026-10-16 22:45:47 +02:00
Mitrahsoft
6b252ca3a9 fix(pull): don't auto-detect empty pull requests as manually merged (#39807)
With "Autodetect manual merge" enabled, an empty pull request (one with
no commits of its own, for example opened to discuss a branch) was
closed as manually merged as soon as it was checked. Its head commit is
already an ancestor of the base branch, so the detection took the
"fast-forwarded" path and recorded the head itself as the merge commit,
even though nothing was merged.

Detection now skips a PR whose head commit is its merge base, the same
condition Gitea already uses to show a PR as "already included in the
target branch". Real manual merges are still detected, including when
the base branch and the PR branch are pushed together.

The merge base is refreshed on recheck, so a fast-forward manual merge
whose merge base was already refreshed to the head is not detected
either. This only misses a detection and never closes a PR wrongly;
telling the two apart would need a value stored at PR creation.

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-16 08:29:25 -07:00
wxiaoguang
2fe74d3cdd refactor: clean up legacy templates and js (#39808)
* Remove unused `"root"` data attributes from templates
* Use our "confirmModal" as much as possible (partially for #39643)
* Use "data-global-init" to refactor a lot of legacy code
* Refactor and clearify initRepoIssueCommentDelete's behavior to help #36862
* Remove dead code `common-organization.ts`
2026-10-16 20:02:11 +08:00
Manas Choksi
a25d1d77eb fix(webauthn): accept unsolicited client extension outputs from WebKit clients (#39792)
28.0.0 bumped `github.com/go-webauthn/webauthn` from v0.17.4 to
v0.18.x. v0.18.0 made the ceremony fail whenever the client returns a
client extension output the relying party did not request, unless
`Config.ExtensionsUnsolicitedOutputPolicy` says otherwise; the strict
behavior is the zero value, so not configuring a policy opts into it
(upstream MIGRATION.md §2.4).

Every security-key and passkey login from Safari on iOS fails with 403:
Client returned the "appid" extension output which was not requested

To fix, set `ExtensionsUnsolicitedOutputPolicy:
protocol.UnsolicitedOutputPolicyIgnore` in the webauthn config

Fixes #39790

---------

Signed-off-by: choksi2212 <choksi2212@users.noreply.github.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: choksi2212 <choksi2212@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-16 08:39:07 +00:00
fixfon
217300fff7 fix(pull): fix code context of review comments on unchanged lines (#39799)
Fixes #39794

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-16 08:19:57 +00:00
bircni
70a5ff725c enhance(oauth): pre-register a builtin OAuth2 application for gitea-vscode (#39785)
The [Gitea VS Code extension](https://gitea.com/gitea/gitea-vscode)
currently signs in through the builtin `tea` OAuth2 application. Gitea
keeps one grant per user and application, so anyone who has already
signed in with the tea CLI gets `a grant exists with different scope`
when the extension requests its own scopes, and sign-in fails
(gitea/gitea-vscode#24).

This adds a dedicated builtin application for the extension, following
the same mechanism as the existing CLI entries:
2026-10-16 06:48:13 +00:00
silverwind
a36bb47c84 enhance(ui): use normal line-height for buttons and selection dropdowns (#39796)
The legacy Fomantic `line-height: 1` on buttons made ellipsized button
text clip descenders like `g` letters, like in the PR target branch
selector. Selection dropdowns were 3px taller than the inputs and
buttons next to them because their padding assumed `line-height: 1em`.

- Buttons and selection dropdowns use `--line-height-default` with
smaller vertical padding, `min-height` keeps their sizes for every font
including CJK
- Remove overrides that only compensated for the old values and dropdown
declarations that never applied
- Label-edit and push-mirror modals use regular-size action buttons like
all other modals
2026-10-16 02:24:19 +00:00
GiteaBot
8d6b3e48d3 [skip ci] Updated translations via Crowdin 2026-10-16 00:49:28 +00:00
wxiaoguang
222136ee71 refactor: clean up pull request comment related code and template dict (#39789)
1. drop comment.commit_id
2. refactor templates, remove `dict "." .` from templates
2026-10-15 23:09:41 +00:00
wxiaoguang
1c117a9352 refactor: clean up comment model (#39779)
Try to figure out whether we can reuse the "comment" table for #36862

The legacy "CommitID" field is removed, and updated the comment.

Some useless and outdated "doctor fixes" are also removed together.
2026-10-14 19:55:30 +00:00
Zettat123
919116568f fix(actions): fix schedule cleanup and default branch switch (#39740)
Cancelling running schedule runs has not worked since
https://github.com/go-gitea/gitea/pull/35767: callers pass a branch name
like `main`, while schedule runs store the full ref like
`refs/heads/main`. Disabling Actions, archiving the repository or
changing the default branch left them running.

Fixing the ref alone would also cancel them on every push to the default
branch, which GitHub does not do, and whenever the Actions setting is
saved, so those paths now only replace the schedules.

Changing or renaming the default branch deleted the schedules without
registering the new branch's ones
(https://github.com/go-gitea/gitea/pull/28691), so they stopped until
the next push, and the API path kept the old ones. Both now switch the
schedules to the new default branch, since scheduled workflows only run
on the default branch.
2026-10-14 19:34:46 +00:00
Yunus Ozen
41ccfce3e9 fix(markup): parse \(...\) inline math next to letters and digits (#39773)
Fixes https://github.com/go-gitea/gitea/issues/39770

`\(x^2\)is` and `\(x^2\)123` rendered as plain text because `\(...\)`
went through the same check on surrounding characters as `$...$`. That
check is there to skip things like `US$1`, which can't happen with
explicit `\(` `\)` delimiters, so it's now skipped for them, the same as
for "$`...`$". `$x$a` still isn't parsed.
2026-10-14 12:11:13 -07:00
Shudhanshu Singh
c6069f8d9f fix(gitdiff): unquote C-quoted paths from git diff-tree --raw (#39781)
`parseGitDiffTreeLine` (`services/gitdiff/git_diff_tree.go`) read
`BasePath`/`HeadPath` straight from `git diff-tree --raw` output without
undoing git's C-quoting, unlike its sibling `parseLsTreeLine`
(`modules/git/parse.go`), which already does this with
`strconv.Unquote`.

This fix applies the same unquoting to both paths in the diff-tree
parser.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-14 20:54:33 +02:00
Yunus Ozen
76f8dc38e7 fix(user): don't match a commit with an empty author email to a user (#39777)
Fixes https://github.com/go-gitea/gitea/issues/39776
2026-10-15 00:29:38 +08:00
wxiaoguang
ccc9b928fe refactor: issue comment context menu and buttons (#39771)
1. Support show "Quote reply" shortcut in help menu, fix the TODO
2. Help #36862 to reuse existing "issue/comment context menu"
2026-10-14 12:07:31 +00:00
Enoque Duarte
b015cd67e2 feat(repo): allow deleting protected branches (#39243)
This adds a Branch Deletion setting with three modes: disabled by
default, anyone with push access, or a restricted user/team/deploy-key
allowlist. The policy applies to Git pushes, API/UI deletion, and
post-merge cleanup; the default branch and the pull request target
branch remain undeletable.

Closes https://github.com/go-gitea/gitea/issues/33305

---------

Co-authored-by: Enoque Duarte <12515551+enoquefcd@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-14 07:19:49 +00:00
Trevor Dixon
aebdbd85fb enhance(repo): allow custom content in issue and pull request sidebars (#39764)
Add custom template support for issue sidebar.

* Fix https://github.com/go-gitea/gitea/issues/39763

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-13 23:59:39 -07:00
GiteaBot
f0434cd8f2 [skip ci] Updated translations via Crowdin 2026-10-14 00:50:13 +00:00
Basavaraj S m
bac747518a fix(lfs): validate OID before building filesystem object path (#39762)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-13 23:29:29 +00:00
silverwind
0eb57b927a fix(markup): restore code copy newline strip and fix copying rendered blocks (#39755)
1. Fix JS error when copying Mermaid and Math block sources, regression
from https://github.com/go-gitea/gitea/pull/39718.
1. Restore the newline trim which is consistent with the majority of
forges and markdown renderers as per
https://github.com/go-gitea/gitea/pull/39751#issuecomment-6129002625 and
my strong preference.

This is the minimal fixes to fix the bug and restore the existing
behaviour, more discussions and followups can be had in
https://github.com/go-gitea/gitea/pull/39751.
2026-10-13 18:43:40 +00:00
Basavaraj S m
a1cc651267 fix(markup): resolve and proxy srcset links of <source> elements (#39708)
1. use correct links for srcset attribute of picture source
2. wrap the picture element with a link correctly

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-13 01:33:19 +00:00
GiteaBot
bd28f254fa [skip ci] Updated translations via Crowdin 2026-10-13 01:03:45 +00:00
wxiaoguang
c6e2513570 refactor: migrate protected branch fields into one json column and support system users (#39735)
1. migrate protected branch fields into one json column
2. use the dropdown list for push/force-push user selection, and Actions
user can be added into the list now.
2026-10-13 00:40:21 +00:00
Shivansh Garg
f270534e83 fix: validate "scope" for OAuth2, "state" for OpenID (#39642)
1. Fixes: https://github.com/go-gitea/gitea/issues/39641
* The OAuth2 authorize/grant endpoint accepted and persisted arbitrary
scope strings without validation, unlike personal access token creation
which rejects unknown scopes. Normalize and validate form.Scope before
creating or updating a grant, ignore invalid scopes
* `// TODO: at the moment invalid tokens are treated as "all" to avoid
breaking 1.22 behavior` is fixed.
2. To avoid CSRF attack for OpenID endpoint, add a state check

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-12 20:57:41 +00:00
bircni
4b5f1f46de enhance(actions): show the steps the runner reports (#39720)
Gitea derives the steps of a job from the workflow file, so the `pre`
and `post` steps of actions only show up inside "Set up job" and
"Complete job".
Fixes https://github.com/go-gitea/gitea/issues/24604.

A runner can now report the steps it actually runs, and Gitea shows
those instead. Runners that don't report them keep the current view, and
older Gitea versions ignore them.

Related:
- Proto: https://gitea.com/gitea/actions-proto-def/pulls/19
- Runner: https://gitea.com/gitea/runner/pulls/1293
2026-10-12 22:36:25 +02:00
Alisa Lafoks
c501255d47 fix(issues): allow selecting a tag in the issue Branch/Tag dropdown (#39747)
* Fixes #39746
* Fixes #35505

The click handlers were bound only to the first `.reference-list-menu`
(branches), so clicking a tag did nothing. Bind them to the items of
both lists.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-12 17:46:15 +00:00
wxiaoguang
0e32b4a362 fix: correct label deletion and release attachment load (#39743) 2026-10-13 01:27:40 +08:00
Giteabot
0eb0126fa2 chore(deps): update dependencies (#39742) 2026-10-12 18:12:22 +02:00
silverwind
6384ed4d11 fix(packages/npm): restore former tarball URLs (#39681)
Gitea changed the `dist.tarball` it advertises for already published
versions twice:

| Gitea | `dist.tarball` path of `@scope/name@1.0.0` |
|---|---|
| 1.27 | `%40scope%2Fname/-/1.0.0/name-1.0.0.tgz` |
| 28.0, https://github.com/go-gitea/gitea/pull/39061 |
`@scope%2Fname/-/1.0.0/name-1.0.0.tgz` |
| 28.1, https://github.com/go-gitea/gitea/pull/39434 |
`@scope/name/-/name-1.0.0.tgz` |

pnpm 11.5.2+ verifies the tarball URLs pinned in its lockfile against
the registry metadata, so frozen installs of lockfiles written against
1.27 fail with `ERR_PNPM_TARBALL_URL_MISMATCH`.

- Versions uploaded before this PR get their 1.27 URL back.
- New versions store their canonical tarball path at upload, so later
URL changes can't reach published versions. The host still follows the
request.

Lockfiles written against 28.0 for scoped packages, or by pnpm 12 with
`@scope:registry` against 28.1, need those pinned `tarball:` entries
removed, as 1.27 lockfiles of the same versions need the other URL.

Signed-off-by: silverwind <me@silverwind.io>
2026-10-12 13:52:39 +00:00
wxiaoguang
2f1761e3fe fix(auth): avoid "compromised account" false-alarm when login with remember-me token (#39686)
1. Remember the last token that has been rotated and skip its validation
2. Don't alarm when the token mismatch occurs in a short window
2026-10-12 08:44:52 +00:00
Calvin Tjoaquinn
8c7b8d085c fix(models): fix incorrect or unnecessary "scan rows" (#39516)
The six batched loaders in `IssueList` iterate their rows and never call
`rows.Err()`.

Refactored the code by a new helper function.

---------

Signed-off-by: Calvin Tjoaquinn <calvintjoa23@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-12 16:00:54 +08:00
Ehsan-hash
aa32eb97fa fix(packages): copy selected Debian installation values (#39718)
Fixes https://github.com/go-gitea/gitea/issues/39715.

Make "copy code" button work with changed content.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-11 07:51:37 +00:00
bircni
8a9e8c4e8c fix(actions): harden log cursor, activity type and actor filter handling (#39721)
Fixes a few Actions bugs where user input or nondeterminism caused
failures:

- The job log view indexed task steps with the client-supplied cursor
step without a bounds check, so an out-of-range value panicked and
returned a 500.
- Workflow activity type filters (`types:`) were compiled with
`glob.MustCompile`, so an invalid pattern in a workflow file panicked
during event detection. Invalid patterns now simply don't match.
- Listing workflow runs with an unknown `actor` returned a 500; it now
returns 404.
- The v4 artifact `ListArtifacts` response was built by ranging over a
map, so its order changed between calls. It now follows database order.
- The error from marshalling a workflow job when inserting run jobs was
silently ignored.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-10 19:52:23 +00:00
bircni
321d3ec104 refactor(actions): pass secret and variable scope as a struct (#39719)
Secret and variable functions took an adjacent `ownerID, repoID int64`
pair, so swapping the two compiled and only failed at runtime. They now
take a `BelongingScope` value instead.

`BelongingScope` also replaces the "drop OwnerID when RepoID is set"
step that `InsertEncryptedSecret` and `InsertVariable` each implemented
separately

No behaviour change. It also makes adding another scope dimension, such
as the environment ID in https://github.com/go-gitea/gitea/pull/38149, a
one-field change.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-10-10 18:21:28 +00:00
silverwind
23fb49fea7 fix(git): match git config values exactly when syncing (#39728)
Since https://github.com/go-gitea/gitea/pull/39703, Gitea fails to start
when its gitconfig holds a `gc.reflogExpire` other than `90` (e.g.
`30.days`), because `git config --unset-all` exits 5 when no value
matches. Value patterns were also unanchored regexps, so `90` also
removed `90.days`.

- Match values exactly in `configUnsetAll` and `configAddNonExist`
- Treat "nothing to unset" as success
- Use `--replace-all` in `configSet` so a key with multiple values no
longer fails startup
2026-10-10 17:23:07 +00:00
wxiaoguang
01fe36985d chore: update license files with line wrapping (#39726)
fix #28960

---------

Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2026-10-11 00:57:21 +08:00
wxiaoguang
e60d1c7fc8 refactor: remove NotFoundOrServerError (#39727)
follow up #39722
2026-10-10 16:34:53 +02:00
bircni
c30ddc3e14 refactor(actions): small cleanups across models, services and routers (#39722)
Collection of small, behaviour-preserving cleanups in the Actions code.

- Fix log and error prefixes that named non-existent or wrong functions,
and wrap errors with `%w` so `errors.Is` keeps working.
- Remove unused code.
- Rely on `ctx.ServerError` / `ctx.APIErrorAuto`, which already map
`util.ErrNotExist` and friends, instead of equivalent hand-written
switches.
- Load runs referenced by commit statuses with one query instead of one
per run (used on every PR view); the result order is now stable.
- Encode an event's payload once instead of up to three times.
- Share the duplicated runner-token "get or create" logic and the
"notify and emit" call pair.
- Simplify `ToActionArtifact` / `ToActionRunner` signatures.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-10 12:55:28 +00:00
BenV
c344004e53 fix(actions): settle cancelled reusable workflow runs stuck in cancelling (#39706)
Fixes #39702

**Root Cause:**

Cancelling a reusable workflow caller wrote it `cancelled` right away,
even while a child was still `cancelling`. When the runner acknowledged,
the caller did not change, so the run was never refreshed and stayed
`cancelling`.

SQLite hid this because the child is cancelled a second time, which
re-aggregates the caller, while MySQL reports no affected rows for that
unchanged update.

**Changes:**

- A caller now takes its status only from its children: it stays
`cancelling` until its last child finishes, and its final status then
refreshes the run.
- Cancelling a run whose jobs are all done only settles the run, which
did not wake the runs waiting for its concurrency group. It now does, so
force-cancelling an already stuck run also frees them.

---------

Signed-off-by: BenV <165034+benv666@users.noreply.github.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-10-10 09:46:57 +02:00
silverwind
251e697b0a fix(git): leave reflog expiry to git's defaults (#39703)
Gitea's default `gc.reflogExpire = 90` is parsed by git as
`1990-<month>-<day>`, currently `1990-10-09`, so reachable reflog
entries never expired.

Since git 2.54, auto maintenance runs its reflog-expire task in the
foreground of every push, and its trigger ignores reachability, so busy
repos rerun `git reflog expire --all` on every push without pruning
anything, stalling large repos for over a minute.

- Stop setting `gc.reflogExpire` so git's own defaults apply, and remove
the `90` written by earlier versions
- Treat the legacy `[git.reflog] EXPIRATION` as days, as documented

The next push to each repo prunes the accumulated entries once.

Fixes: https://github.com/go-gitea/gitea/issues/39693
2026-10-09 19:37:41 +00:00
Jspring
39ae5d6e6b test(api): refactor organization repository listing testing code (#39699)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-09 12:11:47 +00:00
Enoque Duarte
76aabbff9f refactor(repo): reuse branch protection user and team dropdowns (#39691)
Extract the repeated user and team dropdowns of the branch and tag
protection forms into shared templates, keeping the same fields and
rendered output.

Co-authored-by: Enoque Duarte <12515551+enoquefcd@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-09 04:11:19 +00:00
Giteabot
2f94380222 fix(deps): update module golang.org/x/net to v0.60.0 [security] (#39695)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [golang.org/x/net](https://pkg.go.dev/golang.org/x/net) | [`v0.59.0` →
`v0.60.0`](https://cs.opensource.google/go/x/net/+/refs/tags/v0.59.0...refs/tags/v0.60.0)
|
![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fnet/v0.60.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fnet/v0.59.0/v0.60.0?slim=true)
|

---

### HTTP/2 server memory exhaustion due to Trailer headers in net/http
[CVE-2026-78659](https://nvd.nist.gov/vuln/detail/CVE-2026-78659) /
[GO-2026-6603](https://pkg.go.dev/vuln/GO-2026-6603)

<details>
<summary>More information</summary>

#### Details
When "Trailer" headers are sent by a client, the HTTP server internally
uses the header values to populate the Request.Trailer map passed to the
server handler. Because Request.Trailer is a map, each entry incurs
memory overhead. For HTTP/2 servers, a malicious client can exploit this
by sending a "Trailer" header that declares a large number of fields,
causing the server to allocate a disproportionate amount of memory while
bypassing Server.MaxHeaderValueCount and Server.MaxHeaderBytes limits.
This exploit is not applicable for HTTP/1 servers, which do not support
multiplexing a large number of requests over one TCP connection, and
whose Server.MaxHeaderBytes are calculated differently.

#### Severity
Unknown

#### References
- [https://go.dev/cl/847185](https://go.dev/cl/847185)
- [https://go.dev/cl/847314](https://go.dev/cl/847314)
- [https://go.dev/issue/81857](https://go.dev/issue/81857)
-
[https://groups.google.com/g/golang-announce/c/U2fTuyDJznI](https://groups.google.com/g/golang-announce/c/U2fTuyDJznI)
-
[https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs](https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6603) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### HTTP/2 transport accepts malformed framing-related headers in
net/http
[CVE-2026-78660](https://nvd.nist.gov/vuln/detail/CVE-2026-78660) /
[GO-2026-6610](https://pkg.go.dev/vuln/GO-2026-6610)

<details>
<summary>More information</summary>

#### Details
Historically, we have been rather lax about malformed framing-related
headers in our HTTP/2 implementation, as they cannot interfere with
HTTP/2 framing. However, this makes it possible for our HTTP/2
implementation to forward responses containing such headers to an HTTP/1
client when acting as a reverse proxy. If the HTTP/1 client also does
not behave strictly enough, this can result in response smuggling.

#### Severity
Unknown

#### References
- [https://go.dev/cl/835145](https://go.dev/cl/835145)
- [https://go.dev/cl/836385](https://go.dev/cl/836385)
- [https://go.dev/issue/81115](https://go.dev/issue/81115)
-
[https://groups.google.com/g/golang-announce/c/U2fTuyDJznI](https://groups.google.com/g/golang-announce/c/U2fTuyDJznI)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6610) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### Excessive CPU consumption from repeated initial window changes in
net/http
[CVE-2026-78669](https://nvd.nist.gov/vuln/detail/CVE-2026-78669) /
[GO-2026-6611](https://pkg.go.dev/vuln/GO-2026-6611)

<details>
<summary>More information</summary>

#### Details
A malicious HTTP/2 peer can cause excessive CPU consumption in the
client or server by opening a large number of streams and then sending
many small SETTINGS frames containing SETTINGS_INITIAL_WINDOW_SIZE
values.

#### Severity
Unknown

#### References
- [https://go.dev/cl/847186](https://go.dev/cl/847186)
- [https://go.dev/cl/847308](https://go.dev/cl/847308)
- [https://go.dev/issue/81742](https://go.dev/issue/81742)
-
[https://groups.google.com/g/golang-announce/c/U2fTuyDJznI](https://groups.google.com/g/golang-announce/c/U2fTuyDJznI)
-
[https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs](https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6611) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### Double flow control refund on HTTP/2 server streams in net/http
[CVE-2026-78663](https://nvd.nist.gov/vuln/detail/CVE-2026-78663) /
[GO-2026-6612](https://pkg.go.dev/vuln/GO-2026-6612)

<details>
<summary>More information</summary>

#### Details
The HTTP/2 server can refund connection-level flow control twice for the
same data: Once when a client resets a stream (refunding data for any
sent-but-unread portion of the stream), and again when a request handler
reads the buffered data. A malicious client can exploit this to bypass
the configured connection-level flow control limit
(MaxReceiveBufferPerConnection). Total buffered data is still limited by
the concurrent stream limit and stream-level flow control.

#### Severity
Unknown

#### References
- [https://go.dev/cl/847187](https://go.dev/cl/847187)
- [https://go.dev/cl/847310](https://go.dev/cl/847310)
- [https://go.dev/issue/81743](https://go.dev/issue/81743)
-
[https://groups.google.com/g/golang-announce/c/U2fTuyDJznI](https://groups.google.com/g/golang-announce/c/U2fTuyDJznI)
-
[https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs](https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6612) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### HTTP/2 server crash due to HPACK encoder race in net/http
[CVE-2026-97032](https://nvd.nist.gov/vuln/detail/CVE-2026-97032) /
[GO-2026-6617](https://pkg.go.dev/vuln/GO-2026-6617)

<details>
<summary>More information</summary>

#### Details
HTTP/2 servers could end up crashing due to inadvertently modifying its
HPACK encoder concurrently. This happens because the server modifies the
HPACK encoder from two goroutines without synchronization: one uses the
encoder to encode a HEADERS frame as part of a response sent to a client
and the other modifies the encoder's table size when handling a SETTINGS
frame containing SETTINGS_HEADER_TABLE_SIZE that a client sends. A
malicious client can repeatedly send a request while changing the header
table size to crash the server.

#### Severity
Unknown

#### References
- [https://go.dev/cl/847188](https://go.dev/cl/847188)
- [https://go.dev/cl/847313](https://go.dev/cl/847313)
- [https://go.dev/issue/81867](https://go.dev/issue/81867)
-
[https://groups.google.com/g/golang-announce/c/U2fTuyDJznI](https://groups.google.com/g/golang-announce/c/U2fTuyDJznI)
-
[https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs](https://groups.google.com/g/golang-announce/c/ZPwCyRUuGBs)

This data is provided by
[OSV](https://osv.dev/vulnerability/GO-2026-6617) and the [Go
Vulnerability Database](https://redirect.github.com/golang/vulndb)
([CC-BY 4.0](https://redirect.github.com/golang/vulndb#license)).
</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend Renovate
CLI](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMjEuNCIsInVwZGF0ZWRJblZlciI6IjQ0LjEyMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
2026-10-09 05:47:50 +02:00
GiteaBot
437ca2dc6f [skip ci] Updated translations via Crowdin 2026-10-09 01:02:07 +00:00
Shivansh Garg
c0a8eb8e74 fix: OIDC discovery advertises unsupported id_token response_type (#39498)
The OIDC discovery document at `/.well-known/openid-configuration`
advertised `id_token` in `response_types_supported`, but
`/login/oauth/authorize` only implements the authorization code flow and
rejects any other response_type with `unsupported_response_type`. This
mismatch caused OIDC client libraries that rely on discovery to attempt
the implicit flow and fail silently.

This removes `id_token` from `response_types_supported` so discovery
matches actual server behavior, and adds an integration test asserting
`response_type=id_token` is rejected consistently.

Fixes #39482.
2026-10-08 18:10:54 +00:00
silverwind
1903c61627 fix(build): use the VERSION file consistently in docker builds and source tarballs (#39683)
Fix two builds that reported a non-semver version.

Docker images built with a `VERSION` file reported a bare commit hash as
their version, and binaries built from the nightly source tarball
reported `main-nightly`. Docker builds now honor `VERSION` like `make`
does, and the nightly source tarball stores the actual version.

Also remove the obsolete `main` to `main-nightly` rename, the branch
case already appends `-nightly`.

| Build | Before | After |
|---|---|---|
| Docker release | `28.1.0` | `28.1.0` |
| Docker nightly | `29.0.0+dev-83-gab9bcd6a79` |
`29.0.0+dev-83-gab9bcd6a79` |
| Docker with `VERSION` | `c55da3e` | `29.0.0+custom` |
| Release tarball | `28.1.0` | `28.1.0` |
| Nightly tarball | `main-nightly` | `29.0.0+dev-83-gab9bcd6a79` |
2026-10-08 17:49:59 +00:00
Giteabot
871d29553b chore(deps): update go toolchain directive to v1.27.2 (#39685)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [go](https://go.dev/)
([source](https://redirect.github.com/golang/go)) | toolchain | patch |
`1.27.1` → `1.27.2` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend Renovate
CLI](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xMjEuNCIsInVwZGF0ZWRJblZlciI6IjQ0LjEyMS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
2026-10-08 19:10:01 +02:00
Sergio Benitez
ab9bcd6a79 fix(markdown): parse escaped alerts by default (#39633)
Previously, recognition of escaped alert markers such as `\[!NOTE\]`
accidentally depended on enabling the optional `inline-parentheses` math
setting, resulting in such escaped alerts not rendering properly.
Goldmark splits the escaped marker differently based on the setting
being enabled or disabled, and only one form was handled. This commit
handles both forms.

For example, in a README or issue:

```
> \[!NOTE\]
> text
```

Before this commit, we'd get an ordinary blockquote displaying literal
`[!NOTE]` followed by `text`. With this commit, we get the styled
admonition.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-08 13:47:17 +00:00
bircni
8a553332f7 ci: skip reverted commits in release notes, publish tagged snaps to stable (#39657)
Release notes: revert commits and the commits they revert are now left
out of the git-cliff changelog. A step before git-cliff finds commits
whose subject starts with "revert", reads the PR numbers on their revert
lines, and writes both SHAs to `.cliffignore`.

Snap: since the move from Launchpad to GitHub Actions, every build was
uploaded to `latest/edge`, including the stable-grade build that
`part-gitea-pull.sh` makes for an unreleased tag. Launchpad used to
release those builds to candidate and stable automatically. Without
that, v28.0.0 had to be promoted by hand and v28.1.0 stayed in edge.
Stable-grade snaps now go to `latest/stable` and `latest/candidate`;
main builds still go to edge. Candidate has to be updated too because
the pull script compares against it.

---------

Signed-off-by: bircni <bircni@icloud.com>
2026-10-08 11:35:03 +02:00
KShot
02da29f2da fix(webhook): keep line breaks in Telegram rich messages (#39650)
Restore the old layout by converting line breaks (`\n`, `\r\n`, `\r`) to `<br>`
in `createTelegramPayloadHTML`, after sanitizing, covering every Telegram
payload type at once.

Fixes https://github.com/go-gitea/gitea/issues/39649
2026-10-08 09:16:17 +00:00
Roland Singer
a0b9ce05c4 fix: go get fails with GO_GET_CLONE_URL_PROTOCOL=ssh on the default SSH port (#39674)
Go rejects scp-style addresses like `git@host:owner/repo.git` in the
go-import meta tag because they have no URL scheme, so
GO_GET_CLONE_URL_PROTOCOL=ssh did not work with the default SSH port
unless USE_COMPAT_SSH_URI was set. Always use the ssh:// form for
go-get.

When DISABLE_HTTP_GIT is enabled, the https clone URL can never work, so
GO_GET_CLONE_URL_PROTOCOL now defaults to ssh in that case.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-08 08:52:24 +00:00
wxiaoguang
cc623c5f2f refactor: clean up git migration forms (#39672)
Only extracted shared code, removed unnecessary code, and use
form-fetch-action to replace RenderWithErrDeprecated, slightly
refactored JS to use data-global-init

Nothing else is changed, although there are still many problems.

Written by AI

Diff with hiding spaces:
https://github.com/go-gitea/gitea/pull/39672/changes?w=1
2026-10-08 10:36:08 +02:00
GiteaBot
89ec271d9c [skip ci] Updated translations via Crowdin 2026-10-08 01:00:05 +00:00
wxiaoguang
c8fc705632 fix: correct change password page and redirection (#39671)
Backport fix is #39670
2026-10-07 19:34:47 +00:00
wxiaoguang
21cf3f774d refactor: clean ui modal component (#39654)
1. use "button" element for the modal close button
2. remove "ui modal" width hacks
2026-10-07 11:49:50 -07:00
Nico Schlömer
5bcb0ce0fc fix(markup): display MathML has no space below it (#39663)
Co-authored-by: silverwind <me@silverwind.io>
2026-10-07 16:45:07 +00:00
wxiaoguang
5a529ffd58 fix: various bugs (#39661)
1. fix #39660: relax email validation
2. fix #39658: use "int64" instead of time.Duration (for JSON v2)
2026-10-08 00:33:55 +08:00
wxiaoguang
4cba1e18ed feat: global shortcut support (#39604)
* Fix #5796

Global shortcuts can be easily introduced by adding "data-shortcut-keys"
to the elements

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-07 13:35:38 +08:00
bircni
9b1d6f47f1 ci: reject breaking marker on non-breaking PR types (#39584)
Reject the breaking marker `!` on `build`, `chore`, `ci`, `docs`,
`style` and `test` PR titles, since these types never change user-facing
behavior. The check runs in `lint-pr-title`, so it fails before labels
are synced and such PRs never get `pr/breaking`.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-07 03:08:08 +02:00
wxiaoguang
26475404d4 fix: show "merge" form for empty PR (#39640)
fix #39637
2026-10-06 17:28:28 +00:00
silverwind
20ea3e204d perf(repo): fetch the initial commit instead of pushing it (#39628)
Creating a repository with an initial commit, via `auto_init` or a
template with git content, pushed that commit through receive-pack and
three server-side hooks that do nothing for this internal push except
refresh server info. Fetching it into the bare repository instead makes
such repository creation about 3.7x faster locally (median 790ms to
214ms). E2E tests that create initialized repositories get about
0.2-0.3s faster each.

1. Run `update-server-info` after template content generation, which the
push's `post-receive` hook did before.
2. Remove the now unused `InternalPushingEnvironment` and
`PushOptions.LocalRefName`.
2026-10-06 14:39:36 +00:00
KShot
a835fd78d1 fix(models): chunk commit status queries to avoid SQLite expression depth limit (#39611)
Fixes https://github.com/go-gitea/gitea/issues/39606

Refactor GetLatestCommitStatusForRepoCommitIDs to 
query commit IDs by batch to avoid generating a large SQL

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-06 15:50:41 +02:00
wxiaoguang
1065f03454 refactor: clarify GOOS detection (#39620)
Introduce `consts.IsWindows`, now it's clearer to see how Windows build works
2026-10-06 08:22:36 +00:00
Sergio Benitez
052f660ba5 fix(web): normalize content for edit history diff and fix comment history dropdown (#39616)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-06 08:03:06 +00:00
wxiaoguang
fc1f0dbec4 fix: correct RemoveWithRetry error handling (#39619)
* Fix #39618
* Follow up #38588
* Remove unrelated errors
2026-10-06 07:45:31 +00:00
dziulatex
1264072754 fix(pull): refresh commits behind when an AGit pull request is updated (#39613)
Fixes #39598

The AGit update path in `services/agit` moved `refs/pull/N/head` without
recomputing commits_behind.

The AGit update path now calls `syncCommitDivergence` in
`UpdateRefForAgit`, the same as AGit PR creation (`NewPullRequest`)
already does.

---------

Signed-off-by: dziulatex <paweldziurasoftware@yahoo.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-06 07:21:43 +00:00
TheFox0x7
bd2a6c40d7 fix(egress): expose more ranges as restricted rather than reserved (#39560)
Introduce second list of addresses which are classified as dialable if
explicitly allowed when in Lax mode.
Restricted pool now includes: link-local, site local, private (including
ULA), CGNAT, discard, dummy, documentation and test addreses.
Reserved pool shrinks to: this network, wireserver embedding/translation
ranges and multicasts

Rationale for the choice is that while items in restricted pool can be
dangerous to allow they could be a legitimate target in some
deployments. Ranges left in reserved list are ranges which make no sense
to dial, are public (wireserver) or are 6to4 embedding which cannot be
reasonably verified to be safe. To unlock those a proxy should be used
instead

fixes: https://github.com/go-gitea/gitea/issues/39557

---------

Signed-off-by: TheFox0x7 <thefox0x7@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-06 14:36:06 +08:00
bircni
43fedd662a ci(release): automate signed release tags and release notes (#39544)
Automate release tagging as proposed in
https://github.com/go-gitea/gitea/pull/39544#issuecomment-5955939142,
part of https://github.com/go-gitea/gitea/issues/39550.

A maintainer selects a release branch and version in the
`release-create-tag` workflow. After approval through the
`release-signing` environment, it pushes a GPG-signed tag. The tag
starts the existing release build, which generates GitHub release notes
with git-cliff from commits since the previous release, skipping `chore`
and `ci` commits.

`CHANGELOG.md` and release-candidate releases are removed. The workflow
reuses the existing `GPGSIGN_KEY`, `GPGSIGN_PASSPHRASE`, and
`RELEASE_TOKEN` repository secrets.


Closes https://github.com/go-gitea/gitea/issues/39550

---------

Co-authored-by: bircni <bircni@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-06 05:21:07 +00:00
GiteaBot
6e7de91f99 [skip ci] Updated translations via Crowdin 2026-10-06 00:58:27 +00:00
wxiaoguang
b1726adebb fix: make "edit pr title & target branch" get correct branch (#39612)
* Fix #39610
* Regression of #39262

Also, the old code is very fragile: `#branch_target` is from translation
string, so refactored it together

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-05 20:29:33 +00:00
silverwind
4e2c3e43f2 fix(pull): fetch PR head refs instead of pushing them (#39603)
Creating a PR fetches the head commit into the base repo, then pushes
the same objects into `refs/pull/N/head`. Since git 2.54, background
repacks race that push:

1. The push can be rejected with "unable to migrate objects to permanent
storage", see
https://github.com/go-gitea/gitea/actions/runs/37171442185/job/111345034829.
2. For heads with 100+ new objects, the repack can delete the reused
pack, leaving the PR ref pointing at missing objects.

Fetching the head commit with `FetchRemoteTempCommit` and setting the PR
ref with `UpdateRef` avoids both, as the fetch transfers nothing when
the objects exist. Fork PR refs are now updated like AGit PR refs
already are, without going through receive hooks.

Also syncs the PR ref when a PR is reopened again.
https://github.com/go-gitea/gitea/pull/37077 inverted that condition, so
a reopened PR kept a stale ref.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-05 20:31:21 +02:00
Mitrahsoft
d16b20b972 feat(user): allow renaming security keys (webauthn/passkey) (#39413)
Closes https://github.com/go-gitea/gitea/issues/39287

Security key nicknames could only be set at registration, so a skipped
nickname left an auto-generated hex name until the key was
re-registered. Each key now has a Rename button opening a dialog with
the current nickname. A nickname used by another of the user's keys
(case-insensitive) or a blank nickname is rejected. Renames are recorded
as `user:webauth:rename` audit events.

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-05 19:12:22 +02:00
bircni
66546045b5 fix(actions): refresh reusable caller status when children are skipped (#39589) 2026-10-05 17:58:42 +02:00
silverwind
64f4b5856a enhance(actions): improve matrix job titles, bump actionslib (#39485)
Bump actionslib to v1.3.0 and format matrix job titles like GitHub.

- Matrix values are listed in declaration order, nested arrays and
objects are flattened, null and empty values are skipped
- Expression-evaluated names are trimmed and fall back to the job ID
when blank
- Matrix `include` and `exclude` match keys case-insensitively and
coerce numbers like GitHub

| Matrix | Before | After |
|---|---|---|
| `v: ["a,b"]` | `job (a,b)` | `job (a,b)` |
| `v: ["a, b"]` | `job (a, b)` | `job (a, b)` |
| `v: [[a, b]]` | `job ([a b])` | `job (a, b)` |
| `os: [x], arch: [y]` | `job (y, x)` | `job (x, y)` |
| `v: [{t: a, p: "b,c"}]` | `job (map[p:b,c t:a])` | `job (a, b,c)` |
| `v: [{t: a, p: [b, c]}]` | `job (map[p:[b c] t:a])` | `job (a, b, c)`
|

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-05 10:29:49 +00:00
Giteabot
27d876e311 chore(deps): update dependencies, add new lint rules, fix lint (#39601)
Co-authored-by: silverwind <me@silverwind.io>
2026-10-05 10:11:50 +00:00
wxiaoguang
b0d5a63e7a fix: migrate broken team authorize access mode (#39579)
* fix:  #39571
* ref: https://github.com/go-gitea/gitea/pull/38938#pullrequestreview-4945228548
* fix the bug in `assignTeamPermissionUnits` which can result in wrong team access

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-05 09:03:34 +00:00
wxiaoguang
b71967b254 fix: avoid useless "Failed authentication attempt" logs (#39602) 2026-10-05 01:45:50 -07:00
silverwind
fc44404843 test: keep integration ssh independent of user ssh config (#39600)
The SSH integration tests read the user's `~/.ssh/config`, so options
like `ControlMaster` reused a connection authenticated with another
test's key and the tests failed with "Cannot find key". Pass `-F none`
so ssh reads no config files.
2026-10-05 05:25:54 +00:00
wxiaoguang
49adfd065d fix: avoid FetchRemoteTempCommit touching unnecessary resources (#39583)
Also rename FetchRemoteCommit to FetchRemoteTempCommit to clarify its
purpose
2026-10-04 22:03:18 -07:00
GiteaBot
b576f5bb34 [skip ci] Updated translations via Crowdin 2026-10-05 01:04:01 +00:00
silverwind
2705abf7f3 perf(citation): optimize CITATION.cff rendering (#39575)
Rendering a CITATION.cff could use memory far out of proportion to the
file, as every YAML alias copies its target into the formatted citation
and the parser copies `%TAG` prefixes into every node. Files past these
limits show no citation, like unparseable ones do today.

- Skip files over 256 KiB, largest real-world file found is 80 KiB
- Skip files with `%TAG` directives
- Skip files whose aliases add more than 64 Ki nodes and value bytes
- Skip self-referencing anchors, except a sequence listing itself

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-04 23:50:46 +00:00
KBS
9bb8751b29 fix(git): return no submodule web link when the URL cannot be parsed (#39274) 2026-10-04 13:40:27 +00:00
breken
6809ecf2d2 fix(httpcache): raw files return 304 after a change when the new commit is older (#39435) 2026-10-04 13:13:24 +00:00
breken
db7d1da28f fix(markup): link team mentions that use a different org name case (#39436)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-04 14:51:46 +02:00
Dr Alex Mitre
826c65d0ec fix(actions): return 401 for unregistered runner (#39578)
## Summary

When an Actions runner's registration has been deleted (or the
UUID/token is invalid), `FetchTask` and other authenticated runner RPCs
currently return **HTTP 500**


## Change

- Return `connect.NewError(connect.CodeUnauthenticated, ...)` via a
small `unregisteredRunnerError()` helper for both unregistered /
bad-token paths in the interceptor.
- Leave Internal `status.Error` paths unchanged (those should remain
5xx).
- Add a unit test asserting `connect.CodeOf(err) ==
connect.CodeUnauthenticated`.

Fixes #39576


---------

Signed-off-by: Alex Mitre <mitre88@users.noreply.github.com>
Co-authored-by: Alex Mitre <mitre88@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-04 10:28:32 +00:00
Piyush Kumar
a9ac8c0afd fix(label): sort labels by open issue count, not total (#39464)
The labels page shows open issue counts, but "Most issues" and "Least
issues" sorted by the total including closed issues, so a label with no
open issues could land in the middle of the list.

- Sort repository and organization labels by their open issue count
- Sort organization labels on a repository's labels page by their open
issues in that repository, which is the count they display

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

---------

Signed-off-by: piyush295 <mr.piyush295@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-04 08:48:18 +00:00
silverwind
eb4468ff4e enhance!: raise minimum git version to 2.34 (#39565)
Raise the minimum git version to 2.34, the version in Ubuntu 22.04,
Debian 12 and RHEL 8 ship newer, and remove the fallbacks it makes
obsolete.

- Always enable AGit
- Use `diff --skip-to` and `apply -3` unconditionally
- Set the default branch of new repos and wikis via `git init
--initial-branch`
- Detect rebase conflicts via `REBASE_HEAD`
2026-10-04 16:26:14 +08:00
Zettat123
4bebd86285 fix(user): restore organizations tab on user profile (#39577)
Fixes #39572

The shared user cards template calls the User-only `IsTypeBot` method.
The profile organizations tab passed `*organization.Organization` values
to that template, so `/{username}?tab=organizations` returned a 500
error.

Organizations are now converted to Users before rendering.
2026-10-04 15:30:04 +08:00
bircni
7641fc3a8c fix(actions): restore pushes to protected branches (#39564)
Use the Actions token's loaded write permission when checking
protected-branch pushes. Preserve push and force-push allowlists and add
regression coverage.

Fixes https://github.com/go-gitea/gitea/issues/39563
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-03 19:54:55 +02:00
bircni
cca466caae fix(api): allow bots with pending password changes (#39551)
Allow bot accounts to use the API when a legacy password-change flag is
set, since bots cannot complete the interactive password-change flow.
Preserve password-change enforcement for human accounts and restrictions
for inactive or prohibited accounts.

Fixes: https://github.com/go-gitea/gitea/issues/39542
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-03 17:22:07 +00:00
silverwind
516a4883fa enhance(ui): cleanup navbar template and styling (#39554)
Clean up the navbar template and styling, and fix the navbar stopwatch,
which navigated to the issue instead of opening its popup since
https://github.com/go-gitea/gitea/pull/36965.

1. Add hover background to the create and user menus
2. Simplify navbar HTML and CSS and remove Fomantic styles
3. Render the notification and stopwatch icons once instead of separate
mobile and desktop copies
4. Make the stopwatch a keyboard accessible button whose popup updates
on push and closes when the stopwatch stops

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-03 18:35:54 +02:00
Timur Moziev
f65e01226a enhance(webhook): add select all and deselect all buttons for custom events (#35980)
Adds `Select All` and `Deselect All` buttons to the custom events
section of the webhook form. This is useful when you need all events but
one.

- The buttons toggle every event checkbox and keep the unsaved-changes
prompt working
- Fix the "Trigger On" radio spacing by removing a leftover Fomantic
checkbox margin

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-03 14:27:17 +00:00
okxint
ab1979bcfb fix(api): normalize all API due dates to end of day (#38677)
Due dates set through the API were stored inconsistently. The create
endpoints kept the raw timestamp, issue and pull request edits used end
of day in the client's offset, and only the deadline and milestone edit
endpoints normalized to end of day in the server's UI timezone like the
web UI does. All API due dates now go through
`ParseAPIDeadlineToEndOfDay`.

Editing a pull request with `unset_due_date: false` and no `due_date`
dereferenced a nil pointer and returned 500. It now shares the issue
edit logic and returns 400.

Related to https://github.com/go-gitea/gitea/issues/37620, which is
about the web sidebar and isn't fixed here.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-03 11:59:43 +00:00
silverwind
ad38b60983 refactor!: remove go-git backend (#39487)
Removes the go-git backend so every build uses the git CLI backend. Its
Windows performance advantage is gone, it lacks SHA-256 support, and it
breaks repositories on Windows.

The performance changes moved to
https://github.com/go-gitea/gitea/pull/39526.

Fixes https://github.com/go-gitea/gitea/issues/38359
Fixes https://github.com/go-gitea/gitea/issues/34694

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-03 10:04:24 +00:00
silverwind
3932624947 ci: relay fork PR reviews to giteabot through workflow_run (#39546)
`pull_request_review` runs on fork PRs, which includes all backport PRs,
get a read-only token and no secrets, so giteabot cannot write lgtm
labels and statuses there. A no-op `giteabot-review` workflow now
triggers giteabot through `workflow_run`, which gets both. This allows
retiring the legacy fly.io webhook bot.

Part of https://github.com/go-gitea/giteabot/issues/15
2026-10-03 09:36:38 +00:00
kiara
e0e18fc286 perf(actions): index action_run.commit_sha (#39559)
The API filter `head_sha` on `GET /repos/{owner}/{repo}/actions/runs`
selects runs by `commit_sha`, so `commit_sha` needs an index. For a
action_run table with 212k rows:

- Without the index: the query read 212k rows, and the API request took
35-52 s.
- With the index: the query read 94 rows in 0.14 s, and the API request
took 2-4 s.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-03 11:19:27 +02:00
silverwind
457510fa09 fix: use READ_COMMITTED_SNAPSHOT on MSSQL (#39512)
MSSQL's default READ COMMITTED makes reads wait on writers, so the
runner pickup deadlocks with concurrent claims, flaking
`TestCreateTaskForRunnerConcurrentClaim`.

- Enable `READ_COMMITTED_SNAPSHOT` on MSSQL so it reads like PostgreSQL
and MySQL
- Read the pickup cursor before claiming, a lost claim could skip
waiting jobs
- Add tests that fail without consistent READ COMMITTED

Performance: Writes on MSSQL now also store the previous row version in
tempdb, the same versioning cost PostgreSQL and MySQL always pay, and
Azure SQL enables it by default. Reads no longer block on writers, and a
32-runner pickup stress test ran 2.5x faster with it.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-03 15:25:47 +08:00
silverwind
2baa5a16f8 enhance(citation): render citations server-side (#39373)
Replace citation-js with a Go port of ruby-cff, which GitHub uses for
"Cite this repository", rendering APA and BibTeX server-side and
dropping about 770KB of JS. Output matches GitHub on 1568 of 1571
real-world files, the rest are improvements over GitHub.

- `CITATION.cff` wins over `CITATION.bib`, matched case-insensitively
and through symlinks
- `CITATION.bib` is offered as-is, without APA

Signed-off-by: silverwind <me@silverwind.io>
2026-10-03 02:28:34 +00:00
silverwind
fb067e1115 fix(git): tolerate concurrent repacks in go-git storage (#39536)
Since `transfer.fsckObjects` makes fetches keep a pack, git 2.54+
background maintenance repacks a mirror right after its sync fetch, and
go-git then misses objects mid-repack. Fixes these flakes:

-
https://github.com/go-gitea/gitea/actions/runs/36875305717/job/110419770196
-
https://github.com/go-gitea/gitea/actions/runs/36900439473/job/110498845882

Changes:

- Keep reindexing while the pack set changes instead of retrying once
- List packs only once their `.idx` exists and don't fail the listing on
files removed mid-repack
- Look up large objects again when their file is gone before reading
2026-10-03 03:58:36 +02:00
GiteaBot
daaed0d7f4 [skip ci] Updated translations via Crowdin 2026-10-03 00:55:34 +00:00
silverwind
99573bde0e chore(lint): apply the main eslint config to vue files (#39545)
Vue files only got `eslint-plugin-vue` rules, so stylistic and
TypeScript rules never ran on them, and `import-x` could not see cycles
between `.ts` and `.vue` files.

- Apply the main ESLint config to `.vue` files
- Let `import-x` parse `.vue` files, which surfaced a cycle between
`repo-findfile.ts` and `RepoFileSearch.vue`
- Fix the resulting lint issues

`vue-eslint-parser` is added as a direct dependency because `import-x`
resolves parsers by package name, see
https://github.com/un-ts/eslint-plugin-import-x/issues/381.
2026-10-03 00:47:13 +00:00
Copilot
986b0bcae0 fix(markup): use installed math fonts for MathML in Chromium (#39491)
On Linux and ChromeOS, Chromium resolves the `math` font family to Latin
Modern Math, which neither installs, so MathML renders with a text font
and brackets and large operators don't stretch, see
https://issues.chromium.org/issues/40069293. The new `--fonts-math`
variable keeps `math` first, so browsers that always resolve it keep
their font. Only Chromium falls through to the math fonts Linux and
ChromeOS install by default:

- `STIX Two Math`: Fedora, and `fonts-stix` on Ubuntu 26.04
- `DejaVu Math TeX Gyre`: Debian 13 and openSUSE
- `Noto Sans Math`: Fedora and ChromeOS, last because Debian and Ubuntu
ship an older version without a `MATH` table

Math fonts also have smaller x-heights than UI fonts, so MathML rendered
smaller than KaTeX and the surrounding text in every browser, see
https://github.com/w3c/mathml-core/issues/41. `font-size-adjust:
ex-height 0.52` scales whichever math font is used to KaTeX's x-height.
KaTeX output is unchanged.

Fixes: https://github.com/go-gitea/gitea/issues/39489
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: bircni <75789103+bircni@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-02 20:03:22 +00:00
Lunny Xiao
6dad3ad43d refactor: only update sync status columns when syncing push mirror (#39517)
`UpdatePushMirror` used `AllCols()`, so a sync could overwrite columns
changed concurrently (e.g. `interval`) with stale values. It now updates
only `last_update` and `last_error`, and is renamed to
`UpdatePushMirrorSyncStatus` to match.

Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
2026-10-02 19:28:51 +00:00
silverwind
cf89ecd887 refactor!: move go-chi/session into Gitea (#39504)
The `gitea.com/go-chi/session` package only exists for Gitea, so it
moves into `modules/session` to fix its bugs directly. Fixes the flake
in
https://github.com/go-gitea/gitea/actions/runs/36726154500/job/109923538400.

- Sessions are only written back when changed, so a read-only request
can't revert a concurrent change or restore a logged-out session, like
ae808a4a46
- The session cookie is only set once a session holds data
- Every backend refreshes the expiry on load and file sessions are
written atomically
- Also fix  https://github.com/go-gitea/gitea/issues/36176

## ⚠️ BREAKING ⚠️

* the `mysql`, `postgres`, `couchbase` and `memcache` session providers
are removed, use `file`, `db` or `redis` instead
* login-related cookies are renamed to `gitea_session` and
`gitea_remember`, if you'd like to use the old names, set `COOKIE_NAME`
and `COOKIE_REMEMBER_NAME` in app.ini

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-02 21:08:14 +02:00
silverwind
bea6fcaa84 refactor: move go-chi/cache into Gitea (#39530)
The `gitea.com/go-chi/cache` package only exists for Gitea, so it moves
into `modules/cache`.

- `ITEM_TTL = -1` disables caching as documented
- Sub-second TTLs round up instead of never expiring
- The Redis adapter no longer grows a `MacaronCache` hash
- Use two-queue cache for in-memory cache

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-02 18:16:34 +02:00
silverwind
873efed5a6 refactor: move go-chi/captcha into Gitea (#39529)
The `gitea.com/go-chi/captcha` package only exists for Gitea,
so it moves into `modules/imagecaptcha`.

- Reloading an expired challenge shows a new image instead of a broken one
- Every answer is consumed on its first check
- OpenID registration stops after a failed captcha

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-02 22:21:41 +08:00
silverwind
e6ffbea888 refactor: move go-chi/binding into Gitea (#39528)
The `gitea.com/go-chi/binding` package only exists for Gitea, so it
moves into `modules/web/binding` to fix its bugs directly. Split out of
https://github.com/go-gitea/gitea/pull/39504.

- GET and HEAD always bind the query
- JSON `null` slice elements and nested `TrimSpace` fields bind
correctly
- Integer fields reject out-of-range values instead of wrapping
- An empty JSON body binds nothing and an unknown binding rule is an
error

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-02 15:37:41 +02:00
silverwind
2f01ec38bd fix: use READ COMMITTED transactions on MySQL and MariaDB (#39506)
MariaDB 11.6.2+ defaults `innodb_snapshot_isolation` to `ON`, which
fails REPEATABLE READ transactions with error 1020 when a row they write
changed after their first read. Gitea's background work like push
processing writes the same rows, so merges, issue closes and workflow
runs fail sporadically.

- Use READ COMMITTED on MySQL and MariaDB, like PostgreSQL and MSSQL
- Update xorm to v1.4.3

Replaces: https://github.com/go-gitea/gitea/pull/39494
Fixes: https://github.com/go-gitea/gitea/issues/39492

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-10-01 19:55:58 +00:00
Calvin Tjoaquinn
fc81f2832a fix(git): avoid unnecessary timers during language stats (#39531)
Replace time.After with a stoppable time.Timer in BatchChecker.CheckPath.
Avoid accumulating up to 6 pending 5-second timers per file on the normal path.
Preserve the existing per-attribute timeout behavior.

---------

Signed-off-by: Calvin Tjoaquinn <calvintjoa23@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-01 17:34:38 +00:00
silverwind
075ff8f516 perf(git): speed up activity top authors and subdirectory listings (#39526)
Speeds up two slow git paths. Results match `main` apart
from the `log.follow` fix.

- Activity top authors skip `--numstat` and an unused `rev-list
--count`, they only need names and emails
- Subdirectory listings pass only the directory as pathspec, which
already matches all its entries
- Directory listings pass `--no-follow` to `git log`, a configured
`log.follow` disabled parent rewriting and gave wrong last commits

| Benchmark | main | PR | Change |
|---|--:|--:|--:|
| Top authors, gitea, 1 month | 632 ms | 21 ms | -97% |
| Top authors, gitea, 1 year | 3216 ms | 68 ms | -98% |
| Top authors, tea | 62 ms | 12 ms | -80% |
| Listing, git `Documentation/technical` | 189 ms | 91 ms | -52% |
| Listing, gitea `options/license` | 329 ms | 208 ms | -37% |
| Listing, gitea `templates/repo` | 294 ms | 223 ms | -24% |

Tested with unit and sqlite integration tests in default and `gogit`
builds on git 2.25 and 2.56, and by comparing listing results and cache
writes with `main` on randomized histories and the gitea, tea and git
repos. Benchmarks are medians of 8 interleaved macOS runs.
2026-10-01 15:51:07 +00:00
silverwind
2f5cdbd5c1 fix(git): reindex go-git storage when a concurrent repack removes packs (#39510)
Improve the go-git workaround to fix these flakes:

- https://github.com/go-gitea/gitea/actions/runs/36721877142/job/109908823684
- https://github.com/go-gitea/gitea/actions/runs/36799665163/job/110170983591
2026-10-01 11:04:41 +00:00
Jon Fuller
aae0a218c3 fix(api): add index tiebreaker to commit status ordering (#39508)
Commit status list orders only by `created_unix`/`updated_unix`, which
have 1-second resolution while CI often posts many statuses per second.
With LIMIT/OFFSET paging, databases (e.g. PostgreSQL using a Sort plan)
may order tied rows differently per page, so `GET
/repos/{owner}/{repo}/commits/{ref}/statuses` returns some statuses
twice and never returns others.

This became visible after https://github.com/go-gitea/gitea/pull/36521
made requests without `page` paginated. Clients like Renovate that page
until `X-Total-Count` can miss a context's newest status and see a stale
`pending`, blocking automerge.

Fix: add `index` (unique per commit) as a tiebreaker to the
timestamp-based orders.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-01 10:43:01 +00:00
wxiaoguang
9b5c87a6b6 fix: trace git command correctly (#39520)
Help  #39410
2026-10-01 10:01:45 +00:00
silverwind
51b93d1d27 enhance(packages/npm): improve npm client compatibility (#39434)
Aligns the npm registry with what npm, pnpm and yarn expect:

1. Raise the publish body cap from
https://github.com/go-gitea/gitea/pull/37890 to 256 MiB like npmjs,
larger bodies get 413
2. Pick the tarball attachment by name, `npm publish --provenance`
failed at random
3. Store and serve `libc`, so mismatched glibc/musl optional binaries
are skipped
4. Treat root `*.gyp` files as an install script, like npm does
5. Always serve a `latest` dist-tag, yarn and pnpm fail without it
6. Take top-level metadata from `latest` and drop the per-version readme
7. Serve tarballs at the npmjs path `/<name>/-/<file>`, former URLs keep
working
8. Add ETag revalidation for metadata, `npm ping` and `npm whoami`

Tested with npm 12.1, pnpm 12.4, yarn 1.22 and yarn 4.18.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-01 07:50:47 +00:00
Roshan Ramani
f3aed8b81d docs: fix the default REPOSITORY_AVATAR_FALLBACK_IMAGE path in app.example.ini (#39514)
The example shows `REPOSITORY_AVATAR_FALLBACK_IMAGE =
/img/repo_default.png`, but public files moved under `/assets` in
https://github.com/go-gitea/gitea/pull/15219 and nothing serves `/img/`
anymore. The value is also used as-is without the sub-path, so even
`/assets/img/repo_default.png` 404s when `ROOT_URL` has one. Leave the
key empty and document the real default
`{AppSubURL}/assets/img/repo_default.png` from
`modules/setting/picture.go`.

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-01 09:31:10 +02:00
Harsh Sharma
fc68608603 fix(oauth2): allow users to approve scope changes (#38942)
Lets users approve an OAuth2 scope change on an existing grant instead
of failing with `a grant exists with different scope`.

- Approving a different scope updates the existing grant. Issued tokens
follow immediately, since their scope is read from the grant.
- Confidential and trusted apps show the consent page when the scope set
changes, instead of silently reusing the old grant.
- An omitted `scope` reuses the existing grant's scope, like GitHub.
- The consent page lists newly added scopes.

Fixes: https://github.com/go-gitea/gitea/issues/38940
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-01 09:08:33 +02:00
wxiaoguang
fe31237fd8 fix: handle git branch name with special chars correctly (#39483)
Fix the bugs:
* Commit graph page doesn't show
* PR command line instructions are wrong

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-10-01 04:01:16 +00:00
Zettat123
a71c5c94c5 fix(actions): reject jobs without runs-on (#39480)
Align job and `runs-on` validation with github.com, as implemented by
the parser in https://github.com/actions/runner. A job without `runs-on`
could be claimed by any runner, so a job meant for a container could run
on the host.

- Jobs without `runs-on` fail with `Required property is missing:
runs-on`, called workflows included
- Unknown job keys and callers (`uses:`) mixed with steps-only keys like
`runs-on` are rejected
- Empty, null and nested `runs-on` values are rejected
- A `runs-on` evaluating to such a value fails only that job
- Called workflows are validated at run creation, an invalid one fails
the run as an invalid workflow file
- Zero labels (`runs-on: []` or `{}`) never match a runner, including
jobs queued before upgrading

<img width="960" alt="image"
src="https://github.com/user-attachments/assets/e746ce5a-b711-4e8b-aab8-81336ff53d86"
/>

**Behavior Change:** workflows that omit `runs-on`, use unknown job keys
or mix `uses` with `runs-on` stop running until fixed.

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-30 21:42:07 -06:00
GiteaBot
f81a2ab69a [skip ci] Updated translations via Crowdin 2026-10-01 01:08:18 +00:00
Zain Qureshi
b0d6cc1d22 docs: correct three stale defaults in app.example.ini (#39500)
Three commented defaults in `custom/conf/app.example.ini` differ from
what Gitea actually uses, so the file says they default to something
else:

- `MINIMUM_KEY_SIZE_CHECK`: example `false`, code `true`
(`modules/setting/ssh.go:55`, read at `:152`).
- `SSH_SERVER_HOST_KEYS`: example lists `ssh/gitea.rsa, ssh/gogs.rsa`,
code also loads `ssh/gitea.ed25519` and `ssh/gitea.ecdsa`
(`modules/setting/ssh.go:57`).
- `[queue] DATADIR`: example `queues/`, code `queues/common`
(`modules/setting/queue.go:33`), which the comment above it already
states.

Co-authored-by: silverwind <me@silverwind.io>
2026-09-30 15:49:22 -07:00
silverwind
8936303510 fix: add missing checks to several API and web handlers (#39501)
Several handlers skipped checks that their sibling routes or settings already enforce. This brings them in line.

- Push mirror API honors `DISABLE_NEW_PUSH` and checks the caller's permission
- Media API serves small files with the usual content headers
- Issue attachment API ignores comment attachments
- Push-to-create respects `FORCE_PRIVATE`
- Profile feeds and follow actions respect `ENABLE_FEED` and owner visibility
- Tag delete route refuses release tags
- Refresh token grant only accepts refresh tokens
- Gitea migrations bound the source's page size

Co-authored-by: bircni <bircni@icloud.com>
2026-09-30 20:25:14 +02:00
Zettat123
3d085dbaf1 feat(admin): show and filter users by authentication source (#38900) 2026-09-30 11:28:35 -06:00
Nico Schlömer
9b2a3c267b fix(markup): don't escape ambiguous characters in MathML (#39493)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-30 16:37:30 +00:00
silverwind
590d2984d9 fix(ui): ignore code line anchors below 1, show JS errors in vite dev mode (#39432) 2026-09-30 15:03:40 +00:00
Nico Schlömer
61e0343580 fix(markup): skip post-processing inside MathML (#39497)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-30 16:41:41 +02:00
wxiaoguang
0b43bde974 fix: copy new access token to clipboard (#39496)
Co-authored-by: silverwind <me@silverwind.io>
2026-09-30 21:41:55 +08:00
wxiaoguang
cc95f141f8 fix: npm route (#39488) 2026-09-30 19:49:50 +08:00
Roshan Ramani
a25fbd43c4 docs: update app.example.ini for defaults changed in #39400 (#39456)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-30 11:28:59 +00:00
JerryLien
f365a6b9c8 fix(actions): keep runs order after auto refresh (#39479)
On a repository's Actions tab, runs are sorted newest first on initial
page load. After the first auto refresh (added in #38329, every 3
seconds while runs are active and every 12 seconds otherwise), the same
runs may appear in a different order and move again as their status
changes.

Example with four runs (Gitea 28.0.0, SQLite):

```
page load:     #10 success, #9 failure, #8 success, #7 running
after refresh: #8 success, #10 success, #9 failure, #7 running
```

To reproduce, open the Actions tab of a repository with runs in
different statuses and wait for an auto refresh. On SQLite, the runs may
be regrouped by status, with each group ordered oldest first.

`preparePartialRefreshRuns` reloads the runs currently shown on the page
using `GetRunsByRepoAndID`. That query has no `ORDER BY`, while the
initial page load uses `FindRunOptions.ToOrders` and sorts by index
descending.

With SQLite, the query planner used the `(repo_id, status)` index, so
the returned row order differed from the original page order. Since the
query has no explicit ordering, this behavior is database-dependent. I
have not tested MySQL or PostgreSQL.

This change orders `GetRunsByRepoAndID` by index descending, the same
order `FindRunOptions.ToOrders` uses for the initial page load. The
refresh only reloads the runs already on the page, so they come back in
the original order, with or without filters and on any page.

The other caller of `GetRunsByRepoAndID`, run approval, does not depend
on result ordering.

Testing:

- Added `TestPreparePartialRefreshRunsKeepsRequestedOrder`. Without the
fix, runs 794, 793, 792, 791 are returned as 791, 792, 794, 793; with
the fix, the test passes.
- `go test` passes for `./routers/web/repo/actions/`,
`./models/actions/` and `./services/actions/`.
- `go vet` and `golangci-lint v2.13.2` pass for the changed packages.
- Manually tested by building Gitea 28.0.0 with this patch and running
it on our SQLite instance. The runs list keeps its newest-first order
across auto refreshes. The official 28.0.0 binary reproduces the
reordering.

AI-assisted: drafted with Claude Code (claude-opus-5-5), reviewed by me.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-30 09:17:04 +02:00
bircni
e0095af8c3 ci: Also release for other versions than 1 majors (#39475) 2026-09-29 21:47:12 +02:00
bircni
5a96cc454a docs: Add changelog for 28.0.0 (#39474) 2026-09-29 19:06:21 +00:00
GiteaBot
1ab2358d92 [skip ci] Updated translations via Crowdin 2026-09-29 19:04:59 +00:00
TheFox0x7
fb4d5e9d50 fix(git): reject fsck-invalid objects on push (#39472)
Set receive.fsckObjects=true in Gitea's internal global git config so
the receiving git process rejects bad, malicious or duplicate objects at
push time, before Gitea ever stores them.

Assisted-by: Codet:claude-opus-4-8

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-29 20:31:50 +02:00
silverwind
9c715c0f9d chore: update giteabot to v1.0.7 (#39470)
Bump `giteabot` to
[`v1.0.7`](https://github.com/go-gitea/giteabot/releases/tag/v1.0.7),
which supports backport branch names in major-only format `release/v28`
(https://github.com/go-gitea/giteabot/pull/14).

Co-authored-by: Claude (Opus 5.5) <noreply@anthropic.com>
2026-09-29 15:25:33 +02:00
TheFox0x7
1b1274486c fix(git)!: use internal proxy for all git operations (#39426)
Introduces gitproxy module which spawns a small forward proxy as scanner
for git calls
Replaces hostmatcher with matchlist which supports port rules
Deprecates ALLOWED_DOMAINS/BLOCKED_DOMAINS and ALLOW_LOCALNETWORKS
settings in migration in favor of full names we have in security
configs.
Removes `external` preset in favor of lax/strict modes, strict mode
requiring explicit ports if they aren't standard http/s ones.

Breaking changes:
- `external` preset no longer works as deny rule. To enforce that, use
`strict` mode and allow ranges to connect to
- Wildcards are no longer accepted in IP addresses
- `*` is no longer allowed as entry in lists
- domain rules now use curl like syntax `*.example.com` matching
subdomains but not `example.com`, `example.com` matching itself and all
subdomains. `example.*` is not a valid rule
- In the default `lax` mode, `[security] ALLOWED_HOST_LIST` no longer
restricts public hosts, set `EGRESS_MODE = strict` to keep an exclusive
list. A startup warning flags this
- Invalid list entries are logged at startup, invalid
`BLOCKED_HOST_LIST`/`BLOCKED_DOMAINS` entries stop it

Docs: https://gitea.com/gitea/docs/pulls/557
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-29 14:43:36 +02:00
Max Azatian
ebcb0150d1 fix(api): commit tree SHA is the commit ID (#39449)
Fixes #39448
2026-09-29 11:56:51 +00:00
Stochastic
6bb74ed65d fix(actions): preserve admitted jobs and runs in their concurrency group (#39461) 2026-09-29 04:36:33 +00:00
Giteabot
fba8d7eace chore(deps): update dependencies (#39462) 2026-09-28 14:05:07 +02:00
GiteaBot
47529e225c [skip ci] Updated translations via Crowdin 2026-09-28 01:03:09 +00:00
silverwind
9e7b302bd3 enhance(actions): add pending job status and align job statuses with GitHub (#39376) 2026-09-27 18:30:42 +00:00
Zettat123
0d09986790 fix(actions): keep the caller's event and inputs in reusable workflow jobs (#39452)
Fixes https://github.com/go-gitea/gitea/issues/38705
Fixes https://gitea.com/gitea/runner/issues/1232

Jobs of a called workflow saw `gitea.event_name` as `workflow_call` and
`gitea.event.inputs` replaced by the caller's `with:`, so a condition
like `gitea.event_name == 'push'` never held in them, and a dispatched
run's own inputs were lost there.

They now keep the caller's trigger event and their `inputs` are the
run's `workflow_dispatch` inputs overlaid with the caller's `with:`, as
on GitHub. For example, with `workflow_dispatch` inputs `{target: prod,
debug: true}` and caller's `with: {target: dev}`, the called workflow's
`inputs` are `{target: dev, debug: true}`.

A runner cannot resolve these inputs itself, so they are sent in a new
`gitea_workflow_call` context entry, with the original event name and
inputs for the runner to restore. For more details, see the runner PR:
https://gitea.com/gitea/runner/pulls/1250

Co-authored-by: silverwind <me@silverwind.io>
2026-09-27 19:08:46 +02:00
GiteaBot
3c5d2d1b63 [skip ci] Updated translations via Crowdin 2026-09-27 01:03:20 +00:00
bircni
f44e64be81 fix(actions): harden fork pull request run approval (#39399)
Fixes several gaps in the approval of fork pull request runs:

1. Approving a run that was cancelled while awaiting approval revived
its cancelled jobs. Such a run is no longer treated as awaiting approval
by the merge box, run page, approve actions and API, and rerunning it
approves it.
2. Approval no longer revives jobs cancelled while the run was pending,
no longer lets two jobs sharing a concurrency group cancel each other,
and re-emits the run so jobs needing a cancelled job get resolved.
3. An unapproved run applies its workflow-level concurrency only once
approved.
4. For workflows from the pull request, both the event actor and the
pull request author must be trusted to skip approval. Workflows from the
default branch, like `issue_comment`, still only check the actor.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-26 22:52:19 +00:00
Max Azatian
031c5f1ba6 docs(webhook): review.type comment lists values the webhook never sends (#39451)
Refs #39450.

The `ReviewPayload.Type` comment said the value is approved, rejected or
comment. The webhook and Actions notifiers set it to the event type,
`pull_request_review_approved`, `pull_request_review_rejected` or
`pull_request_review_comment`, and the comment now lists those. The same
wording on the docs site (`docs/usage/repository/webhooks.md` in
gitea/docs) needs the matching change, which is why this refs the issue
rather than closing it.
2026-09-27 00:32:14 +02:00
wxiaoguang
ea91028028 fix: PR merge (#39442)
* Revert the behavior introduced by #30805
* Now the PR status is still managed in Gitea's code where the operation
is triggerred but not in post-receive hook
* Fix #39254 and many more related bugs.
    * Fix #39124

```
// MarkAsMerged sets a pull request to merged and closes the corresponding issue
// To make sure the pull request is marked as merged correctly, the caller uses multiple-stage operations:
//  1. Create a temp repo from base, merge the head into the temp repo, and get the merged commit ID and timestamp,
//  2. The merged commit ID and related information are stored into pull request
//  3. Push the merged commit to the base repo
//  4. Call MarkAsMerged to mark the pull request as merged and do post-processing (notification, close issues, etc)
//
// If failure occurs in step 1/2/3: the pull request is still open, the base repo is not changed, the doer can start a new merge.
// If failure occurs in step 4: the pull request can be marked as merged by the merged commit ID stored in it later.
```
2026-09-26 17:38:42 +00:00
Sergio Benitez
dcef88a233 fix(api): allow pending-inline-comment-only reviews (#39433)
Prior to this change, the API rejected reviews without a summary
comment, even if it had pending inline comments. This differs from the
web UI, which accepts such reviews. The affected endpoints are:

1. Submitting via POST /repos/{owner}/{repo}/pulls/{index}/reviews/{id}.
2. Creating via POST /repos/{owner}/{repo}/pulls/{index}/reviews, both
when finishing an existing pending review and when creating a new
pending review.

The endpoints ran their own emptiness check, which ignored comments
already in a pending review. The fix drops it for comment and pending
reviews and relies on the model's check, which counts them, as the web
UI does.

A review with neither a body nor inline comments remains invalid.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
2026-09-26 16:51:55 +00:00
Roshan Ramani
3fac5059de docs(api): document verification and files on the compare endpoint (#39440)
`GET /repos/{owner}/{repo}/compare/{basehead}` reads `verification` and
`files` (both default true), the same as the commit endpoints do:

```go
verification := ctx.FormString("verification") == "" || ctx.FormBool("verification")
files := ctx.FormString("files") == "" || ctx.FormBool("files")
```

The spec only lists `output`, so generated clients cannot turn either
off. This documents both with the wording the commit endpoints already
use and regenerates the spec.

Co-authored-by: bircni <bircni@icloud.com>
2026-09-26 16:29:02 +00:00
silverwind
4eebefbce5 fix: dedupe issue cross-reference timeline entries (#38881)
Removing and re-adding a mention, or changing `closes #1` to a plain
`#1`, added duplicate references to the issue's timeline.

The timeline now renders a single entry per referencing issue or pull
request, positioned at the first mention, like GitHub does.
2026-09-26 11:31:01 +00:00
bircni
3875db1974 feat(actions): add build queue view (#38585)
Adds a read-only Actions job queue: running jobs first, then waiting
jobs in the order a runner picks them up. It is shown instance-wide in
the admin Actions section with owner, repository and status filters, and
per repository in the Actions tab. Both lists refresh in place.

Pending work is currently only visible per repository and newest-first,
so nothing shows what is queued, in which order, or what occupies the
runners. Reordering the queue will be proposed separately.

A migration adds indexes for the runner pickup query and
repository-scoped status lookups.

* Fix #34198

<img width="1345" height="451" alt="image"
src="https://github.com/user-attachments/assets/7d52ff76-81b4-44e8-b583-d7d89c9dffcd"
/>
<img width="1809" height="1134" alt="image"
src="https://github.com/user-attachments/assets/4d56c0cb-bae7-4ce2-8f3c-75163b2bc7f4"
/>

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-26 08:01:41 +00:00
bircni
5a56e118e4 feat(actions): Add artifact preview in Actions run view (#36754)
Closes https://github.com/go-gitea/gitea/issues/33579.

Adds browser previews for Actions artifacts. Selecting an artifact opens
its file browser; selecting a file renders it in the same tab. The ZIP
download remains available separately.

Previews require sign-in and read access to the run. Text, image and PDF
files are supported; rendered HTML and JavaScript run in a sandboxed
frame and are labeled as automatically generated. The frame loads files
from a signed link that expires after an hour, because its requests
carry no session cookie. `[actions] ARTIFACT_PREVIEW_MAX_SIZE` limits
total previewable artifact size (`0` disables previews; `-1` removes the
limit); individual files also follow `[ui] MAX_DISPLAY_FILE_SIZE`.

<img width="1803" height="913" alt="image"
src="https://github.com/user-attachments/assets/a38fd704-2244-44fa-9181-c695ecbe0276"
/>

Docs: https://gitea.com/gitea/docs/pulls/533

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-09-26 07:42:36 +00:00
silverwind
857d3d3df3 chore: update eslint, enable more rules and fix their findings (#39438)
Update eslint and its plugins, enable more rules and fix their findings:

1. `unicorn/no-unsafe-string-replacement` found that uploading a file
whose name contains `$&` inserted a broken markdown link, because
`String#replace` expands such patterns in the replacement string
2. `@typescript-eslint/require-await` removes `async` from functions
that never await
3. Plugin rules not covered by a preset are now listed explicitly

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-09-26 07:20:39 +00:00
Zettat123
a15f032026 fix(actions): evaluate job-level if: before concurrency check (#39437)
Gitea doesn't evaluate a job's `if:` before checking the job's
concurrency group, which causes a job that should have been skipped to
incorrectly cancel other jobs in the same concurrency group.

This PR makes Gitea decide `if:` for every job before it becomes
waiting, including jobs without `needs` at insertion, on approval and on
rerun. A skipped job therefore no longer takes part in job concurrency
or holds a max-parallel slot, and a reusable caller whose `if:` is false
is no longer expanded on approval or rerun. An invalid `if:` skips the
job with an error summary.

After this PR, Gitea decides all jobs' `if:` expressions and sends `if:
always()` to the runner, so the runner no longer needs to evaluate a
job's `if:` again ([gitea/runner
`run_context.go`](81add27459/act/runner/run_context.go (L1195))).

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-26 09:01:21 +02:00
Roshan Ramani
f0f53a76ee docs(api): name the unadopted-repository search parameter query (#39370)
`GET /admin/unadopted` documents a `pattern` query parameter, but the
handler reads `query`:

```go
repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, ctx.FormString("query"), &listOptions)
```

Nothing in the tree reads `pattern`. A client generated from the
published spec sends it, the server ignores it, and the caller gets the
full unadopted list with no error — the failure is silent.

Both spellings date from the commit that added the endpoint,
https://github.com/go-gitea/gitea/pull/12920, so the documentation has
been wrong since 2020 rather than drifting. The handler side is the one
clients already depend on, so this renames the documented parameter and
regenerates the spec.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-09-25 18:15:24 +00:00
TastyHeadphones
03c7255c9b docs: remove unused COOKIE_USERNAME from app.example.ini (#39365)
`COOKIE_USERNAME` is documented in `app.example.ini` but nothing in the
tree reads it (unlike `COOKIE_REMEMBER_NAME` / session `COOKIE_NAME`).
An admin who sets it gets silence and no effect.

Removed the dead example lines.

Fixes #39353

AI disclosure: assisted with drafting; I verified the setting is unused
and reviewed the diff.

Signed-off-by: TastyHeadphones <tastyheadphones@icloud.com>
2026-09-25 17:44:00 +00:00
silverwind
a7b0327ff6 chore: quote test name patterns in make targets (#39429)
`test-backend#%` and `test-integration#%` passed the test name to the
shell unquoted, so regex characters broke the command. For example `make
'test-backend#^(TestA|TestB)$'` failed with `syntax error near
unexpected token '('`. The pattern is now single-quoted like the
neighbouring `-tags` argument.
2026-09-25 14:32:51 +00:00
bircni
d42128d71d fix: preserve SNI for local internal API (#39412)
https://github.com/go-gitea/gitea/pull/38406 stopped setting
`ServerName` on the internal API client, which
https://github.com/go-gitea/gitea/pull/5820 had added for ACME. Internal
requests to a local `LOCAL_ROOT_URL` now send SNI `localhost` (or none
for IPs). The ACME listener selects its certificate by SNI, finds none
and aborts the handshake with `tls: internal error`, breaking SSH access
and git hooks.

Send the `ROOT_URL` host as SNI again for local targets, and treat
unspecified addresses (`0.0.0.0`, `::`) as local since dialing them
reaches the local host. Remote targets are still verified against their
own hostname.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-25 13:46:52 +00:00
TheFox0x7
448f8c67e0 fix(ssh): fetch ssh key by fingerprint (#39423)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-25 06:22:04 -07:00
silverwind
6d59229331 enhance: allow auto-closing PRs from PRs (#39393)
On GitHub, one can close PRs via `Fixes: #123` references which was not
possible on Gitea before, but now is. Verified fully that behaviour
matches GH and ensured no regressions for external trackers.
2026-09-25 11:35:13 +00:00
silverwind
455f30a7b6 chore(frontend): target ES2022 (#39420)
Bump the TypeScript and Vite targets from ES2020 to ES2022. The frontend
already calls ES2022 APIs like `Object.hasOwn` and `Array.prototype.at`
without polyfills, so downleveling class syntax only added helpers. JS
output shrinks by 36 KB.

The only new syntax-level requirement is Safari 16.4 for mermaid, which
ships class static blocks. Pale Moon supports all emitted syntax since
32.2.

The ES2022 target implies `useDefineForClassFields`, so
`noImplicitOverride` is enabled to flag class fields that shadow
inherited DOM properties.
2026-09-25 05:56:07 +00:00
Sergio Benitez
722e52334a fix(feed): use meaningful lines as comment excerpt (#39276)
Comment excerpts in activity feeds previously used either the first 200
display characters of a comment or, for review comments, its first
physical line. That excerpt is rendered as Markdown in the feed, so if
the excerpt began with a leading blank lines or structural markdown
syntax, the excerpt would render as empty or produce broken output. For
example, a review comment beginning with a code fence stored only the
opening fence, which rendered as an empty code block.

This commit instead renders feed excerpts as prose only, dropping code,
math, tables, images and HTML, which also fixes already stored excerpts.
New excerpts start at the first prose line, and review comments get the
same excerpt as issue comments.

This produces meaningful excerpts in more cases while preserving their
original Markdown.

---

For a comment that contains the following:

````
```
some code
```

hello
````

This previously rendered as:
 
<img width="816" height="118" alt="Screenshot 2026-09-08 at 5 06 47 PM"
src="https://github.com/user-attachments/assets/9d125363-72de-46bf-b47a-961245a79c5f"
/>

And now renders as:

<img width="807" height="89" alt="Screenshot 2026-09-08 at 5 08 30 PM"
src="https://github.com/user-attachments/assets/d2dcdc6d-d7a9-437f-8e9c-b845fe99fa5e"
/>

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
2026-09-25 03:08:15 +00:00
Jon Fuller
03058691c3 fix(migrations): stop endless comment paging when migrating from Gitea (#39419)
`GET /repos/{owner}/{repo}/issues/{index}/comments` ignores `page` and
`limit` and always returns every comment. The Gitea downloader pages it
and stops only on a short page, so migrating from Gitea or Forgejo loops
forever on any issue with at least `MAX_RESPONSE_ITEMS` comments,
without an error.

Paging is kept in case the endpoint gets paginated
(https://github.com/go-gitea/gitea/issues/6132,
https://github.com/go-gitea/gitea/issues/18082). The loop now stops when
a page is longer than the limit or starts with an already seen comment.

Prior art: Forgejo fixed its copy in
https://codeberg.org/forgejo/forgejo/pulls/9274 (report:
https://codeberg.org/Codeberg/Community/issues/1542).

---
AI-assisted: drafted with Claude Code (claude-opus-5-5), reviewed by me.

Please let me know if you have any suggestions or comments, I ran into
this issue myself when I was trying to migrate repositories from Forgejo
-> Gitea.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-25 01:23:50 +00:00
GiteaBot
27d787d70a [skip ci] Updated translations via Crowdin 2026-09-25 00:54:17 +00:00
silverwind
7f3b4a0bdd fix(ui): misc ui fixes (#39336)
1. Give menu items an inset pill highlight, text position and menu width are unchanged in most menus
1. Add arrow key, Enter, Space and Escape handling to all tippy menus
1. Mark the keyboard cursor of fomantic and tippy menus with the focus ring instead of the hover background
1. Mark the current dropdown item with the active color so it stands out from the hovered one
1. Stop navbar dropdown links from taking the navbar item hover color
1. Replace the actions job log options dropdown with the shared tippy menu, its hover highlight was invisible
1. Stop changing font weight on active and selected menu items, it resized rows while arrowing
1. Stretch the "All extensions" button in the diff file extension filter to the full menu width
1. Fix the actions run summary block covering the panel's rounded corners and indenting wrapped stats
2026-09-25 00:01:09 +00:00
silverwind
f757631a47 feat(actions): update actionslib, support self:, misc fixes (#39358)
Updates actionslib to https://gitea.com/gitea/actionslib/releases/tag/v1.2.1, moves workflow
parsing into it and aligns behaviour with GitHub.

1. `uses:` supports `self:` (Gitea-only feature) and `$/` paths.
1. `strategy`, `matrix`, `max-parallel` and `fail-fast` accept
expressions, including over `needs`. A job whose `name`, `runs-on` or
`continue-on-error` reads `needs` is resolved once they finish.
1. A job `if:` may only read `github`, `needs`, `vars` and `inputs` and
is decided before the matrix, as on github.com.
1. Matrix `fail-fast` cancels the other combinations, and `always()`
jobs keep running when a run is cancelled.
1. Invalid workflow files, including a malformed `on:` and unknown or
cyclic `needs`, show up on push as failed runs with the error.
1. A job whose `if:` or `concurrency:` fails to evaluate is skipped or
failed with the error, instead of staying blocked.
1. Reusable workflows: a missing and an unreadable repository fail
alike, public callers cannot use private workflows, nested jobs cannot
exceed the caller's token permissions.
1. Runner labels match case-insensitively, and `runs-on` accepts an
array from an expression.

Runner PR: https://gitea.com/gitea/runner/pulls/1247
Docs PR: https://gitea.com/gitea/docs/pulls/553
Fixes: https://github.com/go-gitea/gitea/issues/38990
Fixes: https://github.com/go-gitea/gitea/issues/39382
Fixes: https://github.com/go-gitea/gitea/issues/32364
Fixes: https://github.com/go-gitea/gitea/issues/36077
Fixes: https://github.com/go-gitea/gitea/issues/23277
Fixes: https://github.com/go-gitea/gitea/issues/29020
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-09-25 00:06:42 +02:00
silverwind
64f31d9b70 enhance(emoji): update to Unicode 17, unify and lazy-load emoji data (#39363)
Generate emoji data from Unicode 17's `emoji-test.txt`, keeping existing
aliases. `public/assets/emoji.json` is now the single emoji data file,
also loaded by the backend. Rendered emoji drop their `aria-label`, the
dark theme inverts key on a new `data-alias` attribute instead.

Skin tone variants and their Gitea-only aliases are removed, GitHub has
none either.

Emoji autocompletion is now lazy-loaded with the markdown editor,
shrinking the index JS chunk from 653KB to 563KB.

---------

Signed-off-by: silverwind <me@silverwind.io>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-09-24 20:23:15 +00:00
bircni
05f049e8bb fix(issues): don't render team review requests as Ghost (#39416)
Closes https://github.com/go-gitea/gitea/issues/39414

`CommentList.loadAssignees` set a Ghost assignee on every comment
without an `AssigneeID` whenever another comment in the same batch had
one. Since https://github.com/go-gitea/gitea/pull/38413 the timeline
prefers `Assignee` over `AssigneeTeam`, so team review requests, from
CODEOWNERS or added manually, rendered as "Ghost" whenever the timeline
also had a user assignee or user review request. Skip comments without
an `AssigneeID`.

Co-authored-by: silverwind <me@silverwind.io>
2026-09-24 19:01:34 +00:00
wxiaoguang
671a2f5dc0 fix: sanitize external render command line arguments (#39417) 2026-09-24 19:38:00 +02:00
Myle
d5ed7375ce fix(LFS): recalculate repo LFSSize after gc-lfs removes orphaned data (#39406)
Related to #36169 

this PR makes `gc-lfs` update the `repo.LFSSize` on object removal,
updates the test to also cover the recalculation of sizes.
doesn't touch `storage-lfs` as it removes files with no LFSMetaObject
row, and LFSSize is calculated from those rows

---------

Signed-off-by: Myle <myle.ataraxia@pm.me>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-24 13:28:14 +00:00
Copilot
484981fd74 enhance(acme): add configurable ACME profile (#39375)
Adds server-side ACME profile configuration so operators can select a
non-default ACME profile. This covers issuers such as Let's Encrypt
where raw-IP certificate issuance requires the `shortlived` profile.

Fixes: #39374
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: techknowlogick <164197+techknowlogick@users.noreply.github.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-24 10:30:25 +00:00
Rafail Giavrimis
defc9d5ca3 fix(git): keep leading dashes in git grep search patterns (#39404)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-24 09:46:24 +00:00
silverwind
19ae1f842b refactor: fix go vet errors related to composite literals (#39341)
Fix these `go vet` errors which I stumbled upon because AI likes to run
`go vet` standalone and `go vet` does not understand `//nolint` like
golangci does, so it produced confusing output.

Co-authored-by: bircni <bircni@icloud.com>
2026-09-24 09:17:25 +00:00
Rafail Giavrimis
72243bead7 fix(indexer): index full file paths and real offsets in bleve (#39405)
The bleve path token filter added in
https://github.com/go-gitea/gitea/pull/32210 never generated the full
path of a file, so searching a file by its path (e.g. `potato/ham`)
found nothing. It also gave the path tokens made-up offsets instead of
their position in the path.

The filter is replaced by a tokenizer that emits the raw path suffixes
starting at each segment and word (`potato/ham.md`, `ham.md`). Paths
below the root now match too, as do names containing `-` or spaces and
dotfiles. An exact file name also ranks above files inside a directory
with the same name.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-24 07:57:42 +00:00
Roland
8b46a956d8 perf(references): scan only the keyword window before a reference (#39396)
Fixes https://github.com/go-gitea/gitea/issues/39395

`findActionKeywords` ran the close and reopen keyword patterns over all
content before each reference, making `FindAllIssueReferences` quadratic
on comments and commit messages. The patterns are anchored at the
reference, so only the last few bytes can match. Scan only that window,
sized from the longest keyword with room for `(?i)` matching wider runes
like `ſ` for `s`.

`"#1 "` repeated 4000 times: 2.04 s before, 16 ms after.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-24 07:12:02 +00:00
GiteaBot
2177969aba [skip ci] Updated translations via Crowdin 2026-09-24 00:54:31 +00:00
wxiaoguang
f14cedc4fa refactor: "install" page (#39400)
1. remove useless options
2. set AppDataPath instead of repo root path
3. make "disable self-registration" default enabled
4. avoid writing corrupted ini file
5. avoid auto-sign-in the existing admin user
2026-09-23 23:14:31 +00:00
wxiaoguang
71065941a9 fix: use correct content-type for container registry response (#39398)
spec:
https://github.com/opencontainers/distribution-spec/blob/main/spec.md

* `/v2/<name>/manifests/<reference>`: media type
* `/v2/<name>/blobs/<digest>` : binary content (spec doesn't require
content type)
2026-09-23 18:45:56 +00:00
Even
06e334121f fix(user): allow unblocking users promoted to admin (#39192)
Fixes #39189.

`IsUserBlockedBy` intentionally treats admin users as not blocked, but
`CanUnblockUser` was also using it to determine whether a blocking
relationship exists. If a previously blocked user is later promoted to
admin, the existing `user_blocking` record remains but can no longer be
removed.

This change separates those two concerns by adding `HasBlocking` for
checking the persisted blocking relationship. `CanUnblockUser` uses that
relationship check while `IsUserBlockedBy` keeps its existing admin-user
behavior.

A regression test verifies that an admin is still not considered blocked
while an existing blocking relationship can still be unblocked.
2026-09-23 16:54:12 +00:00
Sean Yang
191287d8be fix(repo): commit page fails to render unsigned commits with a different committer (#39381)
Since #39229 the commit page header dereferences
`.Verification.CommittingUser` when the committer is not the author.
`Verification` is `nil` for unsigned commits (see `repo.Diff`), so
opening such a commit — a rebased or cherry-picked one, for example —
logs a template error and the page comes out truncated:

```
Render failed: failed to render template: repo/commit_page, error: template error: builtin(bindata):repo/commit_page:138:22 : executing "repo/commit_page" at <.Verification.CommittingUser>: nil pointer evaluating interface {}.CommittingUser
```

This guards the access and adds an integration test that creates a
commit with distinct author and committer identities and checks the page
renders completely (the status stays 200 on a mid-render failure, so the
test looks at the body).

_The fix was worked out with help from an AI assistant; I reviewed and
tested it myself._

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-23 14:34:19 +00:00
wxiaoguang
08149f9bec refactor: make git http respond error message (#39390)
* Refactor some bad smells in legacy code
* Fix #37999: instead of creating an empty (undesired) wiki page, just tell users to create a wiki page first
2026-09-23 08:51:58 +00:00
wxiaoguang
6146a4869e fix: use clearer message for ldap auth failure (#39392)
* fix #34942

log: `user does not exist ...: not in LDAP database or invalid password`
2026-09-23 16:30:58 +08:00
Copilot
8164130349 fix(markup): raise KaTeX MAX_CHARS limit to 10000 (#39387) 2026-09-22 15:12:56 +00:00
wxiaoguang
7637b1b816 chore: refactor StaticRootPath (#39384)
When need to use some settings in testing code, always call `SetupGiteaTestEnv`
2026-09-22 22:49:36 +08:00
GiteaBot
de5913d647 [skip ci] Updated translations via Crowdin 2026-09-22 00:54:27 +00:00
Giteabot
fc28937a8d chore(deps): update dependencies (#39367) 2026-09-21 12:23:03 -04:00
GiteaBot
9b6a82d7e1 [skip ci] Updated translations via Crowdin 2026-09-21 00:59:22 +00:00
GiteaBot
9ae7ea7ec6 [skip ci] Updated translations via Crowdin 2026-09-20 01:00:17 +00:00
silverwind
cdf786ce92 fix: pass merge commit messages to git via stdin (#39269)
`git commit --message=` passes the merge message as a single argument,
which Linux caps at 128 KiB and Windows at 32 KiB for the whole command
line. Long messages failed with `argument list too long` and the merge
box toast showed the raw HTML 500 page.

Pass the message via `--file=-` on stdin instead, and answer
fetch-action requests with JSON on server errors so the toast shows the
error text. Limits merge commit messages to 512KB which could be
extended or made configurable later.

Fixes: https://github.com/go-gitea/gitea/issues/39261
Fixes: https://github.com/go-gitea/gitea/issues/30276
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-19 13:04:53 +00:00
breken
cc34c26172 fix(migrations): preserve SHA-256 pull request commit IDs (#39343)
* Fixes #39339

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-19 12:41:41 +00:00
GiteaBot
2a3d047339 [skip ci] Updated translations via Crowdin 2026-09-19 00:53:07 +00:00
silverwind
a5a645824f chore: remove CLAUDE.md (#39360)
Ref:
https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#21277

Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
2026-09-18 17:51:39 -07:00
silverwind
309c872ce5 chore: update eslint plugins and configure new unicorn rules (#39357)
Update all eslint plugins and configure the new unicorn rules.

The new unicorn CSS rules stay off because stylelint already covers most
of them.

`unicorn/prefer-iterator-helpers` is disabled because it forces too-new
browser requirements onto Gitea.

No new violations in the code.
2026-09-18 23:58:36 +00:00
Roshan Ramani
0a8b24c2b3 docs: document NOTICE_ON_SUCCESS for every cron task (#39352)
`app.example.ini` documented cron success notices with keys Gitea does
not read:

1. `[cron.update_checker]` had `ENABLE_SUCCESS_NOTICE`, which never
existed.
2. Six sections still had `NO_SUCCESS_NOTICE`, removed in
https://github.com/go-gitea/gitea/pull/19221. Its `false` value also
implied notices were on, while the default is off.

Cron sections map onto `BaseConfig`, whose `NoticeOnSuccess` binds to
`NOTICE_ON_SUCCESS`. This documents that key for every cron task, adds
it to four sections that omitted it, and fixes the "Notice if not
success" descriptions that inverted its meaning.

Signed-off-by: Roshan Ramani <roshanramani.dev@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-18 23:53:59 +00:00
Abhay Pratap Singh
3296046b4a fix: focus confirm button and use red for delete confirmations (#39350)
Fixes https://github.com/go-gitea/gitea/issues/39347

1. Focus the confirm button in generic confirm modals so `Enter` runs
the highlighted action.
2. Delete actions that lacked the risky flag now get it, so their
confirm button is red.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-18 19:42:22 +00:00
afishcalledwander
fce7b9d531 fix: package registry keypair creation race (#39319)
Alpine, arch, debian, and rpm package types have a race in key creation.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-18 18:30:27 +00:00
breken
b27e7d0289 enhance: improve issue-pattern capture groups and support both internal&external trackers enabled (#39354)
* Fix #39351
* Fix #17621
* Fix #34881

By the way, fix error handling bugs in `updateRepoUnits`

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-18 08:53:45 -07:00
silverwind
85eaf5c71c refactor(api): convert bot accounts through the admin user edit endpoint (#39355)
Follow-up to https://github.com/go-gitea/gitea/pull/38966. Replaces the
unreleased `POST /admin/users/{username}/convert-type` endpoint with a
`type` field on `PATCH /admin/users/{username}`.
2026-09-18 17:11:56 +02:00
Joe (Agent) Stump
3bec08f998 feat: manage bot accounts from the admin UI, API and CLI (#38966)
Adds first-class bot accounts (`UserTypeBot`): local, password-less
users for automation that authenticate only with access tokens.

1. Admin UI: create bots, filter users by type, manage a bot's access
tokens, convert between user and bot
2. API: `POST /admin/users/{username}/convert-type`, and user objects
gain a GitHub-compatible `type` (`User`, `Organization`, `Bot`)
3. CLI: `gitea admin user change-type`, `--user-type` accepts `User` or
`Bot` case-insensitively
4. Converting keeps the password, 2FA, OAuth2 grants and access tokens,
and since sign-in rejects bots, converting back restores the account.
Only local, non-admin accounts can be converted, and conversions are
audited
5. Session, reverse proxy, SSPI, external source and password reset
sign-in reject non-individual users, so a bot never gets an interactive
session
6. Bots receive no notifications or emails

Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: joestump <joe@joestump.net>
Co-authored-by: Joe Stump <joe@stu.mp>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-09-18 12:43:36 +00:00
silverwind
db7dbd5a6b test: stop tests from writing into ~/.ssh (#39348) 2026-09-18 17:07:45 +08:00
GiteaBot
6400c1545e [skip ci] Updated translations via Crowdin 2026-09-18 00:54:14 +00:00
silverwind
2b10f77e42 enhance: update mermaid to v12 (#39331)
Update to [mermaid
v12](https://github.com/mermaid-js/mermaid/releases#release-mermaid@12.0.0),
ELK is now the default layout so the plugin and all supporting code is
gone.

Layout switching to previous `dagre` layout via frontmatter works as
expected. Diagrams use the new `neo` default look which renders them
slightly different (smaller boxes) then the `classic` look that was
default in v11.

The `neo` look got some tweaks to remove shadows and gradient to look
better. Also did some related refactors and mermaid dragging now works
on touch devices too.
2026-09-17 23:28:51 +00:00
Roshan Ramani
c0c573f7af docs: correct ALLOW_LOCALNETWORKS description in app.example.ini (#39240)
Fixes the `app.example.ini` half of #39224.

The comment says:

> If a domain is allowed by `ALLOWED_DOMAINS`, this option will be
ignored.

That is no longer true. `checkByAllowBlockList` in
`services/migrations/migrate.go` consults the block list **first** and
returns immediately:

```go
if blockList.MatchHostName(hostName) || ipBlocked {
    return &git.ErrInvalidCloneAddr{Host: hostName, IsPermissionDenied: true}
}
// if we have an allow-list, check the allow-list before return to get the more accurate error
if !allowList.IsEmpty() { ... }
```

`IsMigrateURLAllowed` resolves the host with `net.LookupIP` and passes
the addresses in, so with `ALLOW_LOCALNETWORKS = false` a host that
appears in `ALLOWED_DOMAINS` is still rejected once any resolved address
is private or loopback — the allow list never gets a look. The reporter
traced this to the validation hardening in #38324 / #38400, and the code
matches their description.

The new wording states the precedence rather than the old override
claim.

Scope: this only covers `custom/conf/app.example.ini`, which lives here.
The same stale sentence is on the config cheat sheet in `gitea/docs`
(both the English and zh-cn pages) per the issue; that is a separate
repository.

Assisted-by: Claude, via Claude Code
2026-09-17 12:23:40 -07:00
Nico Schlömer
4892a55e29 enhance: allow attribute-less MathML elements and complete the Core allowlist (#39337)
Follow-up to https://github.com/go-gitea/gitea/pull/38034.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-17 13:18:33 +00:00
Zhaoqi Xu
be7cde7539 fix(repo): surface unrelated histories on Sync Fork (#39258)
Sync Fork already maps merge conflicts to a JSON error. Unrelated
histories still went through `ServerError`, so the UI showed a 500 HTML
snippet instead of the same user-facing message PR merge already uses
(`repo.pulls.unrelated_histories`).

The API path returned 500 for the same git error; PR merge returns 409.
Match that.

Fixes #36772

AI assistance was used to locate the handler gap and draft the mapping.
I reviewed and take responsibility for the change.

Signed-off-by: Zhaoqi Xu <lzy00419@outlook.com>
2026-09-17 12:25:43 +00:00
silverwind
afb7edef07 refactor: replace AWS SDK with a REST client for CodeCommit migration (#39330)
Replace `aws-sdk-go-v2` in the CodeCommit migration with a minimal REST
client for the four read-only API calls it uses. Binary shrinks by ~3.1
MB.

Mock data was recorded against live from a AWS CodeCommit repo and then
anonymized.

Also stop storing the repo description as the default branch which fixes
below issue. All comments in
https://github.com/go-gitea/gitea/issues/34627 are addressed, the merge
metadata was fixed earlier in
https://github.com/go-gitea/gitea/pull/34645.

Fixes: https://github.com/go-gitea/gitea/issues/34627
2026-09-17 09:58:05 +00:00
silverwind
e77d3bffae perf(frontend): enable vite module preload (#39332)
`modulePreload: false` came in with the webpack to vite migration, when
chunks were cache-busted through import maps. With content-hashed chunks
that reason is gone. Vite's default makes every lazy import preload its
whole static dependency tree in parallel instead of the browser
discovering it one level per round trip, which renders lazy features like
mermaid, the code editor and the activity charts noticeably faster on
real-world latency without changing chunks or requests.

Assisted-by: Claude Code:claude-opus-5
2026-09-17 11:22:53 +02:00
GiteaBot
f0d2195998 [skip ci] Updated translations via Crowdin 2026-09-17 00:54:44 +00:00
afishcalledwander
b2e11ddb37 fix: add default timeout and handle errors for HaveIBeenPwned API (#39316)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 20:41:57 +02:00
Abhay Pratap Singh
7ebb2caa9e fix(user): unify email validation for registration and settings (#39304)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 17:34:41 +00:00
silverwind
c6c671e113 refactor: replace Azure Blob SDK with a REST client (#39315)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 17:06:00 +02:00
wxiaoguang
f0a535b641 build(gogit): disable gogit builds for stable releases (#39324) 2026-09-16 13:36:10 +00:00
silverwind
2b6500aca4 test(e2e): log out to switch users in pr-review test (#39328) 2026-09-16 15:32:02 +02:00
silverwind
c04802b6b3 enhance: support ETag on streamed repository archives, support If-None-Match: * (#39289)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 12:30:22 +02:00
afishcalledwander
31b4d79a84 fix: match install page update checker setting with app.ini (#39317)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 07:47:34 +00:00
silverwind
7efd24b28f fix(actions): use gitea's clock for actions durations (#39323) 2026-09-16 06:53:15 +00:00
GiteaBot
62945266d4 [skip ci] Updated translations via Crowdin 2026-09-16 00:55:08 +00:00
Mitrahsoft
a583a30d4a enhance(notifications): mark current notification page as read (#39294)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-16 00:31:16 +00:00
silverwind
f9d3268dbe fix(actions): never show negative running durations (#39322) 2026-09-15 21:33:17 +02:00
silverwind
812191c0f9 fix: classify git failures on stderr, restrict migration failure detail (#39010)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-15 10:59:34 +02:00
GiteaBot
7b036e96c2 [skip ci] Updated translations via Crowdin 2026-09-15 00:55:42 +00:00
wxiaoguang
c0ceea2f8f chore: fix various problems (#39298)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-09-14 22:15:21 +02:00
Abhay Pratap Singh
85cbf477e5 fix: correct stdErr match in isErrBlameNotFoundOrNotEnoughLines (#39309)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-14 14:59:18 +00:00
silverwind
13033827b1 chore(deps): update actionslib to v1.0.0 (#39295)
Co-authored-by: bircni <bircni@icloud.com>
2026-09-14 16:29:31 +02:00
Giteabot
cefb81a16f chore(deps): update dependencies (#39306)
Co-authored-by: silverwind <me@silverwind.io>
2026-09-14 13:18:00 +00:00
GiteaBot
25984bef0f [skip ci] Updated translations via Crowdin 2026-09-14 00:59:43 +00:00
GiteaBot
4ed7020d07 [skip ci] Updated translations via Crowdin 2026-09-13 01:00:55 +00:00
wxiaoguang
1280de5704 chore: fix system users (#39299) 2026-09-12 16:26:20 +00:00
Sergio Benitez
1e13badb39 enhance: truncate but show long lines in diffs (#39279)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-12 11:56:25 +00:00
bircni
da37b7916b feat: Add audit logging (#38189)
Co-authored-by: bircni <bircni@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-12 08:15:23 +00:00
GiteaBot
4d43445532 [skip ci] Updated translations via Crowdin 2026-09-11 00:52:31 +00:00
k3n
8c0911669b fix(ui): use button elements for branch and tag dropdown tabs (#39285)
Signed-off-by: Francesco Passaquindici <k3n.solver@gmail.com>
2026-09-10 20:51:23 +00:00
Petr Vaněk
40385ef284 fix(projects): allow max columns to the limit (#39272)
Signed-off-by: Petr Vaněk <pevadev@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-10 22:32:46 +02:00
GiteaBot
579de92b8a [skip ci] Updated translations via Crowdin 2026-09-10 00:53:46 +00:00
k3n
79fc119641 fix(auth): fix ssh and gpg key verification on windows (#39283)
Signed-off-by: Francesco Passaquindici <k3n.solver@gmail.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-09 20:42:29 +00:00
silverwind
81dee529ed enhance: improve e-mail templates (#38396)
Co-authored-by: bircni <bircni@icloud.com>
2026-09-09 17:58:27 +00:00
GiteaBot
92f2f6161b [skip ci] Updated translations via Crowdin 2026-09-09 00:54:44 +00:00
silverwind
f5c7b21f1d refactor(templates): update djlint to 1.46.0 and resolve its new findings (#39231) 2026-09-08 23:17:17 +00:00
silverwind
459fc13783 enhance(packages): implement npm single-version API and add per-version repository (#39267)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-09-08 18:25:29 +00:00
Harsh Sharma
d93bd06d0c fix: allow re-requesting uncounted review approvals (#38988) 2026-09-08 15:59:06 +00:00
Zettat123
c9193adb68 fix: restore owner name in PR target branch selector (#39262)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-08 15:34:49 +00:00
wxiaoguang
45a78bbc8e refactor: npm route handlers (#39275) 2026-09-08 15:15:40 +00:00
Rathina Devan E M
8b6ad49a5f fix(api): enforce mustNotBeArchived on repo topics routes (#39260)
Co-authored-by: silverwind <me@silverwind.io>
2026-09-08 09:28:57 +00:00
silverwind
df8e7db02f test: release fixtures loader lock before database work (#39263) 2026-09-08 11:10:14 +02:00
GiteaBot
e8254bd5c1 [skip ci] Updated translations via Crowdin 2026-09-08 00:55:59 +00:00
Giteabot
cc6fc41723 chore(deps): update dependencies (#39256)
Co-authored-by: silverwind <me@silverwind.io>
2026-09-07 16:35:03 +02:00
GiteaBot
1770470148 [skip ci] Updated translations via Crowdin 2026-09-07 01:00:02 +00:00
wxiaoguang
3176f37887 fix: avoid nil panic and refactor some trivial problems (#39251) 2026-09-06 09:49:27 +00:00
silverwind
87d5497da0 enhance: improve commit page header (#39229)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-06 11:27:04 +02:00
silverwind
7c280c0ce6 refactor: replace jquery.are-you-sure with first-party code (#39233)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-06 14:43:49 +08:00
wxiaoguang
3bd7ea4c9f refactor: GetDiffShortStat and fix panic caused by inconsistent "changed file number" (#39248) 2026-09-06 05:20:42 +00:00
GiteaBot
e5e7b2e76e [skip ci] Updated translations via Crowdin 2026-09-06 01:01:12 +00:00
Even
cf0f4dce72 fix(automerge): validate head commit before merge (#39235)
Co-authored-by: zihao_wang <zihao_wang@geelark.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-05 10:20:00 +00:00
Grigor Tonikyan
bde1af541c enhance(web): show attachment URL and UUID in dropzone preview (#39203)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-05 10:01:27 +00:00
silverwind
eb501f6b19 enhance: move window.config to JSON, improve CSP format (#39236)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-05 08:50:43 +00:00
Huang-404-Q
efa69e7230 fix: restore missing blob file when re-publishing a package (#39239)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-05 10:33:36 +02:00
GiteaBot
e4455fb494 [skip ci] Updated translations via Crowdin 2026-09-05 00:52:41 +00:00
GiteaBot
231ee19cff [skip ci] Updated translations via Crowdin 2026-09-04 00:53:26 +00:00
Ross Golder
2d3ad4a530 enhance: Improve validation errors for secrets/variables (#39221)
Signed-off-by: Ross Golder <ross@golder.org>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-04 00:28:58 +00:00
Roshan Ramani
ce2fd8d882 docs: fix dead localization doc link in the READMEs (#39211)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-09-03 23:47:40 +00:00
Grigor Tonikyan
c56b2d35ff enhance(web): hide attachment dropzone on preview tab in combo editor (#39204)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-03 23:32:11 +00:00
silverwind
c285440e03 refactor: share package registry error status classification (#39133)
Signed-off-by: silverwind <me@silverwind.io>
2026-09-03 18:43:30 +00:00
wxiaoguang
0fff5f481e fix: action run list page error (#39212) 2026-09-03 18:23:27 +00:00
agoudbg
9dd5747a21 docs: fix typo in README about app.ini restart (#39223)
Signed-off-by: agoudbg <agoudbg@gmail.com>
2026-09-03 11:02:00 -07:00
Giteabot
1b2479b548 fix(deps): update module golang.org/x/crypto to v0.56.0 [security] (#39219) 2026-09-03 13:58:21 +02:00
GiteaBot
dc6ac9489e [skip ci] Updated translations via Crowdin 2026-09-03 00:56:21 +00:00
Elisei Roca
bcd913a4e2 fix(httplib): prevent leaking localhost:3000 in public links (#39217)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-02 19:32:33 +00:00
开心
52fcd2d2a0 fix(pulls): hide branch deletion when an open PR uses the branch (#39193)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-02 17:36:22 +00:00
Mitrahsoft
8cf7330556 enhance(repo): check full repo name for dangerous operations (#39213)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-02 17:07:04 +00:00
Giteabot
f735df4dee chore(deps): update dependency go to v1.27.1 (#39206) 2026-09-02 16:39:30 +02:00
GiteaBot
5ec9714dde [skip ci] Updated translations via Crowdin 2026-09-02 00:55:19 +00:00
GiteaBot
ee8f2b4039 [skip ci] Updated translations via Crowdin 2026-09-01 01:05:52 +00:00
wxiaoguang
4875bb3b2b fix: correct repo/attatchment absolute url and release layout (#39178) 2026-08-31 23:31:17 +00:00
aminoy
fc9800b383 fix(setting): honor bare -1 for timeout settings (#39181)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-31 19:48:59 +00:00
silverwind
70678e9a3d chore: remove dead CSS rules (#39188) 2026-08-31 21:08:58 +02:00
Harsh Satyajit Thakur
72afd222f0 chore(webhook): GET method is not recommended (#39169)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-31 22:10:03 +08:00
wxiaoguang
be3a6d1647 fix: charset lookup (#39187) 2026-08-31 11:48:52 +00:00
Zhaoqi Xu
7c36ae7668 fix(git): keep IPv6 brackets in submodule web links (#39186)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-31 11:29:18 +00:00
silverwind
7c93ead628 refactor: improve types in the frontend, misc fixes (#39142) 2026-08-31 04:12:25 -07:00
Giteabot
535fc29ae8 chore(deps): update dependencies (#39176) 2026-08-31 12:34:15 +02:00
GiteaBot
4f307ec685 [skip ci] Updated translations via Crowdin 2026-08-31 01:00:29 +00:00
tara~ ♡
27570ea685 enhance(repo): unify diff stats on commit pages, misc diff tweaks (#39134)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-30 22:17:32 +08:00
bircni
8ef7a9e46d docs: Update CHANGELOG for release 1.27.3 (#39170) 2026-08-30 12:53:19 +00:00
GiteaBot
48d2403c0f [skip ci] Updated translations via Crowdin 2026-08-30 12:31:22 +00:00
GiteaBot
8c9305f060 [skip ci] Updated translations via Crowdin 2026-08-30 00:59:29 +00:00
Royce Remer
eea03676d3 fix(process): reap entire process group on cmd.Cancel (#39143)
Signed-off-by: Royce Remer <royceremer@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-29 19:54:07 +00:00
Karthik Bhandary
88974d2db9 feat(web): Add org removal functionality to admin user details page (#38013)
Signed-off-by: techknowlogick <techknowlogick@gitea.com>
Signed-off-by: Karthik Bhandary <34509856+karthikbhandary2@users.noreply.github.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-29 07:50:04 +00:00
bircni
3c0bfe9463 fix(actions): run every due schedule exactly once per occurrence (#39078)
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-29 06:44:51 +00:00
wxiaoguang
fedf219e77 refactor: pagination/pager (#39162) 2026-08-29 08:20:46 +02:00
GiteaBot
9297205f2c [skip ci] Updated translations via Crowdin 2026-08-29 00:49:02 +00:00
Mitrahsoft
78d3c60c7a enhance(actions): make workflow dispatch choice dropdown support search (#39154)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-28 22:05:04 +00:00
Nitish-1303
e806566b39 fix(web): populate the reason for "cannot commit to branch" in web editor commit form (#39155)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-28 21:46:58 +00:00
Willem Kokke
60326ca03d refactor(automerge): fix error handling, populate recent automerge tasks on restart (#39001)
* Refactor "automerge" related code, clarify many details (including "unique queue item", start check by pull head or commit)
* Fix automerge queue handler's error handling, clarify error messages
* Populate recent automerge tasks on restart to restore the previous aborted automerge tasks

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-28 14:17:01 -07:00
silverwind
1652dfe62a chore(frontend): avoid loading CSS twice in vite dev mode (#39160) 2026-08-28 22:57:08 +02:00
Eduard Dzhumagaliev
453c38d7d6 fix(packages): preserve SemVer prerelease identifiers in Swift Registry (#39156)
Co-authored-by: terriblegoodday <terriblegoodday@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-28 05:15:55 +00:00
GiteaBot
062c8f2241 [skip ci] Updated translations via Crowdin 2026-08-28 03:21:07 +00:00
silverwind
fbdeea0af7 ci(snap): pack snaps without an LXD container (#39152) 2026-08-27 20:30:17 +02:00
wxiaoguang
b7cfa4e67e chore: apply golangci "forbidigo" to all packages (#39151) 2026-08-27 16:34:44 +00:00
silverwind
66c672d0b7 refactor: drop two unmaintained dependencies, rename the byte size helpers (#39083) 2026-08-27 15:31:20 +00:00
bircni
3b6ce586a7 fix(actions): keep step-level continue-on-error expressions unevaluated (#39141)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-27 05:46:46 +00:00
GiteaBot
2c6dacb015 [skip ci] Updated translations via Crowdin 2026-08-27 01:57:53 +00:00
ToastyTheBot
646ea0f253 feat: add deploy tokens (#37306)
Deploy keys only work over SSH. A deploy token is their counterpart for HTTPS: a repository scoped credential, used as the password of a Git request, with read or read and write access. It covers Git operations and LFS, and can be regenerated in place.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude Mythos <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-26 19:32:44 +00:00
silverwind
3c4d5a6a5c chore: raise minimum git version to 2.25 (#39131) 2026-08-26 18:35:44 +00:00
Even
3005efa88d fix: recognize linguist language aliases (#39135)
Co-authored-by: loveulvu <ax9inl@google.com>
2026-08-26 17:24:07 +02:00
bircni
e21c37703e fix(repo): centralize repository-scoped authorization (#39063)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-26 20:12:53 +08:00
GiteaBot
90c43e8e78 [skip ci] Updated translations via Crowdin 2026-08-26 00:23:26 +00:00
wxiaoguang
7668e7c00d chore: repo compare link (#39088)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-25 17:52:21 +00:00
bircni
38747d48fe fix(pull): keep the merged state in sync with git (#39062)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-26 01:04:49 +08:00
silverwind
1680ac24e6 chore: anchor golangci exclusion paths to directories (#39116)
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
2026-08-25 16:36:45 +00:00
Artem Lytkin
c8660364d9 fix(asymkey): do not verify OpenPGP signatures with an SSH instance key, require git 2.18 (#39073)
With SIGNING_FORMAT = ssh the OpenPGP verification path builds its
GPGSettings from the instance signing key but leaves the format empty,
so it runs `gpg -a --export` on an SSH public key path. Depending on the
local gpg setup that either exports nothing, so an OpenPGP signed commit
reports gpg.error.generate_hash instead of a missing key, or it fails
outright and logs an export error for every such commit.

Both guards are needed. The first covers SIGNING_KEY set to a path with
SIGNING_FORMAT=ssh; the second covers the shipped default
SIGNING_KEY=default, where the format comes from git's own gpg.format
and never gets reconciled with the hardcoded "openpgp". Drop either one
and a working config goes back to broken.

Also raise minimum git version to 2.18 which was already required before this change.

Fixes: https://github.com/go-gitea/gitea/issues/37452
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-25 12:02:31 +00:00
yousimu
d17ccd4434 fix(repo): prevent MarkAsBrokenEmpty when repository is being migrated (#39091) 2026-08-25 12:01:16 +02:00
GiteaBot
9eb4a9afad [skip ci] Updated translations via Crowdin 2026-08-25 00:23:02 +00:00
silverwind
0b1067484f chore: bump declaration-strict-value and reconfigure it (#39080)
Bump https://github.com/AndyOGo/stylelint-declaration-strict-value and
configure it to avoid needing these lint exclusions.

Related:
https://github.com/AndyOGo/stylelint-declaration-strict-value/issues/201
2026-08-24 20:05:37 +00:00
Artem Lytkin
2a17cf7ded fix(git): parse co-author trailers that are not RFC 5322 addresses (#39076)
Bot co-authors like `dependabot[bot]` render as one long string with the
email inside the name, and never get truncated, so they overflow the
column.

Co-author idents are parsed with `net/mail`, but a git ident isn't an
email address. `mail.ParseAddress` rejects the whole value when the name
holds characters RFC 5322 reserves, like a `[bot]` suffix or a comma, so
the error branch keeps the raw trailer as the display name and throws
the address away. No address means no `mailto:` link, and the anchor is
what `.avatar-stack-names` truncates.

So parse the angle-addr ourselves when `net/mail` won't take it.
Splitting on the last `<` is safe because git strips angle brackets from
idents. The bare-name branch gets the class too.

Fixes https://github.com/go-gitea/gitea/issues/38949
2026-08-24 19:06:30 +00:00
Artem Lytkin
6b929ccb15 fix(pull): name the head repository in default compare links (#39075)
The "New Pull Request" buttons and the `/pulls/new/{branch}` redirect
build their compare link as `{owner}:{branch}`. If a fork and its parent
share an owner, through ALLOW_FORK_INTO_SAME_OWNER, or after a transfer,
that head resolves back to the base repo, so the link compares the base
against itself and 404s on a branch that only exists in the fork.

Switching to `{owner}/{repo}:{branch}` names the head repo
unambiguously, and it's what the compare page's own links already use.

Also clears the 404 in #37649; the archived-parent half of that report
is separate.
2026-08-24 18:46:40 +00:00
silverwind
32728fc581 chore: misc go 1.27 tweaks (#39069)
Follow-up to https://github.com/go-gitea/gitea/pull/39068, which
disabled `modernize` entirely.

- re-enable `modernize`, with only the new `embedlit` rule disabled. It
flattens embedded struct literals across ~145 files, and orphans imports
in 6 of them that the fixer does not remove
- apply the rest of the suite: `errors.AsType`, `reflect.TypeAssert`,
`strings.Cut`, and dropping the legacy import comment
- use the new stdlib `uuid` package, `github.com/google/uuid` becomes
indirect
- use `strings.CutLast` in place of manual `LastIndex` slicing in label
scopes, email domains and the diff tree list
- take the header lint skip dirs from the `go.mod` `ignore` directive
and skip dot-directories, instead of hardcoding the list

Assisted-by: Claude Code:claude-opus-5
2026-08-24 18:26:10 +00:00
chudnyi
59a43c8733 fix(packages/npm): use PathEscape for package name in tarball URL (#39061)
## Summary

Replace `url.QueryEscape` with `url.PathEscape` when building
`dist.tarball` in the npm package registry. `QueryEscape` leaves `@`
unescaped, producing `dist.tarball` URLs like `@scope%2Fname` for scoped
packages — which npm clients cannot resolve. `PathEscape` produces the
RFC 3986 path-segment-safe encoding (`%40scope%2Fname`) that the npm
registry URL format requires.

## Reproduction

1. Publish a scoped npm package (`@scope/name@1.0.0`) to a Gitea package
registry.
2. Inspect the `dist.tarball` field in the metadata response.
3. Observe that the package name in the URL is `@scope%2Fname` instead
of `%40scope%2Fname`.
4. `npm install @scope/name` fails because npm rejects the malformed
tarball URL.

## Fix

One-line change in `routers/api/packages/npm/api.go`:

```diff
-Tarball:   fmt.Sprintf("%s/%s/-/%s/%s", registryURL, url.QueryEscape(pd.Package.Name), url.PathEscape(pd.Version.Version), url.PathEscape(pd.Files[0].File.LowerName)),
+Tarball:   fmt.Sprintf("%s/%s/-/%s/%s", registryURL, url.PathEscape(pd.Package.Name), url.PathEscape(pd.Version.Version), url.PathEscape(pd.Files[0].File.LowerName)),
```

## Tests

- `routers/api/packages/npm/api_test.go`: extended
`TestCreatePackageMetadataResponse` to use `Package.Name: "@scope/test"`
and added an `assert.Equal` on `Dist.Tarball` (per review feedback to
consolidate the test instead of adding a new one).
- `tests/integration/api_packages_npm_test.go`: switched three
`url.QueryEscape(packageName)` to `url.PathEscape(packageName)` to match
the new production encoding (lines 125, 126, 446). The `TestPackageNpm`
assert at line 219 against `pmv.Dist.Tarball` now passes for scoped
packages.

The unit test fails on `main` (excluding the `QueryEscape` →
`PathEscape` swap) and passes with the fix.

## Related

Closes #39060.

## Disclosure

This contribution was prepared with assistance from an AI coding
assistant (limited to language polishing in maintainer-facing messages).
The contributor reviewed and validated all changes, including the test
cases.

---------

Signed-off-by: Dmitriy Chudnyi <dmitriy@chudnyi.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-24 18:00:44 +00:00
bircni
3a806a58d0 fix(attachments): enforce owning repository path (#39048)
Reject attachment requests routed through a repository other than the
attachment owner.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-24 17:32:23 +00:00
Giteabot
4c915ae0c2 chore(deps): update dependencies (#39066) 2026-08-24 17:05:31 +00:00
silverwind
9914db8898 chore: add modelmigration to GO_DIRS (#39072)
`modelmigration` was missing here since its creation, leading to those
files not being covered by fmt.
2026-08-24 17:49:16 +02:00
wxiaoguang
51b8da8b01 chore: update Go to v1.27 (#39068)
Only made some necessary changes:

1. remove `GOEXPERIMENT`, only use jsonv2
1. `make fmt`
* `SigningKey` and `Signature` were affected due to some bugs in the
toolchain, so rewrote them
1. remove or fix fragile magic numbers and strings
    * the outputs of image/gzip/zlib packages are different
1. update "nolint" comments for the changed lint behaviors
1. add `tls.MLKEM1024`
2026-08-24 07:28:17 +00:00
GiteaBot
e50e4ed869 [skip ci] Updated translations via Crowdin 2026-08-24 00:24:14 +00:00
McMichalK
2bcf950b78 feat(diff): Add search and extension filter to diff sidebar (#37068)
Adds a search box and a file-extension filter to the pull request diff
sidebar, so reviewers can narrow a large diff down to the files they
care about.

Both filters apply to the file tree and to the diff itself. The
extension menu follows GitHub: extensions sorted alphabetically,
dotfiles and extension-less files in their own buckets, and the
selection kept in the same `file-filters[]` query parameter, so a
filtered view is shareable and survives a reload.

The menu can list every extension in a diff, so `createTippy` gains an
opt-in `limitSizeToViewport` option that caps a popup to the space left
in the viewport and scrolls its content. Popups that do not ask for it
are unchanged.

Closes https://github.com/go-gitea/gitea/issues/27256
Signed-off-by: silverwind <me@silverwind.io>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: Copilot <copilot@github.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-23 18:31:22 +02:00
roman.s
4852091e85 fix(auth): record last sign-in on reverse proxy login (#38672)
Reverse proxy and SSPI logins establish a session but never recorded
`last_login_unix`, so those users stayed "Never Signed-In" in admin.

The write is folded into the language update that `handleSignIn` already
does, so it stays at one query and only runs when a session is
established.

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

---------

Co-authored-by: roman s <roman.sukach@dust-labs.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-23 08:39:44 +00:00
silverwind
1c16f04bf5 fix(db): make paginated database reads always require "order" option (#39017)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-23 08:18:18 +00:00
bircni
0bed1232ee fix(packages): restrict limited owner package access (#39043)
Apply restricted-viewer visibility rules when resolving package access.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 15:57:51 +08:00
bircni
adc3db1f27 fix(api): enforce organization listing token scope (#39041)
Enforce organization token scope before listing organizations and retain
public-only filtering.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 07:23:16 +00:00
bircni
55a5f50961 fix(actions): enforce workflow badge token scope (#39044)
Apply repository token-scope and public-only checks to workflow badges.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 06:59:58 +00:00
bircni
204c0bafd3 fix(repo): require organization owners for team access (#39046)
Require organization ownership before changing repository team
associations when team access is restricted.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 06:35:55 +00:00
bircni
bedd2afb47 fix(org): hide limited organizations from restricted users (#39047)
Do not expose limited organization memberships to restricted viewers.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 23:11:04 -07:00
GiteaBot
d8f0e7e679 [skip ci] Updated translations via Crowdin 2026-08-23 00:24:38 +00:00
Federico A. Corazza
1fa6465efd feat(actions)!: add RUN_RETENTION_DAYS to delete old action runs (#38855)
Gitea keeps completed Actions runs forever. Artifacts and logs expire on
their own schedule, but the run rows never go away, so `action_run` and
its child tables grow without bound.

Adds `RUN_RETENTION_DAYS` to delete completed runs along with their
jobs, tasks and anything the earlier expiries left behind. It defaults
to 400 days, matching how long GitHub keeps run history browsable. A
dedicated `cleanup_action_runs` cron task performs the cleanup, so
admins can schedule it separately from the nightly artifact and log
sweep.

`0` now means "keep forever" for all three retention settings, where
`LOG_RETENTION_DAYS` and `ARTIFACT_RETENTION_DAYS` previously took it
literally and deleted everything at the next sweep.

Docs: https://gitea.com/gitea/docs/pulls/502

----

## ⚠️ BREAKING ⚠️

`RUN_RETENTION_DAYS` defaults to 400, so completed runs older than that
are deleted when the cron task next runs at midnight. Set
`RUN_RETENTION_DAYS = 0` before upgrading to keep all runs.

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 21:32:59 +00:00
bircni
e6af4c341c fix(repo): preserve transfer recipient collaboration (#39042)
Remove temporary recipient access after a transfer ends while preserving
existing collaboration.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 19:43:42 +00:00
bircni
9251eeb66b fix(markup): enforce same-repository issue access (#39045)
Enforce Issues and Pull Requests access for references within the
current repository.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 19:13:53 +00:00
bircni
5fc3cec87f fix(actions): verify raw artifact signatures first (#39049)
Validate raw-artifact signatures before resolving the requested
artifact.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 18:47:55 +00:00
water
51e42d4b11 fix: drop queued job updates for deleted runs instead of requeueing forever (#39037)
When a repository is deleted while one of its Actions runs still has a
pending job update in the emitter queue, `checkJobsByRunID` returns an
error because the run no longer exists. The queue handler in
`jobEmitterQueueHandler` treats every error as unhandled and requeues
the item, creating an infinite retry loop that fills the log with error
messages.

### Changes

1. **`services/actions/job_emitter.go`** — swap the `!exist`/`err` check
order so a database error is reported first, then treat a non-existent
run as handled (nil error). The queue consumer drops the item instead of
requeueing it.

2. **`services/actions/job_emitter_test.go`** — add
`Test_checkJobsByRunID_DeletedRunIsHandled`, which verifies that a
deleted run produces nil (handled, not requeued).

### Related issue

Fixes #39034

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-08-22 13:55:08 +00:00
silverwind
66d6f74cb0 test: speed up tests, fix transaction bug (#39030)
Speed up tests: `make test-backend` 103s to 37s, `make test-integration`
908s to 852s.

Most of it is a detached system notice insert blocking on the SQLite
write lock until the busy timeout expired, and `ExternalServiceHTTP`
re-probing on every call with an untimed `http.Get`.

- fixed one correctness bug with nested transactions: files were deleted
while the outer transaction was open, so a later failure could roll the
database back with the files gone
- git push branch counts were far above the hook batch size
- Fix makefile dependencies so running tests and lint work in fresh
worktrees.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 11:04:35 +00:00
Lunny Xiao
cce2360846 build(release): use native golang toolchain for official release builds (#37828)
Official releases are built by Golang toolchain with CGO disabled.

For packagers who need to cross-compile with CGO, use "build" target
with proper TAGS/LDFLAGS/CGO_CFLAGS to make "$(EXECUTABLE)" target run
the "go build" command.

By the way, drop i386 arch support

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 10:38:21 +00:00
bircni
89c7019a3c fix(repo): limit gitignore template selections (#39027)
Bound gitignore template selections at both web and API request
boundaries before repository initialization.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 10:03:17 +00:00
SEONGHYUN HONG
84b67d50a6 fix(base): correct natural sort of numbers with leading zeros (#38163)
### Description

`NaturalSortCompare` (`modules/base/natural_sort.go`) compares two
numeric run parts by **raw string length**:

```go
if len(part1) != len(part2) {
    return len(part1) - len(part2)
}
```

"Longer digit string = larger number" only holds without leading zeros.
With zero-padded numbers the comparison inverts:

- `file0001` vs `file2` → claims `file0001 > file2`, but `1 < 2`
- `a08` vs `a9` → claims `a08 > a9`, but `8 < 9`

This affects any natural-ordered listing where zero-padded and shorter
unpadded numbers mix (branch/tag/file names, etc.).

### Fix

Strip leading zeros before comparing digit-count magnitude; on equal
magnitude fall back to collation, then to the original length so fewer
leading zeros sort first. Added a small `naturalSortTrimZeros` helper
(keeps one char so `"000"` → `"0"`).

Signed-off-by: Seonghyun Hong <s3onghyun.hong@gmail.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 09:40:49 +00:00
silverwind
fa0b39a42b fix(release): separate publication time from the release date (#36761)
`published_at` was an alias for `created_at`, so a release created from
an existing tag reported that tag's commit date as its publication time,
and drafts reported one despite never having been published. It is now
stored separately, set when a release is published and null for drafts.

`created_at` in turn means the date of the commit the release points at,
matching what GitHub documents it to be, and the latest release is
selected by it again. Publishing a release for an old commit no longer
takes over the latest badge, and a tag created in the web UI is dated
the same way as one pushed from the CLI.

Fixes https://github.com/go-gitea/gitea/issues/11206
Fixes https://github.com/go-gitea/gitea/issues/38714
Fixes https://github.com/go-gitea/gitea/issues/31789

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-08-22 08:59:37 +00:00
bircni
6bb6ce678b fix(api): hide limited users from restricted viewers (#39004)
Use the canonical profile-visibility check for user API content and
prevent restricted users from enumerating public repositories owned by
limited users.

This keeps feeds, heatmaps, keys, and issue search consistent with
profile visibility.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 08:08:36 +00:00
Lunny Xiao
f7072b0305 feat(api): list all packages for site administrators (#38968)
Add `GET /admin/packages` so site administrators can review packages
across every owner without querying each owner separately.

It returns the same package version representation as `GET
/packages/{owner}` and supports `page`, `limit`, `type`, and `q`
filters.

---
Assisted by Codet(DeepSeek)

---------

Signed-off-by: bircni <bircni@icloud.com>
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 09:29:25 +02:00
silverwind
2d6fea5bdf enhance: add permalinks to pull request reviews (#38849)
1. Make review threads linkable via `#pullrequestreview-<reviewID>`
2. Improve CSS so username and timestamp go colored on hover.
3. CSS cleanup, remove dead rules, nonexistant class name, make `.suppressed` actually do what it says in the doc above.
2026-08-22 02:13:43 +00:00
bircni
1f349eb1eb fix(migrations): cancel GitLab version probes (#39023)
Bind the GitLab version probe to the migration context so a cancelled
migration does not remain blocked on a remote response.
2026-08-22 01:54:07 +00:00
wxiaoguang
d2bc0097bc fix: make local queue PopItem can be notified (#39011) 2026-08-22 01:30:41 +00:00
bircni
5eba4f92ce fix(migrations): bound OneDev version responses (#39024)
Limit OneDev version responses before parsing so a remote server cannot
make a migration retain an unbounded response.
2026-08-22 00:54:20 +00:00
bircni
aa96725ae7 fix(packages): limit Swift package manifests (#39025)
Bound the number and aggregate size of Swift manifests retained from an
uploaded archive.
2026-08-22 00:11:29 +00:00
bircni
fa4c0b1cf6 fix(packages): limit Maven checksum uploads (#39028)
Bound checksum uploads to the maximum usable digest length before
buffering their content.
2026-08-21 23:41:12 +00:00
bircni
2b81cbbe70 fix(packages): bound Alpine metadata entries (#39026) 2026-08-22 01:17:04 +02:00
bircni
ad05aaee80 fix(actions): enforce fork pull request trust boundaries (#39005)
Preserve fork pull request restrictions across review-triggered
workflows, reusable workflow access, job scheduling, and filtered
workflow statuses.

This prevents untrusted fork workflow content from bypassing approval,
accessing private reusable workflows, or satisfying protected status
checks.


_Assisted-by: Codex:GPT-5_
2026-08-21 12:35:28 +00:00
bircni
a52e5f53c0 fix(git): restrict hook permissions (#39008)
Create delegate hook files and directories without group or other write
access, including correcting existing hook directories.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:52:23 +00:00
bircni
db24633e6d fix(api): enforce repository creation token authorization (#39007)
Reject public-only tokens for repository migrations and require
repository scope for canonical organization repository creation. This
aligns both routes with the existing token authorization boundaries.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:28:23 +00:00
bircni
920b5f1e68 fix(api): enforce public-only scope for compare heads (#39006)
Enforce public-only token scope for repositories resolved as compare
heads.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:06:56 +00:00
bircni
7306d5aff8 fix(repo): hide repositories of hidden owners (#39009)
Exclude public repositories owned by hidden individual accounts from
broad repository listings, while preserving visibility through explicit
access and ownership.

_Assisted-by: Codex:GPT-5_
2026-08-21 06:32:57 +00:00
GiteaBot
fcc23af280 [skip ci] Updated translations via Crowdin 2026-08-21 01:49:11 +00:00
Julian Scholle
fe567d26c1 fix(actions): allow larger scheduled workflows (#38985)
MySQL stores `action_schedule.content` as `BLOB`, limiting scheduled
workflow definitions to 65,535 bytes. Oversized workflows fail schedule
refresh and can also suppress default-branch push handling.

Store scheduled workflow content as `LONGBLOB`, migrate existing MySQL
columns, and cover the migration by persisting 65,536 bytes.

Fixes https://github.com/go-gitea/gitea/issues/38613
2026-08-21 01:06:44 +02:00
bircni
ffd982c7ab fix(actions): show "Complete job" logs when the last step is skipped (#38939)
`FullSteps` only gave the synthetic "Complete job" step the remaining
log range when the last step that had run was also the final step of the
job. A skipped step does not count as having run, so any job ending in a
skipped step left the post step with an empty range: its logs were
stored but never rendered, and the duration showed as `0s`.

Reproducible with any job whose last step is skipped, which is common
for failure notifications:

```yaml
steps:
  - run: echo hello
  - run: echo never
    if: failure()
```

The gate now checks whether the final step is done, which preserves the
behaviour from https://github.com/go-gitea/gitea/pull/29926 of showing
the post step as waiting while steps are still pending.
--> Regression from https://github.com/go-gitea/gitea/pull/29926

---------

Signed-off-by: bircni <bircni@icloud.com>
2026-08-20 19:06:21 +00:00
Lunny Xiao
475e51c7e0 fix: avoid enumerating every public repository in issue search (#38992)
Both issue search endpoints resolve their repository filter with
`SearchRepositoryIDs` and pass the result to the indexer as `RepoIDs`.
They mean
to leave public repositories to the indexer, but
`SearchRepoOptions.AllPublic` is
only read when `OwnerID > 0`, so without an `owner` filter the flag does
nothing
and every public repository is enumerated, without a `LIMIT`, into
`repo_id IN (...)`.

Those IDs are redundant, as `allPublic` is passed to the indexer, which
already
matches every public repository. On a large instance this binds tens of
thousands
of parameters and can fail in the driver, making the endpoint return 500
for every
filter. Admins are worst hit, as `SearchRepositoryCondition` skips their
accessible-repository condition and enumerates the whole table.

Restrict the enumeration to private repositories. The result set is
unchanged, as
the dropped IDs are a subset of what `allPublic` matches.

Both endpoints held copies of this block, so it moves to
`routers/common`.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-20 16:29:54 +00:00
wxiaoguang
943f026844 refactor: deploy key and private route handlers (#38999)
clean up legacy code, fix various bugs:

* add missing "return"
2026-08-20 15:57:17 +00:00
silverwind
61be9fcdfa chore: update eslint and stylelint configs and re-sync modern-normalize (#38982)
- update the vendored `modern-normalize` to v3.0.1
- require descriptions for lint disables in TS and CSS, same as we
already have in Go.
- disable core rules covered by `regexp/*` and `unicorn/*`, and ones
that cannot fire
- stop applying vitest rules to the playwright files in `tests/e2e`
- enable 7 stylelint rules, mostly `no-unknown` and `no-invalid` checks
- drop 2 unnecessary vendor prefixes (safari v17+, chrome v120+)
- look up ids via `querySelector` with `CSS.escape` instead of
`getElementById`
- remove stale doc about `@ts-expect-error`, it's forbidden
- misc dev doc fixes

Every declaration that `modern-normalize` v3 removes was checked against
chromium, webkit and firefox defaults first. The `hr` color and the
`:-moz-focusring` outline are kept as documented deviations, dropping
those does change rendering.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-20 09:57:56 -04:00
wxiaoguang
c778c6c920 enhance: use browser's locale to detect week's first day for the contribution map (#38995)
fix #6058

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-20 07:10:15 +00:00
bn-zr
fa5d876171 fix(actions): Fix how jobs in matrixes are grouped (#38980)
The workflow graph decided which job rows belonged to the same matrix by
parsing display names: it stripped a trailing `" (...)"` off `name` and
grouped rows sharing the prefix. That guesses at a string the user
controls, and it fails both ways. `jobparser` only appends the `
(<combination>)` suffix when `name:` contains no `${{ }}`, so a leg
named `E2E on ${{ matrix.browser }}` never grouped, while two unrelated
jobs `build (fast)` and `build (slow)` folded into one bogus matrix
panel.

Matrix legs already have a real identity: expansion clones one row per
combination, all sharing the workflow's `JobID` and differing only in
`Name`. Group on that instead, so a matrix is whatever the backend says
it is. Matrix expansion state is keyed on the graph node id for the same
reason.

Closes https://github.com/go-gitea/gitea/issues/38975, though that
report's own example already groups on main, since `explicit (${{
matrix.leg }})` interpolates to a name that still ends in a suffix. The
interpolated shapes above are the broken ones.

Assisted-by: Claude Code:claude-opus-5
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-20 05:41:14 +00:00
silverwind
8f7eb9f161 enhance(ui): forced colors mode enhancements (#38991)
Improve various UI elements while in [forced color
mode](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/forced-colors).
2026-08-20 05:11:57 +00:00
silverwind
ed4d7ea08d fix: resolve YAML anchors and aliases in Actions workflows (#38984)
Workflows using YAML anchors are rejected as invalid, because a workflow
is split into one document per job and an alias whose anchor lands in
another job's document no longer resolves.

Aliases are now expanded once, right after the workflow is parsed and
before anything reads or splits it, bounded like GitHub's parser so
nested aliases cannot expand without limit. Merge keys stay unsupported,
as they are upstream.

Fixes https://github.com/go-gitea/gitea/issues/38983
Signed-off-by: silverwind <me@silverwind.io>
2026-08-20 05:03:10 +00:00
GiteaBot
89b891b168 [skip ci] Updated translations via Crowdin 2026-08-20 01:44:24 +00:00
Iaroslav
35786a6ca1 fix(lfs): ensure lock listing paginates with a total order (#38850)
`GetLFSLockByRepoID` applies `LIMIT`/`OFFSET` to a query with no `ORDER
BY`. The order of such a query is unspecified (according to the SQL
standard), so the resulting queryset might be inconsistent.

These locks AFAIK are never updated, so in practice the order is
insertion-based, but that's not guaranteed.
2026-08-19 11:54:21 -07:00
silverwind
4e813a262f fix: resolve actions commit status permission per repository (#38977)
Various pages did not display the correct action run list tooltips. Fix
those tooltips like here on the `/pulls` page:

`ctx.Repo.Permission` is the zero value outside a repository route, so
on `/pulls`, `/issues`, `/notifications/subscriptions` and the dashboard
repo list the commit status "Details" link was always stripped. The live
job status is looked up from that target URL, so running checks also
rendered as a static pending dot instead of a spinner.

Resolve the Actions unit permission per repository instead.

Also drops the releases page's gate on *loading* statuses, which hid
external CI results from anyone without Actions read; it now loads them
and hides only the URL, like every other page.

Co-authored-by: bircni <bircni@icloud.com>
2026-08-19 20:09:00 +02:00
Giteabot
e355c39e91 chore(deps): update dependency go to v1.26.7 (#38987) 2026-08-19 16:32:52 +00:00
wxiaoguang
f261adb53f chore: form binding trim space (#38978)
Use "binding:TrimSpace" instead of fragile IsEmptyString

And fix a bug in locale's `HasKey`: it should also try the default
language if current language doesn't have the translation key, a new
test is added.
2026-08-19 12:50:06 +00:00
APZN
c121f02a7e fix: honor environment variables during install (#38974)
Environment variables must be applied to the "install form" config
before the config values are used.

Fixes #38911

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-19 15:19:49 +08:00
wxiaoguang
6904f6480c refactor: http request binding (#38971)
Better than before, still not good enough (more work can be done in the
future)

And add the missing error handling in the PrivateContext "bind"
middleware.

By the way, picked some "TrimSpace" changes from "fix: trim whitespace
from SMTP address and port - #38934" (fix #38926)
2026-08-19 14:15:42 +08:00
GiteaBot
6c425fae6e [skip ci] Updated translations via Crowdin 2026-08-19 01:45:26 +00:00
wxiaoguang
5433c23dec enhance(ui): tint toast backgrounds by level (#38919)
Toasts now use the same tinted backgrounds and borders as the flash
messages, replacing the solid full-color style. The first commit reverts
https://github.com/go-gitea/gitea/pull/38842, the second re-applies it
with tinting.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-18 21:13:30 +02:00
silverwind
83af7aa92e ci: improve caching (#38958)
- only `cache-seeder` writes caches, every other workflow restores.
Saves were being rejected once the repo went over its cache budget,
leaving main's caches stale and PR runs building cold
- seed the pnpm store and uv caches next to the go ones, so PRs
warm-start on them rather than installing from scratch
- prune keeps a single generation per key, including across go versions,
where a toolchain bump leaves the previous build cache unusable.
Reclaims ~2.6 GB immediately
- prune runs every 6h instead of daily and trims to 6 GB, since CodeQL
writes ~200 MB per push to main from outside this repo's workflows
- pull requests and release branches no longer write pnpm, uv and binfmt
caches, whose ref-scoped copies are never read again

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-18 18:26:03 +00:00
wxiaoguang
c95e3f3b00 refactor: private endpoints (#38964)
1. remove dead code (SetDefaultBranch)
2. remove useless and unsafe code (AddLogger)
2026-08-18 08:51:47 +00:00
water
c082b9a5ff fix: grant limited-org unit read access to authenticated non-members (#38871)
Fixes #38870

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-18 06:58:37 +00:00
bircni
e8e1973e16 fix: allow anonymous theme switching when REQUIRE_SIGNIN_VIEW is set (#38956)
Fixes https://github.com/go-gitea/gitea/issues/38950


Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-18 04:02:52 +00:00
GiteaBot
19ee791fe7 [skip ci] Updated translations via Crowdin 2026-08-18 01:43:54 +00:00
silverwind
3842e021e0 fix(actions): drop wrapper span around the action status icon (#38957)
Fixes https://github.com/go-gitea/gitea/issues/38955
2026-08-17 22:46:49 +00:00
silverwind
df71d5f5e2 test: run frontend unit tests in browsers (#38860)
Run them in headless [vitest browser
mode](https://vitest.dev/guide/browser/) in chromium and firefox.
Similar UX than current tests, it's about 5 times as slow (goes from 1s
to 5s on my machine), but definitely worth it as it removes all
happy-dom problems.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-17 22:22:54 +00:00
Minjie Fang
55e7cafcb6 chore(maintainers): add wingsallen as maintainer (#38913)
[PRs](https://gitea.com/gitea/tea/commits/branch/main/search?q=wingsallen&all=)

@wingsallen on gitea.com
2026-08-17 22:03:18 +00:00
bircni
ed4a23e893 enhance: inherit team access for all units (#38938)
Admin and write team authorize now grant that mode on every unit,
including units added later, instead of only rows present in
`team_unit`. Granular teams keep `authorize=none` and explicit unit
rows.

Closes the `TEAM-UNIT-PERMISSION` design gap from
https://github.com/go-gitea/gitea/pull/34128.

Maybe also fix #15962 (actually maybe it had been fixed before, the root
cause is out-of-sync "access" table)


## Screenshots

only writing selected:
<img width="1399" height="1007" alt="image"
src="https://github.com/user-attachments/assets/1d1b4c49-a59a-47b6-998f-0464a067395b"
/>


_Created with the help of AI_

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 20:30:24 +00:00
bircni
1cf904f101 feat(repo): add quick repository switcher to repo header (#38188)
Add a GitHub-style quick repo switcher: a caret next to the owner/repo
breadcrumb
opens a dropdown that lists and searches the current owner's
repositories and
navigates to the selected one. The current repository is marked with a
check, and
private/fork repos show an icon.

Also, fix various bugs in fomtantic dropdown remote query

## Screenshots

<img width="505" height="198" alt="image"
src="https://github.com/user-attachments/assets/9f673d1b-fe60-41f0-b9e2-b00dc43720b5"
/>

Fixes #38187

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 20:08:15 +00:00
Giteabot
e223c42ee6 fix(deps): update module golang.org/x/mod to v0.40.0 [security] (#38914) 2026-08-17 19:29:07 +00:00
Hsukqi Lee
551a6bb3a4 fix(issues): sort scoped labels by exclusive order in dropdowns (#38893)
Closes #38872

Labels in the label selection dropdown (issue/PR sidebar, new issue
form) were always listed alphabetically, so a scoped set like the
default Priority labels showed up as Critical, High, Low, Medium even
though each label carries an exclusive order.

This adds `CompareLabelForDisplay`/`SortLabelsForDisplay` in
`models/issues`: labels are grouped by their exclusive scope and sorted
by exclusive order within a scope (unordered ones last), falling back to
name order. The sorting is applied to the issue page sidebar data and
the shared label filter data, so the filter dropdown on the issue list
gets the same ordering.

Unscoped labels are unaffected and still sort by name. Includes a unit
test covering the default Priority label set.
2026-08-17 18:47:28 +00:00
Mitrahsoft
7857c5f843 feat(user): Personal access tokens can be regenerated (#38907)
Lets users regenerate a personal access token's value in place, keeping
its name and scopes, instead of deleting and recreating it. Useful when
a token was shared with a third party (e.g. an AI agent) and needs to
be invalidated immediately without redoing scope selection.

Follows the same pattern already used for OAuth2 application client
secrets (`GenerateClientSecret`/`RegenerateSecret`).

**Testing**: added a model unit test and a web integration test;
manually
verified in the running dev server that the old token stops
authenticating
and the new one works immediately after regenerating.

<img width="1040" height="245" alt="image"
src="https://github.com/user-attachments/assets/4de0d8b4-1fc4-49cf-a859-95e24d0b2c0a"
/>

Fixes #38683.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 18:17:16 +00:00
silverwind
346e6bab67 ci: install node for renovate post-upgrade tasks (#38953)
Containerbase declares `node` as the parent of `pnpm`, so `install-tool
pnpm` aborts with `MissingParent` (exit 16) when node was never
installed as a containerbase tool. Renovate's npm manager installs node
itself, so this only breaks on branches without an npm update, where the
failed install blocks every post-upgrade command including `make tidy`,
leaving an untidy `go.sum` behind.

Seen on https://github.com/go-gitea/gitea/pull/38914.

Verified in `ghcr.io/renovatebot/renovate:latest`:

```
install-tool pnpm 11.22.0                    exit=16
  FATAL: parent tool not installed  tool: "pnpm"  parent: "node"
install-tool node 22.18.0 && install-tool pnpm 11.22.0   exit=0
```
2026-08-17 10:51:09 -07:00
wxiaoguang
dea71bb8ba enhance: user-friendly packages setup manual (#38946)
* replace #35564
* fix #36992
2026-08-17 17:43:34 +00:00
Giteabot
596b7f7a25 chore(deps): update dependencies (#38947) 2026-08-17 15:10:17 +02:00
Mitrahsoft
1b21c8a1e6 fix(indexer): correct bleve indexer token filters (#38853)
* fix #36228
* fix #37221

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 07:59:41 +00:00
GiteaBot
3cd6672e48 [skip ci] Updated translations via Crowdin 2026-08-17 00:22:34 +00:00
wxiaoguang
63f2918336 fix: make "login_name" field optional for API edit user (#38917) 2026-08-16 13:39:28 +00:00
wxiaoguang
5e4d21acd5 chore: fix repo watch (#38921) 2026-08-16 03:00:59 +00:00
GiteaBot
56ad4689ad [skip ci] Updated translations via Crowdin 2026-08-16 00:24:11 +00:00
Giteabot
133a3b8567 fix(deps): update module golang.org/x/image to v0.45.0 [security] (#38930)
Co-authored-by: bircni <bircni@icloud.com>
2026-08-15 15:43:20 +02:00
bircni
2b8ea5476c fix(ui): respect FEED_PAGING_NUM on the dashboard feed (#38935)
The dashboard activity feed was paginated with `[ui.user]
REPO_PAGING_NUM`
instead of `[ui] FEED_PAGING_NUM`.

The wrong setting was picked up when the page size was hoisted into a
local
variable in https://github.com/go-gitea/gitea/pull/34994, most likely
copied
from the `dashboardRepoList` block a few lines above. `REPO_PAGING_NUM`
should
only control repository lists.

Fixes https://github.com/go-gitea/gitea/issues/38925
2026-08-15 19:31:10 +08:00
GiteaBot
43ace7cc8a [skip ci] Updated translations via Crowdin 2026-08-15 00:22:56 +00:00
bircni
a96a73c364 docs: Update CHANGELOG for version 1.27.2 (#38923)
Signed-off-by: bircni <bircni@icloud.com>
2026-08-14 22:06:05 +02:00
Lunny Xiao
07843086c2 ci: remove AWS S3 uploads from release workflows (#38928)
Release binaries and downloads have been served from Cloudflare R2 for a
while now, so the AWS S3 upload is redundant.

This removes the `configure aws` and `upload binaries to s3` steps from
the nightly, RC and version release workflows. Since
`configure-aws-credentials` no longer runs in those jobs, the
`AWS_REGION: auto` workaround in the R2 step can be dropped as well.

The `AWS_*` secrets for S3 can be removed from the repository settings
afterwards.
2026-08-14 21:50:27 +02:00
Giteabot
5b7b00477a chore(deps): update dependency go to v1.26.6 (#38912) 2026-08-14 10:28:00 -07:00
bircni
dbe311197c enhance(admin): show impersonation banner and keep password change with the user (#38924)
Follow-up to https://github.com/go-gitea/gitea/pull/38614

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-14 22:23:30 +08:00
wxiaoguang
b6368965fb refactor: wiki edit form (#38918)
1. the fragile `document.querySelector('.repository.wiki.new
.ui.form')!` is broken (again), rewrite to "data-global-init"
    * regression from #37571 because a new form was added
3. use "form-fetch-action" and JSON response instead of
"RenderWithErrDeprecated"
2026-08-14 11:31:23 +02:00
Mitrahsoft
befeacdf7d docs(api): document 401/403 responses for user key endpoints (#38711) 2026-08-14 02:55:44 +00:00
wxiaoguang
72a9debaff refactor: clean up form binding & validation (#38873)
Clarify the "validation" and "error display" logic.

All the copied&pasted `Validate` functions are removed.
2026-08-14 02:15:33 +00:00
GiteaBot
8b40df255b [skip ci] Updated translations via Crowdin 2026-08-14 00:37:15 +00:00
Ban Yongping
68feaba2ed fix(migrations): use all configured GitHub tokens (#38841)
GitHub migrations accept multiple comma-separated OAuth tokens, but
clients with unknown rate data are never selected. After the first
client is used, every later token stays unknown and can never
participate in quota-aware selection.

Select each client with unknown rate data once before falling back to
the existing highest-remaining-rate choice. The regression test covers
initial probing of all clients and then selection by remaining quota.

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

Assisted-by: Codex:GPT-5

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-13 14:46:45 +00:00
wxiaoguang
d2be79a942 fix: update collaborator access mode (#38894)
There is already AddOrUpdateCollaborator, don't duplicate the code.
2026-08-13 09:54:33 +00:00
bircni
01e9febbea fix(actions): keep github.event.inputs as strings for workflow_dispatch (#38899)
`github.event.inputs` must mirror the raw `workflow_dispatch` payload,
where
GitHub keeps every input as a string. Only the separate `inputs` context
preserves declared types, e.g. booleans. A previous fix coerced boolean
inputs in the single map that fed both contexts, so
`github.event.inputs.someBool` became a real boolean and comparisons
like
`== 'true'` stopped matching.

`github.event.inputs` now stays string-only again. The `inputs` context
used
for server-side `if:` evaluation of needs-gated/matrix-deferred jobs
re-coerces booleans independently, from the job's own workflow
declaration,
so that path keeps working correctly.

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

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-13 09:36:41 +02:00
GiteaBot
5287860efb [skip ci] Updated translations via Crowdin 2026-08-13 00:36:53 +00:00
Zettat123
c186cc4b8d fix(actions): let a rerun of selected jobs read the previous attempt's artifacts (#38857)
Fixes #38773

## Background

Artifacts became attempt-scoped in #37119, and the runner-facing
artifact APIs filter strictly by the attempt of the running job. "Re-run
failed jobs" creates a new attempt whose passed-through jobs never
upload their artifacts again, so a re-run job that downloads one of them
fails with "artifact not found".

## Fix

The read paths (v3 and v4 list and download) now resolve artifacts
across the running job's attempt plus the attempts it inherits from, and
an inherited artifact is shadowed by a same-named one from a newer
attempt.

## Note

GitHub's documentation does not document these behaviors. The
conclusions below are based on manual testing, so consistency with
GitHub cannot be guaranteed.

- In a "partial re-run", a job can download artifacts uploaded by an
earlier attempt, every attempt keeps its own copy of a name, and a
lookup by name resolves to the newest one.
- A full "Re-run all jobs" never downloads artifacts from earlier
attempts.

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-08-12 19:55:26 +00:00
wxiaoguang
53d7d3f053 refactor: external render (#38885)
make the "command variable replacement" more accurate and
OS-independent, add a test for it.
2026-08-12 19:30:26 +00:00
bircni
8161479fde fix(actions): resolve pull_request_target reusable workflows at the base commit (#38886)
For a `pull_request_target` (PRT) run, Gitea loads the top-level
workflow from the trusted base branch, but any local reusable workflow
it calls (`uses: ./...`) was read from the PR **head** commit, which the
fork author controls.

## Fix

**Record the source commit where the content is read.**
`DetectedWorkflow` now carries a `SourceCommitSHA` filled in next to
`Content`, so the PRT detection pass at the base commit records the base
SHA automatically.

**Defense in depth.** `loadReusableWorkflowSource` pins the PR base
commit for a PRT run's local `uses: ./...` rather than trusting the
stored SHA. This also covers runs recorded before this change, whose
rows still hold the head SHA and would otherwise resolve from the fork
on rerun.

Existing run rows are not migrated.

---------

Co-authored-by: Zettat <zettat123@gmail.com>
2026-08-12 19:03:44 +02:00
bircni
2551f9949a enhance(repo): add default object format setting (#38877)
Adds `[repository] DEFAULT_OBJECT_FORMAT` to default new repositories to
`sha1` or `sha256`.

Applies the setting to repository creation defaults in the UI and API,
reducing repeated manual selection.

Docs: https://gitea.com/gitea/docs/pulls/504

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-12 16:12:57 +00:00
Lunny Xiao
3f833fd681 chore: Pre-register a builtin OAuth2 application for the official Gitea mobile app (#38880)
This is a prepare and required step for upcoming Gitea Official Mobile
APP which supports login with OAuth2.

The official Gitea mobile app needs the same mechanism. This adds a
builtin application for it:

| | |
|---|---|
| client ID | `b757811a-05c8-4c76-8d74-a5ee3d2073f2` |
| config name | `gitea-app` |
| display name | `Gitea App` |
| redirect URI | `com.gitea.app://oauth/callback` |

Unlike the existing entries, which are CLIs and can therefore use a
loopback `http://127.0.0.1` redirect, a mobile app authorises through a
system browser session (`ASWebAuthenticationSession` on iOS, Custom Tabs
on Android) that can only receive a custom-scheme callback, hence the
custom scheme here.
2026-08-12 14:34:22 +00:00
bircni
300331313b docs: Adjust Changelog to include all Security relevant details (#38876)
Signed-off-by: bircni <bircni@icloud.com>
2026-08-12 11:56:51 +00:00
silverwind
f8d2d79394 fix(server): set ReadHeaderTimeout on HTTP servers (#38878)
Add `ReadHeaderTimeout` which limits how long a client can take to send HTTP headers.
2026-08-12 10:42:26 +00:00
GiteaBot
9b24e8c76d [skip ci] Updated translations via Crowdin 2026-08-12 00:35:36 +00:00
somaz
3580591e28 fix(repo): avoid a repo-sized temp file for every bundle download (#38863)
Bundle downloads run `git bundle create` into a temp file under
`data/tmp/git-repo-content` and copy it to the response, so every
download puts a second, repo-sized copy on disk before the first byte is
sent. If the process dies mid-request that copy is stranded: the startup
sweep only drops files older than 3 days, and cannot remove a directory
that still holds a newer file.

Streaming `git bundle create -` to the response removes that copy.
`CreateArchive` above already uses the same gitcmd pattern, and the
bundle bytes are unchanged (existing integration assertions on the exact
length still pass).

One consequence: git can now fail after output starts, so a mid-stream
failure truncates the body instead of returning an error.

I did not act on the TODO. A temp ref only works under `refs/heads/*`;
with `refs/bundle/temp-*` the bundle carries no branch and clones empty,
so I noted that on the TODO.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-11 16:33:15 +00:00
wxiaoguang
938cde959e refactor: markup render (#38864)
1. add missing CSP header to api & web render endpoints.
2. make jupyter render skip post-processors, nothing to process
3. make ShortLinkProcessor correctly validate URL schemes and respect
the CustomURLSchemes setting
2026-08-11 18:01:02 +02:00
Pachink0
eeeb5d7c7b fix(lfs): accept successful transfer responses (#38866)
Accept all 2xx LFS transfer responses so uploads returning 201 Created
are not handled as errors and decoded as empty error bodies.

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

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-11 11:47:20 +02:00
techknowlogick
88b2b84cf0 chore: bump pnpm to v11 in nix flake (#38856) 2026-08-11 07:38:42 +00:00
wxiaoguang
b3f547ac16 refactor: api token scope check (#38862)
"ApiTokenScope" already means "IsApiToken=true".

All "IsApiToken" should be removed.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-11 09:37:44 +02:00
GiteaBot
7e3eeca779 [skip ci] Updated translations via Crowdin 2026-08-11 00:31:26 +00:00
silverwind
e3ee28f15b fix(avatar): use sha256 and inline the federated avatar lookup (#38843)
- Hash emails with sha256. Gravatar moved to sha256, and both it and
libravatar.org serve the same image for either hash.
- Drop `strk.kbt.io/projects/go/libravatar` for a 46 line inline SRV
lookup. It could not bound or cancel its DNS query and panicked on an
unexpected resolver error. The replacement carries the request context
and a 3s timeout.
- Fix federated avatars querying DNS for every avatar on every render.
`loadAvatarSetting` compared a cache field that was never assigned, so
each call rebuilt the resolver and dropped its cache. That cache is
gone, both settings are read where they are used.
- Migration 348 recreates `email_hash` with a 64 char hash column and a
`hash_type` column, so a later algorithm change can tell old rows apart.
The MD5 rows are unreachable and their `UNIQUE` email index would reject
the SHA256 replacements.
- Fix a re-saved avatar form replacing an uploaded avatar with a random
one.
- Remove the `duoshuo` `GRAVATAR_SOURCE` alias, that service shut down
in 2017.
- Remove dead i18n key.

Fixes: https://github.com/go-gitea/gitea/issues/34284
Fixes: https://github.com/go-gitea/gitea/issues/28110
Docs: https://gitea.com/gitea/docs/pulls/499
Signed-off-by: silverwind <me@silverwind.io>
2026-08-10 23:13:28 +00:00
Vitalii Tverdokhlib
52d0e18dac enhance(actions): set ref_protected in context (#38852)
## Problem

The github.ref_protected Actions context value was hard-coded to false,
even when Gitea has a matching protected-branch or protected-tag rule.

That prevents policy-driven deployment workflows from relying on Gitea
as the source of truth. A deployment runner or external identity
provider may require a protected ref before releasing credentials. The
workaround is an exact-ref allowlist outside Gitea, which duplicates
repository protection policy and can drift when rules change.

## Solution

Resolve configured protection rules for branch and tag refs.
Non-branch/tag refs remain false; lookup failures are logged and
conservatively return false.

This changes the Actions context only; it does not add Actions OIDC
issuance.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2026-08-10 22:55:29 +02:00
Giteabot
a8fe401613 chore(deps): update dependencies (#38851) 2026-08-10 15:50:32 +02:00
silverwind
26e373542a chore: drop make update* targets (#38845)
We no longer need these with renovate and if desired I run `updates` via
global command, e.g. no need to keep it a dependency.
2026-08-10 08:43:03 +00:00
wxiaoguang
9c915e4e1a refactor: form binding validation (#38832)
Make "form-fetch-action" highlight the invalid field as "error"
2026-08-10 01:25:31 +00:00
GiteaBot
b5aa8c4ff0 [skip ci] Updated translations via Crowdin 2026-08-10 00:32:58 +00:00
silverwind
7ac505ff0d enhance(ui): restyle toasts (#38842)
Restyle toasts, add success toast to match the alerts.

<img width="500" alt="Screenshot 2026-08-09 at 11 33 44"
src="https://github.com/user-attachments/assets/d36422b8-0030-41dd-8056-b2d10d86280d"
/>

Alternatively we could also tint to match the alerts, but I do prefer
untinted.

<img width="500" alt="Screenshot 2026-08-09 at 11 34 44"
src="https://github.com/user-attachments/assets/73fcfde4-618b-424f-9aa9-4ada5c2904f6"
/>

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-08-09 22:17:28 +02:00
TheFox0x7
85558c28fc feat(packages): add support for uploading helm provenance files (#36695)
Adds `POST helm/api/prov` endpoint for helm repository allowing for upload of provenance files.

Tested manually to a degree but I really didn't want to mess with gpg
again so I'm not sure if helm will correctly verify the chart.
Initial draft made by gemini 3 flash but was finetuned somewhat.

Additionally there's an route that allows for upload of both files via
/api/charts - as separate files in form. If there's any interest in that
I guess it can be added but I think helm is moving to OCI anyway which
we support.

Fixes: https://github.com/go-gitea/gitea/issues/36678
Co-authored-by: silverwind <me@silverwind.io>
2026-08-09 12:55:43 +00:00
TheFox0x7
7e34eae370 chore: rename migration package for next release to v28 (#38844) 2026-08-09 12:00:39 +00:00
silverwind
ecbef41c06 refactor: replace gliderlabs/ssh with golang.org/x/crypto/ssh (#38837)
Migrate away from this thin ssh wrapper module while adding more test
coverage.

Removes `sessionPartial`, which hand-copied the layout of a private
`gliderlabs/ssh` struct and reinterpreted a pointer to it via
`reflect.UnsafePointer` to reach the permissions of the authenticated
connection. The layout is unchecked, so an upstream field reorder would
mismatch silently.

The builtin server only needs the session channel with `exec` and
`shell`. Serving those on `x/crypto` drops the hack and the dependency,
since `PublicKeyCallback` returns permissions per key and `x/crypto`
assigns them only after verifying the signature.

Two benign behavior changes:

1. Internal session handler errors report exit status 1 rather than 0,
so a client no longer reads a failure as success.
1. An unusable host key is fatal at startup instead of being replaced by
an ephemeral one that would trigger an error at the client.
2026-08-09 11:32:50 +00:00
Sergio Benitez
79535f4e01 fix(gitdiff): render exact-limit diffs and zero-limit comments (#38838)
This commit fixes the treatment of various edge cases related to diff
limits.

- Exact-limit diffs: a file containing exactly the configured maximum
was incorrectly treated as too large and hidden behind “Load diff.” It
now renders normally.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-09 11:13:22 +00:00
silverwind
ad6107ab88 enhance: refine repo watching (#38835)
Follow-up to https://github.com/go-gitea/gitea/pull/37571.

"Participating and mentions" deleted the watch row, so choosing it
dropped you out of the watcher count. It is a watch like the others, so
it now keeps a row and simply subscribes to no events.

The dashboard feed ignored the per-event options, so a "Custom: issues"
watcher still got pull request activity there. It now gates on the same
options as mail and notifications. That also closes a gap where pull
request reviews bypassed the permission check.

Also, address
https://github.com/go-gitea/gitea/pull/37571#discussion_r3740487363 and
reword a UI text for clarity.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-09 10:54:31 +00:00
silverwind
76a81b24f9 chore: enable forcetypeassert linter, fix issues (#38804)
Enable [`forcetypeassert`](https://github.com/gostaticanalysis/forcetypeassert)
linter to prevent unchecked type assertions. ~650 issues fixed, most
fixes were clean, some use `setting.PanicInDevOrTesting`.

The only behaviour changes are where code would previously send a 500 error
or panic, a 4xx error is now emitted.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-09 10:25:06 +00:00
silverwind
fac8bf2eca ci: install tools for renovate post-upgrade tasks (#38815)
Renovate installs a toolchain only for the managers a branch actually
updates. A branch without a gomod update therefore has no `go` on
`PATH`, and `make tidy`
[fails](https://github.com/go-gitea/gitea/pull/38813#issuecomment-5211013198).

Fix by installing those tools. `node` is not needed because it comes
with renovate.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-09 10:04:14 +00:00
silverwind
0282bf861f chore: update AGENTS.md (#38833)
Few small tweaks that should improve the quality:

- Make agents read `docs` folder
- Shorter comments, shorter PR descriptions
- Modern Go features

Signed-off-by: silverwind <me@silverwind.io>
2026-08-09 09:37:43 +00:00
GiteaBot
fecd2f3bc2 [skip ci] Updated translations via Crowdin 2026-08-09 00:32:02 +00:00
silverwind
7fae3d5db3 feat(api): add project APIs (#38691)
Adds REST APIs for project boards for repo, org and user scopes, using
as much shared code as possible for all 3 scopes.

Fixes: https://github.com/go-gitea/gitea/issues/14299
Fixes: https://github.com/go-gitea/gitea/issues/31769
Fixes: https://github.com/go-gitea/gitea/issues/35921
Replaces: https://github.com/go-gitea/gitea/pull/37518
Replaces: https://github.com/go-gitea/gitea/pull/36008
Replaces: https://github.com/go-gitea/gitea/pull/28111
Replaces: https://github.com/go-gitea/gitea/pull/31768
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Supen.Huang <supen.huang@qq.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ember <ember@mubergacres.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: beardev-in <abhinav.edulakanti@gmail.com>
2026-08-08 13:53:28 +00:00
Eduard Dzhumagaliev
8163139ec0 fix(packages): ignore nested Package.swift (#38788)
Nested `Package.swift` files overwrote the real package manifest. The
parser matched on the base name and kept the last entry in ZIP order.

`apple/swift-collections` ships `Benchmarks/Package.swift` and
`Utils/Debugger/FormatterFixtures/Package.swift`. The latter sorts after
the root `Package.swift`, so the registry stored a fixture manifest with
the wrong `swift-tools-version`, causing a toolchain mismatch and a
failed build. GRDB, swift-markdown, swift-syntax, sentry-cocoa and
SDWebImage share this layout.

The parser now keeps only manifests from the shallowest directory
holding one. That covers both a package at the archive root and the
single top level directory `swift package archive-source` produces. At
equal depth the first directory by name wins, so an archive always
yields the same metadata.

A nested manifest above the size limit no longer rejects the upload.

Co-authored-by: silverwind <me@silverwind.io>
2026-08-08 13:20:02 +00:00
silverwind
25350d0c79 enhance: add missing npm package metadata properties (#38826)
The npm packument left `time`, `keywords` and `maintainers` empty
although the data was available. `created` and `modified` are derived
from the versions currently served, as there is no package-level
timestamp to read them from.
2026-08-08 10:04:40 +00:00
Alexandre Bontems
7dbfed37eb feat: add watch options (#37571)
Adds per-event watch options, so a user can choose which repository
notifications they receive.

The watch button is now a menu with four modes: participating and
mentions (the unwatched state, as on GitHub), all activity, ignore, and
custom, which opens a dialog to pick issues, pull requests and releases.
The same dialog is reachable from the gear icon on the watched
repository list. Options apply to UI notifications and to mail.

The watcher, star and fork counts moved inside their buttons, so all
three share one shape, and the links to those lists moved to the
repository sidebar.

Assignees and requested reviewers now also receive UI notifications
without having commented first.

Buttons and dropdown:

<img width="688" height="398" alt="image"
src="https://github.com/user-attachments/assets/889b567d-086f-4a06-ac67-3a4205fc63b2"
/>

Modal:

<img width="841" height="327" alt="Screenshot 2026-08-07 at 21 10 59"
src="https://github.com/user-attachments/assets/3bedbff5-9011-4f1b-91a9-e8f5c212fb5d"
/>

New entries in sidebar:

<img width="170" height="207" alt="Screenshot 2026-08-07 at 21 13 28"
src="https://github.com/user-attachments/assets/5f5150cd-f42d-4543-bd0b-7334cf4aab0e"
/>

Fixes: https://github.com/go-gitea/gitea/issues/17238
Ref: https://github.com/go-gitea/gitea/issues/35492

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-08 07:35:13 +00:00
Lunny Xiao
2087d4a1a5 refactor: use the shared workflow model from actionslib (#38768)
Pairs with https://gitea.com/gitea/runner/pulls/1143.

Gitea depends on `gitea.com/gitea/runner` for exactly two packages:
`act/model` and `act/exprparser`, the workflow model and the expression
evaluator it needs to parse workflows and to build the task payload the
runner consumes. Pulling the whole runner module in for that is heavy
and puts shared code in the repository of one of the two consumers.

Both packages now live in `gitea.dev/actionslib` (`pkg/model`,
`pkg/exprparser`), the module Gitea and the runner already share for the
runner API, so the dependency on the runner repository is dropped here.

### Changes

- `gitea.com/gitea/runner/act/model` ->
`gitea.dev/actionslib/pkg/model`, `.../act/exprparser` ->
`gitea.dev/actionslib/pkg/exprparser` (22 files, import paths only).
- `routers/api/actions/runner/interceptor.go` takes the `x-runner-uuid`
/ `x-runner-token` names from `gitea.dev/actionslib/pkg/protocol`
instead of repeating the literals the runner also has.
- `go.mod`: `gitea.com/gitea/runner` removed.

---------

Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-08-07 20:56:40 -07:00
GiteaBot
c14edf3313 [skip ci] Updated translations via Crowdin 2026-08-08 00:28:20 +00:00
wxiaoguang
f899dfd6e0 chore: fix legacy git ref problems (#38827)
1. add correct "refs/heads" prefix to the branch name for commit graph
2. fix incorrect cache key in GetCommitGraphsCount
3. remove the "--" trim for the tag name, there is no security vulnerability, we never do so anywhere else

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-07 23:04:36 +00:00
metsw24-max
09f78aed19 fix: drop newline-bearing member names in arch ParsePackage (#38102)
The arch parser keeps tar member names verbatim. The index writer joins
those values one per line into the pacman database. So a member name
with a newline adds lines to that package's own `files` entry, which
libalpm reads as further fields.

The scope is one package record. An uploader cannot forge entries for
another package, and can set the same fields in `.PKGINFO` anyway. This
is input validation, not a privilege boundary.

`ParsePackage` now drops names that contain CR or LF. `joinFields` drops
such values again when writing the index, which also covers packages
that are already stored. Real packages never carry newlines in file
paths, so well-formed uploads are unaffected.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-07 20:47:33 +00:00
Mitrahsoft
7733f1953f fix(storage): fix Azure Blob dump failing with file does not exist (#38814)
## Issue

Gitea fails to dump LFS (and other object-storage) files when Azure Blob
Storage is configured as the storage backend. The dump reports:

Failed to dump LFS objects: /file/path: copying contents: file does not
exist

This happens with any non-empty base path (the default for LFS storage),
which is why the user could only work around it by using `--skip-*`
flags.

The root cause is in `AzureBlobStorage.IterateObjects()`: Azure's list
API already returns each blob's name including the configured base path,
but the code was building the read client by running that name through
the base-path-prepending helper a second time. This doubled the base
path (e.g. `gitea-lfs/gitea-lfs/aa/bb/hash`), pointing at a blob that
doesn't exist. `Stat()` still succeeded because it doesn't touch the
network, so the failure only surfaced when the dumper actually tried to
read the object's contents.

## Solution

Add `getBlobClientByFullName()`, which builds a blob client from a name
that is already fully qualified, without re-applying
`buildAzureBlobPath()`. `IterateObjects()` now uses it for names
obtained from Azure's list API. `getBlobClient()` (used by `Open`,
`Stat`, `Delete`, `ServeDirectURL`, which take relative paths) is
unchanged in behavior.

Also add `TestAzureBlobStorageDumpArchive`, a regression test that
drives the real dump path (`IterateObjects` → `Stat` →
`dump.Dumper.AddFileByReader` → `mholt/archives` zip writer) against a
**non-empty** `BasePath`, and verifies the produced archive contains the
object with the correct content. The existing Azure tests use an empty
`BasePath` and never read object content via `IterateObjects`, which is
why they didn't catch this.

Fixes #35476

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-07 16:35:30 +00:00
silverwind
4c382cea59 feat(actions): support $/ prefix in reusable workflow uses: (#38822)
Fixes: https://github.com/go-gitea/gitea/issues/38818

Accepts GitHub's `$/` self-repository prefix in a reusable workflow
`uses:`, alongside `./`.

Gitea's `./` already resolves against the caller's own source repo and
commit, which is what `$/` means, so the two are aliases here. Cycle
detection folds both prefixes onto one key.

Related PR for step-level support:
https://gitea.com/gitea/runner/pulls/1150
2026-08-07 15:39:36 +00:00
Mitrahsoft
d4333eb043 fix(release): allow publishing drafts without target #35569 (#38800)
### Issue

Fully publishing a draft release fails when the release does not have a
target set.

A draft release can be created without a target because the Git tag is
not created while the release remains a draft. However, when the draft
is published, Gitea attempts to resolve the release target to create the
tag. If the target is empty, the publish operation fails.

### Solution

Use the repository's default branch as the target when a draft release
is published without an explicitly configured target.

Also add a regression test to verify that a draft release without a
target can be successfully published.

Fixes #35569

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-07 15:06:16 +00:00
Zettat123
9fc5d20006 feat(actions): add force-cancel workflow run API (#38756)
Add `POST /repos/{owner}/{repo}/actions/runs/{run}/force-cancel`, the
counterpart of [GitHub's force-cancel endpoint](https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2026-03-10#force-cancel-a-workflow-run).

It cancels a run like `POST .../cancel`, but bypasses the graceful
cancelling handshake with the runner and stops running tasks
immediately.

Permissions and responses match the `/cancel` endpoint.

References:

- https://github.blog/changelog/2023-09-21-github-actions-force-cancel-workflows/
- https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2026-03-10#force-cancel-a-workflow-run
- https://github.com/orgs/community/discussions/123240
2026-08-07 14:38:14 +00:00
water
2657756cac enhance: fall back to DEFAULT_TEMPLATE.md when style-specific template is missing (#38803)
Closes #38801

Introduce `DEFAULT_TEMPLATE.md` as the default message for all merge
styles.

https://gitea.com/gitea/docs/pulls/491

By the way, fix incorrect os.Expand usage for merge message & repo
template

---------

Co-authored-by: waterWang <waterWang@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-07 14:11:03 +00:00
Lunny Xiao
a34cc4cac4 fix(migration): migration deletion returned json redirection (#38796)
Fix #38596

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-07 13:21:20 +00:00
David Pavlovschii
8873150206 fix(api): document X-Total-Count instead of non-existent X-Total header (#38717)
The `CommitList` and `ChangedFileList` swagger responses documented an
`X-Total` response header that their endpoints never send, they send
`X-Total-Count` via `SetTotalCountHeader`. This renames the documented
header and regenerates the spec.

`repoGetAllCommits` also uses `CommitList` and sends both headers, so
the new name is accurate for all three endpoints.

Also corrects `ChangedFileList` describing its headers as commit counts.

Documentation only, no handler behaviour changes.

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

---------

Signed-off-by: davidpavlovschi <davidpavlov2048@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-07 13:00:35 +00:00
silverwind
e81ab0a5ea refactor: prepare vue components for vapor mode (#38798)
Removes what would block a later switch to Vue's vapor mode:
`vue-chartjs` and the `SvgIcon` render function are virtual DOM
components, and `v-memo` has no vapor equivalent. This does not adopt
vapor mode, which will be stable in upcoming Vue 3.6.

`vue-chartjs` was a thin wrapper over chart.js, so a local
`ChartCanvas.vue` replaces it. Chart data and options move into computed
values to keep their object identity, which is what `v-memo` was
compensating for.

`chartjs-adapter-dayjs-4` is moved first-party, just ~40 lines that are
easy to maintain.
2026-08-07 12:38:31 +00:00
silverwind
d86cb1a498 fix(ui): anchor navbar admin badge to the avatar corner (#38821)
Fix regression from https://github.com/go-gitea/gitea/pull/38614:

Fixes: https://github.com/go-gitea/gitea/issues/38820
2026-08-07 11:58:15 +00:00
silverwind
ec869e3052 fix: misc fixes in pub/gpg/tests (#38809)
- The pub registry reported the oldest version as `latest`, because the
descriptor slice is sorted ascending but the first element was used.
- Verifying a GPG or SSH key flashed success and redirected after
already writing an error response, so a failure was reported as a
success with an empty key id.
- Test packages sharing redis could tear down each other's server.
`PrepareTestRedis` started its own on the well-known port, so a package
running in parallel borrowed it and lost it when the owner's cleanup
fired. It now listens on a socket of its own.
2026-08-07 11:49:23 +00:00
silverwind
dd8ef9c888 fix(ui): change underlines to default browser style (#38819)
Remove all underline style customization on links, letting the browser defaults apply
2026-08-07 13:18:41 +02:00
Giteabot
b349a4e746 fix(deps): update dependency mermaid to v11.16.1 [security] (#38813) 2026-08-07 02:48:19 -04:00
wxiaoguang
a7df0d5f41 fix: git diff blob excerpt (#38808)
1. refactor the legacy code and add more comments, remove the "+1/-1"
tricks, clarify the BuildBlobExcerptDiffSection behavior
2. fix a line-counting bug (see screenshot below)
2026-08-07 02:44:55 +00:00
silverwind
9dac77fdc2 fix(auth): set WebAuthn user verification per request (#38805)
Registration omitted `userVerification`, so Chromium raised the
credential to credProtect level 3 and the authenticator then hid it from
the second-factor login, which asked for `discouraged`. Registration and
each login now set their own value, with `preferred` on the second
factor so credentials already registered at level 3 keep working without
re-enrollment.

Also add relevant e2e test coverage for webauthn, one test chromium only
because Firefox lacks the APIs needed.

Fixes https://github.com/go-gitea/gitea/issues/33531
Fixes https://github.com/go-gitea/gitea/issues/36019
Fixes https://github.com/go-gitea/gitea/issues/38139
2026-08-07 00:11:34 +00:00
ChristopherHX
c210ef6dbb fix(actions): allow cancelling runs without running jobs (#35842)
My idea is to allow cancelling an stuck run with all jobs done, in case
such a bug happens again to not require admin commands.

Related #35832

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-06 23:52:01 +00:00
wxiaoguang
6ff3a65708 refactor: render highlight language (#38793)
Avoid CSS injection

More details are in the comment of CodeBlockAttributes
2026-08-06 18:07:36 +08:00
silverwind
231ba1da19 fix(actions): evaluate each ${{ }} part on its own (#38754)
Every `${{ }}` part was spliced as raw text into a synthesized
`format('...', <raw>)` call and re-parsed, so unbalanced parentheses
restructured the whole expression:

```yaml
run-name: ${{ 1) && (2 }}     # panicked, aborting workflow parsing for the push
if: x ${{ 1) && (2 }} y       # silently evaluated to 2
```

One scanner shaped like GitHub's template reader now splits every value
and each part is evaluated on its own, so nothing builds an expression
out of text. A part that fails is an error instead of an empty string,
making a `run-name`, `runs-on` or job name that GitHub rejects invalid
here too.

Replaces https://github.com/go-gitea/gitea/pull/38736 as the actual root-cause fix.
Signed-off-by: silverwind <me@silverwind.io>
2026-08-06 09:19:20 +00:00
Mitrahsoft
caf2e03605 fix(packages): show error for duplicate cleanup rules #37820 (#38786)
## Issue

Gitea does not display a clear error message when a user tries to create
a cleanup rule for a package type that already has an existing cleanup
rule.

Although the duplicate rule is detected, the user is not informed why
the cleanup rule cannot be created.

## Solution

Add a user-facing error message when a cleanup rule already exists for
the selected package type.

Also add an integration test to verify that the appropriate error
message is displayed when attempting to create a duplicate cleanup rule.

Fixes #37820
2026-08-06 07:06:32 +00:00
silverwind
92ac357b1a enhance(ui): improve luminance calculations (#38682) 2026-08-06 08:41:40 +02:00
silverwind
cc0d348d84 fix(actions): write an action task report in one transaction (#38792)
`UpdateTaskByState` wrote the task, its job and its steps in separate
statements. An interruption in between left the task finished with a
running job, so the run stayed in progress, and the "state is final"
early return made every retry, cancel and cleanup a no-op.

Fixes https://github.com/go-gitea/gitea/issues/38790
2026-08-06 06:08:42 +00:00
silverwind
c6836d0abb refactor: migrate remaining Vue components to <script setup> (#38752)
Migrates the last four Options API components, so all 20 `.vue` files
now use `<script setup>`, and disables the Vue Options API runtime. This
will enable optimizations like Vue Vapor mode in the future.

Bug fixes done while migrating and testing:

- Branch selector: arrowing onto "Create branch …" threw a `TypeError`,
and Enter on it did nothing
- Dashboard: the search term was not escaped, so `&` injected query
parameters and `#` truncated the request
- Dashboard: an unknown `repo-search-filter` threw, and names like
`constructor` leaked `Object.prototype` members into the request
- Dashboard: an unknown archived/private filter rendered `function
Object() { [native code] }` as a checkbox tooltip
- Dashboard: removed a dropdown init that always ran against a
not-yet-rendered element
- Branch selector: the `document.body` click listener was never removed
on unmount
- Contributors: the chart plugin relied on an undeclared chart.js field
- Contributors: `contributorsStats` was mutated behind a `shallowRef`,
so future readers would not update
- Removed template attributes and pageData keys that no component had
read for years

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-06 05:45:31 +00:00
silverwind
d8c3a1afda ci: pin containers to digest, enable more zizmor rules (#38779)
Enable more strict "pedantic" zizmor rules and fix issues. Service
containers are pinned to hash and renovate will update them. Enabled
rules:

- https://docs.zizmor.sh/audits/#excessive-permissions
- https://docs.zizmor.sh/audits/#unpinned-images
- https://docs.zizmor.sh/audits/#template-injection

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-06 05:16:07 +00:00
atiqur rahman
d94f714efa fix(actions): fix runner docs link (#38783)
Fixes: #38782
Signed-off-by: Atiqur Rahman <rahman.atiqur.pro@gmail.com>
Signed-off-by: atiqur rahman <rahman.atiqur.pro@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-08-05 23:15:08 +00:00
silverwind
74804df4a5 enhance(actions): replace ansi_up with first-party code (#38619)
Replaces the `ansi_up` dependency with first-party code and fixes a
number of bugs in turn.

- Faster rendering, around 7x for plain lines and 3x for colored ones.
- Render many SGR features like hyperlinks, blink, inverse, conceal,
strikethrough, overline, underline styles and underline color, including
`:` sub-parameters, which no longer swallow the codes after them.
- Drop OSC, DCS, SOS, PM and APC with their payload, ending them at BEL,
`ESC \` or the 8-bit ST. A truncated sequence is dropped instead of
corrupting a later line.
- A backspace moves the cursor back a column, so what follows overwrites
it, even across a style change.
- A style inside an OSC 8 label renders instead of leaking, and a
private CSI ending in `m` no longer resets the style.
- Log lines render as DOM nodes, never as markup, and only an `http(s)`
url becomes a link.
- Named colors render as CSS classes, only 24-bit color stays inline.
- Invisible text is now selectable, and the `z-index` workaround is
gone.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-05 21:59:26 +00:00
silverwind
42e0c9eca4 enhance(tls): use go's tls defaults (#38687)
The hardcoded cipher suites and curve preferences date from 2021 and no
longer match what Go ships. All four `SSL_*` are now unset by default
which means "use Go's default".

1. TLS 1.3 is now the default instead of TLS 1.2.
2. Post-quantum key exchange (`X25519MLKEM768`) works.
3. Four `ECDHE-*-CBC-SHA` suites become negotiable on TLS 1.2.
2026-08-05 21:34:46 +00:00
silverwind
81551008d3 chore: update pnpm settings (#38772)
- drop `dedupePeerDependents` which is a no-op (produces same lockfile)
- replace `allowBuilds` with `ignoreScripts`, no dependency needs any
builds, on any platform
- disable `confirmModulesPurge`, related to
https://github.com/pnpm/pnpm/issues/11562
- disable `verifyDepsBeforeRun`, `make` already ensures `node_modules`
are up to date, this saves around 150ms per `pnpm exec` and reduces pnpm
spam.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
2026-08-05 23:14:17 +02:00
silverwind
94199a016b ci: skip disk cleanup when unnecessary (#38778)
Speed up all go jobs by ~58s by not cleaning the disk when there's at
least 50GB available.

The job was added because some runners only have like 15GB available (I
assume self-hosted), but on regular GHA runners which have about 90GB
free, this job will now always skip.

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
2026-08-05 20:47:18 +02:00
Mitrahsoft
c46f0d545e enhance(api): add GitHub-compatible /repos/{owner}/{repo}/commits/{ref} endpoint (#38770)
Gitea currently exposes the endpoint:
 
`GET /repos/{owner}/{repo}/git/commits/{sha}`
 
to retrieve a single commit. However, GitHub provides the equivalent
endpoint as:
 
`GET /repos/{owner}/{repo}/commits/{ref}`
 
Applications integrating with both GitHub and Gitea must implement
platform-specific logic to use different endpoints, reducing API
compatibility.
 
Fixes #38225

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-08-05 17:19:31 +00:00
silverwind
c98a1597c9 chore: rename act_runner references to runner (#38791)
The runner repository moved to https://gitea.com/gitea/runner, update references.
2026-08-05 18:53:50 +02:00
wxiaoguang
deccd53c24 fix: git cache (#38763)
1. always use "last commit cache"
2. correctly build the cache key for any input (SafeCacheKey)
3. fix the git note "last commit cache FIXME" and avoid OOM
2026-08-05 00:17:07 +08:00
wxiaoguang
6347a33b34 fix: markup link (#38764)
fix #38762
2026-08-03 14:41:38 +00:00
Giteabot
a954257fb1 chore(deps): update dependencies (#38757)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-03 21:37:12 +08:00
Copilot
ac49dbe1a2 refactor(modelmigration): thread context through migration functions (#38758)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-03 08:13:15 +00:00
TheFox0x7
e23fe79e5e feat(licenses): support REUSE specification in licenses (#38720)
extends the current license detection to support two modes:
- legacy which is using classification and was expanded to handle more
paths (extensions, different spelling or GNU copying file)
- REUSE which avoids classification by relying on the spec dictating
that license must be named as SPDX-ID.extension. Newly created
repositories will default to REUSE based paths

Use of styles at the same time is not allowed by design.

Extends the UI to show all the detected licenses and paths to them,
deduplicating them per SPDX-ID in database as is in github

closes: https://github.com/go-gitea/gitea/issues/28672


---------

Assisted-By: omp:glm5.2
Assisted-By: omp:mimo-v2.5-pro
Assisted-By: omp:mimimax-m3
Assisted-By: omp:kimi-k3
Assisted-By: omp:deepseek-v4-flash
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-03 07:34:53 +00:00
Mitrahsoft
09f7c71e3d enhance(api): expose file mode in contents API response (#38713) 2026-08-03 06:41:27 +00:00
silverwind
89dd456af8 fix: don't report failed network requests as JavaScript errors (#38732)
On the issue ref popup, when the user clicks the link while fetch is
loading, the page will briefly render a network error message before
loading the next page. Suppress unnecessary network errors.
2026-08-03 06:11:22 +00:00
philip-x-rutkowski-intel-com
6487d14855 enhance(packages/npm): expand version metadata and support npm deprecate (#37890)
Fixes #21624

Adds the npm package version metadata fields that Gitea's npm registry
was previously dropping on publish, and implements the `npm deprecate`
command, which Gitea did not accept before.

### New / pass-through metadata fields

The following are now parsed from the publish payload, persisted in the
stored `npm.Metadata`, and re-emitted on the abbreviated version
manifest returned to npm clients:

- `hasInstallScript` — auto-detected from `scripts.preinstall` /
`scripts.install` / `scripts.postinstall` (also honors a client-supplied
value). Without this flag, `npm install` skips lifecycle scripts.
- `_hasShrinkwrap` — authoritatively derived by inspecting the uploaded
tarball for a top-level `*/npm-shrinkwrap.json` entry. Client-supplied
values are ignored. Decompression failures fall back to `false` and do
not block publish (integrity has already been validated).
- `engines` (`map[string]string`)
- `cpu`, `os` (`[]string`)
- `directories` (`map[string]string`)
- `funding` (`any`; preserves the spec's string / object / array shape)
- `acceptDependencies` (`map[string]string`)
- `deprecated` (`string`)

`peerDependenciesMeta` was already in the stored struct but is now
exercised
by tests.

### `npm deprecate` support

`npm deprecate <pkg-spec> <message>` PUTs the package document to the
same URL as publish but with no `_attachments`. The router now detects
that shape and routes to a new handler that updates each affected
version's stored `Metadata.Deprecated` via
`packages_model.UpdateVersion`. An empty message clears the flag
(undeprecate). Unknown versions are silently skipped, matching npm's
behavior. No new routes were added.

Supported invocations include:

- `npm deprecate my-thing@"< 0.2.3" "critical bug fixed in v0.2.3"`
- `npm deprecate my-thing@1.x "1.x is no longer supported"`
- `npm deprecate my-thing@1.0.0 ""` (undeprecate)

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-08-03 05:26:08 +00:00
wxiaoguang
c2ebe3724f refactor: introduce trString for frontend (#38741)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
2026-08-03 04:40:02 +00:00
GiteaBot
cb4c65f035 [skip ci] Updated translations via Crowdin 2026-08-03 00:53:25 +00:00
Zettat123
a3b747801c fix: set a minio part size when the content size is unknown (#38753)
## Background

`MinioStorage.Save` is called with `size = -1` on several paths,
including Actions logs, Actions artifacts, repository archives, avatars
and attachments. With an unknown size (-1) minio-go assumes a 5TiB
object and allocates a single part-sized buffer of 528MiB per upload,
regardless of the real payload size, which can exhaust the memory of
small instances.

Measured against a local S3 stub, five sequential uploads of a 4KiB
payload grew RSS by 1041MiB before the fix and by 34MiB after it.

## Fix

Pass an explicit 16MiB part size in that case, the same value minio-go
uses as minimum part size
(https://github.com/minio/minio-go/blob/v7.2.1/constants.go#L28).

Uploads with a known size are left untouched, since minio-go already
derives a part size proportional to the real object size.

## Note:

One behaviour change: with an unknown size the object is now limited to
16MiB * 10000 parts = 156.25GiB
(https://github.com/minio/minio-go/blob/v7.2.1/api-put-object-common.go#L112-L116).
`putObjectMultipartStreamNoLength` completes the upload once it runs out
of parts without checking that the reader was drained, so a stream past
that limit is silently truncated rather than rejected. The previous
limit was 5TiB. No payload Gitea uploads comes close to either.

Parts are also uploaded serially, so a smaller part size means
proportionally more round trips for large unknown-size uploads.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-02 23:47:43 +00:00
Zettat123
fdc1d613da test: fix flaky TestPullView_CodeOwner racing the push hook (#38751)
`TestPullView_CodeOwner/First Pull Request` called
`PullRequestCodeOwnersReview` directly and asserted it returned one
notifier. That return value holds only the review requests that call
creates. But the push (by `files_service.ChangeRepoFiles`) to the PR
branch already triggers the same function asynchronously through
`AddTestPullRequestTask`, which makes `PullRequestCodeOwnersReview`
return an empty slice and the test then fails with
```
    Error: "[]" should have 1 item(s), but has 0
```

Fix: make `require.Eventually` wait for the review request to exist.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-02 21:30:59 +00:00
TheFox0x7
602d095b81 fix: bad path escape in subpath archive download (#38749) 2026-08-03 03:02:23 +08:00
Lunny Xiao
87ed4117c6 docs: Update PGP key expiration date to July 23, 2027 (#38747)
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-08-02 17:52:16 +00:00
wxiaoguang
7f7dc2d16c chore: fix git diff render (#38746)
Fix a regression from #38517 (the tail section index is not correctly
assigned).

And add a test to cover GetDiffForRender.
2026-08-02 19:34:26 +02:00
Lunny Xiao
4e4ea75fb9 refactor(diff): drive diff DOM init from the global selector observer (#38740)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-02 13:19:57 +00:00
silverwind
b87fdd5da4 ci: fix jq broken pipe in cache-prune (#38734)
Fix minor issue: `cache-prune` logs `jq: error: writing output failed:
Broken pipe` when it has nothing to delete, because the loop stops
reading as soon as it is under the limit while `jq` still has output
pending. Reading from a here-string removes the pipe.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-08-02 12:57:37 +00:00
wxiaoguang
d390d77879 fix: remove the pull merge box from UI when the refreshed page doesn't contain it (#38742)
On the PR view page, the "merge box" is refreshed periodically.

If the PR changes (e.g.: merged and the head branch has been deleted),
then the merge box doesn't exist in the refresh response. For such case,
the merge box should also be removed from the UI.
2026-08-02 08:51:10 +02:00
Ross Golder
a65f422b89 feat(actions): Add Actions API endpoints for workflow run management and logs (#35382)
Implements the missing REST API endpoints for Actions workflow run
management:

1. `POST /actions/runs/{run}/cancel` cancels a run and its jobs, `409`
when it already finished
1. `POST /actions/runs/{run}/approve` approves a run awaiting approval,
idempotent, `409` when it never awaited one
1. `GET /actions/runs/{run}/logs` downloads the latest attempt's job
logs as a zip archive

`ActionWorkflowRun` gains `created_at`, `updated_at` and the `jobs_url`,
`logs_url`, `artifacts_url`, `cancel_url` and `rerun_url` fields, and
now always emits `conclusion` and `head_branch`.

Cancellation is shared with the web handler in `services/actions`.

Fixes https://github.com/go-gitea/gitea/issues/35176
Fixes https://github.com/go-gitea/gitea/issues/36554

---------

Co-authored-by: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Co-authored-by: OpenCode Agent <opencode@rossgolder.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-02 12:22:07 +08:00
GiteaBot
c5b6e044d7 [skip ci] Updated translations via Crowdin 2026-08-02 00:53:53 +00:00
GiteaBot
cb79bb367e [skip ci] Updated translations via Crowdin 2026-08-01 00:53:28 +00:00
silverwind
4f363d2748 ci: improve go caches (#38730)
Cache saves have been rejected since June because the repo sat above its
10 GB allowance, so every PR run fell back to a cache built with an
older toolchain and rebuilt the backend from scratch.

- go version in the `gobuild` and `golint` keys and `restore-keys`
- seeder triggers on `go.mod` and gained `workflow_dispatch`
- single writer for the `gomod` cache, the two were racing
- new daily `cache-prune` workflow holding the total under a limit
- `cache: false` for `setup-go` in release and cron workflows, it held
815 MB
- add `workflow_dispatch` so the cache workflows can be triggered
on-demand

Assisted-by: Claude Code:claude-opus-5
2026-07-31 20:56:13 +00:00
wxiaoguang
76a787a79d chore: drop "oidc_wellknown" tmpl, use json directly (#38731)
`testOAuth2WellKnown` already covers the endpoint's response.
2026-07-31 18:32:01 +02:00
silverwind
f0a95eebe3 refactor: serve the api specs as plain json (#38715)
The api specs were Go templates whose committed form was not a valid
swagger document, so `swagger-validate`, `generate-openapi.go` and
`.spectral.yaml` each worked around it. They are now plain json,
substituted at serve time.

Renaming them off `.tmpl` also stops `make fmt` rewriting them, which
used to bump their mtime and silently skip the next `make
generate-swagger`.

Also enables stricter spectral linting: extends `lint-swagger` to the
OpenAPI 3 spec, turns on `openapi-tags`, `operation-singular-tag` and
`operation-tag-defined`, adds a top-level `tags` array with descriptions
to the swagger input, and drops the redundant `repository` tag from
`POST /user/repos`.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-07-31 14:16:27 +00:00
Shudhanshu Singh
a3e7fe1f11 fix(gitdiff): prevent index out of range panic in GetLineTypeMarker (#38728)
Fixes a `runtime error: index out of range [0] with length 0` panic when
executing template `repo/diff/section_unified` because
`GetLineTypeMarker()` indexes into empty `DiffLine` content.

The regression was introduced in PR #38706 (`94c61137e4`) where
`Content: " "` was removed from the initialization of `tailDiffLine`.
Guarding `GetLineTypeMarker()` directly makes Gitea defensive and robust
against empty diff line values regardless of the source path.

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

### Tests
- Added `TestDiffLine_GetLineTypeMarker` covering empty content,
prefixes, and normal text formats.

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
2026-07-31 13:49:47 +00:00
silverwind
7ac32c9b51 chore: tweak AGENTS.md (#38723)
Further refinements to AGENTS.md:

- consolidate some points
- add point about locales
- rewrite some points for clarity and size

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
2026-07-31 13:12:30 +00:00
Dominique Hummel
1f6cfe611a feat: Add block on pending codeowner reviews branch protection (#34995)
This commit introduces a new branch protection rule that allows merge
blocking if there are pending reviews from one or more code owners (as
defined in any valid `CODEOWNERS` file). This is determined by
evaluating each rule present in the `CODEOWNERS` file individually. For
every rule, at least one named code owner (or member of a code owner
team) must have given an approving review for merging to be possible.

Closes #32602 

---

This PR does NOT display code owners separately from other reviewers
(#28137)

### Screenshot

<details>
<summary>Pull Request</summary>
<img
src="https://github.com/user-attachments/assets/74560f9c-9f59-477a-b270-63f937b26d6a"
/>
</details>

<details>
<summary>Branch Protection Rule</summary>
<img
src="https://github.com/user-attachments/assets/f0a9ce00-38fe-4eed-b6a3-5290aa404610"
/>
</details>

Doc PR
https://gitea.com/gitea/docs/pulls/245

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-31 13:00:59 +00:00
silverwind
805697089e fix(markdown): fix double strikethough on code (#38707)
`.markup del code { text-decoration: inherit }` makes inline code inside
`<del>` paint its own line-through in addition to the one already
propagating from the parent. Since `code` is `font-size: 85%`, the two
land at different heights and render as a doubled strikethrough.

The rule was inherited from primer-markdown, where it was added in
762b8b8264
("so that `<del>` or `<a>` has the same effects on `<code>` tags") at a
time when inline `code` was `display: inline-block`, a box that text
decorations do not propagate into. That `display: inline-block` was
removed 11 days later in
f1131d5ab6,
which made the rule redundant, but it survived the squashed import into
primer/css and every copy downstream of it.

No other popular markdown stylesheet carries an equivalent rule, GitHub
still ships it and shows the same doubled line.

Fixes: https://github.com/go-gitea/gitea/issues/34786
2026-07-31 12:32:08 +00:00
roman.s
38b07d0c29 feat(webhook): fire repository event on repo rename (#38641)
Repository create and delete already fire `repository` webhooks, rename
did not. This adds the `renamed` action with `changes.name.from`
carrying the previous name, and renders it in the chat converters.

Actions workflows are unaffected, they still do not trigger on rename.

AI assistance was used for the implementation and tests.

Fixes https://github.com/go-gitea/gitea/issues/34891.
Co-authored-by: roman s <roman.sukach@dust-labs.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-31 11:31:17 +00:00
hsdfat
7ca64566f5 fix(lfs): failed upload deletes a concurrent upload's meta object (#38693)
UploadHandler creates the LFS meta object only as the last step of
uploadOrVerify, after the content is already in the store, so a request
that errors has never created a row of its own. The removal on the error
path was a real compensating action when it was added in #14726, where
the meta object was created before contentStore.Put, but #16865 moved
creation after the Put and left the removal behind.

Since then it can only ever delete a row created by a different request:
a stalled git-lfs PUT that fails after its own retry has already
succeeded wipes the winner's meta object, leaving the content in the
store unreachable and eligible for orphan cleanup.

Drop the removal and add a regression test asserting that a failing
upload keeps a pre-existing meta object.

Fixes: #38424
Assisted-by: Claude Code:claude-opus-5
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-31 11:05:41 +00:00
Mitrahsoft
bcf45803af feat(api): add tag_filter query parameter to release list API (#38681)
Adds a `tag_filter` query parameter to `GET /repos/{owner}/{repo}/releases` that filters releases by tag name, with `*` as a wildcard (e.g. `v1*`, `*beta`, `*rc*`). Matching is
case-insensitive and done in the database query. Literal `%`, `_` and `\` in the filter are escaped.

Fixes: https://github.com/go-gitea/gitea/issues/38513
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-31 10:40:17 +00:00
wxiaoguang
a30d865b78 fix: correct full url when using sub-path (#38712)
fix #38708

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-07-30 21:09:32 +02:00
Shudhanshu Singh
d87d26d735 perf(gitdiff): optimize inline diff highlighting using cache (#38706)
This pull request optimizes code diff highlighting in the PR Files
Changed view, which is a major CPU bottleneck and hot path in
production.

Previously, Gitea executed the expensive `DiffMatchPatch` algorithm
twice for every matching deleted/added line pair (once for the deleted
line, and once for the added line). We now run the diff algorithm once
and cache the resulting `DiffInline` on the `DiffLine` struct itself,
allowing the second line to render via a 0-cost cache lookup.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-30 16:43:36 +00:00
bircni
11d0ed699b fix(actions): dynamic matrix expansion correctness fixes (#38690)
Follow-up to https://github.com/go-gitea/gitea/pull/36564 (dynamic
matrix) and https://github.com/go-gitea/gitea/pull/36357 (max-parallel),
fixing issues found reviewing the two features together.

- **A placeholder could stall its run forever.** Its payload keeps the
raw matrix but loses its `needs`, so `ParseJob` re-expanded it instead
of reading it back — fatal for `include: ${{ fromJson(needs.*.outputs.*)
}}`.
- **An `if:` reading `matrix.*` skipped the whole job**, with or without
the `${{ }}`. It now reduces to the needs gate, except under
`always()`/`failure()`/`cancelled()`, and each combination is decided on
its own values once the matrix expands.
- **Dependents could be skipped before the combinations ran**, since
inserted siblings are absent from the resolver's job set. The pass now
stops after an insert and defers to the re-emit it schedules.
- **Expansion failures stranded the placeholder.** A retryable one is
returned so the queue retries it; a malformed payload fails the job
instead of requeueing forever.
- **Rerun could rewind a pass-through row** into a raw placeholder
keeping its old terminal status, which nothing expands. Now gated on the
anchor itself.

Plus: `max-parallel` distinguishes an unevaluated `${{ }}` (debug) from
a non-numeric literal (warn — it silently drops the cap).

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-30 09:13:47 +00:00
silverwind
e80a62f555 chore: tweak AGENTS.md (#38702) 2026-07-30 04:58:55 +00:00
GiteaBot
b244391e18 [skip ci] Updated translations via Crowdin 2026-07-30 00:46:23 +00:00
wxiaoguang
f34073b887 fix: avoid markup render panic (#38698)
fix #38697

The root problem is that "commitChecker.repo" can be nil when the render
is called without a real repo.
2026-07-29 20:26:21 +00:00
Brecht Van Lommel
43390e802c fix(ui): too many participants shown in commit avatar stacks (#38689)
Show only author and co-authors without committer, and deduplicate the
same user with multiple email addresses.

The commit list "Author" column should not show the committer. This is
somewhat misleading, and arguably showing it on the individual commit
page is sufficient and consistent with other forges.

The same user with multiple email addresses often happens when
DEFAULT_KEEP_EMAIL_PRIVATE is enabled and Gitea does not use an actual
email address by default for edits. Showing the same avatar and name
twice is not helpful then.

Ref #37594
Fix #38488

---

Before
<img width="4088" height="2138" alt="before"
src="https://github.com/user-attachments/assets/a9e919f4-72d0-495f-a0b5-ec094f148f5a"
/>

After
<img width="4124" height="2142" alt="after"
src="https://github.com/user-attachments/assets/7212e527-ab83-440a-a8a4-95b5a79b3580"
/>

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-29 12:39:35 +00:00
GiteaBot
298d73cde6 [skip ci] Updated translations via Crowdin 2026-07-29 00:48:51 +00:00
wxiaoguang
4d6e172a0d chore: still keep ctx in git.Repository struct for cat-file batch command (#38684)
Unfortunately, we can't completely remove the ctx from git.Repository,
because the CatFileBatch still heavily depends on a parent context.

If we remove the Repository ctx, the CatFileBatch will become a mess and
create a lot of unnecessary git processes.

http://localhost:3000/-/admin/monitor/perftrace

* Before: open a repo home, dozens of git processes (duplicate cat-file)
* After: only a few (no duplicate cat-file)
2026-07-28 17:04:22 +00:00
Pascal Zimmermann
5672b1c4cf feat: Add support for dynamic matrix evaluation in Gitea Actions workflows (#36564)
Adds dynamic matrix evaluation to Gitea Actions: a job's
`strategy.matrix` can be built from the outputs of the jobs it needs.

```yaml
jobs:
  generate:
    runs-on: ubuntu-latest
    outputs:
      matrix: ${{ steps.set.outputs.result }}
    steps:
      - id: set
        run: echo "result=[1,2,3]" >> $GITHUB_OUTPUT

  build:
    needs: [generate]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        version: ${{ fromJson(needs.generate.outputs.matrix) }}
    steps:
      - run: echo "building ${{ matrix.version }}"
```

Such a matrix cannot be expanded at planning time, so the job is planned
as a single placeholder and expanded by the job emitter once its needs
finish. Each combination is then gated by `if:` and concurrency as
usual.

- A matrix that resolves to no combination fails the job, as on GitHub.
- Expansion is capped at `MaxJobNumPerRun`.
- Workflows without a needs-dependent matrix are unaffected.

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

---------

Signed-off-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Signed-off-by: ZPascal <pascal.zimmermann@theiotstudio.com>
Co-authored-by: Claude <claude-sonnet-4-5@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.8) <noreply@anthropic.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-28 15:59:00 +00:00
water
717db275d5 fix: Alpine registry APKINDEX.tar.gz returns 405 for HEAD requests [fj4WqyCCw3C5ShR1RfB7MoBPTpkRrBFYP1uT35g3MvT] (#38686)
### Description

Fixes #38676

The Alpine package registry registers for only, so a request returns .
Clients that probe the index with before fetching it (like and other
-based tools) fail outright.

**Fix:** Change to for the APKINDEX.tar.gz route, matching the pattern
already used by the i386 registry a few lines below.

### Related issue

Closes #38676

Co-authored-by: waterWang <waterWang@users.noreply.github.com>
2026-07-28 16:22:01 +02:00
wxiaoguang
0ab3d569b4 fix: repo home page 500 due to the timeout of "get last commit info" (#38678)
Regression of the ctx removal from "git.Repository" struct (the old code
was already wrong and can still cause 500, the "ctx removal" just makes
the problem easier to reproduce).

Merge duplicate code.

Reviewd by codex: no actionable findings.
2026-07-28 19:12:53 +08:00
GiteaBot
a75ed103f4 [skip ci] Updated translations via Crowdin 2026-07-28 00:48:26 +00:00
bircni
a12634fd8d docs: Update Changelog for release v1.27.1 (#38670)
Add changelog for version 1.27.1 with security, API, enhancements, bug
fixes, build updates, and miscellaneous changes.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-27 12:31:04 -07:00
Shudhanshu Singh
d47f9f37d6 refactor(git): clarify GetBranch behavior to make it only gets an existing branch (#38662)
`GetBranch` silently returned soft-deleted branches, contradicting
`IsBranchExist` and forcing callers to manually check `branch.IsDeleted`
everywhere.

Refactored it to `GetBranchExisting` to have a clear behavior: it only
returns the existing branch.

---------

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-27 17:45:27 +00:00
Giteabot
94a2c3ec18 chore(deps): update dependencies (#38660)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-27 16:55:27 +00:00
Pascal Zimmermann
10c678a0a0 feat: Add max-parallel Support for Gitea Actions (#36357)
Add support for `strategy.max-parallel` on Gitea Actions matrix jobs.

**How it works**
Jobs over the limit are inserted as `Blocked` instead of `Waiting`, so
runners never see them. When a job finishes, the job-status resolver
promotes one `Blocked` job per freed slot, in job order. Slots are
counted per `JobID` and scoped by reusable-workflow caller. A
`Cancelling` job still owns its runner, so it keeps its slot.

The cap is applied wherever a job can become `Waiting`: initial insert,
rerun, approval, and resolver promotion.

Best effort, not a hard invariant: two concurrent emitter passes can
each promote into the last slot, overshooting by one. It does not
compound, since every later pass recounts.

**Parsing**
Any YAML number, cast to an int as GitHub does (`1.5` → 1). `0` or
negative means unlimited. Expressions (`${{ ... }}`) are not evaluated
yet and fall back to unlimited.

**Migration**
Adds the `max_parallel` column on `action_run_job`. No index or
constraint changes.

**Compatibility**
Existing rows default to `0`, so behaviour is unchanged. No runner
changes needed: the runner protocol is untouched, and since the server
splits the matrix each runner still receives a single job.

Closes https://github.com/go-gitea/gitea/issues/35561
Signed-off-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Signed-off-by: ZPascal <pascal.zimmermann@theiotstudio.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-27 16:18:43 +00:00
Harsh Satyajit Thakur
e67dd4c818 fix: skip OIDC end-session after password login for OAuth2 users (#38439)
Fixes #38209

OAuth2-linked accounts that sign in via the password form were still
redirected to the provider end_session_endpoint on logout because the
redirect was keyed off account LoginType.

Store the session sign-in method (password vs oauth2) and only use
RP-initiated OIDC logout when this session was authenticated via OAuth2.
Sessions without the new key keep the previous LoginType behavior.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-27 15:35:48 +00:00
Lunny Xiao
559aa89ca7 ci: set AWS_REGION for Cloudflare R2 upload steps (#38658)
The `configure-aws-credentials` step earlier in the same job exports
**both** `AWS_DEFAULT_REGION` and `AWS_REGION` into `$GITHUB_ENV`
(verified in `exportRegion()` at the pinned SHA `517a711`), so
`secrets.AWS_REGION` (the real AWS region) stays set for every later
step in that job.

The AWS CLI v2 region resolution order is `--region` > `AWS_REGION` >
`AWS_DEFAULT_REGION`. The R2 step only set `AWS_DEFAULT_REGION: auto`,
so the leaked `AWS_REGION` won and R2 rejected it, since R2 only accepts
`wnam`, `enam`, `weur`, `eeur`, `apac`, `oc` or `auto`.

The failure log shows both `AWS_DEFAULT_REGION: auto` and `AWS_REGION:
***` in the step env, which confirms the leak.

### Fix

Set `AWS_REGION: auto` explicitly in the R2 upload step env of all three
release workflows. Step-level `env:` is applied after
`GITHUB_ENV`-derived variables, so this reliably overrides the leaked
value.

No other variable leaks from `configure-aws-credentials` matter here:
`AWS_SESSION_TOKEN` is only exported when a session token exists, and
these workflows use static access keys without `role-to-assume`;
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are already overridden
in the R2 step.

Consolidating the three duplicated upload steps into a composite action
remains a follow-up, as noted in #38635.
2026-07-27 14:54:03 +00:00
wxiaoguang
dd407ee3e8 fix: make Actions log parser support multiple line message encoding (#38659)
fix #38652


UI part (`.log-msg`) uses "white-space: break-spaces;" so the new line
can be correctly rendered.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-27 14:24:46 +00:00
silverwind
7efcb8d6ca test(pubsub): stop racing the Redis SUBSCRIBE ack (#38661)
`RedisBroker.Subscribe` returns before the server acks `SUBSCRIBE`, so a
publish right after it can be dropped, making
`TestRedisBroker/CrossBroker` fail intermittently on loaded CI runners
([example](https://github.com/go-gitea/gitea/actions/runs/30257188479/job/89948425118)).

Each scenario now uses its own topic and waits for `PUBSUB NUMSUB`
before publishing. `MemoryBroker` registers synchronously and skips the
wait.
2026-07-27 16:12:06 +02:00
silverwind
341caf8aa7 chore(ci): fix renovate custom manager regex (#38656)
Make this regex compatible with https://github.com/uhop/node-re2 used by
renovate and which does not support lookahead.

`renovate-config-validator` which I had used earlier fails to run `re2`
on node 26 because of missing prebuilt binary and falls back to JS regex
which does support lookahead, giving incorrect validation results.

Upstream bug report:
https://github.com/renovatebot/renovate/discussions/44873
Fixes: https://github.com/go-gitea/gitea/issues/38648
2026-07-27 09:54:21 +00:00
mohammad rahimi
13d0f24423 feat: Replace SSE with WebSocket for UI notifications (#36965)
* Closes #36942
* Fixes #19265 

Replaces the SSE-based push channel (`/user/events`) with a WebSocket
endpoint (`/-/ws`).

### What changes

- **New `/-/ws` endpoint** (authenticated). One WebSocket per origin,
shared across tabs via a single `SharedWorker`.
- **Pubsub broker** (`services/pubsub`) for fan-out by topic, behind a
`Broker` interface. `MemoryBroker` is the default (single process); a
Redis backend is available for multi-process setups, configured via
`[websocket].PUBSUB_TYPE` / `PUBSUB_CONN_STR`. The internal Gitea queue
was not usable here because it has FIFO/single-consumer semantics.
- **Push-only event production.** Events are emitted by write-triggered
notifiers — `NotificationCountChange`, `PublishStopwatchesForUser`, and
the logout publisher — wired into the existing `notify.Notifier`
interface. No server-side pollers.
- **Typed pub/sub on the client.** `web_src/js/modules/worker.ts` is a
singleton transport; features subscribe per event type via
`onUserEvent('notification-count', cb)` instead of branching on
`event.data.type`.
- **Wire contract** (`UserEventType` union) is shared between the worker
and consumers via `web_src/js/types.ts`, kept in sync with
`services/websocket/events.go`.
- **Client-side periodic polling fallback** kicks in only when the
WebSocket cannot be established (e.g. proxy blocks WS, browser lacks
module-SharedWorker support).

### What's removed

- `modules/eventsource` (SSE manager, run loop, messenger).
- `/user/events` route and `tests/integration/eventsource_test.go`.
- All server-side polling for stopwatches and notification counts.

### Stopwatch multi-tab fix

The navbar stopwatch icon was previously rendered conditionally on `{{if
$activeStopwatch}}`, so tabs loaded before the timer started had no DOM
element to update. The icon and popup are now always rendered (toggled
with `tw-hidden`), and the start/stop/cancel handlers POST silently so
all open tabs reflect the change in real time.

### Deployment note

WebSocket needs the upgrade headers to pass through a reverse proxy,
e.g. for nginx:

```nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```

Without them the WebSocket cannot be established, and after 3
consecutive failed opens the shared worker signals `push-unavailable`:
the notification count and stopwatch fall back to periodic polling on
the existing `[ui.notification]` timeouts. Real-time push is lost, the
features keep working. The reverse-proxy docs need the same note (see
the `docs-update-needed` label).

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Epid <rexmrj@gmail.com>
2026-07-27 07:46:00 +00:00
Shudhanshu Singh
e15a7e9066 fix(actions): use base branch ref for pull_request_target context (#38636)
Fixes a bug in Actions context generation for `pull_request_target`
workflows where `github.ref` / `gitea.ref` was incorrectly populated
with `refs/heads/owner:branch` instead of `refs/heads/branch`.

### Problem Statement
In `services/actions/context.go`, when constructing the `ref` string for
`pull_request_target` events:
```go
ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-27 06:34:38 +00:00
Zettat123
528b1f211b fix(actions): skip already-approved runs in ApproveRuns (#38653)
The handler of `/actions/runs/{run}/approve` doesn't check if the run is
already approved. If a run is re-approved, its jobs' status will be
reset to `StatusWaiting`, causing incorrect job status.
2026-07-27 06:01:46 +00:00
roman.s
cebdc90ed9 fix(api): accept fully-qualified refs in contents API (#38650)
`GET/POST` repository contents endpoints resolve the `ref` query
parameter through `ResolveRefCommit`, which previously only accepted
short branch/tag names or commit IDs. Clients that pass fully-qualified
Git refs (GitHub-compatible), e.g. `ref=refs%2Fheads%2Fmain` or
`ref=refs%2Ftags%2Fv1.0`, received 404 "object does not exist".

This change accepts fully-qualified **`refs/heads/*`** and
**`refs/tags/*`** only, then falls back to the existing short-name and
SHA logic. Other `refs/*` prefixes (e.g. `refs/pull/`, `refs/for/`) are
rejected.

Fixes #38197

---------

Co-authored-by: roman s <roman.sukach@dust-labs.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-27 05:31:58 +00:00
GiteaBot
4da9b59414 [skip ci] Updated translations via Crowdin 2026-07-27 00:55:53 +00:00
Lunny Xiao
230e7bdf91 chore(build): upload release to Cloudflare R2 (#38635)
The download site’s bandwidth costs are growing rapidly, while
Cloudflare R2 does not charge egress fees. Therefore, we would like to
migrate the download site from S3 to R2.

To ensure a smooth transition, we will keep the existing S3 upload
process temporarily and remove it after the migration is complete.

The upload steps are currently duplicated in three places. They could be
consolidated into a composite action in a separate PR.
2026-07-26 17:22:25 -07:00
silverwind
e75d583212 ci: match # renovate: markers that trail the value (#38640)
The preset we extended only matches a `# renovate:` comment on the line
above the `_VERSION` assignment. Ours trails the value, like the ones in
the `Makefile`, so it never matched and the pin has sat at `43.141.5`
since https://github.com/go-gitea/gitea/pull/37050.

Co-authored-by: Claude (Opus 5) <noreply@anthropic.com>
2026-07-26 18:06:48 +00:00
wxiaoguang
a3caf21440 feat: admin impersonates a user (#38614)
* fix #3631
* fix #21599

by the way, refactored the "profile avatar card" to simplify the code.
2026-07-26 17:26:02 +00:00
wxiaoguang
470d34b1de refactor: git patch apply (#38637)
Merge duplicate code and add a unit test

There are already integration test cases in `TestEditor` ->
`WebGitCommitEmail` for these two endpoints.
2026-07-26 16:57:00 +00:00
wxiaoguang
3e6cb7c16b fix: orgmode render include path (#38642)
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2026-07-26 14:22:49 +00:00
bircni
1b18b25c8e fix(actions): cancel tasks immediately when the runner stopped reporting (#38616)
Fixes jobs that get stuck in `cancelling` after Gitea is restarted while
a job is running.

Reproduction:

1. Run a job with `sleep 100`
2. Stop Gitea and wait 120s (> 100s)
3. Start Gitea — the job is still `running`; cancel it, and it stays in
`cancelling`

## Cause

A cancellation is only ever delivered to a runner as the *response* to
its `UpdateTask`
RPC. A runner that has already given up on the task — it crashed, or it
failed to report
the final state while Gitea was unreachable — never calls `UpdateTask`
again, so it never
learns about the cancellation. The task then sits in `cancelling` until
`stop_zombie_tasks`
reaps it, which needs `ZOMBIE_TASK_TIMEOUT` (10m) of silence and only
runs every 5 minutes.

Cancelling actually made this worse. xorm rewrites the `updated` column
on every `UPDATE`,
even when `Cols()` restricts the update to `status`, so persisting the
`cancelling` status
reset the zombie clock. Pressing cancel pushed the cleanup a full
`ZOMBIE_TASK_TIMEOUT`
into the future instead of bringing it forward.

## Change

`StopTask` now skips the `cancelling` handshake when the task has had no
state report from
its runner for longer than `TaskReportTimeout` (1 minute) and cancels it
directly. This
joins the two existing fallbacks — runner deleted, and runner without
cancelling support —
so every cancel path (web UI, API, concurrency, rerun) is covered.

Runners report the state of a running task every few seconds, so a
minute of silence means
the runner is gone. The value is a constant rather than a setting
because it only decides
whether the runner is still reachable, not whether a task should be
killed —
`ZOMBIE_TASK_TIMEOUT` still owns that.

### Tradeoff

If a runner is alive but has been silent for over a minute and is
cancelled in that window,
it skips the graceful post-step cleanup added in #37275. No work is
lost: the runner still
learns the outcome on its next report, because `UpdateTask` returns the
task status as the
result and the runner stops there. That is the behaviour that existed
before #37275.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-26 13:50:06 +00:00
Elisei Roca
47565ae93e fix(issues): fix label bulk-load key and reduce log noise in LoadLabel (#38632)
CommentList.loadLabels keyed the result map by label.ID but looked up by
comment.ID, so every label event fell back to individual DB queries.

This caused log spam for any comment where the label was deleted, since
ToTimelineComment calls LoadLabel unconditionally for all comment types
including those with LabelID=0.

Fix the map key, skip the query when LabelID=0, demote the now rarely
triggered orphaned-label log to Debug, and fix a typo ("Commit" ->
"Comment") in that message.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-26 12:31:30 +00:00
silverwind
14ca2e7526 fix(ui): avoid layout shifts in overflow-menu and repo filter (#37818)
Eliminate two layout shifts in the menu, one related to non-existant
label on page load and one to `0` value rendering.

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-07-26 10:26:52 +00:00
silverwind
b4eb8c03c4 chore: generate codemirror languages from linguist-languages package (#38624)
Replace the previous main-branch fetch with data from npm package
[`linguist-languages`](https://github.com/ikatyang-collab/linguist-languages)
and only run generate targets when actually needed. The removed
`fileFilters` were leftovers from the nolyfill removal.
2026-07-26 10:19:53 +00:00
GiteaBot
a2166293f7 [skip ci] Updated translations via Crowdin 2026-07-26 00:56:06 +00:00
bircni
396ec646f6 fix(actions): improve runner list status sorting, labels and task job links (#38586)
Several small fixes to the Actions runner management UI.

### Runner task list links to the job, not the workflow run
Relabeled the first column from "Run" to "Job"; it now shows the job ID
and links to the specific job (`/actions/runs/{runID}/jobs/{jobID}`).
Renamed locale key `task_list.run` to `task_list.job`.

### Missing "Disabled" translation
The runner list rendered a grey label via `actions.runners.disabled`,
but that key
did not exist in `locale_en-US.json`, so the raw key string leaked into
the UI.
Replaced `"actions.runners.disabled"` with `"disabled"`.

### Status column sorting ignored active vs idle
Sorting by status ordered purely on `last_online`, but the displayed
status is
computed from both `last_online` (offline) and `last_active` (idle vs
active).
As a result idle runners were interleaved with active ones. Sorting now
ranks by
the computed status (active → idle → offline). Disabled runners sink to
the bottom
of their status group (`is_disabled` as a secondary key), with
`last_online`/`id`
as stable tiebreakers so pagination stays deterministic.

### Status label colors
Active and idle both rendered green. Idle is now yellow, active green,
and
offline/unknown grey; the separate grey "Disabled" badge is unchanged.
This keeps
connectivity visible even for disabled runners (e.g. a disabled runner
still shows
whether it is idle or offline).

<img width="715" height="406" alt="image"
src="https://github.com/user-attachments/assets/9ef06aa8-a870-4de5-9d94-603a58186908"
/>

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-25 21:31:19 +00:00
bircni
69f0a10364 fix(actions): correctness and hardening fixes (#38518)
Various fixes to actions

1. **Cap total jobs per run in reusable-workflow expansion** — only
nesting depth was capped, so fan-out + nested reusable workflows could
explode job-row inserts and exhaust the DB from a single push. Now
enforces `MaxJobNumPerRun` in the insert path.
2. **Reject rerun-failed when a run has no failed jobs** — an empty job
list meant "re-run everything", so `rerun-failed` on a green run re-ran
all jobs. Now errors (web + API).
3. **Don't adopt external commit statuses into the legacy hash** — the
pre-#35699 Context-only hash matched API-posted statuses too, collapsing
two same-named workflows into one check. Now limited to Actions-user
rows.
4. **Don't cut post-cancel cleanup short in `StopEndlessTasks`** — the
sweep force-stopped just-cancelled jobs mid-cleanup. Now targets
`StatusRunning` only; stalled cancels stay covered by `StopZombieTasks`.
5. **Avoid redundant run reload in `GenerateGiteaContext`** — resolving
`github.triggering_actor` reloaded the run already passed in. Now loads
only the trigger user via new `ActionRunAttempt.LoadTriggerUser`.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-25 16:07:40 +00:00
silverwind
7a1941e384 chore: remove goreportcard badge (#38630)
Service is discontinued as per https://goreportcard.com/, remove the dysfunctional badge.
2026-07-25 17:39:25 +02:00
Shudhanshu Singh
fadaf36e95 fix(repo): prevent double-write redirect collisions on dependency errors, fix ui (#38627)
1. fix `AddDependency` and ` RemoveDependency` to respond correctly
2. refactor the form to use "form-fetch-action"
3. fix the issue dependency icon layout regression

---------

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-25 20:39:36 +08:00
Giteabot
39cc4db4ba fix(deps): update module github.com/getkin/kin-openapi to v0.144.0 [security] (#38623) 2026-07-24 22:14:44 +00:00
silverwind
13406e7aa7 chore: update and relax revive naming rules (#38615)
- enable `skip-initialism-name-checks`, so names like `sessionUid` are
allowed
- replace removed `skip-package-name-checks` option with new
`package-naming` with fitting rules for gitea
- drop dead exclusion paths
2026-07-24 21:47:23 +00:00
silverwind
818da42728 ci: add missing type/refactoring label for refactor PR titles (#38611)
Add missing mapping for `refactor` type to set `type/refactoring` label automatically on PRs.
2026-07-24 21:21:46 +00:00
Giteabot
be41cff5e2 fix(deps): update dependency js-yaml to v5.2.2 [security] (#38622) 2026-07-24 23:20:03 +02:00
bircni
608c248638 docs: Update Security docs (#38422)
Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2026-07-24 21:28:35 +02:00
silverwind
37e80730ec fix: abort superseded issue suggestion requests (#38620)
Fix regression from https://github.com/go-gitea/gitea/pull/38610

`perfect-debounce` allowed only one request in flight; `debounce` does
not, so a slow older request can resolve after a newer one and leave a
stale suggestion menu. Superseded requests are now aborted.
2026-07-24 21:25:38 +02:00
Luc
9ad80e1eba fix: delete repo-scoped rows of seven more tables when deleting a repository (#38534)
Fixes #38494

`DeleteRepositoryDirectly` left rows behind in seven registered tables
carrying a repo-scoped key: `action_variable`, `action_run_attempt`,
`action_tasks_version`, `renamed_branch`, `commit_status_summary`,
`commit_status_index` and `repo_transfer`. Repository IDs are `pk
autoincr` and never reissued, so the orphaned rows were unreachable, but
they accumulated forever (unbounded table growth, referential
inconsistency; not a security issue, see the issue discussion).

This adds all seven to the `deleteBeans` cascade, each placed next to
its sibling bean
(`CommitStatus`/`CommitStatusIndex`/`CommitStatusSummary`,
`Branch`/`RenamedBranch`, `Secret`/`ActionVariable`,
`ActionRun`/`ActionRunAttempt`). `repo_transfer` goes through the same
cascade rather than `DeleteRepositoryTransfer` so teardown stays one
mechanism; the existing reaper remains for the transfer flows.

The test inserts one row per previously-orphaned table (the fixture
files ask test cases to prepare their own data), deletes the repository,
and asserts each table is purged. Without the fix it fails on all seven
tables.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: Luc <luuuc@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-24 17:40:39 +00:00
silverwind
66794e0549 refactor: replace debounce/throttle deps with first-party code (#38610)
Adds `web_src/js/utils/func.ts` with `debounce` and `throttle`, dropping
`throttle-debounce` and `perfect-debounce` dependencies. Both were
needed before: the former has no promise-returning debounce, which
`TextExpander` requires, and the latter no `throttle`.

Signature follows lodash and es-toolkit: `(func, wait, {leading,
trailing})` plus `cancel`, so argument order flips at the migrated call
sites.
2026-07-24 16:39:15 +00:00
mohammad rahimi
1e42317b66 feat(setting): add shared [redis] section as default for redis-backed subsystems (#38550)
Adds a shared `[redis]` config section with a single `CONN_STR` key that
acts as the default Redis connection for every redis-backed subsystem
whose own connection string is empty: `[cache].HOST`,
`[session].PROVIDER_CONFIG`, `[queue].CONN_STR` and
`[global_lock].SERVICE_CONN_STR`.

Precedence: per-subsystem value > shared `[redis].CONN_STR` > existing
built-in defaults. Fully backward compatible: when `[redis]` is not set,
every code path behaves exactly as before.

Motivation: today an admin with one Redis instance has to repeat the
same connection string in up to four places (five once WebSocket lands).
Requested by @wxiaoguang in [this review
discussion](https://github.com/go-gitea/gitea/pull/36965#discussion_r3613306976)
as a prerequisite for #36965. This follows the pattern used by GitLab
and Mastodon: one global Redis definition, per-subsystem overrides fall
back to it.

Since #12385 all identical connection strings already share one client
via `nosql.Manager`, so pointing all subsystems at `[redis].CONN_STR`
naturally converges onto a single shared connection pool.

Covered by table-driven unit tests per subsystem (fallback applies, own
value wins, absent `[redis]` = unchanged behavior). A config-cheat-sheet
update for gitea/docs will follow once this is merged.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2026-07-24 08:11:20 +00:00
Chaehyeon Lee
5db58f930e fix(webhook): remove slack channel name check (#38608)
Users should know what they are doing, don't check everything,
especially for that we don't understand.

Fixes #23840

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-24 07:45:41 +00:00
silverwind
29b99de940 ci: derive topic labels from PR title (#38595)
Derives topic labels from the PR title scope: `enhance(actions): ...`
gets `topic/gitea-actions`. Scopes map to `topic/<scope>`, with aliases
for names that differ (`oauth` → `topic/authentication`).

Labels stay in sync with the title, while ones applied by hand are left
alone. Scopes with no matching label on the repo are ignored, so no new
labels get created.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2026-07-24 04:43:42 +00:00
Richard Mahn
0e8bafc267 fix: download dropdown menu clipped on the branches page (#38604)
Fixes #38603

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-24 04:24:25 +00:00
silverwind
6791d5a418 enhance(actions): action view enhancements (#38594)
- avoid layout shift on actions log view page load
- retain header border when actions log is scrolled
- when copying step output, only copy with timestamp when timestamp
display is enabled (useful for example when step output is plain json
and copying with timestamp would make that json unparsable).
2026-07-24 04:17:02 +00:00
silverwind
9430e3230f enhance: keep status check list scrolled on merge box reload (#38597)
Remember the scroll offset when the merge box reloads. This is a crude
patch now, a proper fix would be a fully vue-rendered template with a
json payload from backend, which is much more work. Idiomorph was also
considered but deemed unviable.
2026-07-24 04:10:26 +00:00
silverwind
99ca4bff7d fix: make auth source group sync correctly handle team removal (#37161)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-24 02:05:38 +00:00
Mitrahsoft
3c66ec4064 fix(oauth2): enforce mandatory 2FA policy on OAuth2 authorize/grant endpoints (#38591)
Fixes #37407 

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-24 01:39:38 +00:00
wxiaoguang
7065637e61 refactor: hide git repo path details from more packages (#38601)
Remove `RepoPath` from "models/repo" package, remove `UserPath` from
"models/user" package, use `WithRepo` for more places, fine tune tests.
2026-07-23 17:42:55 +00:00
Shudhanshu Singh
86a3048247 fix(project): prevent database mutations on invalid MoveIssues payload (#38600)
Add missing "return" after ServerError

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
2026-07-23 08:51:49 -07:00
wxiaoguang
e992b0a7cc refactor: retry file remove/rename when a file is busy and clean up os detection (#38588)
Rename functions "util.Remove" (remove.go) to "util.RemoveWithRetry"
(file_retry.go) and add comments to clarify their behaviors, also add
tests.

Refactor callers: when no concurrent access (cmd cli, migration, app
init, test), use "os.Xxx" directly.

More details are in `modules/util/file_retry.go`

By the way, clean up OS (windows) detection, make FileURLToPath test
always run
2026-07-23 14:31:29 +00:00
Mohamed Belkamel
c4fc4d363b fix(actions): make SingleWorkflow.Marshal round-trip multi-line run blocks (stop silent job stranding) (#38520)
## Problem
Jobs that call a reusable workflow (`uses:`) whose steps contain a `run:
|` block that **starts with blank lines** never start — the child jobs
stay `Blocked` forever, the run never finishes, and nothing is shown to
the user (the error is only logged at DEBUG). The reusable is valid YAML
and worked on 1.26.

## Root cause
`jobparser` serializes each expanded job via `SingleWorkflow.SetJob()`,
which uses `yaml.NewEncoder(...).SetIndent(2)`, but
`SingleWorkflow.Marshal()` used `yaml.Marshal`, whose default
indentation is **4**. Re-emitting a multi-line literal block scalar at a
different indentation makes the encoder write a wrong explicit
indentation indicator (`run: |4`) whose declared indent doesn't match
the actual content indent. The stored `workflow_payload` is then
unparseable:

```
run: |4


                while ...
```

`jobparser.Parse` / `model.ReadWorkflow` (both go.yaml.in/yaml/v4)
reject it: `did not find expected key`. This surfaces in
`services/actions/job_emitter.go` `resolve()` →
`updateConcurrencyEvaluationForJobWithNeeds` → `ParseJob`, where the
error is swallowed at `log.Debug` and the job is left `Blocked`.

Encoding at indent 4 triggers the bad indicator; indent 2 does not —
matching the value already used by `SetJob`.

## Fix
Encode `SingleWorkflow.Marshal()` with `SetIndent(2)` so both encoders
agree and the serialized single workflow round-trips. Adds a regression
test (`Parse → Marshal → Parse` on a `run:` block with leading blank
lines) that fails before the change with `did not find expected key`.

---------

Signed-off-by: quasimodo <mohamed.belkamel@intelcia.com>
Co-authored-by: quasimodo <mohamed.belkamel@intelcia.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-23 15:42:21 +02:00
Shudhanshu Singh
6ac19f6226 fix(issue): display error toast on batch action failures instead of reloading page (#38593)
Batch operations in the issues list (labels, milestones, projects,
assignees, etc.) could fail silently because `updateIssuesMeta` caught
and suppressed fetch errors internally. The caller would then proceed to
reload the page, clearing all selected issue checkboxes and giving the
impression that the operation had succeeded.

- Remove updateIssuesMeta and use our "fetch-action" framework to handle errors and page reloading
- Refactor backend code to use ctx.JSONError

---------

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-23 09:33:47 +00:00
Shudhanshu Singh
ee10ae168c perf(emoji): optimize FindEmojiSubmatchIndex using slice-based Trie (#38573)
This pull request optimizes `FindEmojiSubmatchIndex` in Gitea's emoji
package (`modules/emoji/emoji.go`) by replacing the
`strings.Replacer`-based search with a slice-based trie and a
constant-time starting-byte check (`isStartingByte`).

The new implementation avoids heap allocations during the search and
reduces CPU overhead when rendering Markdown, particularly for plain
text that does not contain emojis.

### Verification

Verified with unit tests:

```sh
go test -count=1 ./modules/emoji/...
```

Benchmarks:

```sh
go test -bench=. -benchmem ./modules/emoji/...
```

### Results

| Benchmark | Before | After |
| ---------- | ------ | ----- |
| `BenchmarkFindEmojiSubmatchIndex` | 168.3 ns/op, 2 allocs/op | 85.78
ns/op, 1 alloc/op |
| `BenchmarkFindEmojiSubmatchIndexNoMatch` | 239.8 ns/op, 1 alloc/op |
105.1 ns/op, 0 allocs/op |
### Benchmark Output

```text
goos: linux
goarch: amd64
pkg: gitea.dev/modules/emoji
cpu: Intel(R) Core(TM) i5-7400 CPU @ 3.00GHz

BenchmarkFindEmojiSubmatchIndex-4              13498539        85.78 ns/op      16 B/op   1 allocs/op
BenchmarkFindEmojiSubmatchIndexNoMatch-4       11220450       105.1 ns/op        0 B/op   0 allocs/op
BenchmarkFindEmojiSubmatchIndexOld-4            6569360       168.3 ns/op      48 B/op   2 allocs/op
BenchmarkFindEmojiSubmatchIndexOldNoMatch-4     5026116       239.8 ns/op      32 B/op   1 allocs/op
```

---------

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-23 08:29:28 +00:00
silverwind
acbdc44d00 test(e2e): add pull request merge box test, update AGENTS.md (#38576)
Adds browser-level coverage for the pull request merge box, which
currently had no tests. Exercises the full merge flow: open the PR,
expand the merge form, submit, and assert the merged state.

Extracted from https://github.com/go-gitea/gitea/pull/36759.

Also updated AGENTS.md with instructions for e2e tests.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-07-23 09:29:36 +02:00
Shudhanshu Singh
560edd45e9 fix(api): align Swagger schemas for UserSettings and TopicListResponse (#38590)
Corrects the Swagger/OpenAPI schemas for `/user/settings` and
`/topics/search` to match the actual JSON objects returned by the API.

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
2026-07-23 14:49:52 +08:00
yszl666
20221e1faa fix(file-tree): handle submodule links and missing view container (#38033)
Prevent tree view navigation from intercepting submodule entries so the
browser can follow external repository links normally.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: dzf <douzf@sparkspacetech.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-23 02:05:43 +00:00
wxiaoguang
1acf93d854 refactor: clean up git repo and model migration packages (#38564)
enable the golangci depguard lint rule: deny "models" and its sub
packages in "modelmigration" package.
2026-07-23 01:22:26 +00:00
GiteaBot
fa64b4627a [skip ci] Updated translations via Crowdin 2026-07-23 00:51:47 +00:00
Zettat123
5edb45dc1a fix(actions): fail unexpandable reusable workflow callers and decouple the job emitter's cross-run processing (#38565)
## Changes

### 1. Handle reusable workflow expansion failures

If a reusable workflow caller job is invalid (e.g. uses a workflow with
syntax error), the job emitter should mark it as failed instead of
retrying.

Related:
https://github.com/go-gitea/gitea/pull/38518#discussion_r3608882095

### 2. No longer process concurrent run inline

**Before**: If a run(R1)'s status change unlocks another blocked run(R2)
via concurrency group, the job emitter will process R2 in R1's
transaction.

**Current**: No longer process R2 inline and emit the ID of R2 to let
another pass process it.
2026-07-22 20:54:51 +00:00
Paarth
fa3780268c feat(repo): support file exclusion logic in .gitea/template in template generation (#38064)
This PR adds support for file exclusion into `.gitea/template`

Docs: https://gitea.com/gitea/docs/pulls/470

### Usage

Template owners add a `.gitea/template` file to their template
repository:

```
# Files to include for variable expansion
*.go
text/*.txt
**/modules/*

# Files/directories to exclude from the generated repo
[exclude]
vendor/*
node_modules/
src/build/
```

Resolves #37202

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-22 20:14:18 +00:00
silverwind
1d698f12ce chore: replace uint8-to-base64 with native btoa/atob (#38579)
Replace the single-use `uint8-to-base64` dependency with native
`btoa`/`atob`. The implementation is exactly identical.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-07-22 21:37:11 +02:00
wxiaoguang
ae2ff1b467 fix: branch protection user list (#38570)
fix #38569, also fix the UI
2026-07-22 18:48:39 +00:00
Harsh Satyajit Thakur
4563c2c1b7 fix: keep serving valid ACME cert when renewal fails at startup (#38554)
Fix #38519

When `ENABLE_ACME` renew fails during startup (e.g. CA unreachable),
`ManageSync` currently aborts even if a still-valid certificate is on
disk, so HTTPS never comes up.

If `CacheManagedCertificate` finds a non-expired cert, log the manage
error, continue with that cert, and kick `ManageAsync` for background
retries. First-time install / expired-or-missing cert still fails
closed.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-22 18:03:42 +00:00
silverwind
ed5f254ee3 chore: separate go minimum and build toolchain (#38559)
- split the go version in `go.mod` into `go` and `toolchain` again
- add a workaround for https://github.com/golang/go/issues/75331 so
`make tidy` never drops `toolchain`
- configure renovate to bump `go` on minor releases and `toolchain` on
every release
- go and toolchain bumps land in a separate, fast-tracked PR, as both
carry security fixes

Replaces: https://github.com/go-gitea/gitea/pull/37846
2026-07-22 17:26:55 +00:00
Shudhanshu Singh
48e067e4b3 fix(issue): make issue action (issue list batch operation) elements have correct attributes (#38575)
The "Clear projects" action in the issue list batch operations doesn't
work. When users select multiple issues and choose to clear their
project assignments, the operation fails because:

1. The frontend sends a project ID of `0` to represent "no project"
2. The backend passes this invalid ID directly to
`IssueAssignOrRemoveProject` without filtering
3. The backend tries to look up a project with ID `0`, which doesn't
exist, resulting in a `project 0 not found` error
4. Selected issues remain assigned to their projects instead of being
removed

Fixes #38571 

## Root Cause

The issue is a regression from the multi-project feature (#36784). The
frontend was using `data-element-id="0"` to represent "clear" actions,
but the backend doesn't filter out this invalid ID before validation.

## Solution

### Template Changes (`templates/repo/issue/filter_actions.tmpl`)
- Changed `data-element-id="0"` to `data-element-id=""` for the "Clear
projects" action (line 78)
- Changed `data-element-id="0"` to `data-element-id=""` for the "Clear
milestone" action (line 47)
- Removed the duplicate "no select" assignee option that was using
`data-element-id="0"` (lines 116-118)

### Frontend Logic Changes (`web_src/js/features/repo-issue-list.ts`)
- Made `elementId` a `const` instead of `let` (line 60) to prevent
mutations
- Removed the workaround code that was trying to handle
`data-element-id="0"` for assignees (lines 65-69)
- Updated comment from "for toggle" to "for label toggle" for clarity
(line 71)

---------

Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-22 16:44:27 +00:00
Elisei Roca
c8df67c0d0 fix(pulls): respect diff.orderFile in diff file tree (#38566)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-22 15:56:29 +00:00
Giteabot
bc3f63095b fix(deps): update module google.golang.org/grpc to v1.82.1 [security] (#38567) 2026-07-22 16:47:29 +02:00
Rayan Salhab
ae7bafe445 enhance: improve diff contrast in light and dark themes (#37477)
Fixes https://github.com/go-gitea/gitea/issues/37448

Adjust the diff stat counter and syntax colors in both light and dark
themes to github-like colors that meet a >= 5:1 contrast floor (>= 7:1
for syntax names on diff rows), and make the counters semibold.

---------

Signed-off-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: Hermes Agent (GPT-5.5) <hermes-agent@nousresearch.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Hermes Agent <hermes@noreply.local>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-22 13:42:52 +02:00
silverwind
871c807b95 enhance(ui): tweak tooltip style and misc fixes (#38524)
Tooltip changes:

- center tooltips over their target (default placement `top` instead of
`top-start`)
- fix the arrow rendering off-center on references narrower than 22px
like 16px icons, caused by tippy's hardcoded 3px arrow padding
- fix arrowed tooltips overlapping their reference by 1px because the
offset distance was derived from the raw arrow option
- tweak tooltip colors and reduce content padding
- fix a Firefox bug where lazily created tooltips would not show on the
first hover, because Firefox skips enter/leave event dispatch when no
such listener existed in the window at the time of the pointer crossing

Other UI tweaks:

- tweak repo sidebar release style
- enhance actions back link
- center step chevron
- add border on workflow graph

<img width="92" height="76" alt="Screenshot 2026-07-21 at 18 07 42"
src="https://github.com/user-attachments/assets/968a51de-e470-48c0-b592-4a207dfcbb63"
/>
<img width="94" height="76" alt="Screenshot 2026-07-21 at 18 07 53"
src="https://github.com/user-attachments/assets/5e8e0ce3-7536-4689-a6ae-9cfb6e867ddb"
/>


<img width="203" height="77" alt="Screenshot 2026-07-19 at 00 14 32"
src="https://github.com/user-attachments/assets/71c16a54-ad14-4636-8af4-e68b3599f1d0"
/>
<img width="309" height="290" alt="Screenshot 2026-07-18 at 23 37 21"
src="https://github.com/user-attachments/assets/29630504-d69a-4e23-978b-41ae6d0bff92"
/>
<img width="151" height="110" alt="Screenshot 2026-07-18 at 23 35 47"
src="https://github.com/user-attachments/assets/b8107602-12f1-4f9b-a3f6-b930af25c9e7"
/>

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-22 00:22:05 +00:00
wxiaoguang
4af9156c36 refactor: implement mcaptcha client and add comments/tests (#38561)
End users still need it, so it's better to make it maintainable with
tests and OOM-safe.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-07-21 13:41:54 +00:00
wxiaoguang
91eb14c944 refactor: use WithRepo instead of WithDir for most git operations, clean up model migrations (#38555)
by the way, remove some unnecessary "models" imports from model
migration package, fix migration test model init bug
(modelmigration/migrationtest/tests.go)
2026-07-21 12:18:00 +00:00
okxint
8731ea4bbb fix(actions): align status icon span for Safari rendering (#38558)
Fixes #38553

The `<span>` wrapping the SVG in `ActionStatusIcon.vue` had no explicit
display or alignment styles. Safari computes baseline alignment for
inline spans differently from Chrome/Firefox, causing the icon to shift
vertically in the action matrix build status list.

**Fix:** make it inline-flex

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-21 17:44:15 +08:00
Zettat123
7cfd421aa8 fix(actions): support matrix when evaluating workflow if expression (#38474)
Partially fixes #38466

Added `matrix` to the evaluator for `if` expressions

---------

Signed-off-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
2026-07-21 06:43:45 +00:00
Shudhanshu Singh
c0f4e5583e feat(repo): prioritize well-known READMEs and optimize discovery (#38532)
This PR adjusts the repository README discovery logic to mirror GitHub's
priority order, while retaining support for Gitea's specific `.gitea/`
directory.

Previously, Gitea would prioritize a `README.md` at the root of the
repository over any READMEs in `.gitea/` or `.github/`. With this
change, well-known subdirectories are evaluated first when viewing the
repository root.

**New Priority Order:**
1. `.gitea/README.md`
2. `.github/README.md`
3. `/README.md` (root directory)
4. `docs/README.md`

This allows repository maintainers to use `.github/README.md` (or
`.gitea/README.md`) as the repository homepage without having to remove
or rename generic root `/README.md` files required by package managers
(like NPM, Crates.io, Docker, etc.).

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-21 11:44:25 +08:00
wxiaoguang
a4526d5a82 refactor: remove Path field from git.Repository (#38552)
The "path" details should be hidden to other packages

By the way, fix a resource leaking in gogit's CommitNodeIndex
(the file was not closed in CacheCommit)
2026-07-20 18:03:28 +00:00
wxiaoguang
9dc04289aa refactor: make git package handle all git operations (#38543)
before: gitrepo vs git packages
after: git package fully handle all git operations

by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide
path details.

benefits:

1. remove all unnecessary wrappers, developers no need to struggle with
"which package should be used"
2. simplify code, RepositoryFacade can (will) be used everywhere, all
"path" details are (will be) hidden
2026-07-20 16:07:38 +00:00
silverwind
b8977eeb47 chore(renovate): group all dependency updates into one weekly PR (#38547)
Group updates from all managers into a single `dependencies` group so
monday produces one combined PR instead of one per manager, matching
what is regularly done manually to reduce CI waiting time. The `gomod`
and `npm` rules remain for their `postUpgradeTasks`, which run once on
the combined branch.

Also replace the all-day monday schedule with the `schedule:weekly`
preset (monday 0-4 UTC) so updates whose `minimumReleaseAge` lapses
later in the day wait for the next weekly batch instead of raising a new
PR the same day, as happened in
https://github.com/go-gitea/gitea/pull/38546. `vulnerabilityAlerts` and
the go toolchain rule bypass the schedule as before.
2026-07-20 15:01:32 +00:00
Giteabot
17ce342dcb chore(deps): update dependency @codemirror/lang-markdown to v6.5.1 (#38546) 2026-07-20 10:57:04 +00:00
wxiaoguang
6525e6df15 fix: revert git clone http redirection forbidden (#38530)
Revert to 1.26 behavior.

Incomplete, HandleGitCmdHTTPRedirection can be improved
2026-07-20 10:04:23 +00:00
Giteabot
2fec2affc4 chore(deps): update dependencies (#38538)
Also include:
chore(deps): update npm dependencies #38542
chore(deps): update action dependencies #38541
chore(deps): update module golang.org/x/vuln to v1.6.0 #38540
2026-07-20 09:27:21 +00:00
silverwind
f9819dbb74 test(e2e): deterministically wait for event stream in logout propagation test (#38535)
The test raced the server-side SSE channel registration: a logout
emitted before registration is silently dropped ([example
flake](https://github.com/go-gitea/gitea/actions/runs/29699349255/job/88225654080)).
The 500ms wait from https://github.com/go-gitea/gitea/pull/37403 only
made this unlikely.

The server now registers the channel before sending the initial response
bytes, so an open connection implies registration. The shared worker
forwards the built-in `open` event (replaying it to late-attaching ports
via `EventSource.readyState`), the page exposes it as a
`data-user-events-connected` attribute, and the test waits for that
attribute instead of a fixed timeout.
2026-07-20 08:44:53 +00:00
Giteabot
173965f66b chore(deps): update python dependencies (#38539) 2026-07-20 08:17:04 +00:00
silverwind
b3b9c4903e chore(deps): update eslint, remove eslint-plugin-sonarjs (#38536)
- update eslint and related dependencies, `eslint-plugin-unicorn` to v72
with new rules enabled
- remove `eslint-plugin-sonarjs` (mostly obsolete or slow rules)
- extract shared eslint args into `ESLINT_ARGS` make variable
- rewrite ignore patterns in `eslint.json.config.ts` as explicit
root-relative paths: add `data` (local server data failed `lint-json`),
drop `node_modules` (ignored by default)
- fix a broken link in `CONTRIBUTING.md` (discovered during markdown
linting with eslint, but decided to not add that yet)

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-20 10:08:34 +02:00
wxiaoguang
fff32e9469 refactor: prepare to decouple the "model migration" package and "models" package (#38533)
Migrations should never use model structs directly, because the model
structs can be different in different releases. e.g. if one migration uses
"User" model, it works in the early releases, then one day, when the
User model changes, the migration breaks because it will use the
new (incorrect) User model, it should only use the old User model.

The same to "modules/structs".

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
2026-07-20 03:42:02 +00:00
wxiaoguang
775e3bdb34 refactor: remove unnecessary git command wrapper functions (#38531)
Removed `gitrepo.RunCmd*` functions, because gitcmd.Command works with
Repository directly.

Move some "local filesystem" related function into "localfs.go"
2026-07-20 03:17:25 +00:00
GiteaBot
730b6f2daf [skip ci] Updated translations via Crowdin 2026-07-20 00:53:27 +00:00
wxiaoguang
b06002f449 refactor: git repo and relative path handling (#38522)
1. simplify "StorageRepo" code
2. simplify git.OpenRepository code
3. by the way, clean up some unused files in git test fixtures.
2026-07-19 07:32:00 +00:00
YumeMichi
ae176cd649 refactor: clean up fragile diff render templates, use backend typed structs (#38517)
Blob.Size requires a context after the repository context removal
(5b078f72aa).

Actually the template code should just render, it should not depend on
the fragile dynamic calls to backend functions.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-19 11:20:01 +08:00
bircni
e8befe0268 fix(actions): coerce workflow_dispatch boolean inputs to native types (#38472)
A `workflow_dispatch` job that has `needs:` **and** an `if:` comparing a
boolean
input against a boolean literal never runs — it stays `Blocked` forever
even
though its needs succeed. Minimal repro:

```yaml
on:
  workflow_dispatch:
    inputs:
      deploy:
        type: boolean
        default: true
jobs:
  build:
    runs-on: ubuntu-latest
    steps: [{ run: echo build }]
  deploy:
    needs: build
    if: ${{ inputs.deploy == true }}   # never true on Gitea
    runs-on: ubuntu-latest
    steps: [{ run: echo deploy }]
```

On GitHub this runs; on Gitea `deploy` is stuck. Jobs **without**
`needs` are
unaffected, which makes it look like a matrix/`needs` bug — it isn't.

## Root cause

`workflow_dispatch` stores boolean inputs as the strings
`"true"`/`"false"`
(`ctx.FormBool` → `strconv.FormatBool` in the web path, plain strings in
the API
path). Since #37478 (shipped in **v1.27.0**), `evaluateJobIf` runs
**server-side**
as part of the job-emitter resolver passes. For a `needs`-gated job the
server
therefore evaluates `inputs.deploy == true` with `inputs.deploy` being
the string
`"true"`; comparing a string to a boolean coerces to `NaN == 1` →
`false`, so the
job is never dispatched.

Jobs without `needs` skip this server-side gate and are evaluated by the
runner,
which coerces the string to a real bool — that's why they keep working,
and why
the same input yields opposite results in the two paths.

## Fix

Normalize `type: boolean` dispatch inputs to native JSON booleans in the
shared
`DispatchActionWorkflow` path, so it covers both the web and API entry
points in
one place. The coerced value flows into both the run's `EventPayload`
(read back
by the server-side `if` evaluation and by the runner) and the
creation-time
parse, so all consumers agree.

This matches GitHub, whose `inputs` context "preserves Boolean values as
Booleans
instead of converting them to strings", and mirrors the native JSON
types Gitea
already sends for `workflow_call`. Only booleans are coerced; other
input types
are left untouched, and a value that is already a bool (JSON API
request) passes
through.

Note: this makes dispatch payloads carry native booleans, which the
runner
consumes correctly as of gitea/runner#1087 (it accepts a native bool and
keeps
the string fallback) — the same expectation `workflow_call` already
relies on.

Fixes https://github.com/go-gitea/gitea/issues/38466
2026-07-18 17:08:09 +02:00
wxiaoguang
66e3849a70 refactor: correct git repo design and fix some legacy problems (#38512) 2026-07-18 15:28:14 +02:00
wxiaoguang
7786df34cf fix: make the merge box button red if some checks fail (#38508)
fix #38506

* 1.26: the "show form" button and "submit form" button are all red if
some checks fail
* 1.27.0: the "show form" button uses primary color, while "submit form"
button is red if some checks fail
* this fix: revert to 1.26
2026-07-18 09:44:01 +00:00
Knight
c6791c3c58 fix: clean up orphaned user-keyed tables in deleteUser (#38511)
### Description
This PR addresses orphaned user-keyed tables during user deletion by
adding the missing models to the `db.DeleteBeans` call in
`services/user/delete.go`:
- `auth_model.TwoFactor` (TOTP secrets)
- `auth_model.WebAuthnCredential` (WebAuthn keys)
- `activities_model.Notification` (Notifications)
- `issues_model.IssueWatch` (Issue watches)

Additionally, it adds corresponding unit test coverage in
`services/user/user_test.go` to assert that these records are cleaned up
successfully.

### Related Issues
Fixes #38510

Signed-off-by: pranav718 <raypranav718@gmail.com>
2026-07-18 09:38:36 +02:00
GiteaBot
2cc28ac2a9 [skip ci] Updated translations via Crowdin 2026-07-18 00:46:45 +00:00
bircni
ba7c84673b docs: Update Changelog for 1.27 (#38440)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-17 15:31:40 +00:00
wxiaoguang
3027794c44 refactor: fix legacy problems in cmd/serv.go (#38505)
1. use var names "reqOwnerName, reqRepoName", because these values are
from request and should not be really used for path construction
2. simplify enable-pprof logic, don't "log.Fatal"
3. don't make runServe command to guess the repo storage path, instead,
let server return RepoStoragePath
4. don't process lfs verbs when the repo is a wiki
5. construct the request URI path correctly for the lfs transfer backend
(moved to the caller)
6. don't call "owner, err := user_model.GetUserByName", the "owner
rename redirection" has been done before
7. fix incorrect "repo.OwnerName = ownerName", the real owner might have
been "redirected"
8. fix incorrect "inactive owner" check, it should be checked even if
the repo is redirected


Use general error functions to handle responses, error handling code is
hugely simplified.
2026-07-17 22:28:59 +08:00
wxiaoguang
5b078f72aa refactor: remove Ctx field from git.Repository (#38500) 2026-07-17 18:44:31 +08:00
wxiaoguang
2c8e99bbf7 fix: make commit message merge correctly (#38490)
fix #38487

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
2026-07-17 07:44:05 +00:00
TowyTowy
8a3daef525 fix(repo): stop advertising HTTP clone URLs when DISABLE_HTTP_GIT is set (#38378)
Fixes #38339

---------

Signed-off-by: TowyTowy <towy@airreps.link>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-17 05:17:28 +00:00
GiteaBot
bca44bd736 [skip ci] Updated translations via Crowdin 2026-07-17 00:51:13 +00:00
silverwind
b4e601339f ci: retry snap store upload on transient connection drops (#38461)
Add a retry to snapstore. Claude can't seem to figure out the reason why
so many nightly uploads fail and the only logical conclusion is it's a
server-side issue at Canonical, so retries might help.

Example:
https://github.com/go-gitea/gitea/actions/runs/29395231850/job/87287126446
2026-07-16 21:56:31 +00:00
Eyüp Can Akman
a49c893842 fix(pull): sign the commit when updating a branch by merge (#38441)
Updating a branch by merge produced an unsigned commit even when merges
are configured to be signed. Update by rebase was unaffected.

`Update()` builds a fake reverse PR to switch head and base, and it has
no `Index`, so `pr.GetGitHeadRefName()` resolves `refs/pull/0/head`.
Since #36186 `SignMerge` looks that ref up in the base repository
instead of the temporary merge repo. That lookup fails. The caller
dropped the error, so `sign` stayed false.

Sync fork goes through the same fake-PR path.

Pass both sides of the merge to `SignMerge` as refs and evaluate them in
the temp repo, where `base` and `tracking` always exist.

Tests cover a signed and an unsigned update by merge.

Fixes #38066
2026-07-16 21:38:39 +00:00
bircni
9d3f04eb7e fix(actions): explain why a blocked or waiting job has not started (#38476)
When an Actions job is blocked or waiting, the job view only shows the
generic **Blocked** / **Waiting** label. Users have no way to tell *why*
a job is stuck — whether it's waiting on dependencies, waiting for a
runner that doesn't exist, waiting for a runner whose labels don't
match, or simply queued behind busy runners.

## Change

The current-job detail line now surfaces the actual cause:

- **Blocked** → lists the dependency jobs (`needs`) that haven't
finished yet, e.g. *"Waiting for the following jobs to complete:
build."*
- **Waiting**, no online runner → *"No runner is online to pick up this
job."*
- **Waiting**, online runners but none match `runs-on` → *"No matching
online runner with label: X"* (reuses the existing string)
- **Waiting**, a matching runner exists but hasn't claimed the job →
*"Waiting for a matching runner to become available."*

The runner lookup reuses the same available-online-runner query the run
list already performs, and only runs while a selected job is actually
pending. Dependency resolution is scoped to the same parent job and
treats matrix expansions of a `need` as pending until all of them
complete.
2026-07-16 23:10:43 +02:00
Zettat123
2e0aa7ec74 fix(actions): make cancelled() work in job if evaluation (#38495)
Fix #38485

#37478 moved `if` evaluation from runner to server. But `NewInterpeter`
always provides a nil `JobContext` for the evaluator, which makes
[`cancelled()`](ad967330a8/act/exprparser/functions.go (L299))
panic on `Job.Status` because `Job` is a nil pointer.
2026-07-16 22:28:53 +02:00
silverwind
f0d9af3a18 enhance(ui): use OpenAPI 3.0 spec in API viewer, add spec buttons (#38478)
Follow-ups from https://github.com/go-gitea/gitea/pull/37038:

- Render the OpenAPI 3.0 spec in the API viewer, it is richer than the
Swagger 2.0 rendering
- Replace the back link with gitea-styled buttons to view both specs and
return to Gitea, flowing above swagger-ui on viewports where they would
overlap the title
- Key `VisibilityModes` by the string enum type, now named
`VisibilityString`, removing the `string()` casts at API call sites
- Drop the raw visibility strings from the `Service` settings struct in
favor of the typed mode fields, which also removes the org default
visibility row from the admin config page
- Fix two pre-existing issues surfaced in review: an invalid
`DEFAULT_USER_VISIBILITY` was silently accepted as public, and the org
visibility error message showed the enum zero value instead of the
submitted input

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-16 17:38:33 +00:00
bircni
02f82a2054 fix(actions): show retention info on hover for expired artifacts (#38477)
The artifact info hover tooltip (#37100) only worked for live artifacts.
Hovering an expired/deleted artifact showed nothing, and even on live
artifacts the retention date was missing on real runs.

Two root causes:

1. **Expired artifacts had no tooltip.** In the run view sidebar,
expired artifacts render in a separate branch that omitted
`data-tooltip-content` entirely, so there was nothing to hover.
2. **`ExpiresUnix` was no longer sent.** The `ActionRunAttempt` refactor
(#37119) dropped `ExpiresUnix` from `fillViewRunResponseArtifacts`, so
real runs always returned `0` and the tooltip fell back to size-only.
Only the devtest mock still populated it, which masked the regression.

Artifacts without a recorded expiry (`expiresUnix <= 0`) still degrade
gracefully to a size-only tooltip. Both states can be previewed on the
`/devtest/mock/*` actions run page.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-16 15:58:31 +02:00
bircni
1850a108e5 fix(actions): group reusable-workflow matrix legs in the workflow graph (#38475)
Fixes the UI issue reported in #38466: matrix jobs that invoke a
reusable workflow (`build-call (linux)`, `build-call (windows)`, …) were
rendered as separate nodes in the run's workflow graph, while an
equivalent regular matrix
(`build (linux)`, `build (windows)`, …) collapsed neatly into a single
matrix node.

Before: three loose `build-call (...)` boxes next to one grouped `build` box.
After: both matrices collapse into a single node.
2026-07-16 20:58:05 +08:00
wxiaoguang
875b2e8def fix: full file highlighting for git diff with CR char (#38484)
fix #38481
2026-07-16 11:36:46 +00:00
Gaurav Dubey
f15868d442 fix(packages): serve noarch Alpine index for any requested architecture (#38479)
Fixes #38456

## Problem
The Alpine package registry serves one `APKINDEX.tar.gz` per
architecture. When a repository contains only `noarch` packages (no
architecture-specific packages), only the `noarch` index is built.
Because `apk` substitutes `$ARCH` with the host architecture and
requests e.g. `x86_64/APKINDEX.tar.gz`, such a repository returned HTTP
404 and was unusable, matching the report in #38456.

## Fix
`GetRepositoryFile` now falls back to the `noarch` index when the
requested architecture has no index of its own, mirroring the fallback
already present in the sibling `DownloadPackageFile` handler. `noarch`
packages are installable on every architecture, so serving them for any
requested architecture is correct. The index-build side is unchanged;
only the serving path gains the fallback, so mixed repositories (which
already merge `noarch` into each per-architecture index) are unaffected.

## AI assistance disclosure
This change was implemented with the help of an AI coding assistant,
which gitea's CONTRIBUTING.md explicitly welcomes when disclosed. I have
reviewed the change, understand it, and can explain and defend it.

## Tests
Added a `NoArchOnly` subtest to `TestPackageAlpine` that publishes only
a `noarch` package to a fresh repository and asserts that `GET
.../x86_64/APKINDEX.tar.gz` now returns `200` (previously `404`) and
that the served index lists the noarch package. Verified locally with
`go build`/`go vet` on the changed package and a compile of the
integration test package (`go test -c`); the full integration run relies
on CI.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-16 11:51:36 +02:00
Zettat123
a77bf48b41 fix: 500 error when updating user visibility (#38480)
## How to reproduce this bug

1. Create a user with `visibility=public`
2. Update `ALLOWED_USER_VISIBILITY_MODES` setting to `limited, private`
3. Modify any field other than "User visibility" (e.g. "Full Name")
4. UI shows 500 error

## Fix

Only update the visibility field when it actually changes.
2026-07-16 09:53:43 +02:00
silverwind
11363e2f0c chore(renovate): bundle major updates, use chore commit type (#38470)
1. combine PRs for major/non-major, less PRs is less work with the
fixups.
2. always set `chore`, more often than not, this is more correct then
`fix`.

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-07-15 22:10:43 +02:00
wxiaoguang
82edc3da01 refactor: decouple git.Repository(ctx) from git.Commit & git.Tree (#38464)
1. Storing "ctx" in a long-living object is wrong
2. Make the commit & tree cacheable (for the future performance
optimization)
3. Also fix some bad designs like `// FIXME: bad design, this field can
be nil if the commit is from "last commit cache"`

ref:
* #33893
2026-07-15 17:30:01 +00:00
Shudhanshu Singh
ed678b9d45 fix(actions): make job list item fully clickable (#38462)
Clicking the empty space to the right of a job in the Actions sidebar
didn't switch jobs: the interactive `<a>`/`<button>` used `display:
contents` and so generated no clickable box.

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

Drop the wrapper `div` and `display: contents`, moving the `.item` and
layout styles directly onto the `<a>`/`<button>` so the whole row is
clickable. Reusable-caller `<button>` rows also get `width: 100%` and
`line-height: inherit` to match the `<a>` rows.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-07-15 17:06:34 +00:00
wxiaoguang
ce683b34c6 fix: mail template for push event (#38467)
fix #38465
2026-07-15 18:57:44 +02:00
Giteabot
fa50ad2aa9 chore(deps): update dependency djlint to v1.40.4 (#38428) 2026-07-15 13:32:27 +00:00
Giteabot
c86eb7081b fix(deps): update npm dependencies (#38431) 2026-07-15 07:14:50 +00:00
Giteabot
506075c480 fix(deps): update go dependencies (#38429) 2026-07-15 06:47:08 +00:00
Jeremy Stover
9fa2bff5fd fix(admin): exit dev test queue producer loop when context is cancelled (#38451)
Co-authored-by: Kadajett <jeremy@semfora.ai>
2026-07-15 03:25:50 +00:00
xkm
880ddb5724 fix(actions): prevent bulk actions from affecting all runners (#38453)
Fix the bug in the site-admin runner bulk actions introduced by #37869:
the runner IDs are empty then all runners will be deleted.

Fixes #38449

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-15 02:36:03 +00:00
GiteaBot
7c629e1ba7 [skip ci] Updated translations via Crowdin 2026-07-15 00:45:33 +00:00
Harsh Satyajit Thakur
b6904c9730 fix: make "test push webhook" always work (#38425)
* fix #38309
* fix #26238
* fix #37886

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-14 18:24:18 +00:00
Shudhanshu Singh
9c08df8bc8 fix(org): align follow button and wrap description (#38448)
### Description
Fixes the organization page header layout issue where:
1. Long organization descriptions did not wrap and instead stretched the
header container out of bounds.
2. The "Follow" button floated dynamically adjacent to the description's
right edge depending on the description length, instead of remaining at
a fixed position aligned with the right edge of the page container.

Fixes https://github.com/go-gitea/gitea/issues/38445
### Cause
The flex container `<div class="flex-relaxed-list">` lacked `tw-flex-1`
(to grow to fill the container) and `tw-min-w-0` (to allow it to shrink
below its content's size). Consequently, the flex minimum width
defaulted to `min-content`, stretching the container for long unwrapped
descriptions.

### Solution
Added `tw-flex-1 tw-min-w-0` to the `<div class="flex-relaxed-list">`
container in `templates/org/header.tmpl`. This restricts the width,
enables proper text wrapping, and fixes the "Follow" button to the right
edge of the content container.

### Screenshots
Before
<img width="1300" height="615" alt="image"
src="https://github.com/user-attachments/assets/11e6ab99-b847-45ff-8bdb-8622bfeee8aa"
/>
After
<img width="1300" height="615" alt="image"
src="https://github.com/user-attachments/assets/84ac0633-b192-4be5-8226-13bfad3ab2ec"
/>

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-14 17:54:55 +00:00
silverwind
0e4d93537a fix(actions): populate github.event for scheduled runs (#38446)
Scheduled runs stored a `null` event payload, so
`github.event.repository`/`sender`/`organization` were empty in
scheduled workflows. Every other event carries them, and so does GitHub
Actions. `CreateScheduleTask` now synthesizes them.
2026-07-14 18:30:16 +02:00
Giteabot
da5a004fc4 chore(deps): update npm dependencies (major) (#38432) 2026-07-14 13:04:08 +02:00
Giteabot
ed9b02985a fix(deps): update module github.com/google/go-github/v88 to v89 (#38433) 2026-07-14 08:48:58 +00:00
Giteabot
f12a0a9183 chore(deps): update action dependencies (#38430) 2026-07-14 10:24:40 +02:00
wxiaoguang
d15cfa363a chore: don't auto refresh the merge box when user has interacted with it (#38435)
Otherwise, the user just isn't able to use "auto merge" form
2026-07-13 08:38:41 +02:00
bircni
f69e15afe7 fix: various security fixes (#38406)
Addresses a batch of privately reported security issues, grouped by
area:

- **SSRF** - migration PR-patch/asset fetches, OAuth2 avatar & OpenID
discovery, pull-mirror URL re-validation, and the outbound proxy path.
- **Access-token scope** - prevent scope escalation on token creation;
keep public-only tokens confined (feeds, packages, Actions listings,
star/watch lists, limited/private owners).
- **Access control / disclosure** - go-get default-branch leak, webhook
authorization-header leak, watch clearing on private transitions,
label/attachment scoping.
- **Denial of service** - input bounds for npm dist-tags, Debian control
files, Arch file lists, and SSH keys.

### 📌 Attention for site admins

Not breaking - existing configs keep working - but two changes are worth
a look:

- **New SSRF protection** Outbound requests (migrations, OAuth2 avatars,
OpenID discovery, pull mirrors, proxy path) are now validated against
the allow/block host lists. If your instance legitimately reaches
internal hosts, you may need to add them to
`[security].ALLOWED_HOST_LIST` (and the relevant `ALLOW_LOCALNETWORKS`
settings).
- **Deprecation** `[webhook].ALLOWED_HOST_LIST` is deprecated and will
be removed in a future release. Use `[security].ALLOWED_HOST_LIST`
instead; the old key still works for now.

---------

Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-12 17:14:09 +00:00
TowyTowy
d2bd1589fe fix(util): reject invalid characters between time-estimate units (#38416)
### What / why

`TimeEstimateParse` (used by the issue time-estimate form) only checked
that the first token starts at the beginning of the string and the last
token ends at its end, but never checked the gaps between consecutive
tokens. Non-whitespace garbage embedded between two valid units was
silently dropped and the string accepted with a wrong value instead of
being reported as invalid.

Examples that were wrongly accepted before this change:

- `1h 2x 3m` → 3780s (parsed as 1h3m)
- `1h_2m`    → 3720s
- `1h,1m`    → 3660s

All three now return an "invalid time string" error, while valid inputs
such as `1h 1m 1s` and `1h1m1s` keep working.

### How

Reject any non-whitespace content between two matched units.

---------

Signed-off-by: TowyTowy <towy@airreps.link>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-12 13:38:34 +00:00
Shudhanshu Singh
b998c3c1aa feat(actions): implement adaptive auto-refresh for workflow runs list (#38329)
### Description
This PR implements an optimized, adaptive client-side auto-refresh
mechanism for the Gitea Actions workflow runs list page. It allows users
to see workflow progress updates dynamically without having to reload
the page.

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

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-12 12:14:37 +00:00
Lovepreet Singh
65c5a5ff7b fix(turnstile): route CAPTCHA verification through the configured proxy (#38412)
Fixes #38217

## Problem

Turnstile CAPTCHA verification uses `http.DefaultClient`, so the request
to `challenges.cloudflare.com` bypasses Gitea's configured HTTP proxy —
unlike other outbound HTTP clients such as the update checker
(`modules/updatechecker/update_checker.go`) and migrations. In
deployments where egress is only permitted through the configured proxy,
verification fails.

## Fix

Build the client with `proxy.Proxy()` as the transport proxy, mirroring
the update checker:

```go
func httpClient() *http.Client {
	return &http.Client{
		Transport: &http.Transport{
			Proxy: proxy.Proxy(),
		},
	}
}
```

The client is built per call (rather than a package-level var) because
`proxy.Proxy()` reads `setting.Proxy` when invoked; building it at
request time ensures it reflects the loaded settings. When no proxy is
configured, behavior is unchanged (`proxy.Proxy()` returns a no-op /
`http.ProxyFromEnvironment`).

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-12 11:33:14 +00:00
Harsh Satyajit Thakur
aba0eb1749 fix: represent a deleted assignee team as a Ghost team (#38413)
Fixes #35472.

`Comment.LoadAssigneeUserAndTeam` already has a Ghost user fallback
for a deleted assignee user, but the parallel branch for a deleted
assignee team just swallowed the not-found error and left
`AssigneeTeam` as `nil`. This is inconsistent (the reporter's example
shows `assignee` becoming a Ghost user while `assignee_team` becomes
`null`), and it's also a latent nil pointer bug: other code that
assumes `AssigneeTeam` is set once this function returns without
error will panic.

Added `organization.NewGhostTeam()` / `Team.IsGhost()`, mirroring the
existing `user_model.NewGhostUser()` / `User.IsGhost()` pattern, and
used it in the same fallback branch.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-12 12:54:59 +02:00
GiteaBot
678b5aba30 [skip ci] Updated translations via Crowdin 2026-07-12 00:54:22 +00:00
wxiaoguang
bd5f881c51 fix: refresh pull request merge box when the commit status is pending (#38410) 2026-07-11 19:36:10 +02:00
Yarden Shoham
d3d57dd9b4 chore: remove Yarden Shoham from maintainers (#38407) 2026-07-11 15:29:41 +02:00
wxiaoguang
1bbd127a1a fix: actions task state concurrent update (#38405)
fix #38333
2026-07-11 15:03:42 +02:00
bircni
f803f8e269 fix(actions): keep workflow run trailing on one row with long branch names (#38382)
The flex-list refactor (#37505) raised the shared `.item-trailing`
selector's specificity, so its `flex-wrap: wrap` started overriding the
run list's intended `flex-wrap: nowrap` — a long branch name pushed the
trailing content past its fixed 280px width and wrapped the kebab menu
onto its own line.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-10 23:43:32 +02:00
bircni
8401fe7c54 fix(pull): re-evaluate review official flag on target branch change (#38319)
The `official` flag of a pull request review is computed against the
target branch's protection rules at submit time and stored on the review
record. `ChangeTargetBranch` updated the base branch but never
re-evaluated it, so an `official` approval obtained against an
unprotected branch could be retargeted onto a protected branch and
satisfy its required approvals, bypassing the branch protection.

This re-evaluates the `official` flag of the latest approve/reject
reviews against the new base branch whenever the target branch changes.
2026-07-10 19:18:47 +00:00
Shudhanshu Singh
c12e92c21d fix(web): use locale-aware date formatting for contribution calendar tooltips (#38398)
The contribution calendar manually constructed localized date strings
instead of using the browser's locale-aware date formatting. Replace
this with `toLocaleDateString()` to correctly format dates for all
locales and calendar systems, including non-Gregorian calendars.

Fixes https://github.com/go-gitea/gitea/issues/38375
2026-07-10 18:38:09 +00:00
bircni
aab3242f7b fix(security): harden access checks and migration validation (#38324)
Harden access checks for issue dependencies, team repository membership,
notifications, stars, tracked times and repository migrations.
2026-07-10 19:30:43 +02:00
bircni
f452c369ac fix: enforce public-only token scope and harden push options / locale parsing (#38323)
- **Locale DoS:** the `Locale` middleware passed the raw
`Accept-Language` header to `ParseAcceptLanguage`, whose guard only
counts `-` while the scanner aliases `_` to `-` — a large `_`-separated
header on an unauthenticated request burned CPU. The header is now
length-bounded before parsing.
- **Public-only token scope:** `GET /teams/{id}/repos`,
`.../repos/{org}/{repo}`, `/teams/{id}/activities/feeds`, and
`/users/{username}/orgs/{org}/permissions` still returned private
repo/activity/permission data to a public-only token. They now filter
via `TokenCanAccessRepo` / `ApplyPublicOnly` and reject non-public org
permissions.
- **Push-option visibility:** `repo.private` / `repo.template` push
options were applied to any existing repo, letting an owner/admin
silently flip visibility bypassing audit, webhooks, and notifications.
They are now honored only on push-to-create.
2026-07-10 16:39:01 +00:00
wxiaoguang
c5c991b1a4 fix: co-author detection (#38392)
Committer can also be co-author, it should only not be included in the
co-author list if it is not in the "Co-author-by" list.

* Author & Co-author: they changed the code (attribution)
* Committer: they submitted the commit but didn't change the code (e.g.:
maintainer signed a commit)

Fix #38384
2026-07-10 12:52:00 +02:00
bircni
362539b78e fix(api): stop leaking private repo metadata after access revocation (#38321)
The `/user/starred` and `/user/subscriptions` endpoints returned private
repositories a user had starred/watched even after their access to those
repositories was revoked, still exposing the repository name,
description and visibility (including later metadata changes).

Private repositories in the starred/watched queries are now gated on the
actor's current access via `AccessibleRepositoryCondition`, so users who
no longer have access no longer receive the metadata. Public
repositories and public-only tokens are unaffected.
2026-07-09 22:21:44 +00:00
bircni
66a3723cbb fix(lfs): require proof of possession for cross-repo objects (#38322)
The LFS batch and upload handlers linked an object that already existed
in the content store but was not linked to the current repo whenever the
token's user could access it in another repo. Deploy-key tokens carry
the repo owner's identity, so a single-repo write deploy key could link
and then download objects from any repo the owner can see.

This drops the cross-repo access check: the batch handler now makes the
client upload (hash-verified) any object not yet linked to the repo, and
the upload handler skips proof of possession only when the object is
already linked to the current repo.
2026-07-09 21:43:08 +00:00
bircni
27e33b7ba1 fix: incorrect co-author detection on commit page (#38386)
The commit page built its "co-authored by" list from
`AllParticipantIdentities()[1:]`, which only drops the author and leaves
the committer in the list even though the committer is already shown
separately as "committed by". This caused the committer to be wrongly
rendered as a co-author (issue #38384): a commit authored by
`silverwind`, committed by `bircni`, with a `Co-authored-by: silverwind`
trailer displayed "co-authored by bircni" instead of the actual trailer
identity. This adds a `Commit.CoAuthorIdentities()` method that excludes
both the author and the committer, uses it on the commit page, and
covers the fix with a regression test.

Closes #38384
2026-07-09 22:31:14 +02:00
Zettat123
761470c01d enhance(actions): only create filtered-out workflow commit status for required contexts (#38371)
Follow #38237

#38237 posts "skipped" commit statuses for every workflow that is not
triggered due to a filter (e.g. `paths` or `branches`) mismatch.
However, for non-required workflows, creating "skipped" commit statuses
for them would generate a lot of noise.

To address this issue, this PR adds a check before creating commit
status:

- For the context that matches any required status check patterns, a
"skipped" commit status will be created. The `Required` label can inform
users that this status check is required, but has been skipped because
of a filter mismatch.
- For a non-required context, nothing will be created.

NOTE: Reducing noise is a best-effort approach and isn't entirely
accurate. When creating commit statuses, it is impossible to predict
which branch protection rule will take effect. Therefore, we have to
compare the commit status context against the required patterns from all
rules. If any rule matches, the context is considered "required".
2026-07-09 15:34:56 +00:00
silverwind
c0bbd82cd4 fix(ui): restore commits table column widths (#38379)
https://github.com/go-gitea/gitea/pull/37594 widened the author column
from `three wide` to `four wide`, leaving a large empty gap around the
SHA column in the common single-author case. The old author cell was
capped at 180px, so the wider column only adds whitespace: avatar-stack
names ellipsize at 240px each, and wider multi-author rows still expand
naturally in the auto-layout table. Restore the pre-existing
`three`/`eight` widths.

Co-authored-by: bircni <bircni@icloud.com>
2026-07-09 14:26:59 +00:00
silverwind
7fd34ff033 test(e2e): fix race in pdf file render test (#38380)
`data-render-name` is set before the plugin's async render runs, so
measuring the container height right after the attribute appears can
observe the pre-render 48px height when the `pdfobject` chunk loads
slowly (flaked in CI). Poll for the height instead, like the asciicast
test in the same file does.
2026-07-09 13:25:01 +00:00
wxiaoguang
1e682a26eb refactor: introduce ActivePageTimer to help to do partial page refresh (#38372)
Before, the logic is already there for "pull merge box".

After, the logic is extracted into a general class ActivePageTimer and
will help more pages (including #38329)
2026-07-09 14:39:03 +02:00
GiteaBot
3b3a06e06f [skip ci] Updated translations via Crowdin 2026-07-09 00:56:40 +00:00
Milwad Khosravi
545ed92354 chore(typo): fix grammar in comments, API docs and error messages (#38370) 2026-07-08 23:52:06 +02:00
wxiaoguang
49ef93940a fix: golang html template url escaping (#38363)
fix #38362
2026-07-08 00:16:32 +00:00
bircni
308a6f12ae perf(actions): debounce runner heartbeat writes and throttle task picks (#38281)
3 reductions in the DB load generated by many runners polling
`FetchTask`:

**1. Debounce runner heartbeat writes**
Every poll wrote `last_online`, and every `UpdateTask`/`UpdateLog` wrote
`last_active` — while a runner streams logs that is many writes per
second per runner. These are now persisted only when stale enough to
actually affect the active/offline status (`ShouldPersistLastOnline` /
`ShouldPersistLastActive`), using the existing columns.

**2. Throttle concurrent task picks**
A new in-process semaphore (`MAX_CONCURRENT_TASK_PICKS`) bounds how many
runners run the task-assignment transaction at once, so a fleet polling
together cannot stampede the query. Throttled polls retry on their next
poll without advancing the runner's tasks version.

**3. Paginate the task-pick query**
`CreateTaskForRunner` previously loaded every waiting job in the
runner's scope into memory on each poll (no `LIMIT`). Now it pages
through the waiting backlog oldest-first with `LIMIT`, claiming the
first label-matching job.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-07 19:16:20 +00:00
bircni
97078b96cf fix(mirror): disable HTTP redirects on pull mirror sync (#38320)
Pull mirror sync ran `git fetch` / `remote update` / `remote prune`
without disabling HTTP redirects. A mirror remote that later starts
redirecting to an otherwise-blocked or internal address could be used as
an SSRF/exfiltration vector on scheduled syncs, bypassing the
allow/block validation applied at migration time.

This sets `http.followRedirects=false` on all three remote-contacting
commands in the pull mirror path, matching the existing guard already
present on the clone path.
2026-07-07 17:35:21 +00:00
Giteabot
6507f1fd94 chore(deps): update dependency djlint to v1.40.1 (#38354) 2026-07-07 16:31:17 +00:00
Giteabot
0964899799 fix(deps): update npm dependencies (#38352) 2026-07-07 18:17:10 +02:00
Giteabot
550efdcdfd chore(deps): update action dependencies (#38353) 2026-07-07 14:41:01 +02:00
Giteabot
b96bd22372 fix(deps): update go dependencies (#38346) 2026-07-07 10:16:00 +00:00
wxiaoguang
a74f618ade fix: minio init check (#38355)
Fix the buggy behavior introduced by "S3: log human readable error on connection failure (#26856)"
2026-07-07 07:32:25 +00:00
Copilot
2b89e2ac97 fix(pulls): add branch-name option for DEFAULT_TITLE_SOURCE (#38356)
Adds a new `branch-name` value for the `[repository.pull-request]`
`DEFAULT_TITLE_SOURCE` setting that always uses the normalized branch
name as the PR title, regardless of commit count.

Fix #38317

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-07 07:08:05 +00:00
wxiaoguang
26bff7f47e fix: org project view assignee list (#38357)
fix #38129
2026-07-07 14:40:12 +08:00
Shudhanshu Singh
582217a0da feat(webhook): add reviewer name to MS Teams review request notifications (#38289)
Include the requested reviewer's username (along with their full name in
parentheses, if available) and render the `Repository` and `Pull
request` fields as clickable links in Microsoft Teams webhook
notifications.

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

## Screenshots

<img width="1246" height="651" alt="image"
src="https://github.com/user-attachments/assets/7299ce10-c6d4-4c89-a05a-a258d72c00e5"
/>

---------

Signed-off-by: Shudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-06 18:30:58 +00:00
Giteabot
a46e331637 chore(deps): update action dependencies (#38340) 2026-07-06 18:04:10 +00:00
Lunny Xiao
580cc26d63 chore: Upgrade xorm to 1.4.1 (#38224)
Fix #22275

Changelog: https://gitea.com/xorm/xorm/compare/v1.3.11..v1.4.1
2026-07-06 17:07:58 +00:00
Giteabot
e3d83bcf9c chore(deps): update tool dependencies (#38344)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
|
[github.com/editorconfig-checker/editorconfig-checker/v3](https://redirect.github.com/editorconfig-checker/editorconfig-checker)
| `v3.7.0` → `v3.8.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2feditorconfig-checker%2feditorconfig-checker%2fv3/v3.8.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2feditorconfig-checker%2feditorconfig-checker%2fv3/v3.7.0/v3.8.0?slim=true)
|
| golang.org/x/vuln | `v1.4.0` → `v1.5.0` |
![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fvuln/v1.5.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fvuln/v1.4.0/v1.5.0?slim=true)
|
2026-07-06 18:40:40 +02:00
Giteabot
44f927eacf fix(deps): update npm dependencies (#38342) 2026-07-06 14:13:28 +00:00
Giteabot
35413d5b65 chore(deps): update dependency djlint to v1.40.0 (#38341) 2026-07-06 15:56:31 +02:00
Zettat123
e797a27d4e fix(actions): release claimed task if context is cancelled during FetchTask (#38343)
When a runner's `FetchTask` request context is cancelled after the job
is claimed but before the task reaches the runner (e.g. request
timeout), the job was left referencing a running task no runner ever
executes, so it stayed unpickable.

Fix: Check the context after assembling the task and release the task so
the job returns to waiting status for another runner.
2026-07-06 06:38:44 +02:00
GiteaBot
4b15260277 [skip ci] Updated translations via Crowdin 2026-07-06 01:02:23 +00:00
GiteaBot
18fdc77130 [skip ci] Updated translations via Crowdin 2026-07-05 01:02:27 +00:00
Zettat123
2c2691b969 test: compare key file contents instead of FileInfo in TestInitKeys (#38330)
`TestInitKeys` verified whether a host key file was regenerated by
comparing `os.FileInfo` before and after a `InitDefaultHostKeys` call.
On systems where the OS clock / filesystem timestamp granularity is
coarse, both writes land in the same tick and get an identical mtime.
The resulting `FileInfo` is then byte-for-byte equal even though the key
was actually regenerated, so `assert.NotEqual` fails.

Since a regenerated key always produces different random bytes,
comparing the content can reliably detect regeneration.
2026-07-04 22:30:12 +02:00
wxiaoguang
08d4abbb46 docs: describe duties of mergers prior to merging a PR (#38308)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: delvh <dev.lh@web.de>
Co-authored-by: delvh <dev.lh@web.de>
2026-07-04 21:00:22 +02:00
Lunny Xiao
e4ef995f2a fix(release): validate web attachment renames against allowed types (#38314)
This fixes the web release edit flow so renamed release attachments are
validated against `[repository.release] ALLOWED_TYPES`.

Previously, the API attachment edit endpoint already enforced release
attachment type restrictions, but the web release edit form passed
`attachment-edit-*` values into `release_service.UpdateRelease`, which
updated attachment names directly without validating the new filename
against `setting.Repository.Release.AllowedTypes`.

As a result, a user with repository write access could rename an
existing release attachment to a disallowed extension through the web
UI.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-04 15:02:17 +02:00
GiteaBot
7fdfb8d642 [skip ci] Updated translations via Crowdin 2026-07-04 00:56:26 +00:00
bircni
d4c4142123 fix(actions): make runner list pagination order deterministic (#38313)
The runner-listing API endpoints (`admin`, `org`, `user`, `repo`, and
`shared`) return runners in a non-deterministic order across pages. When
paging through runners, some runners from page 1 could reappear on page
2 (and others get skipped entirely).

The cause is in `FindRunnerOptions.ToOrders()`. Most sort modes order by
a **non-unique** column only:

- default / `online` / `offline` → `last_online`
- `alphabetically` / `reversealphabetically` → `name`

When multiple runners tie on the sort key (e.g. every offline runner
shares `last_online = 0`, or two runners have the same name), the
database is free to return the tied rows in any order between separate
queries. Combined with `LIMIT`/`OFFSET` pagination, this means the same
runner can land on more than one page.

## Fix

Append the unique primary key `id` as a stable tiebreaker to each
non-unique sort order:

The `newest`/`oldest` modes already sort by the unique `id`, so they are
left unchanged.
2026-07-03 20:35:47 +00:00
bircni
f7fd510224 fix(release): gate draft release attachments on web download endpoints (#38318)
Draft-release access control was enforced only on the API release
endpoints (`/api/v1/repos/{owner}/{repo}/releases/...`) but not on the
UUID-based web attachment endpoints (`/attachments/{uuid}`,
`/{owner}/{repo}/attachments/{uuid}`,
`/{owner}/{repo}/releases/attachments/{uuid}`).

Anyone who obtained an attachment UUID — including unauthenticated
callers — could download files belonging to a hidden draft release,
since `ServeAttachment` only checked repo-level read permission and
never the release's draft state.
2026-07-03 20:07:37 +02:00
silverwind
38a5824753 ci(snap): build snaps natively instead of on launchpad (#38312)
The nightly snap build fails with `snapcraft remote-build`'s
`BadRequest()`: it force-pushes Gitea's full history to a fresh
Launchpad repo each run and creates the recipe before Launchpad has
indexed the `main` ref. The race is unwinnable at Gitea's repo size, and
Canonical does not support `remote-build` in CI.

Build locally on native amd64 + arm64 runners with
`snapcore/action-build` + `snapcore/action-publish` instead — no
Launchpad, no race. Drops the `LAUNCHPAD_CREDENTIALS` secret (publishing
keeps `SNAPCRAFT_STORE_CREDENTIALS`); the `snap/` recipe is unchanged,
so the same snaps ship to `latest/edge`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-07-03 07:22:27 +00:00
silverwind
e8d2c493bb chore: update eslint-plugin-unicorn to v70 (#38310)
Bumps to https://github.com/sindresorhus/eslint-plugin-unicorn/releases/tag/v70.0.0,
enable all new rules, no violations.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.8) <noreply@anthropic.com>
2026-07-02 14:09:37 -07:00
Shudhanshu Singh
b09920a537 feat(webhook): support Telegram Bot API 10.1 Rich Messages (#38298)
Upgrades Gitea's Telegram webhook integration to support Telegram Bot
API 10.1 (June 2026 release). This enables Gitea webhooks to take
advantage of rich formatted messages (tables, nested blocks, collapsible
details, etc.) by routing them through the /sendRichMessage endpoint
with the new rich_message payload structure.

Old `/sendMessage` webhook URLs are written to `/sendRichMessage`
at runtime to prevent the need for database migrations

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-02 15:14:48 +00:00
silverwind
c6184ed184 chore(snap): drop armhf build (#38311)
Launchpad no longer builds `core24` snaps for `armhf`. Since `snapcraft
remote-build` submits a single request for all platforms, the `armhf`
rejection fails with `snapcraft internal error: BadRequest()` and takes
the `amd64` and `arm64` builds down with it, so nothing gets published.

Dropping 32-bit ARM from `snap/snapcraft.yaml` and the workflow's
`--build-for` restores nightly snap publishing for the remaining
architectures.
2026-07-02 13:55:45 +02:00
Shudhanshu Singh
8909958055 fix(actions): prevent chevron overlap with log text when timestamps are enabled (#38227)
### Description
This PR resolves a UI alignment bug in the Gitea Actions log viewer
where the expand/collapse disclosure chevron overlaps with the log text
(specifically the timestamp) when timestamps are enabled.

### Cause
When log timestamps are enabled, the timestamp element
(`.log-time-stamp`) is rendered as the first element next to the line
number. Because it only had a default `10px` left margin, it positioned
itself exactly where the group's expand/collapse chevron is located,
causing them to overlap.

### Solution
Updated the CSS styles in `web_src/js/components/ActionRunJobView.vue`
to dynamically apply the `21px` margin to whichever element is the first
visible element after the line number:
- If the timestamp is visible, it gets the `21px` margin to clear the
chevron, and the subsequent log message gets a `10px` margin.
- If the timestamp is hidden, the log message receives the `21px`
margin.

### Before / After
**Before:**
<img width="853" height="348" alt="actions_log_before"
src="https://github.com/user-attachments/assets/d09a752e-18cb-4fe3-b749-4979cbe45240"
/>


**After:**
<img width="862" height="511" alt="actions_log_after"
src="https://github.com/user-attachments/assets/63063f05-8cd6-4986-a993-ed12f28625c8"
/>

Fixes #38222.

---------

Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-02 05:20:25 +00:00
silverwind
638e4bce09 chore: upgrade go-swagger to v0.35.0 and enforce zero swagger warnings (#38299)
Updates `go-swagger` to v0.35.0 and makes swagger generation and
validation warning-free, with the build now failing on any warning
(`go-swagger` itself exits `0` on warnings).

- Generation passes `--enable-allof-compounding` (keeps `$ref` fields
bare, no spec change) and `--skip-enum-desc` (drops the enum description
that duplicates `x-go-enum-desc` and was the only source of `allOf`
noise in the OpenAPI 3.0 output).
- Fixed warnings at the source: dropped `swagger:strfmt` where it
conflicts with `required: true` (`required` kept, `time.Time` still maps
to `date-time`), fixed a malformed `units_map` example, moved the
`parameterBodies` injection hack to `swagger:parameters`, and removed
unused responses.

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

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-07-01 21:07:34 +00:00
puni9869
a031454586 fix: Improve since/until when counting commits for X-Total-Count (#38243)
Follow up for https://github.com/go-gitea/gitea/pull/38204.

---------

Signed-off-by: puni9869 <80308335+puni9869@users.noreply.github.com>
2026-07-01 20:43:46 +00:00
silverwind
c52a07dcfe chore: remove eslint-plugin-array-func (#38294)
The rules from `eslint-plugin-array-func` are redundant with
`eslint-plugin-unicorn`:

- `from-map` → `unicorn/prefer-array-from-map`
- `no-unnecessary-this-arg` → `unicorn/no-array-method-this-argument`
- `prefer-flat` / `prefer-flat-map` → `unicorn/prefer-array-flat` /
`unicorn/prefer-array-flat-map` (already disabled here)

The two remaining rules (`avoid-reverse`, `prefer-array-from`) are niche
and not worth carrying an extra dependency for.

Co-authored-by: bircni <bircni@icloud.com>
2026-07-01 20:16:42 +00:00
bircni
b6ef881a9f docs: Welcome Zettat to TOC (#38303) 2026-07-01 20:07:44 +00:00
Kausthubh J Rao
6240d8bf89 fix(workflows): branch protection status checks fail when workflow uses on: paths filter (#38237) 2026-07-01 21:47:47 +02:00
silverwind
9cb2719fab chore: update node.js to v26 (#38285)
- bump ci, flake and `@types/node` to node 26
- regenerate flake.lock which is needed for that package
- refactor workflow to use shared composite action
2026-07-01 16:28:36 +02:00
silverwind
e8654c7e06 refactor: replace vue-bar-graph dependency with inlined SVG chart (#38292)
Inlines the small SVG bar graph into `RepoActivityTopAuthors.vue` (its
only consumer) and drops the `vue-bar-graph` npm dependency.

- Bars render at static height (dropped the grow animation).
- Theme-aware axis color instead of a hardcoded `#555555`.
- Removed the dangling `role="img"`/`aria-labelledby` on the `<svg>`.
- Reserve the chart height so the page does not shift when the component
mounts.

<img width="416" height="110" alt="Screenshot 2026-07-01 at 11 15 25"
src="https://github.com/user-attachments/assets/b2db4d0c-20f1-4345-9951-32a908abfaba"
/>
<img width="419" height="110" alt="Screenshot 2026-07-01 at 11 15 35"
src="https://github.com/user-attachments/assets/853305a5-575f-4a26-ba3b-12fc51081324"
/>

fyi @lafriks

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-07-01 11:17:23 +00:00
Zettat123
67a6bd7fc0 feat(auth): add disable-2fa command (#38275)
This PR adds the `gitea admin user disable-2fa` command to disable 2FA
for a user

When the only admin in the instance loses their 2FA credentials, this
command can be used to disable 2FA, allowing them to log in and reset
it.

---------

Co-authored-by: Giteabot <teabot@gitea.io>
2026-07-01 12:33:16 +02:00
Aidan Fahey
77e221ffaf fix(oauth2): persist linkAccountData during auto-link 2FA flow (#38274)
Fixes HTTP 500 when OIDC auto account linking (`ACCOUNT_LINKING=auto`)
requires local 2FA. `oauth2LinkAccount` set `linkAccount` in the session
before redirecting to 2FA but did not persist `linkAccountData`, so
`TwoFactorPost` failed with `not in LinkAccount session`. The manual
linking flow already stored both, this aligns auto-link with that
behavior.

Closes #38171

---------

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-01 10:03:38 +00:00
bircni
458c11bd68 fix(actions): allow Actions bot to push to protected branches (#38284)
Fixes #38278

## Problem

When branch protection matches the branch an Actions workflow pushes to,
the runner's `git push` is rejected — even though the workflow token has
`contents: write` and the same push performed with a PAT (write access)
succeeds. Disabling protection or changing the pattern so it no longer
matches makes the push work.

## Root cause

In `preReceiveBranch` (`routers/private/hook_pre_receive.go`), the "can
the doer push to this protected branch" check resolves the pusher with
`user_model.GetUserByID(ctx, ctx.opts.UserID)`. For an Actions push the
user ID is `-2` (the virtual `ActionsUserID`), which has no database
row, so the lookup fails. Even past that, `CanUserPush` →
`HasAccessUnit`/whitelist membership cannot evaluate a virtual user and
returns `false`. As a result the Actions bot was rejected on every
matching protected branch, despite the earlier `assertCanWriteRef`
already confirming the token's code-write via
`GetActionsUserRepoPermission`.

This was inconsistent: a PAT with identical write access passed the
exact same check.

## Fix

Evaluate the Actions bot against its already-computed token permission
instead of a user lookup, mirroring the existing
`IsUserMergeWhitelisted` pattern:

- Add `CanActionsUserPush` / `CanActionsUserForcePush` on
`ProtectedBranch`, which take the precomputed `access_model.Permission`.
- Allow the push when push is enabled, **no** push whitelist is
enforced, and the token has code-write.
- Keep the bot blocked when a whitelist is enforced — it cannot be added
to one, so it must use a pull request. This preserves the whitelist as a
real security boundary.

Force-push, signed-commit and protected-file-path checks are untouched.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-01 09:19:47 +00:00
GiteaBot
3d2bbd25ec [skip ci] Updated translations via Crowdin 2026-07-01 01:19:35 +00:00
Avinash Thakur
7745720292 feat: extend <video> tag allowed attributes (#38279)
autoplay is useless nowadays without "muted" as browsers won't autoplay
unmuted videos.
Similarly, other attributes are also commonly used and harmless to keep.

<!--
Before submitting:
- Target the `main` branch; release branches are for backports only.
- Use a Conventional Commits title, e.g. `fix(repo): handle empty branch
names`.
- Read the contributing guidelines:
https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md
- Documentation changes go to https://gitea.com/gitea/docs

Describe your change below and link any issue it fixes.
-->

---------

Signed-off-by: Avinash Thakur <19588421+80avin@users.noreply.github.com>
2026-06-30 20:31:13 +00:00
bircni
d46d0540d0 fix(actions): include all aggregable run statuses in status filter (#38280)
The **Status** filter dropdown on the repository Actions run list does
not let you filter for **Blocked** runs (nor **Cancelled** or
**Skipped**). These statuses are missing from the dropdown even though a
run can legitimately end up in any of them.

A run's status is computed by `aggregateJobStatus`, which can return
`Blocked`, `Cancelled` and `Skipped`. Because the filter dropdown only
offered Success, Failure, Waiting, Running and Cancelling, runs in those
other states existed but were impossible to filter for.
2026-06-30 19:59:30 +00:00
techknowlogick
e449018730 non-shallow clone for snapcraft
Signed-off-by: techknowlogick <techknowlogick@gitea.com>
2026-06-30 18:35:29 +02:00
Vinod-OAI
e1cdb71845 fix(archiver): use serializable repo-archive queue payload (#38273)
After upgrading from 1.25.x to 1.26.x, `repo-archive` workers can fail
to unmarshal queued items:

```
Failed to unmarshal item from queue "repo-archive":
json: unable to unmarshal into Go convert.Conversion within "/Repo/Units/0/Config":
cannot derive concrete type for nil interface with finite type set
```

`ArchiveRequest` started embedding `*repo_model.Repository` in 1.26,
which does not round-trip through the JSON queue.

This change stores a minimal `archiveQueueItem` (`RepoID`, `Type`,
`CommitID`, `Paths`) in `repo-archive` and loads the repository in the
worker. `UnmarshalJSON` accepts legacy payloads that used `RepoID` or
embedded `Repo.id`.

Fixes #38272

<!--
Before submitting:
- Target the `main` branch; release branches are for backports only.
- Use a Conventional Commits title, e.g. `fix(repo): handle empty branch
names`.
- Read the contributing guidelines:
https://github.com/go-gitea/gitea/blob/main/CONTRIBUTING.md
- Documentation changes go to https://gitea.com/gitea/docs

Describe your change below and link any issue it fixes.
-->

---------

Co-authored-by: bircni <bircni@icloud.com>
2026-06-30 14:42:05 +00:00
silverwind
a64131e22d chore: update eslint plugins and config (#38264)
1. Bump all eslint dependencies, enable some of the new unicorn rules
2. Remove `eslint-plugin-de-morgan`, it sometimes causes readability
issues
3. Disable some of the unicorn rules that are known to produce
false-positives
4. Remove obsolete type cast
5. Fix one violation of
https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-replace-children.md

---------

Signed-off-by: silverwind <me@silverwind.io>
2026-06-30 13:53:29 +00:00
GiteaBot
0f0a38c1b9 [skip ci] Updated translations via Crowdin 2026-06-30 01:13:52 +00:00
silverwind
535f791166 ci: regenerate codemirror languages on renovate npm updates (#38267)
Adds `make generate-codemirror-languages` to the npm group's
`postUpgradeTasks` in `renovate.json5`, so renovate regenerates
`assets/codemirror-languages.json` whenever `@codemirror/language-data`
(or any npm dep) updates — mirroring the existing `make svg` handling.

Also reformats the `fileFilters` arrays multi-line and regenerates the
asset to pick up current upstream linguist languages.
2026-06-29 22:59:08 +00:00
Lunny Xiao
b34a09be38 build: fix snapcraft release (#38260)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-06-29 14:35:26 -07:00
Giteabot
6f2e328c85 chore(deps): update dependency js-yaml to v5 (#38262)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [js-yaml](https://redirect.github.com/nodeca/js-yaml) | [`4.2.0` →
`5.1.0`](https://renovatebot.com/diffs/npm/js-yaml/4.2.0/5.1.0) |
![age](https://developer.mend.io/api/mc/badges/age/npm/js-yaml/5.1.0?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/js-yaml/4.2.0/5.1.0?slim=true)
|

---

### Release Notes

<details>
<summary>nodeca/js-yaml (js-yaml)</summary>

###
[`v5.1.0`](https://redirect.github.com/nodeca/js-yaml/blob/HEAD/CHANGELOG.md#510---2026-06-23)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/5.0.0...5.1.0)

##### Added

- Collection tags can finalize an incrementally populated carrier into a
  different result value.

##### Changed

- \[breaking] `quoteStyle` now selects the preferred quote style; use
the
  restored `forceQuotes` option to force quoting non-key strings.

###
[`v5.0.0`](https://redirect.github.com/nodeca/js-yaml/blob/HEAD/CHANGELOG.md#500---2026-06-20)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/4.3.0...5.0.0)

##### Added

- Added named exports for schemas, tags, parser events and AST
utilities.
- Reworked `JSON_SCHEMA` and `CORE_SCHEMA` with spec-compliant scalar
resolution
  rules, and added `YAML11_SCHEMA`.
- Added `realMapTag` for lossless mappings with non-string and complex
keys.
Object-based mappings now reject complex keys instead of stringifying
them.
- Added `dump()` `transform` option for changing the generated AST
before
  rendering.
- Added `dump()` options `seqInlineFirst`, `flowBracketPadding`,
`flowSkipCommaSpace`, `flowSkipColonSpace`, `quoteFlowKeys`,
`quoteStyle` and
  `tagBeforeAnchor`.
- Added formal data layers (events and AST) for modular data pipelines.
  - Added low-level parser (to events), presenter and visitor APIs.
- Added the [YAML Test
Suite](https://redirect.github.com/yaml/yaml-test-suite) to the
  test set.

##### Changed

- See the [migration guide](docs/migrate_v4_to_v5.md) for upgrade notes.
- Rewritten in TypeScript and reorganized the public API around flat
named
  exports.
- Reduced the set of exported schemas:
  - YAML 1.2 schemas: `CORE_SCHEMA` (loader default), `JSON_SCHEMA`,
    `FAILSAFE_SCHEMA`.
- `YAML11_SCHEMA`, a combination of all YAML 1.1 tags (YAML 1.1 does not
    specify a schema, only "types").
- `load`/`dump` default behaviour is now specified exactly via schemas:
  - `load` uses `CORE_SCHEMA`, without `!!merge` by default.
- `dump` uses `YAML11_SCHEMA` + `CORE_SCHEMA` for the quoting check, to
    guarantee backward compatibility by default.
- `!!set` is now loaded as a JavaScript `Set`.
- Replaced the `Type` API with a tags API. Similar, but more precise and
  simpler. See examples for details. Tags can be defined via
`defineScalarTag()`, `defineSequenceTag()` and `defineMappingTag()`, or
as a
  spread + override of an existing tag.
- Renamed `Schema.extend()` to `Schema.withTags()`.
- Expanded YAML 1.2 conformance and improved handling of directives,
document
  markers, block keys, multiline scalars, tag syntax and other things.
- `load()` now throws on empty input instead of returning `undefined`.
- Moved browser builds to the `js-yaml/browser` export.
- Deprecated the `loadAll` signature with an iterator (still works, but
is a
  candidate for removal).

##### Removed

- Removed deprecated `safeLoad()`, `safeLoadAll()` and `safeDump()`
exports.
- Removed `DEFAULT_SCHEMA` and the nested `types` export.
- Removed loader options `onWarning`, `legacy` and `listener`.
- Removed dumper options `styles`, `replacer`, `noCompatMode`,
`condenseFlow`,
`quotingType` and `forceQuotes`. Renamed `noArrayIndent` to
`seqNoIndent`.
Formatting and representation are now configured through presenter
options,
  schemas and tag definitions. See migration guide on how to replace.
- Removed support for importing internal files from `lib/`.

###
[`v4.3.0`](https://redirect.github.com/nodeca/js-yaml/blob/HEAD/CHANGELOG.md#430-3150---2026-06-27)

[Compare
Source](https://redirect.github.com/nodeca/js-yaml/compare/4.2.0...4.3.0)

##### Security

- Backported `maxTotalMergeKeys` option.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Only on Monday (`* * * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-06-29 17:22:21 +00:00
Giteabot
55983320ed chore(deps): update actions/cache action to v6 (#38261)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache) | action |
major | `v5.0.5` → `v6.1.0` |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/37531) for more information.

---

### Release Notes

<details>
<summary>actions/cache (actions/cache)</summary>

###
[`v6.1.0`](https://redirect.github.com/actions/cache/releases/tag/v6.1.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v6.0.0...v6.1.0)

##### What's Changed

- Bump
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) to
v6.1.0 - handle read-only cache access by
[@&#8203;jasongin](https://redirect.github.com/jasongin) in
[#&#8203;1768](https://redirect.github.com/actions/cache/pull/1768)

**Full Changelog**:
<https://github.com/actions/cache/compare/v6...v6.1.0>

###
[`v6`](https://redirect.github.com/actions/cache/compare/v6.0.0...v6.0.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v6.0.0...v6.0.0)

###
[`v6.0.0`](https://redirect.github.com/actions/cache/releases/tag/v6.0.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v5.1.0...v6.0.0)

##### What's Changed

- Update packages, migrate to ESM by
[@&#8203;Samirat](https://redirect.github.com/Samirat) in
[#&#8203;1760](https://redirect.github.com/actions/cache/pull/1760)

**Full Changelog**:
<https://github.com/actions/cache/compare/v5...v6.0.0>

###
[`v5.1.0`](https://redirect.github.com/actions/cache/releases/tag/v5.1.0)

[Compare
Source](https://redirect.github.com/actions/cache/compare/v5.0.5...v5.1.0)

##### What's Changed

- Bump
[@&#8203;actions/cache](https://redirect.github.com/actions/cache) to
v5.1.0 - handle read-only cache access by
[@&#8203;jasongin](https://redirect.github.com/jasongin) in
[#&#8203;1775](https://redirect.github.com/actions/cache/pull/1775)

**Full Changelog**:
<https://github.com/actions/cache/compare/v5...v5.1.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - Only on Monday (`* * * * 1`)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://redirect.github.com/renovatebot/renovate).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->
2026-06-29 17:00:15 +00:00
3515 changed files with 136191 additions and 125200 deletions

View File

@@ -11,7 +11,7 @@ include_file = ["main.go"]
include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
exclude_dir = [
"models/fixtures",
"models/migrations/fixtures",
"modelmigration/fixtures",
"modules/avatar/identicon/testdata",
"modules/avatar/testdata",
"modules/git/tests",

View File

@@ -1,59 +0,0 @@
# The full repository name
repo: go-gitea/gitea
# Service type (gitea or github)
service: github
# Base URL for Gitea instance if using gitea service type (optional)
# Default: https://gitea.com
base-url:
# Changelog groups and which labeled PRs to add to each group
groups:
-
name: BREAKING
labels:
- pr/breaking
-
name: SECURITY
labels:
- topic/security
-
name: FEATURES
labels:
- type/feature
-
name: ENHANCEMENTS
labels:
- type/enhancement
-
name: PERFORMANCE
labels:
- performance/memory
- performance/speed
- performance/bigrepo
- performance/cpu
-
name: BUGFIXES
labels:
- type/bug
-
name: TESTING
labels:
- type/testing
-
name: BUILD
labels:
- topic/build
- topic/code-linting
-
name: DOCS
labels:
- type/docs
-
name: MISC
default: true
# regex indicating which labels to skip for the changelog
skip-labels: skip-changelog|backport\/.+

View File

@@ -1,6 +1,6 @@
{
"name": "Gitea DevContainer",
"image": "mcr.microsoft.com/devcontainers/go:1.26-trixie",
"image": "mcr.microsoft.com/devcontainers/go:1.27-trixie",
"containerEnv": {
// override "local" from packaged version
"GOTOOLCHAIN": "auto"

View File

@@ -63,7 +63,6 @@ cpu.out
/public/assets/fonts
/public/assets/img/avatar
/vendor
/VERSION
/.air
/.go-licenses
/Dockerfile

View File

@@ -18,13 +18,10 @@ indent_style = tab
[templates/custom/*.tmpl]
insert_final_newline = false
[templates/swagger/*_json.tmpl]
[templates/swagger/*.generated.json]
indent_style = space
insert_final_newline = false
[templates/user/auth/oidc_wellknown.tmpl]
indent_style = space
[templates/shared/actions/runner_badge_*.tmpl]
# editconfig lint requires these XML-like files to have charset defined, but the files don't have.
charset = unset

7
.gitattributes vendored
View File

@@ -2,11 +2,12 @@
*.tmpl linguist-language=Handlebars
*.pb.go linguist-generated
/assets/*.json linguist-generated
/public/assets/emoji.json linguist-generated
/public/assets/img/svg/*.svg linguist-generated
/templates/swagger/v1_json.tmpl linguist-generated
/templates/swagger/v1_openapi3_json.tmpl linguist-generated
/templates/swagger/*.generated.json linguist-generated
/options/fileicon/** linguist-generated
/services/migrations/_mock_data/** linguist-generated
/tests/integration/_mock_data/** linguist-generated
/vendor/** -text -eol linguist-vendored
/web_src/js/vendor/** -text -eol linguist-vendored
Dockerfile.* linguist-language=Dockerfile
Makefile.* linguist-language=Makefile

59
.github/ISSUE_TEMPLATE/release.yaml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: Release
description: Track a Gitea release (for release managers).
title: "Release Gitea "
body:
- type: markdown
attributes:
value: |
Follow the [release management guide](https://github.com/go-gitea/gitea/blob/main/docs/release-management.md).
Replace the examples below and mark inapplicable tasks as such.
CI signs the tag, generates release notes, and publishes binaries and containers. Track verification here; no manual changelog PR or release upload is needed.
- type: input
id: version
attributes:
label: Version
placeholder: "28.0.1"
validations:
required: true
- type: input
id: branch
attributes:
label: Release branch
placeholder: "release/v28"
validations:
required: true
- type: textarea
id: checklist
attributes:
label: Release checklist
description: Keep workflow runs, release URLs, and follow-up PRs alongside the relevant tasks.
value: |
### Preparation
- [ ] Resolve release blockers and confirm milestone issues and PRs are resolved or deferred.
- [ ] Confirm required backports are merged and release branch CI passes.
- [ ] For a new release line, create the release branch and tag its fork point on main with the next version's -dev tag.
### Release
- [ ] Run https://github.com/go-gitea/gitea/actions/workflows/release-create-tag.yml on the release branch with the selected version and obtain maintainer approval.
- [ ] Confirm https://github.com/go-gitea/gitea/actions/workflows/release-tag-version.yml succeeds for the new tag (binaries and containers).
- [ ] Verify the public GitHub release, generated notes, binary attachments, and signatures at https://github.com/go-gitea/gitea/releases.
- [ ] Verify binaries and signatures at https://dl.gitea.com/gitea/ for this version.
- [ ] Verify versioned regular and rootless images on Docker Hub and GHCR, and smoke-test the release.
### Follow-up
- [ ] Verify the automated https://dl.gitea.com/gitea/version.json update, where applicable to this release line.
- [ ] Verify automated Helm chart and Terraform provider update PRs and follow up if needed.
- [ ] Check Homebrew and Snap availability; record any outstanding packaging follow-up.
- [ ] Confirm documentation reflects the release, where applicable.
- [ ] Confirm and merge the release blog post, if planned: https://gitea.com/gitea/blog.
- [ ] Announce the release in Discord #announcements.
validations:
required: true
- type: textarea
id: notes
attributes:
label: Blockers and notes
description: Link outstanding work or release-specific checks using full URLs. Do not include undisclosed security details.

View File

@@ -1,29 +0,0 @@
name: docker-dryrun
description: Composite action that performs the container build steps for a single platform.
inputs:
platform:
description: "The target platform: linux/amd64, linux/arm64, linux/riscv64."
required: true
runs:
using: composite
steps:
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- name: Build regular image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
platforms: ${{ inputs.platform }}
push: false
file: Dockerfile
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
- name: Build rootless image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
platforms: ${{ inputs.platform }}
push: false
file: Dockerfile.rootless
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless

View File

@@ -7,6 +7,11 @@ runs:
steps:
- shell: bash
run: |
avail=$(df --output=avail -BG / | tr -dc '0-9')
if [ "$avail" -ge 50 ]; then
echo "skipping cleanup, ${avail}G free"
exit 0
fi
echo "free space before cleanup:"
df -h /
for dir in /usr/local/lib/android /usr/local/.ghcup /opt/ghc /usr/share/dotnet; do

View File

@@ -2,12 +2,18 @@ name: go-caches
description: Restore the go module, build, and golangci-lint caches. Save only on the cache-seeder workflow.
# Only the cache-seeder workflow saves; rename requires updating cache-seeder.yml.
# The lint job restores but does not save the gobuild cache, so only one writer
# (the gobuild job) populates it and there is no contention on the cache key.
# The lint job restores but does not save the gobuild and gomod caches, so every key
# has a single writer and there is no contention on it.
# Seeder restores by exact key only (no restore-keys) so each go.sum seeds a clean
# cache and size stays bounded; do not add restore-keys here. PR runs keep them.
# The go version is part of the gobuild and golint keys because a toolchain bump
# invalidates every entry in them, so a cross-version fallback would restore
# gigabytes that no build can use. The module cache is version-independent.
inputs:
go-version:
description: Go version the build caches belong to
required: true
lint-cache:
description: Restore (and save in cache-seeder) ~/.cache/golangci-lint
default: "false"
@@ -15,36 +21,36 @@ inputs:
runs:
using: composite
steps:
- if: ${{ github.workflow == 'cache-seeder' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
- if: ${{ github.workflow != 'cache-seeder' }}
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}
- if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
- if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }}
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/go/pkg/mod
key: gomod-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
restore-keys: gomod-${{ runner.os }}-${{ runner.arch }}-
- if: ${{ github.workflow == 'cache-seeder' && inputs.lint-cache != 'true' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum') }}
restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}
key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }}
- if: ${{ github.workflow != 'cache-seeder' || inputs.lint-cache == 'true' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/go-build
key: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum') }}
restore-keys: gobuild-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-
- if: ${{ inputs.lint-cache == 'true' && github.workflow == 'cache-seeder' }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/golangci-lint
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}
key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }}
- if: ${{ inputs.lint-cache == 'true' && github.workflow != 'cache-seeder' }}
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/golangci-lint
key: golint-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('go.sum', '.golangci.yml') }}
restore-keys: golint-${{ runner.os }}-${{ runner.arch }}
key: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-${{ hashFiles('go.sum', '.golangci.yml') }}
restore-keys: golint-${{ runner.os }}-${{ runner.arch }}-go${{ inputs.go-version }}-

View File

@@ -13,7 +13,8 @@ runs:
using: composite
steps:
- uses: ./.github/actions/free-disk-space
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
id: setup-go
with:
go-version-file: go.mod
check-latest: true
@@ -21,4 +22,5 @@ runs:
- if: ${{ inputs.cache == 'true' }}
uses: ./.github/actions/go-cache
with:
go-version: ${{ steps.setup-go.outputs.go-version }}
lint-cache: ${{ inputs.lint-cache }}

View File

@@ -1,22 +1,30 @@
name: node-setup
description: Set up pnpm and node and restore caches
description: Set up pnpm and node and restore the pnpm store cache
inputs:
cache:
description: Cache pnpm downloads
description: Restore the pnpm store cache
default: "true"
runs:
using: composite
steps:
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: pnpm/action-setup@73e46992e05b19abd31f2f3c20e0e7370d762115 # v6.1.1
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 26
- if: ${{ inputs.cache == 'true' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
id: store
shell: bash
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- if: ${{ inputs.cache == 'true' && github.workflow == 'cache-seeder' }}
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- if: ${{ inputs.cache != 'true' }}
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
path: ${{ steps.store.outputs.path }}
key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
- if: ${{ inputs.cache == 'true' && github.workflow != 'cache-seeder' }}
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
node-version: 24
path: ${{ steps.store.outputs.path }}
key: pnpm-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-${{ runner.arch }}-

View File

@@ -34,7 +34,7 @@ runs:
env:
# pgsql is chosen to be the unlucky one to run with the slow "race detector", it is about 60% slower.
GOTEST_FLAGS: -race -timeout=40m
TAGS: bindata gogit
TAGS: bindata
TEST_LDAP: 1
TEST_SHARD: ${{ inputs.shard }}
TEST_TOTAL_SHARDS: ${{ inputs.total-shards }}

10
.github/actions/python-setup/action.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
name: python-setup
description: Set up uv and python and restore the uv cache
runs:
using: composite
steps:
- uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
with:
python-version: 3.15 # renovate: datasource=python-version depName=python versioning=python
save-cache: ${{ github.workflow == 'cache-seeder' }}

48
.github/workflows/cache-prune.yml vendored Normal file
View File

@@ -0,0 +1,48 @@
name: cache-prune
# Keeps the repository's total cache size below a fixed limit, so that GitHub never
# rejects a save for being over the allowance.
on:
schedule:
- cron: "37 */6 * * *" # every six hours at :37
workflow_dispatch:
workflow_call:
permissions: {}
concurrency:
group: cache-prune
jobs:
prune:
runs-on: ubuntu-latest
if: github.repository == 'go-gitea/gitea'
permissions:
actions: write # to delete caches
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
steps:
# Keep the newest generation per key, restores never reach the older ones.
- name: delete superseded caches
run: |
gh cache list --limit 1000 --json id,key,ref,createdAt |
jq -r 'group_by([.ref, (.key | sub("(-go[0-9.]+)?-[0-9a-f]{40,64}(-[0-9]+-[0-9]+)?$"; ""))])[]
| sort_by(.createdAt)[:-1][] | "\(.id) \(.key)"' |
while read -r id key; do
echo "deleting $key"
gh cache delete "$id" || true
done
# Deletes least recently used first, the order GitHub itself evicts in.
- name: delete caches over the size limit
run: |
caches=$(gh cache list --limit 1000 --sort last_accessed_at --order asc --json id,key,sizeInBytes)
size=$(jq '[.[].sizeInBytes] | add // 0' <<< "$caches")
echo "cache usage: $((size / 1000000)) MB"
while [ "$size" -gt 6000000000 ] && read -r id bytes key; do
echo "deleting $key"
gh cache delete "$id" || true
size=$((size - bytes))
done <<< "$(jq -r '.[] | "\(.id) \(.sizeInBytes) \(.key)"' <<< "$caches")"

View File

@@ -1,21 +1,25 @@
# Populates main's cache scope so PR runs warm-start from it. Saves the go
# module, go build (incl. test compile), and golangci-lint caches.
#
# Caches are ref-scoped: PR runs read their own scope then fall back to the
# base branch. Per .github/actions/go-cache/action.yml, PRs are restore-only,
# so push-to-main is the only opportunity to populate the fallback scope.
# Populates main's cache scope so PR runs warm-start from it. Caches are ref-scoped:
# PR runs read their own scope then fall back to the base branch, and only this
# workflow saves, so push-to-main is the only chance to populate the fallback scope.
name: cache-seeder
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "go.mod" # a toolchain bump invalidates the build caches
- "go.sum"
- ".golangci.yml"
- "pnpm-lock.yaml"
- "pyproject.toml"
- "uv.lock"
- ".github/actions/go-cache/action.yml"
- ".github/actions/go-setup/action.yml"
- ".github/actions/node-setup/action.yml"
- ".github/actions/python-setup/action.yml"
- ".github/workflows/cache-seeder.yml"
concurrency:
@@ -29,27 +33,19 @@ jobs:
gobuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- run: make deps-backend deps-tools
- run: TAGS="bindata" make backend
- run: TAGS="bindata gogit" GOEXPERIMENT="" make backend
- name: warm test compile cache (bindata)
env:
TAGS: bindata
GOTEST_FLAGS: -race -list=^$$ -count=1
run: make test-backend
- name: warm test compile cache (bindata gogit)
env:
TAGS: bindata gogit
GOEXPERIMENT:
GOTEST_FLAGS: -race -list=^$$ -count=1
run: make test-backend
- name: warm integration compile cache
run: |
TAGS="bindata" make test-integration-compile
TAGS="bindata gogit" GOEXPERIMENT="" make test-integration-compile
TAGS="bindata gogit" GOTEST_FLAGS="-race" make test-integration-compile
TAGS="bindata" GOTEST_FLAGS="-race" make test-integration-compile
lint:
runs-on: ubuntu-latest
@@ -59,7 +55,7 @@ jobs:
include:
- { tags: "bindata", target: "lint-backend" }
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
with:
lint-cache: "true"
@@ -67,6 +63,28 @@ jobs:
- run: make generate-go
env:
TAGS: ${{ matrix.tags }}
- run: make ${{ matrix.target }}
- run: make "$TARGET"
env:
TAGS: ${{ matrix.tags }}
TARGET: ${{ matrix.target }}
frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/node-setup
- run: make deps-frontend
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/python-setup
- run: make deps-py
# reclaims the caches this run superseded, so the next save still fits in the allowance
prune:
needs: [gobuild, lint, frontend, python]
permissions:
actions: write
uses: ./.github/workflows/cache-prune.yml

View File

@@ -5,6 +5,8 @@ on:
# - cron: "7 0 * * 1" # every Monday at 00:07 UTC
workflow_dispatch:
permissions: {}
jobs:
cron-licenses:
runs-on: ubuntu-latest
@@ -12,11 +14,12 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
check-latest: true
cache: false
- run: make generate-gitignore
timeout-minutes: 40
- name: push translations to repo

View File

@@ -9,7 +9,7 @@ concurrency:
group: cron-renovate
env:
RENOVATE_VERSION: 43.141.5 # renovate: datasource=docker depName=ghcr.io/renovatebot/renovate
RENOVATE_VERSION: 44.210.0 # renovate: datasource=npm depName=renovate
permissions:
contents: read
@@ -20,13 +20,14 @@ jobs:
if: github.repository == 'go-gitea/gitea' # prevent running on forks
timeout-minutes: 30
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: renovatebot/github-action@6d859fc95779be83a0335ca704879b47e5d79641 # v46.1.16
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: renovatebot/github-action@1c0cf989c02653fba0efe7ca5772338860ca71dc # v46.3.12
with:
renovate-version: ${{ env.RENOVATE_VERSION }}
configurationFile: renovate.json5
token: ${{ secrets.RENOVATE_TOKEN }}
env:
RENOVATE_BINARY_SOURCE: install # auto-install go/node toolchains needed by post-upgrade tasks.
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg)$"]'
RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^make (tidy|svg|generate-codemirror-languages)$"]'
RENOVATE_REPOSITORIES: '["go-gitea/gitea"]'
RENOVATE_DOCKER_MAX_PAGES: 10 # https://github.com/renovatebot/renovate/issues/46925

View File

@@ -5,6 +5,8 @@ on:
- cron: "7 0 * * *" # every day at 00:07 UTC
workflow_dispatch:
permissions: {}
jobs:
crowdin-pull:
runs-on: ubuntu-latest
@@ -12,8 +14,8 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: crowdin/github-action@52aa776766211d83d975df51f3b9c53c2f8ba35f # v2.16.3
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: crowdin/github-action@5b9790df740c85af5412a3a3a91b24988d5d324f # v3.5.1
with:
upload_sources: true
upload_translations: false

View File

@@ -15,8 +15,6 @@ on:
value: ${{ jobs.detect.outputs.templates }}
docker:
value: ${{ jobs.detect.outputs.docker }}
dockerfile:
value: ${{ jobs.detect.outputs.dockerfile }}
swagger:
value: ${{ jobs.detect.outputs.swagger }}
yaml:
@@ -42,22 +40,21 @@ jobs:
actions: ${{ steps.changes.outputs.actions }}
templates: ${{ steps.changes.outputs.templates }}
docker: ${{ steps.changes.outputs.docker }}
dockerfile: ${{ steps.changes.outputs.dockerfile }}
swagger: ${{ steps.changes.outputs.swagger }}
yaml: ${{ steps.changes.outputs.yaml }}
json: ${{ steps.changes.outputs.json }}
e2e: ${{ steps.changes.outputs.e2e }}
shell: ${{ steps.changes.outputs.shell }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
id: changes
with:
filters: |
backend:
- "**/*.go"
- "templates/**/*.tmpl"
- "assets/emoji.json"
- "public/assets/emoji.json"
- "go.mod"
- "go.sum"
- "Makefile"
@@ -73,10 +70,11 @@ jobs:
frontend:
- "*.ts"
- "web_src/**"
- "tools/generate-svg.ts"
- "tools/generate-svg-vscode-extensions.json"
- "tools/**/*.ts"
- "tools/**/*.json"
- "tools/playwright.sh"
- "tsconfig.json"
- "assets/emoji.json"
- "public/assets/emoji.json"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
@@ -102,19 +100,13 @@ jobs:
docker:
- ".github/workflows/pull-docker-dryrun.yml"
- ".github/actions/docker-dryrun/**"
- "Dockerfile"
- "Dockerfile.rootless"
- "docker/**"
- "Makefile"
dockerfile:
- "Dockerfile"
- "Dockerfile.rootless"
swagger:
- "templates/swagger/v1_json.tmpl"
- "templates/swagger/v1_input.json"
- "templates/swagger/*.json"
- "Makefile"
- "package.json"
- "pnpm-lock.yaml"
@@ -135,6 +127,7 @@ jobs:
e2e:
- "tests/e2e/**"
- "tools/test-e2e.sh"
- "tools/playwright.sh"
- "playwright.config.ts"
shell:

View File

@@ -4,13 +4,14 @@ on:
push:
branches:
- main
- release/v*
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
@@ -19,8 +20,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: go-gitea/giteabot@912675d47455ac93be82d8bda4667a02b20a6fe4 # v1.0.4
- uses: go-gitea/giteabot@15b18fdedd98a80d0968d0bd9db3a9428814b808 # v1.5.1
with:
github_token: ${{ secrets.GITEABOT_TOKEN }}
gitea_fork: giteabot/gitea
checks: backport
checks: backport,backport_conflict_free

19
.github/workflows/giteabot-review.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
name: giteabot-review
# Relays PR reviews to giteabot.yml through its workflow_run trigger, because review
# runs on fork PRs get no secrets and a read-only token. The job itself does nothing.
on:
pull_request_review:
types:
- submitted
- edited
- dismissed
permissions: {}
jobs:
relay:
runs-on: ubuntu-latest
steps:
- run: "true"

View File

@@ -1,16 +1,11 @@
name: giteabot
on:
# When main advances, rerun merge queue maintenance so the oldest
# reviewed/wait-merge PR can be updated against the new base promptly.
push:
branches:
- main
# pull_request_target gives this workflow access to GITEABOT_TOKEN on PRs from
# forks, which the bot needs to write labels, statuses and comments. Safe here
# because the job only runs a pinned action and never checks out PR HEAD.
# These PR lifecycle events drive label maintenance, queue maintenance, and
# explicit bot actions triggered by relevant label changes.
# These PR lifecycle events drive label maintenance, lgtm, and explicit bot
# actions triggered by relevant label changes.
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- opened
@@ -20,15 +15,13 @@ on:
- closed
- review_requested
- review_request_removed
# Review events keep review-derived state such as lgtm labels and status checks
# in sync after approvals, edits, or dismissals.
pull_request_review:
# Reviews arrive through giteabot-review because fork PR review runs get no secrets
workflow_run:
workflows:
- giteabot-review
types:
- submitted
- edited
- dismissed
# Periodic maintenance is still useful as a backstop for queue cleanup and
# other housekeeping, even though main pushes now trigger it promptly.
- requested
# Daily housekeeping like locks, stale feedback issues, last calls and milestones
schedule:
- cron: "15 3 * * *"
# Allow maintainers to rerun selected checks manually when debugging bot
@@ -38,27 +31,26 @@ on:
checks:
description: Comma-separated list of non-backport checks to run
required: false
default: labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions
default: backport_branches,backport_conflict_free,labels,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions
permissions:
contents: read
issues: write
pull-requests: write
statuses: write
permissions: {}
concurrency:
group: ${{ format('{0}-{1}', github.workflow, (github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review') && format('pr-{0}', github.event.pull_request.number) || 'maintenance') }}
group: ${{ format('{0}-{1}', github.workflow, github.event_name == 'pull_request_target' && format('pr-{0}', github.event.pull_request.number) || github.event_name == 'workflow_run' && format('review-{0}', github.event.workflow_run.head_sha) || 'maintenance') }}
cancel-in-progress: false
jobs:
giteabot:
if: github.repository == 'go-gitea/gitea'
if: github.repository == 'go-gitea/gitea' && (github.event_name != 'workflow_run' || github.event.workflow_run.event == 'pull_request_review')
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
issues: write
pull-requests: write
statuses: write
steps:
# pull_request_review runs without repository secrets on fork PRs, so fall
# back to the workflow token for the non-backport checks handled here.
- uses: go-gitea/giteabot@912675d47455ac93be82d8bda4667a02b20a6fe4 # v1.0.4
- uses: go-gitea/giteabot@15b18fdedd98a80d0968d0bd9db3a9428814b808 # v1.5.1
with:
github_token: ${{ secrets.GITEABOT_TOKEN || github.token }}
checks: ${{ github.event.inputs.checks || 'labels,merge_queue,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions' }}
github_token: ${{ secrets.GITEABOT_TOKEN }}
checks: ${{ github.event.inputs.checks || 'backport_branches,backport_conflict_free,labels,lock,feedback,last_call,milestones,lgtm,translation_comment,pr_actions' }}

16
.github/workflows/merge-group.yml vendored Normal file
View File

@@ -0,0 +1,16 @@
name: merge-group
# Reports the required giteabot/lgtm check on merge queue commits. giteabot only sets it
# on PR heads, which already passed it before entering the queue.
on:
merge_group:
permissions: {}
jobs:
lgtm:
name: giteabot/lgtm
runs-on: ubuntu-latest
steps:
- run: "true"

View File

@@ -2,6 +2,7 @@ name: compliance
on:
pull_request:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -19,19 +20,19 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
with:
lint-cache: "true"
- run: make deps-backend deps-tools
- run: TAGS="bindata" make generate-go # lint-go also lints with "bindata" tags which requires "_bindata.go"
- run: make lint-backend
- run: TAGS="bindata" make generate-go lint-backend # lint-go can lint with "bindata" tags
lint-on-demand:
if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true' || needs.files-changed.outputs.docs == 'true' || needs.files-changed.outputs.swagger == 'true' || needs.files-changed.outputs.json == 'true' || needs.files-changed.outputs.shell == 'true'
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
with:
cache: "false"
@@ -39,12 +40,8 @@ jobs:
with:
cache: "false"
- run: make lint-spell
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true' || needs.files-changed.outputs.actions == 'true'
uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
with:
python-version: 3.14
uses: ./.github/actions/python-setup
- if: needs.files-changed.outputs.templates == 'true' || needs.files-changed.outputs.yaml == 'true'
run: make deps-py lint-templates lint-yaml
@@ -62,7 +59,7 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- run: make deps-backend deps-tools
- run: make --always-make checks-backend # ensure the "go-licenses" make target runs
@@ -72,11 +69,12 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/node-setup
- run: make deps-frontend
- run: make lint-frontend
- run: make checks-frontend
- run: make playwright
- run: make test-frontend
- run: make frontend
@@ -85,25 +83,27 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- run: make deps-backend generate-go
# no frontend build here as backend should be able to build, even without any frontend files
# CGO is not used when cross-compile, so these steps also test if the code is compatible with CGO disabled
- name: build-backend-arm64
run: go build -o gitea_linux_arm64
run: go build -ldflags '-s -w' -o gitea-linux-arm64
env:
GOOS: linux
GOARCH: arm64
TAGS: bindata gogit
TAGS: bindata
- name: build-backend-windows
run: go build -o gitea_windows
run: go build -ldflags '-s -w' -o gitea-windows
env:
GOOS: windows
GOARCH: amd64
TAGS: bindata gogit
TAGS: bindata
- name: build-backend-386
run: go build -o gitea_linux_386
run: go build -ldflags '-s -w' -o gitea-linux-386
env:
GOOS: linux
GOARCH: 386
- name: binary-sizes
run: ls -l gitea-*

View File

@@ -2,6 +2,7 @@ name: db-tests
on:
pull_request:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -21,10 +22,12 @@ jobs:
timeout-minutes: 50
services:
pgsql:
image: postgres:14
image: postgres:14@sha256:156f0b253fd61366d5fc2107ad45955027d5612f695a8436ce20167f3fa79bff
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
ports:
- "5432:5432"
ldap:
@@ -35,14 +38,14 @@ jobs:
minio:
# as github actions doesn't support "entrypoint", we need to use a non-official image
# that has a custom entrypoint set to "minio server /data"
image: bitnamilegacy/minio:2025.7.23
image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7
env:
MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678
ports:
- "9000:9000"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- uses: ./.github/actions/pgsql-shard
with:
@@ -57,10 +60,12 @@ jobs:
timeout-minutes: 50
services:
pgsql:
image: postgres:14
image: postgres:14@sha256:156f0b253fd61366d5fc2107ad45955027d5612f695a8436ce20167f3fa79bff
env:
POSTGRES_DB: test
POSTGRES_PASSWORD: postgres
options: >-
--mount type=tmpfs,destination=/var/lib/postgresql/data
ports:
- "5432:5432"
ldap:
@@ -71,14 +76,14 @@ jobs:
minio:
# as github actions doesn't support "entrypoint", we need to use a non-official image
# that has a custom entrypoint set to "minio server /data"
image: bitnamilegacy/minio:2025.7.23
image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7
env:
MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678
ports:
- "9000:9000"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- uses: ./.github/actions/pgsql-shard
with:
@@ -90,24 +95,22 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- run: make deps-backend
- run: make backend
env:
TAGS: bindata gogit
GOEXPERIMENT:
TAGS: bindata
- run: GITEA_TEST_DATABASE=sqlite make test-migration
env:
TAGS: bindata gogit
TAGS: bindata
- name: run tests
run: GITEA_TEST_DATABASE=sqlite make test-integration
timeout-minutes: 50
env:
# sqlite driver can contain large amount of Golang code, so don't use race detector for it, otherwise, extremely slow
GOTEST_FLAGS: -timeout=40m
TAGS: bindata gogit
GOEXPERIMENT:
TAGS: bindata
test-unit:
if: needs.files-changed.outputs.backend == 'true'
@@ -115,7 +118,7 @@ jobs:
runs-on: ubuntu-latest
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.15
image: elasticsearch:8.19.23@sha256:ef5cafd961d7e273f13fed8def82e63c5ae0491106b5eecb5a69aa12d64f089b
env:
discovery.type: single-node
xpack.security.enabled: false
@@ -125,13 +128,13 @@ jobs:
ports:
- "9200:9200"
meilisearch:
image: getmeili/meilisearch:v1
image: getmeili/meilisearch:v1@sha256:88f2a08eb9532cc54a60d8fd014a23bf5cfda33d0c01ecbbaca9b4b463e17466
env:
MEILI_ENV: development # disable auth
ports:
- "7700:7700"
redis:
image: redis:latest@sha256:c904002d182255b6db3cbe3a1e8ce6c187d15390c39500b59fc07181aabff7bf
image: redis:8@sha256:298e5b3bc566bade82f46ad5511777a4a07a294097ce16ada2f6a42be5239df5
options: >- # wait until redis has started
--health-cmd "redis-cli ping"
--health-interval 5s
@@ -140,18 +143,18 @@ jobs:
ports:
- 6379:6379
minio:
image: bitnamilegacy/minio:2025.7.23
image: bitnamilegacy/minio:2025.7.23@sha256:8935e75fa5d11295c17171e4aa49efe390a1193cd7f12e4d21b92af9ffef09d7
env:
MINIO_ROOT_USER: 123456
MINIO_ROOT_PASSWORD: 12345678
ports:
- "9000:9000"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:dae2a5f96553962901304b94e72ef87e299d0825e4b679673bcc527a25076fe4
image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:f9791d0a0613e93057c06ae31f7ac0c0222f0530590e67e020b0986c66a36fa6
ports:
- 10000:10000
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 minio devstoreaccount1.azurite.local mysql elasticsearch meilisearch smtpimap" | sudo tee -a /etc/hosts'
@@ -165,14 +168,6 @@ jobs:
GOTEST_FLAGS: -race -timeout=20m
TAGS: bindata
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
- name: unit-tests-gogit
run: make test-backend
env:
GOTEST_FLAGS: -race -timeout=20m
TAGS: bindata gogit
GOEXPERIMENT:
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
GITEA_TEST_CI_SKIP_EXTERNAL: true
- run: make test-check
test-mysql:
@@ -182,7 +177,7 @@ jobs:
services:
mysql:
# the bitnami mysql image has more options than the official one, it's easier to customize
image: bitnamilegacy/mysql:8.4
image: bitnamilegacy/mysql:8.4@sha256:7089d796fc9b4629a628bd445e4afabe607351ee665444c3197bdeaed812ea65
env:
ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: testgitea
@@ -191,7 +186,7 @@ jobs:
options: >-
--mount type=tmpfs,destination=/bitnami/mysql/data
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.19.15
image: elasticsearch:8.19.23@sha256:ef5cafd961d7e273f13fed8def82e63c5ae0491106b5eecb5a69aa12d64f089b
env:
discovery.type: single-node
xpack.security.enabled: false
@@ -208,7 +203,7 @@ jobs:
- "587:587"
- "993:993"
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mysql elasticsearch smtpimap" | sudo tee -a /etc/hosts'
@@ -221,7 +216,6 @@ jobs:
run: GITEA_TEST_DATABASE=mysql make test-integration
env:
TAGS: bindata
TEST_INDEXER_CODE_ES_URL: "http://elastic:changeme@elasticsearch:9200"
test-mssql:
if: needs.files-changed.outputs.backend == 'true'
@@ -229,7 +223,7 @@ jobs:
runs-on: ubuntu-latest
services:
mssql:
image: mcr.microsoft.com/mssql/server:2019-latest
image: mcr.microsoft.com/mssql/server:2019-latest@sha256:07d7dedecdbfcf6776035dc5eefb5bfd9aa1804b2f6b99f7dedb397d8a599069
env:
ACCEPT_EULA: Y
MSSQL_PID: Standard
@@ -237,11 +231,11 @@ jobs:
ports:
- "1433:1433"
devstoreaccount1.azurite.local: # https://github.com/Azure/Azurite/issues/1583
image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:dae2a5f96553962901304b94e72ef87e299d0825e4b679673bcc527a25076fe4
image: mcr.microsoft.com/azure-storage/azurite:latest@sha256:f9791d0a0613e93057c06ae31f7ac0c0222f0530590e67e020b0986c66a36fa6
ports:
- 10000:10000
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- name: Add hosts to /etc/hosts
run: '[ -e "/.dockerenv" ] || [ -e "/run/.containerenv" ] || echo "127.0.0.1 mssql devstoreaccount1.azurite.local" | sudo tee -a /etc/hosts'

View File

@@ -14,34 +14,26 @@ jobs:
files-changed:
uses: ./.github/workflows/files-changed.yml
# QEMU-based build is slow (40-50 minutes), so run arm64 and riscv64 when dockerfile changes.
# Run amd64 when any docker-related files change, which is fast (4 minutes).
container-amd64:
container:
if: needs.files-changed.outputs.docker == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: ./.github/actions/docker-dryrun
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
platform: linux/amd64
container-arm64:
if: needs.files-changed.outputs.dockerfile == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: ./.github/actions/docker-dryrun
cache-image: false
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Build regular image
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
platform: linux/arm64
container-riscv64:
if: needs.files-changed.outputs.dockerfile == 'true'
needs: [files-changed]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: ./.github/actions/docker-dryrun
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
- name: Build rootless image
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
platform: linux/riscv64
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
file: Dockerfile.rootless
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless

View File

@@ -2,6 +2,7 @@ name: e2e-tests
on:
pull_request:
merge_group:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -19,7 +20,7 @@ jobs:
needs: files-changed
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: ./.github/actions/go-setup
- uses: ./.github/actions/node-setup
- run: make deps-frontend

View File

@@ -10,6 +10,8 @@ concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions: {}
jobs:
labeler:
runs-on: ubuntu-latest
@@ -17,7 +19,7 @@ jobs:
contents: read
pull-requests: write
steps:
- uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
- uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7
with:
sync-labels: true
@@ -30,12 +32,12 @@ jobs:
pull-requests: write
steps:
# Base-branch checkout only: pull_request_target runs with elevated token; never run PR-head code here.
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ github.event.pull_request.base.sha }}
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7
with:
node-version: 24
node-version: 26
# Labels are only synced after the title lints, so an invalid title never reaches the label diff.
- run: node ./tools/ci-tools.ts lint-pr-title
env:
@@ -43,5 +45,6 @@ jobs:
- run: node ./tools/ci-tools.ts set-pr-labels
env:
PR_TITLE: ${{ github.event.pull_request.title }}
PR_TITLE_BEFORE: ${{ github.event.changes.title.from }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ github.token }}

View File

@@ -0,0 +1,34 @@
name: release-create-tag
run-name: Release v${{ inputs.version }} from ${{ github.ref_name }}
on:
workflow_dispatch:
inputs:
version:
description: Version to release, for example 28.0.1
required: true
permissions: {}
jobs:
tag:
runs-on: ubuntu-latest
environment:
name: release-signing
deployment: false
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
token: ${{ secrets.RELEASE_TOKEN }}
- uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
git_user_signingkey: true
git_committer_email: teabot@gitea.io
- env:
VERSION: ${{ inputs.version }}
run: |
[[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]
git tag -s -m "v$VERSION" "v$VERSION"
git push origin tag "v$VERSION"

View File

@@ -1,45 +0,0 @@
name: release-nightly-snapcraft
on:
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
jobs:
build-and-publish:
runs-on: ubuntu-latest
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- name: Install snapcraft
run: sudo snap install snapcraft --classic
- name: Authenticate snapcraft
shell: bash
run: snapcraft login --with <(printf '%s' "$SNAPCRAFT_STORE_CREDENTIALS")
- name: Remote build
run: |
snapcraft remote-build \
--launchpad-accept-public-upload \
--build-for=amd64,arm64,armhf
- name: List built snaps
run: find . -maxdepth 1 -type f -name '*.snap' -print
- name: Upload and release snapcraft nightly build
run: |
set -euo pipefail
for snap in ./*.snap; do
echo "Uploading $snap to edge"
snapcraft upload --release="latest/edge" "$snap"
done

View File

@@ -1,39 +1,66 @@
name: release-nightly
on:
push:
branches: [main, release/v*]
schedule:
- cron: "17 */12 * * *"
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: {}
jobs:
branches:
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
outputs:
branches: ${{ steps.changed.outputs.branches }}
steps:
- id: changed
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
run: |
since=$(gh run list --workflow release-nightly.yml --status success --limit 1 --json createdAt -q '.[0].createdAt')
branches=$(gh api --paginate "repos/$GH_REPO/branches" -q '.[].name | select(. == "main" or startswith("release/v"))' |
while read -r branch; do
if [ -z "$since" ] || [ -n "$(gh api "repos/$GH_REPO/commits?sha=$branch&since=$since&per_page=1" -q '.[].sha')" ]; then
echo "$branch"
fi
done | jq -Rnc '[inputs]')
echo "branches=$branches" >> "$GITHUB_OUTPUT"
nightly-binary:
needs: branches
if: needs.branches.outputs.branches != '[]'
strategy:
matrix:
branch: ${{ fromJSON(needs.branches.outputs.branches) }}
runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ matrix.branch }}
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
cache: false
- uses: ./.github/actions/node-setup
- run: make deps-frontend deps-backend
# xgo build
- run: make release
- run: make release GITHUB_REF_NAME="$BRANCH"
env:
TAGS: bindata
BRANCH: ${{ matrix.branch }}
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: import gpg key
@@ -51,34 +78,20 @@ jobs:
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
# clean branch name to get the folder name in the object storage
- name: Get cleaned branch name
id: clean_name
env:
REF: ${{ github.ref }}
REF: ${{ matrix.branch }}
run: |
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
env:
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
run: |
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
- name: upload binaries to cloudflare r2
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_REGION: auto
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
@@ -86,27 +99,37 @@ jobs:
aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress
nightly-container:
needs: branches
if: needs.branches.outputs.branches != '[]'
strategy:
matrix:
branch: ${{ fromJSON(needs.branches.outputs.branches) }}
runs-on: namespace-profile-gitea-release-docker
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ matrix.branch }}
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
cache-image: false
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- name: Get cleaned branch name
id: clean_name
env:
REF: ${{ github.ref }}
REF: ${{ matrix.branch }}
run: |
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\///' -e 's/release\/v//')
echo "branch=${REF_NAME}-nightly" >> "$GITHUB_OUTPUT"
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
id: meta
with:
context: git
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
@@ -114,9 +137,10 @@ jobs:
type=raw,value=${{ steps.clean_name.outputs.branch }}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
id: meta_rootless
with:
context: git
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
@@ -128,18 +152,18 @@ jobs:
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular docker image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
@@ -149,7 +173,7 @@ jobs:
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootful,mode=max
- name: build rootless docker image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
@@ -159,3 +183,47 @@ jobs:
annotations: ${{ steps.meta_rootless.outputs.annotations }}
cache-from: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless
cache-to: type=registry,ref=ghcr.io/go-gitea/gitea:buildcache-rootless,mode=max
nightly-snap:
needs: branches
if: contains(fromJSON(needs.branches.outputs.branches), 'main')
strategy:
fail-fast: false
matrix:
runner: [ubuntu-24.04, ubuntu-24.04-arm]
runs-on: ${{ matrix.runner }}
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: main
fetch-depth: 0
- name: Build snap
id: build
env:
SNAPCRAFT_IMAGE_INFO: '{"build_url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"}'
run: |
# snapcraft installs build-snaps and apt packages system-wide, which needs root
sudo snap install --classic snapcraft
sudo SNAPCRAFT_BUILD_INFO=1 SNAPCRAFT_IMAGE_INFO="$SNAPCRAFT_IMAGE_INFO" snapcraft pack --destructive-mode
echo "snap=$(ls ./*.snap)" >> "$GITHUB_OUTPUT"
# retry snapcraft uploads which can be flaky
- name: Publish snap
run: |
# part-gitea-pull.sh decides what is built, see the comments there:
# * grade devel (main or a prerelease tag) goes to latest/edge
# * grade stable (a new release tag) goes to latest/stable, and to latest/candidate which marks it as released
channel=latest/edge
if unsquashfs -cat "$SNAP" meta/snap.yaml | grep -qx 'grade: stable'; then
channel=latest/stable,latest/candidate
fi
for attempt in 1 2 3 4 5; do
snapcraft upload "$SNAP" --release "$channel" && exit 0
echo "::warning::snap upload attempt $attempt failed, retrying in 15s"
sleep 15
done
exit 1
env:
SNAP: ${{ steps.build.outputs.snap }}
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}

View File

@@ -1,166 +0,0 @@
name: release-tag-rc
on:
push:
tags:
- "v1*-rc*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
- run: make deps-frontend deps-backend
# xgo build
- run: make release
env:
TAGS: bindata
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: import gpg key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
with:
gpg_private_key: ${{ secrets.GPGSIGN_KEY }}
passphrase: ${{ secrets.GPGSIGN_PASSPHRASE }}
- name: sign binaries
env:
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
GPG_PASSPHRASE: ${{ secrets.GPGSIGN_PASSPHRASE }}
run: |
for f in dist/release/*; do
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
- name: Get cleaned branch name
id: clean_name
env:
REF: ${{ github.ref }}
run: |
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
env:
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
run: |
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
- name: upload binaries to cloudflare r2
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_REGION: auto
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
run: |
aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress
- name: Install GH CLI
uses: dev-hanz-ops/install-gh-cli-action@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
with:
gh-cli-version: 2.39.1
- name: create github release
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
gh release create "$TAG" --title "$TAG" --draft --notes-from-tag dist/release/*
container:
runs-on: namespace-profile-gitea-release-docker
permissions:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
id: meta
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
flavor: |
latest=false
# 1.2.3-rc0
tags: |
type=semver,pattern={{version}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
id: meta_rootless
with:
images: |-
gitea/gitea
ghcr.io/go-gitea/gitea
# each tag below will have the suffix of -rootless
flavor: |
latest=false
suffix=-rootless
# 1.2.3-rc0
tags: |
type=semver,pattern={{version}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular container image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
- name: build rootless container image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
push: true
file: Dockerfile.rootless
tags: ${{ steps.meta_rootless.outputs.tags }}
annotations: ${{ steps.meta_rootless.outputs.annotations }}

View File

@@ -3,14 +3,15 @@ name: release-tag-version
on:
push:
tags:
- "v1.*"
- "!v1*-rc*"
- "!v1*-dev"
- "v[0-9]*"
- "!v[0-9]*-*"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions: {}
jobs:
binary:
runs-on: namespace-profile-gitea-release-binary
@@ -19,25 +20,18 @@ jobs:
packages: write # to publish to ghcr.io
id-token: write
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: go.mod
check-latest: true
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
cache: false
- uses: ./.github/actions/node-setup
- run: make deps-frontend deps-backend
# xgo build
- run: make release
env:
TAGS: bindata
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: import gpg key
@@ -55,7 +49,7 @@ jobs:
cosign sign-blob "$f" --bundle "$f.sigstore.json" --yes
echo "$GPG_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --batch --yes --detach-sign -u "$GPG_FINGERPRINT" --output "$f.asc" "$f"
done
# clean branch name to get the folder name in S3
# clean branch name to get the folder name in the object storage
- name: Get cleaned branch name
id: clean_name
env:
@@ -64,40 +58,35 @@ jobs:
REF_NAME=$(echo "$REF" | sed -e 's/refs\/heads\///' -e 's/refs\/tags\/v//' -e 's/release\/v//')
echo "Cleaned name is ${REF_NAME}"
echo "branch=${REF_NAME}" >> "$GITHUB_OUTPUT"
- name: configure aws
uses: aws-actions/configure-aws-credentials@e7f100cf4c008499ea8adda475de1042d6975c7b # v6.2.0
with:
aws-region: ${{ secrets.AWS_REGION }}
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: upload binaries to s3
env:
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
run: |
aws s3 sync dist/release "s3://$AWS_S3_BUCKET/gitea/$BRANCH" --no-progress
# configure-aws-credentials exports AWS_REGION job-wide and it wins over AWS_DEFAULT_REGION, so pin it here
- name: upload binaries to cloudflare r2
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: auto
AWS_REGION: auto
CLOUDFLARE_R2_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_R2_ACCOUNT_ID }}
CLOUDFLARE_R2_BUCKET: ${{ secrets.CLOUDFLARE_R2_BUCKET }}
BRANCH: ${{ steps.clean_name.outputs.branch }}
run: |
aws s3 sync dist/release "s3://$CLOUDFLARE_R2_BUCKET/gitea/$BRANCH" --endpoint-url "https://$CLOUDFLARE_R2_ACCOUNT_ID.r2.cloudflarestorage.com" --no-progress
- name: Install GH CLI
uses: dev-hanz-ops/install-gh-cli-action@af38ce09b1ec248aeb08eea2b16bbecea9e059f8 # v0.2.1
- id: range
run: |
previous=$(git tag --list --sort=-v:refname | grep -xE 'v[0-9]+\.[0-9]+\.[0-9]+' | grep -A1 -xF "$GITHUB_REF_NAME" | tail -1) # highest stable version below this one
echo "range=$previous..$GITHUB_SHA" >> "$GITHUB_OUTPUT"
- name: skip reverts and reverted commits in changelog
env:
RANGE: ${{ steps.range.outputs.range }}
run: ./tools/generate-cliffignore.sh "$RANGE" | sort -u > .cliffignore
- uses: orhun/git-cliff-action@a9a95522b26fe6403f7bb24031f21fb573d0f5ff # v4.9.1
with:
gh-cli-version: 2.39.1
args: --tag ${{ github.ref_name }} ${{ steps.range.outputs.range }}
- name: create github release
env:
GH_CLI_VERSION: 2.103.3 # renovate: datasource=github-releases depName=cli/cli extractVersion=^v(?<version>.+)$
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
TAG: ${{ github.ref_name }}
run: |
gh release create "$TAG" --title "$TAG" --notes-from-tag dist/release/*
curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_CLI_VERSION}/gh_${GH_CLI_VERSION}_linux_amd64.tar.gz" | tar xz --strip-components=2 "gh_${GH_CLI_VERSION}_linux_amd64/bin/gh"
./gh release create "$TAG" --title "$TAG" --notes-file git-cliff/CHANGELOG.md dist/release/*
container:
runs-on: namespace-profile-gitea-release-docker
@@ -105,13 +94,15 @@ jobs:
contents: read
packages: write # to publish to ghcr.io
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# fetch all commits instead of only the last as some branches are long lived and could have many between versions
# fetch all tags to ensure that "git describe" reports expected Gitea version, eg. v1.21.0-dev-1-g1234567
- run: git fetch --unshallow --quiet --tags --force
- uses: docker/setup-qemu-action@06116385d9baf250c9f4dcb4858b16962ea869c3 # v4.1.0
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
- uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0
with:
cache-image: false
- uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
id: meta
with:
images: |-
@@ -128,7 +119,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- uses: docker/metadata-action@80c7e94dd9b9319bd5eb7a0e0fe9291e23a2a2e9 # v6.1.0
- uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0
id: meta_rootless
with:
images: |-
@@ -149,18 +140,18 @@ jobs:
annotations: |
org.opencontainers.image.authors="maintainers@gitea.io"
- name: Login to Docker Hub
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GHCR using PAT
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0
uses: docker/login-action@81c6f93d4bc991e510b342a00b419b3bb4efc5c6 # v4.7.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: build regular container image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64
@@ -168,7 +159,7 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
annotations: ${{ steps.meta.outputs.annotations }}
- name: build rootless container image
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/riscv64

9
.github/zizmor.yml vendored Normal file
View File

@@ -0,0 +1,9 @@
rules:
anonymous-definition:
disable: true
undocumented-permissions:
disable: true
concurrency-limits:
disable: true
self-repository:
disable: true # actionlint rejects "$/" uses, https://github.com/rhysd/actionlint/issues/711

View File

@@ -10,7 +10,9 @@ linters:
- depguard
- dupl
- errcheck
- exhaustruct_v5
- forbidigo
- forcetypeassert
- gocheckcompilerdirectives
- gocritic
- govet
@@ -51,14 +53,23 @@ linters:
desc: do not use the go-chi cache package, use gitea's cache system
- pkg: github.com/pkg/errors
desc: use builtin errors package instead
- pkg: gitea.com/go-chi/binding
desc: use our wrapper
migrations:
files:
- '**/models/migrations/**/*.go'
- '**/modelmigration/**/*.go'
deny:
- pkg: gitea.dev/models$
desc: migrations must not depend on the models package
- pkg: gitea.dev/models
desc: "migrations must not depend on the models package. HINT: MIGRATION-STRUCT-FROZEN"
- pkg: gitea.dev/modules/structs
desc: migrations must not depend on modules/structs (API structures change over time)
desc: "migrations must not depend on modules/structs. HINT: MIGRATION-STRUCT-FROZEN"
forbidigo:
forbid:
- pattern: '^(fmt\.Print(|f|ln)|print|println)$' # default
- pattern: '^exec\.CommandContext$' # use our wrapper for graceful termination
modernize:
disable:
- embedlit
nolintlint:
allow-unused: false
require-explanation: true
@@ -87,6 +98,11 @@ linters:
- name: increment-decrement
- name: modifies-value-receiver
- name: package-comments
- name: package-naming
arguments:
- convention-name-check-regex: '^[a-z][a-z0-9_]*$' # underscores are used by migration packages
skip-default-bad-name-check: true
skip-collision-with-common-std: true
- name: redefines-builtin-id
- name: superfluous-else
- name: time-naming
@@ -96,7 +112,7 @@ linters:
arguments:
- [] # AllowList - do not remove as args for the rule are positional and won't work without lists first
- [] # DenyList
- - skip-package-name-checks: true # supress errors from underscore in migration packages
- - skip-initialism-name-checks: true
staticcheck:
checks:
- all
@@ -118,6 +134,8 @@ linters:
enable:
- nilness
- unusedwrite
exhaustruct_v5:
explicit-mode: true
exclusions:
generated: lax
presets:
@@ -129,16 +147,13 @@ linters:
- linters:
- dupl
- errcheck
- forbidigo
- staticcheck
- unparam
path: _test\.go
- linters:
- dupl
- errcheck
path: models/migrations/v
- linters:
- forbidigo
path: cmd
path: modelmigration/v
- linters:
- dupl
text: (?i)webhook
@@ -155,13 +170,10 @@ linters:
- gocritic
text: '(?i)exitAfterDefer:'
paths:
- node_modules
- .venv
- public
- web_src
- third_party$
- builtin$
- examples$
- ^node_modules/
- ^\.venv/
- ^public/
- ^web_src/
issues:
max-issues-per-linter: 0
max-same-issues: 0
@@ -178,14 +190,15 @@ formatters:
- blank
- default
gofumpt:
extra-rules: true
extra:
group-params: true
exclusions:
generated: lax
paths:
- node_modules
- .venv
- public
- web_src
- ^node_modules/
- ^\.venv/
- ^public/
- ^web_src/
run:
timeout: 10m

View File

@@ -1 +1,2 @@
disable=SC1091,SC2001,SC2002,SC2016,SC2028,SC2046,SC2124,SC2128,SC2129,SC2154,SC2155,SC2164,SC2181,SC2207
# SC2153: false-alert "Possible misspelling: TAGS may not be assigned. Did you mean tags?". We already use strict mode.
disable=SC1091,SC2001,SC2002,SC2016,SC2028,SC2046,SC2124,SC2128,SC2129,SC2153,SC2154,SC2155,SC2164,SC2181,SC2207

View File

@@ -4,9 +4,4 @@ rules:
info-contact: off
oas2-api-host: off
oas2-parameter-description: off
oas2-schema: off
oas2-valid-schema-example: off
openapi-tags: off
operation-description: off
operation-singular-tag: off
operation-tag-defined: off

View File

@@ -1,20 +1,26 @@
- Use `make help` to find available development targets
- Run `make fmt` to format `.go` files, and run `make lint-go` to lint them
- Run `make lint-js` to lint `.ts` files
- Run `make tidy` after any `go.mod` changes
- Run single go tests with `go test -run '^TestName$' ./modulepath/`
- Run single js test files with `pnpm exec vitest <path-filter>`
- Run single playwright e2e test files with `GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e`
- Add the current year into the copyright header of new `.go` files
- Ensure no trailing whitespace in edited files
- Use Conventional Commits for commit messages and PR titles, e.g. `type(scope): subject`; `!` before the colon if breaking. Use `test` type for test-only changes.
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
- Keep comments short, prefer same-line, explain why, never narrate code
- Prefer unit tests over integration tests when logic is testable in isolation
- Aim for sub-2s local runtime for integration and e2e tests
- In TypeScript, use `!` (non-null assertion) instead of `?.`/`??` when a value is known to always exist
- For CSS layout, prefer `flex-*` helpers over per-child `tw-ml-*` / `tw-mr-*` margins; fall back to `tw-*` utilities when specificity requires `!important`
- Include authorship attribution in issue and pull request comments
- Always add `Assisted-By` trailers to commit messages in format `Assisted-by: AGENT_NAME:MODEL_VERSION`
- Never add `Co-Authored-By` `Signed-off-by` trailer to commit messages. Sign off must be done by a human.
- Never assume, verify before claiming
- List development targets with `make help`
- Before working on code, read the `docs` guides for every area touched: `guidelines-backend.md` for Go, `guidelines-frontend.md` for TS, CSS and Vue, both for templates, `testing.md` for tests, `development.md` for config options and migrations, `guidelines-refactoring.md` for refactors
- Before designing a feature or behavior, check prior art in GitHub and GitLab
- Don't add code for theoretical edge cases, only for ones that can realistically occur. Ask humans to make decisions if the edge case matters, comment the edge case for why it can be ignored if it doesn't matter
- Write docs concisely and user-facing, skip implementation details
- PR descriptions: minimal, only what and why, no task or file listings, no headings. Aim for less than 1000 characters. References go at the end of the description like `Fixes: <url>`.
- PR descriptions and update comments: Include screenshots for UI changes, before and after side by side. Take them at 4x scale in dark theme, cropped to the changes with some padding, and embed them at half CSS size
- Reference issues and PRs by full URL, not by number
- Use Conventional Commits for commit messages and PR titles, plus Gitea's `enhance` type for user-facing enhancements
- Add an `Assisted-by: AGENT_NAME:MODEL_VERSION` trailer to commit messages and PR descriptions, never `Co-Authored-By` or `Signed-off-by`
- Attribute agent authorship on one trailing line in issue and PR comments, never as a PR description section
- Never rewrite git history unless asked, update PRs with new commits and normal push
- Comments: write almost none, short and preferably same-line, explaining why for a future reader. Never narrate code, the change or the prompt. Preserve existing ones that still apply. If you need to write a paragraph-long comment, rethink your implementation, it is likely too complicated
- Add the current year to copyright headers of new `.go` files
- In `options/locale`, only edit `locale_en-US.json`, other locales are synced automatically
- In TS, use `!` instead of `?.`/`??` when a value always exists
- In Go, prefer to use modern language features wherever possible
- In Go, function-name prefixes in errors like `fmt.Errorf("Foo: %w", err)` must always name the function they are in
- Write sizes as multiplications like `64 * 1024`, not bit shifts like `64 << 10`
- Prefer `tw-*` utilities over inline `style` and `flex-*` helpers over per-child `tw-ml-*`/`tw-mr-*` margins, falling back to `tw-*` where specificity requires `!important`
- Run `make fmt` after `.go` edits, `make tidy` after `go.mod` edits, `make generate-swagger` after API changes, and lint what changed with `make lint-go`, `lint-js`, `lint-css` or `lint-templates`
- Fix the cause rather than disabling a linter or weakening a test. Where unavoidable, use the narrowest scope with a trailing comment giving the reason
- Run single tests with `go test -run '^TestName$' ./modulepath/` for Go, `pnpm exec vitest <path-filter>` for TS and `GITEA_TEST_E2E_FLAGS='<filepath>' make test-e2e` for e2e
- Write the fewest, fastest tests covering the behavior, extending an existing one where possible. Prefer unit tests where logic is testable in isolation
- Aim for sub-2s per integration test and sub-4s per e2e test. Wait on a deterministic condition rather than `sleep`, and prefer semantic locators in e2e tests

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
@AGENTS.md

View File

@@ -58,7 +58,8 @@ Contributions made with the assistance of AI tools are welcome, but contributors
2. Manually test the changes and add appropriate automated tests where feasible.
3. Only use AI to assist in contributions that you understand well enough to explain, defend, and revise yourself during review.
4. Disclose AI-assisted content clearly.
5. Do not use AI to reply to questions about your issue or pull request. The questions are for you, not an AI model.
5. Do not rely solely on AI to generate replies or engage in discussions.
Ensure your responses are concise, accurate, and thoughtful, avoiding generic or low-quality AI-generated content.
6. AI may be used to help draft issues and pull requests, but contributors remain responsible for the accuracy, completeness, and intent of what they submit.
Maintainers reserve the right to close pull requests and issues that do not disclose AI assistance, that appear to be low-quality AI-generated content, or where the contributor cannot explain or defend the proposed changes themselves.
@@ -111,7 +112,7 @@ If further discussion is needed, we encourage you to open a new issue instead an
## Building Gitea
See [docs/setup.md](docs/setup.md) for prerequisites and [docs/development.md](docs/development.md)
See [docs/build-setup.md](docs/build-setup.md) for prerequisites and [docs/development.md](docs/development.md)
for building Gitea and the development workflow.
## Styleguide
@@ -169,13 +170,13 @@ In the PR title, describe the problem you are fixing, not how you are fixing it.
Use the first comment as a summary of your PR. \
In the PR summary, you can describe exactly how you are fixing this problem.
PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format, because PRs are squash-merged and the PR title becomes the resulting commit message:
PR titles must follow the [Conventional Commits](https://www.conventionalcommits.org/) format, because PRs are squash-merged and the PR title becomes the resulting commit message and release notes entry:
```text
type(scope)!: subject
```
The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`).
The scope in parentheses is optional. A `!` immediately before the colon marks a [breaking change](https://www.conventionalcommits.org/en/v1.0.0/#summary): either `type!:` or `type(scope)!:` (not `type!(scope):`). Do not mark `build`, `chore`, `ci`, `docs`, `style`, or `test` as breaking.
Use one of these types:

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.24 AS frontend-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build
RUN apk --no-cache add build-base git nodejs pnpm
WORKDIR /src
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
@@ -8,8 +8,8 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --froze
COPY --exclude=.git/ . .
RUN make frontend
# Build backend for each target platform
FROM docker.io/library/golang:1.26-alpine3.24 AS build-env
# Cross-compile backend on the native platform to avoid slow QEMU emulation
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS build-env
ARG GITEA_VERSION
ARG TAGS=""
@@ -28,11 +28,15 @@ RUN go mod download
# ".git" directory is mounted separately later only for version data extraction.
COPY --exclude=.git/ . .
COPY --from=frontend-build /src/public/assets public/assets
RUN --mount=type=cache,target="/root/.cache/go-build" make generate-backend
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
# Build gitea, .git mount is required for version data
RUN --mount=type=cache,target="/root/.cache/go-build" \
--mount=type=bind,source=".git/",target=".git/" \
make backend
GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} make gitea
COPY docker/root /tmp/local
@@ -48,7 +52,7 @@ FROM docker.io/library/alpine:3.24 AS gitea
EXPOSE 22 3000
RUN apk --no-cache add \
RUN apk --no-cache upgrade && apk --no-cache add \
bash \
ca-certificates \
curl \

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1
# Build frontend on the native platform to avoid QEMU-related issues with nodejs ecosystem
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.26-alpine3.24 AS frontend-build
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS frontend-build
RUN apk --no-cache add build-base git nodejs pnpm
WORKDIR /src
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
@@ -8,8 +8,8 @@ RUN --mount=type=cache,target=/root/.local/share/pnpm/store pnpm install --froze
COPY --exclude=.git/ . .
RUN make frontend
# Build backend for each target platform
FROM docker.io/library/golang:1.26-alpine3.24 AS build-env
# Cross-compile backend on the native platform
FROM --platform=$BUILDPLATFORM docker.io/library/golang:1.27-alpine3.24 AS build-env
ARG GITEA_VERSION
ARG TAGS=""
@@ -27,11 +27,15 @@ RUN go mod download
# See the comments in Dockerfile
COPY --exclude=.git/ . .
COPY --from=frontend-build /src/public/assets public/assets
RUN --mount=type=cache,target="/root/.cache/go-build" make generate-backend
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
# Build gitea, .git mount is required for version data
RUN --mount=type=cache,target="/root/.cache/go-build" \
--mount=type=bind,source=".git/",target=".git/" \
make backend
GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT#v} make gitea
COPY docker/rootless /tmp/local
@@ -43,7 +47,7 @@ FROM docker.io/library/alpine:3.24 AS gitea-rootless
EXPOSE 2222 3000
RUN apk --no-cache add \
RUN apk --no-cache upgrade && apk --no-cache add \
bash \
ca-certificates \
dumb-init \

View File

@@ -41,7 +41,6 @@ Jimmy Praet <jimmy.praet@telenet.be> (@jpraet)
Leon Hofmeister <dev.lh@web.de> (@delvh)
Wim <wim@42.be> (@42wim)
Jason Song <i@wolfogre.com> (@wolfogre)
Yarden Shoham <git@yardenshoham.com> (@yardenshoham)
Yu Tian <zettat123@gmail.com> (@Zettat123)
Dong Ge <gedong_1994@163.com> (@sillyguodong)
Xinyi Gong <hestergong@gmail.com> (@HesterG)
@@ -65,3 +64,6 @@ Christopher Homberger <christopher.homberger@web.de> (@ChristopherHX)
Tobias Balle-Petersen <tobiasbp@gmail.com> (@tobiasbp)
TheFox <thefox0x7@gmail.com> (@TheFox0x7)
Nicolas <bircni@icloud.com> (@bircni)
Minjie Fang <wingsallen@gmail.com> (@wingsallen)
Shudhanshu Singh <sudhanshuwriterblc@gmail.com> (@SudhanshuMatrix)
Shivansh Garg <shivanshgarg587@gmail.com> (@SHIVANSHGARG07)

224
Makefile
View File

@@ -1,29 +1,23 @@
DIST := dist
DIST_DIRS := $(DIST)/binaries $(DIST)/release
# By default use go's 1.25 experimental json v2 library when building
# TODO: remove when no longer experimental
export GOEXPERIMENT ?= jsonv2
HASH := \#
GO ?= go
SHASUM ?= shasum -a 256
COMMA := ,
XGO_VERSION := go-1.26.x
AIR_PACKAGE ?= github.com/air-verse/air@v1.65.3 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.7.0 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.34.1 # renovate: datasource=go
XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.4.0 # renovate: datasource=go
AIR_PACKAGE ?= github.com/air-verse/air@v1.67.4 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v4/cmd/editorconfig-checker@v4.0.2 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.15.0 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.17 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.36.7 # renovate: datasource=go
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.9.0 # renovate: datasource=go
ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go
SHELLCHECK_IMAGE ?= docker.io/koalaman/shellcheck:v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # renovate: datasource=docker
CONTAINER_RUNTIME ?= $(shell hash docker >/dev/null 2>&1 && echo docker || echo podman)
PLAYWRIGHT_BROWSERS ?= chromium firefox webkit
PLAYWRIGHT_FLAGS ?=
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
ifeq ($(HAS_GO), yes)
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
@@ -42,18 +36,13 @@ endif
TAGS ?=
TAGS_EVIDENCE := $(MAKE_EVIDENCE_DIR)/tags
CGO_TAGS := sqlite_mattn pam
CGO_ENABLED ?= 0
ifneq (,$(findstring sqlite_mattn,$(TAGS))$(findstring pam,$(TAGS)))
ifneq ($(strip $(filter $(CGO_TAGS),$(TAGS))),)
CGO_ENABLED = 1
endif
STATIC ?=
EXTLDFLAGS ?=
ifneq ($(STATIC),)
EXTLDFLAGS = -extldflags "-static"
endif
ifeq ($(GOOS),windows)
IS_WINDOWS := yes
else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
@@ -62,14 +51,13 @@ else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows)
endif
endif
# GOFLAGS and EXTRA_GOFLAGS are for the 'go build' command only
ifeq ($(IS_WINDOWS),yes)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= gitea.exe
else
GOFLAGS := -v
EXECUTABLE ?= gitea
endif
# EXTRA_GOFLAGS is for the 'go build' command only
EXTRA_GOFLAGS ?=
ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu)
@@ -80,20 +68,27 @@ endif
# GOTEST_FLAGS is for unit test and integration test
GOTEST_FLAGS ?= -timeout 40m
# disable race detection for the packages that slow down the tests
GOTEST_FLAGS := -gcflags=modernc.org/sqlite/lib=-race=false $(GOTEST_FLAGS)
STORED_VERSION_FILE := VERSION
GITHUB_REF_TYPE ?= branch
GITHUB_REF_NAME ?= $(shell git rev-parse --abbrev-ref HEAD)
ifneq ($(GITHUB_REF_TYPE),branch)
# VERSION: the name for the build and filenames, e.g.: "1.2.3" for tag "v1.2.3", "main-nightly" for branch "main"
# GITEA_VERSION: the Gitea's internal version for display, e.g. "1.28.0+dev-356-ge47d0b66ea"
ifeq ($(GITHUB_REF_TYPE),tag)
# convert tag "v1.2.3" to "1.2.3"
VERSION ?= $(subst v,,$(GITHUB_REF_NAME))
GITEA_VERSION ?= $(VERSION)
else
else ifeq ($(GITHUB_REF_TYPE),branch)
ifneq ($(GITHUB_REF_NAME),)
# convert branch "release/v1.2" to "1.2-nightly"
VERSION ?= $(subst release/v,,$(GITHUB_REF_NAME))-nightly
else
VERSION ?= main
# no branch name info, use git ref name "HEAD" instead
VERSION ?= HEAD
endif
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
@@ -102,19 +97,15 @@ else
else
GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
endif
# if version = "main" then update version to "nightly"
ifeq ($(VERSION),main)
VERSION := main-nightly
else
$(error unsupported ref type $(GITHUB_REF_TYPE))
endif
LDFLAGS := $(LDFLAGS) -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)"
RELEASE_ENV = GO="$(GO)" TAGS="$(TAGS)" LDFLAGS="$(LDFLAGS)" DIST="$(DIST)" VERSION="$(VERSION)"
LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64,linux/riscv64
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list gitea.dev/models/migrations/...) gitea.dev/tests/integration/migration-test gitea.dev/tests gitea.dev/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list gitea.dev/models/migrations/...)
GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list gitea.dev/modelmigration/...) gitea.dev/tests/integration/migration-test gitea.dev/tests gitea.dev/tests/integration,$(shell $(GO) list ./... | grep -v /vendor/))
MIGRATE_TEST_PACKAGES ?= $(shell $(GO) list gitea.dev/modelmigration/...)
FRONTEND_SOURCES := $(shell find web_src/js web_src/css -type f)
FRONTEND_CONFIGS := vite.config.ts tailwind.config.ts
@@ -135,12 +126,10 @@ GO_LICENSE_FILE := assets/go-licenses.json
TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(DIST) $(MAKE_EVIDENCE_DIR) $(AIR_TMP_DIR)
GO_DIRS := build cmd models modules routers services tests tools
WEB_DIRS := web_src/js web_src/css
GO_DIRS := build cmd modelmigration models modules routers services tests tools
ESLINT_FILES := web_src/js tools *.ts tests/e2e
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) templates options/locale/locale_en-US.json .github $(filter-out CHANGELOG.md, $(wildcard *.go *.md *.yml *.yaml *.toml))
EDITORCONFIG_FILES := templates .github/workflows options/locale/locale_en-US.json
GO_SOURCES := $(wildcard *.go)
@@ -148,11 +137,12 @@ GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go")
GO_SOURCES += $(GENERATED_GO_DEST)
ESLINT_CONCURRENCY ?= 2
ESLINT_ARGS := --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY)
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
SWAGGER_SPEC_INPUT := templates/swagger/v1_input.json
SWAGGER_EXCLUDE := gitea.dev/sdk
OPENAPI3_SPEC := templates/swagger/v1_openapi3_json.tmpl
SWAGGER_SPEC_INPUT := templates/swagger/v1-input.json
SWAGGER_SPEC := templates/swagger/v1-swagger.generated.json
OPENAPI3_SPEC := templates/swagger/v1-openapi3.generated.json
TEST_MYSQL_HOST ?= mysql:3306
TEST_MYSQL_DBNAME ?= testgitea
@@ -211,7 +201,7 @@ fmt: ## format the Go and template code
.PHONY: fmt-check
fmt-check: fmt
@diff=$$(git diff --color=always $(GO_SOURCES) templates $(WEB_DIRS)); \
@diff=$$(git diff --color=always $(GO_SOURCES) templates); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
printf "%s" "$${diff}"; \
@@ -231,7 +221,9 @@ endif
generate-swagger: $(SWAGGER_SPEC) $(OPENAPI3_SPEC) ## generate the swagger spec from code comments
$(SWAGGER_SPEC): $(GO_SOURCES) $(SWAGGER_SPEC_INPUT)
$(GO) run $(SWAGGER_PACKAGE) generate spec --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)'
@output="$$($(GO) run $(SWAGGER_PACKAGE) generate spec --enable-allof-compounding --skip-enum-desc --exclude "$(SWAGGER_EXCLUDE)" --input "$(SWAGGER_SPEC_INPUT)" --output './$(SWAGGER_SPEC)' 2>&1)" || { printf '%s\n' "$$output" >&2; exit 1; }; \
warnings="$$(printf '%s\n' "$$output" | grep -v '^go: ')"; \
if [ -n "$$warnings" ]; then printf '%s\n' "$$warnings" >&2; exit 1; fi
.PHONY: swagger-check
swagger-check: generate-swagger
@@ -244,11 +236,10 @@ swagger-check: generate-swagger
.PHONY: swagger-validate
swagger-validate: ## check if the swagger spec is valid
@# swagger "validate" requires that the "basePath" must start with a slash, but we are using Golang template "{{...}}"
@$(SED_INPLACE) -E -e 's|"basePath":( *)"(.*)"|"basePath":\1"/\2"|g' './$(SWAGGER_SPEC)' # add a prefix slash to basePath
@# FIXME: there are some warnings
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
@$(SED_INPLACE) -E -e 's|"basePath":( *)"/(.*)"|"basePath":\1"\2"|g' './$(SWAGGER_SPEC)' # remove the prefix slash from basePath
@# ensure no warnings
@output="$$($(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)' 2>&1)"; status=$$?; \
printf '%s\n' "$$output" | grep -v '^go: '; \
case "$$output" in *WARNING:*) exit 1;; esac; exit $$status
.PHONY: generate-openapi3
generate-openapi3: $(OPENAPI3_SPEC) ## generate the OpenAPI 3.0 spec from the Swagger 2.0 spec
@@ -275,10 +266,10 @@ checks-frontend: lockfile-check svg-check ## check frontend files
checks-backend: tidy-check swagger-check openapi3-check fmt-check swagger-validate security-check ## check backend files
.PHONY: lint
lint: lint-frontend lint-backend lint-templates lint-swagger lint-spell lint-md lint-actions lint-json lint-yaml lint-shell ## lint everything
lint: lint-frontend lint-backend lint-templates lint-swagger lint-md lint-actions lint-json lint-yaml lint-shell ## lint everything
.PHONY: lint-fix
lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix ## lint everything and fix issues
lint-fix: lint-frontend-fix lint-backend-fix ## lint everything and fix issues
.PHONY: lint-frontend
lint-frontend: lint-js lint-css ## lint frontend files
@@ -294,12 +285,12 @@ lint-backend-fix: lint-go-fix lint-editorconfig ## lint backend files and fix is
.PHONY: lint-js
lint-js: node_modules ## lint js and ts files
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES)
pnpm exec eslint $(ESLINT_ARGS) $(ESLINT_FILES)
pnpm exec vue-tsc
.PHONY: lint-js-fix
lint-js-fix: node_modules ## lint js and ts files and fix issues
pnpm exec eslint --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) $(ESLINT_FILES) --fix
pnpm exec eslint $(ESLINT_ARGS) $(ESLINT_FILES) --fix
pnpm exec vue-tsc
.PHONY: lint-css
@@ -312,7 +303,7 @@ lint-css-fix: node_modules ## lint css files and fix issues
.PHONY: lint-swagger
lint-swagger: node_modules ## lint swagger files
pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC)
pnpm exec spectral lint -q -F hint $(SWAGGER_SPEC) $(OPENAPI3_SPEC)
.PHONY: lint-md
lint-md: node_modules ## lint markdown files
@@ -322,14 +313,6 @@ lint-md: node_modules ## lint markdown files
lint-md-fix: node_modules ## lint markdown files and fix issues
pnpm exec markdownlint --fix *.md
.PHONY: lint-spell
lint-spell: ## lint spelling
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -error
.PHONY: lint-spell-fix
lint-spell-fix: ## lint spelling and fix issues
@git ls-files $(SPELLCHECK_FILES) | xargs go run $(MISSPELL_PACKAGE) -dict assets/misspellings.csv -w
.PHONY: lint-go
lint-go: ## lint go files
GO=$(GO) GOLANGCI_LINT_PACKAGE=$(GOLANGCI_LINT_PACKAGE) $(GO) run ./tools/lint-go-all.go
@@ -346,7 +329,7 @@ lint-editorconfig:
.PHONY: lint-actions
lint-actions: .venv ## lint action workflow files
@$(GO) run $(ACTIONLINT_PACKAGE)
@uv run --frozen zizmor --quiet --min-confidence=medium .github
@uv run --frozen zizmor --quiet --persona=pedantic --min-confidence=medium .github
.PHONY: lint-shell
lint-shell: ## lint shell scripts
@@ -363,11 +346,11 @@ lint-yaml: .venv ## lint yaml files
.PHONY: lint-json
lint-json: node_modules ## lint json files
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY)
pnpm exec eslint -c eslint.json.config.ts $(ESLINT_ARGS)
.PHONY: lint-json-fix
lint-json-fix: node_modules ## lint and fix json files
pnpm exec eslint -c eslint.json.config.ts --color --max-warnings=0 --concurrency $(ESLINT_CONCURRENCY) --fix
pnpm exec eslint -c eslint.json.config.ts $(ESLINT_ARGS) --fix
.PHONY: watch
watch: ## watch everything and continuously rebuild
@@ -387,7 +370,7 @@ test-backend: ## test backend files
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' $(GO_TEST_PACKAGES)
.PHONY: test-frontend
test-frontend: node_modules ## test frontend files
test-frontend: playwright ## test frontend files
pnpm exec vitest
.PHONY: test-check
@@ -402,10 +385,11 @@ test-check:
exit 1; \
fi
.PHONY: test-backend\#%
test-backend\#%:
TEST_BACKEND_GOALS := $(filter test-backend$(HASH)%,$(MAKECMDGOALS))
.PHONY: $(TEST_BACKEND_GOALS)
$(TEST_BACKEND_GOALS): test-backend$(HASH)%:
@echo "Running go test with -tags '$(TAGS)'..."
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run $(subst .,/,$*) $(GO_TEST_PACKAGES)
@$(GO) test $(GOTEST_FLAGS) -tags='$(TAGS)' -run '$*' $(GO_TEST_PACKAGES)
.PHONY: coverage
coverage:
@@ -421,7 +405,12 @@ unit-test-coverage:
.PHONY: tidy
tidy: ## run go mod tidy
$(eval MIN_GO_VERSION := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
$(eval GO_TOOLCHAIN := $(shell grep -Eo '^toolchain\s+go[0-9.]+' go.mod | cut -d' ' -f2))
$(GO) mod tidy -compat=$(MIN_GO_VERSION)
@# workaround https://github.com/golang/go/issues/75331: restore toolchain if tidy dropped it
@if [ -n "$(GO_TOOLCHAIN)" ] && ! grep -qE '^toolchain\s' go.mod; then \
$(GO) mod edit -toolchain=$(GO_TOOLCHAIN); \
fi
@$(MAKE) --no-print-directory $(GO_LICENSE_FILE)
vendor: go.mod go.sum
@@ -444,7 +433,7 @@ $(GO_LICENSE_FILE): go.mod go.sum
GO=$(GO) $(GO) run build/generate-go-licenses.go $(GO_LICENSE_FILE)
.PHONY: test-integration
test-integration:
test-integration: $(EXECUTABLE)
@# Use a compiled binary: testlogger forwards gitea logs to t.Log, so `go test -v`
@# would flood output per passing test. testcache can't help these tests anyway —
@# they mutate the work directory, so cache inputs change between runs.
@@ -455,9 +444,10 @@ test-integration:
test-integration-compile:
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -c -o /dev/null gitea.dev/tests/integration
.PHONY: test-integration\#%
test-integration\#%:
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run $(subst .,/,$*) gitea.dev/tests/integration
TEST_INTEGRATION_GOALS := $(filter test-integration$(HASH)%,$(MAKECMDGOALS))
.PHONY: $(TEST_INTEGRATION_GOALS)
$(TEST_INTEGRATION_GOALS): test-integration$(HASH)%: $(EXECUTABLE)
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' -run '$*' gitea.dev/tests/integration
.PHONY: test-migration
test-migration: migrations.integration.test migrations.individual.test
@@ -473,15 +463,15 @@ migrations.individual.test:
.PHONY: migrations.individual.test\#%
migrations.individual.test\#%:
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' gitea.dev/models/migrations/$*
$(GO) test $(GOTEST_FLAGS) -tags '$(TAGS)' gitea.dev/modelmigration/$*
.PHONY: playwright
playwright: deps-frontend
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) ./tools/test-e2e.sh install
@./tools/playwright.sh $(PLAYWRIGHT_FLAGS) $(PLAYWRIGHT_BROWSERS)
.PHONY: test-e2e
test-e2e: playwright frontend backend
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh run $(GITEA_TEST_E2E_FLAGS)
@CONTAINER_RUNTIME=$(CONTAINER_RUNTIME) EXECUTABLE=$(EXECUTABLE) ./tools/test-e2e.sh $(GITEA_TEST_E2E_FLAGS)
.PHONY: build
build: frontend backend ## build everything
@@ -506,38 +496,38 @@ generate-go: $(TAGS_PREREQ)
.PHONY: security-check
security-check:
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./... || true
go run $(GOVULNCHECK_PACKAGE) -show color ./... || true
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)
$(error pam support set via TAGS does not support static builds)
endif
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build $(GOFLAGS) $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
.PHONY: release
release: frontend generate release-windows release-linux release-darwin release-freebsd release-copy release-compress vendor release-sources release-check
CGO_ENABLED="$(CGO_ENABLED)" CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) build -v $(EXTRA_GOFLAGS) -tags '$(TAGS)' -ldflags '-s -w $(LDFLAGS)' -o $@
$(DIST_DIRS):
mkdir -p $(DIST_DIRS)
# Release builds always use Go's native cross compilation. To cross-compile with CGO,
# use "build" target with proper TAGS/LDFLAGS/CGO_CFLAGS to make "$(EXECUTABLE)" target run the "go build" command.
.PHONY: release
release: frontend release-binaries release-copy release-compress vendor release-sources release-check
.PHONY: release-binaries
release-binaries: | $(DIST_DIRS)
@$(RELEASE_ENV) ./tools/build-release.sh
.PHONY: release-windows
release-windows: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq (,$(findstring gogit,$(TAGS)))
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
endif
@$(RELEASE_ENV) ./tools/build-release.sh windows
.PHONY: release-linux
release-linux: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
@$(RELEASE_ENV) ./tools/build-release.sh linux
.PHONY: release-darwin
release-darwin: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
@$(RELEASE_ENV) ./tools/build-release.sh darwin
.PHONY: release-freebsd
release-freebsd: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
@$(RELEASE_ENV) ./tools/build-release.sh freebsd
.PHONY: release-copy
release-copy: | $(DIST_DIRS)
@@ -553,11 +543,11 @@ release-compress: | $(DIST_DIRS)
.PHONY: release-sources
release-sources: | $(DIST_DIRS)
echo $(VERSION) > $(STORED_VERSION_FILE)
echo $(GITEA_VERSION) > $(STORED_VERSION_FILE)
# bsdtar needs a ^ to prevent matching subdirectories
$(eval EXCL := --exclude=$(shell tar --help | grep -q bsdtar && echo "^")./)
# use transform to a add a release-folder prefix; in bsdtar the transform parameter equivalent is -s
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '/^./gitea-src-$(VERSION)/'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
$(eval TRANSFORM := $(shell tar --help | grep -q bsdtar && echo "-s '|^./|gitea-src-$(VERSION)/|'" || echo "--transform 's|^./|gitea-src-$(VERSION)/|'"))
tar $(addprefix $(EXCL),$(TAR_EXCLUDES)) $(TRANSFORM) -czf $(DIST)/release/gitea-src-$(VERSION).tar.gz .
rm -f $(STORED_VERSION_FILE)
@@ -575,16 +565,14 @@ deps-backend: ## install backend dependencies
$(GO) mod download
.PHONY: deps-tools
deps-tools: ## install tool dependencies
$(GO) install $(AIR_PACKAGE) & \
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \
$(GO) install $(GXZ_PACKAGE) & \
$(GO) install $(MISSPELL_PACKAGE) & \
$(GO) install $(SWAGGER_PACKAGE) & \
$(GO) install $(XGO_PACKAGE) & \
$(GO) install $(GOVULNCHECK_PACKAGE) & \
$(GO) install $(ACTIONLINT_PACKAGE) & \
deps-tools: ## fetch and compile tool dependencies
$(GO) run -exec true $(AIR_PACKAGE) & \
$(GO) run -exec true $(EDITORCONFIG_CHECKER_PACKAGE) & \
$(GO) run -exec true $(GOLANGCI_LINT_PACKAGE) & \
$(GO) run -exec true $(GXZ_PACKAGE) & \
$(GO) run -exec true $(SWAGGER_PACKAGE) & \
$(GO) run -exec true $(GOVULNCHECK_PACKAGE) & \
$(GO) run -exec true $(ACTIONLINT_PACKAGE) & \
wait
node_modules: pnpm-lock.yaml
@@ -595,28 +583,6 @@ node_modules: pnpm-lock.yaml
uv sync
@touch .venv
.PHONY: update
update: update-go update-js update-py ## update dependencies
.PHONY: update-go
update-go: ## update go dependencies
$(GO) get -u ./...
$(MAKE) tidy
.PHONY: update-js
update-js: node_modules ## update js dependencies
pnpm exec updates -u -f package.json
rm -rf node_modules pnpm-lock.yaml
pnpm install
@touch node_modules
.PHONY: update-py
update-py: node_modules ## update py dependencies
pnpm exec updates -u -f pyproject.toml
rm -rf .venv uv.lock
uv sync
@touch .venv
.PHONY: vite
vite: $(FRONTEND_DEST) ## build vite files
@@ -657,6 +623,10 @@ lockfile-check:
generate-gitignore: ## update gitignore files
$(GO) run build/generate-gitignores.go
.PHONY: generate-emoji
generate-emoji: ## update emoji data from Unicode
$(GO) run build/generate-emoji.go
.PHONY: generate-images
generate-images: | node_modules ## generate images
cd tools && node generate-images.ts $(TAGS)

View File

@@ -2,7 +2,6 @@
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/gitea.dev)](https://goreportcard.com/report/gitea.dev "Go Report Card")
[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
@@ -63,13 +62,13 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request
Translations are done through [Crowdin](https://translate.gitea.com). If you want to translate to a new language, ask one of the managers in the Crowdin project to add a new language there.
You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord. For general translation questions there is a section in the docs. Currently a bit empty, but we hope to fill it as questions pop up.
You can also just create an issue for adding a language or ask on Discord on the #translation channel. If you need context or find some translation issues, you can leave a comment on the string or ask on Discord.
Get more information from [documentation](https://docs.gitea.com/contributing/localization).
Get more information from [the translation section of our contributing guide](CONTRIBUTING.md#translation).
## Official and Third-Party Projects
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [tea](https://gitea.com/gitea/tea) and an [action runner](https://gitea.com/gitea/act_runner) for Gitea Action.
We provide an official [go-sdk](https://gitea.com/gitea/go-sdk), a CLI tool called [gt](https://gitea.com/gitea/cli) and an [action runner](https://gitea.com/gitea/runner) for Gitea Action.
We maintain a list of Gitea-related projects at [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea), where you can discover more third-party projects, including SDKs, plugins, themes, and more.
@@ -116,12 +115,12 @@ Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea"
For dynamic config options, you can change it on your admin panel's configuration section.
For static config options, you can edit your `app.ini` file and resart the instance.
For static config options, you can edit your `app.ini` file and restart the instance.
See [app.example.ini](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) or [configuration documentation](https://docs.gitea.com/administration/config-cheat-sheet) for more details.
**Where can I find the security patches?**
In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches.
Check the [release notes](https://github.com/go-gitea/gitea/releases) and [security advisories](https://github.com/go-gitea/gitea/security/advisories) for security patches.
(more FAQs are listed in [FAQ documentation](https://docs.gitea.com/help/faq))

View File

@@ -2,7 +2,6 @@
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/gitea.dev)](https://goreportcard.com/report/gitea.dev "Go Report Card")
[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
@@ -73,11 +72,11 @@
您也可以创建一个 issue 来添加语言,或者在 discord 的 #translation 频道上询问。如果您需要上下文或发现一些翻译问题,可以在字符串上留言或在 Discord 上询问。对于一般的翻译问题,文档中有一个部分。目前有点空,但我们希望随着问题的出现而填充它。
更多信息请参阅 [文件](https://docs.gitea.com/contributing/localization)。
更多信息请参阅 [文件](https://docs.gitea.com/zh-cn/contributing/localization)。
## 官方和第三方项目
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我们提供了一个官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一个名为 [gt](https://gitea.com/gitea/cli) 的 CLI 工具和一个 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
我们在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 维护了一个 Gitea 相关项目的列表,您可以在那里发现更多的第三方项目,包括 SDK、插件、主题等。
@@ -126,7 +125,7 @@ Gitea 的发音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea
**在哪里可以找到安全补丁?**
在 [发布日志](https://github.com/go-gitea/gitea/releases) 或 [变更日志](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索关键词 `SECURITY` 以找到安全补丁。
在 [发布日志](https://github.com/go-gitea/gitea/releases) 中,搜索关键词 `SECURITY` 以找到安全补丁。
## 许可证

View File

@@ -2,7 +2,6 @@
[![](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml/badge.svg?branch=main)](https://github.com/go-gitea/gitea/actions/workflows/release-nightly.yml?query=branch%3Amain "Release Nightly")
[![](https://img.shields.io/discord/322538954119184384.svg?logo=discord&logoColor=white&label=Discord&color=5865F2)](https://discord.gg/Gitea "Join the Discord chat at https://discord.gg/Gitea")
[![](https://goreportcard.com/badge/gitea.dev)](https://goreportcard.com/report/gitea.dev "Go Report Card")
[![](https://pkg.go.dev/badge/gitea.dev?status.svg)](https://pkg.go.dev/gitea.dev "GoDoc")
[![](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest "GitHub release")
[![](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea "Help Contribute to Open Source")
@@ -73,11 +72,11 @@
您也可以創建一個 issue 來添加語言,或者在 discord 的 #translation 頻道上詢問。如果您需要上下文或發現一些翻譯問題,可以在字符串上留言或在 Discord 上詢問。對於一般的翻譯問題,文檔中有一個部分。目前有點空,但我們希望隨著問題的出現而填充它。
更多信息請參閱 [文件](https://docs.gitea.com/contributing/localization)。
更多信息請參閱 [文件](https://docs.gitea.com/zh-tw/contributing/localization)。
## 官方和第三方項目
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [tea](https://gitea.com/gitea/tea) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/act_runner)。
我們提供了一個官方的 [go-sdk](https://gitea.com/gitea/go-sdk),一個名為 [gt](https://gitea.com/gitea/cli) 的 CLI 工具和一個 Gitea Action 的 [action runner](https://gitea.com/gitea/runner)。
我們在 [gitea/awesome-gitea](https://gitea.com/gitea/awesome-gitea) 維護了一個 Gitea 相關項目的列表,您可以在那裡發現更多的第三方項目,包括 SDK、插件、主題等。
@@ -126,7 +125,7 @@ Gitea 的發音是 [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY),就像 "gi-tea
**在哪裡可以找到安全補丁?**
在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 或 [變更日誌](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
在 [發佈日誌](https://github.com/go-gitea/gitea/releases) 中,搜索關鍵詞 `SECURITY` 以找到安全補丁。
## 許可證

View File

@@ -6,20 +6,46 @@ If you discover a security issue, please bring it to their attention right away!
Previous vulnerabilities are listed at https://about.gitea.com/security.
## Supported Versions
Security fixes follow our [support policy](docs/release-management.md#end-of-life-eol): only the most recent major release and the `main` branch receive them.
Reports must be filed against a supported version. Before reporting, verify that the issue reproduces on a supported version; any report that only affects an unsupported version will be closed as out of scope.
## Reporting a Vulnerability
Please **DO NOT** file a public issue, instead send your report privately to `security@gitea.io`.
Please **DO NOT** file a public issue. Instead, report the vulnerability privately by opening a [GitHub security advisory](https://github.com/go-gitea/gitea/security/advisories/new), or optionally by sending your report to `security@gitea.io`. Former is preferred if given a choice.
If reporting via GitHub, open an advisory per found issue. You can group the found issues if they relate to a single area (same feature, same fault present in multiple routes) but do not group unrelated reports into one as it makes it much harder to assess and track for duplicates.
Do not use LLM to write the reports. It's meant for people to read so take your time and use your own words to write it. If you have used LLM to find the bug, be transparent about it and verify that it is not hallucination before submitting the report.
If your report turns out to be a duplicate, you will be credited in the original report.
### Report requirements
When filing the report please document it. Reports must include:
- Version or a range of versions it was successfully tested against. If reproducible against the `main` branch, a commit hash is also acceptable. Ambiguous versions like `latest` are not acceptable.
- Settings used, especially when they diverge from the defaults
- Reproduction steps from freshly installed instance. If the fault is only reproducible through upgrade from older version, reproduction should state that.
Take your time to format it properly as it'll be much easier to read. Prefer a concise writing style.
When submitting an advisory, it should have the `gitea.dev` go package as package `name.cd`
### Triage process
Github does not have any search or tagging in GHSA so in most cases your report will leave triage state when we have a fix for it in works for next release.
This helps us keep track of items fixed and pending release and separate them from things that we either haven't looked at yet or have not fixed.
## Protecting Security Information
Due to the sensitive nature of security information, you can use the below GPG public key to encrypt your mail body.
The PGP key is valid until July 4, 2026.
The PGP key is valid until July 23, 2027.
```
Key ID: 6FCD2D5B
Key Type: RSA
Expires: 7/4/2026
Expires: 7/23/2027
Key Size: 4096/4096
Fingerprint: 3DE0 3D1E 144A 7F06 9359 99DC AAFD 2381 6FCD 2D5B
```
@@ -42,18 +68,18 @@ lzpAjnN9/KLtQroutrm+Ft0mdjDiJUeFVl1cOHDhoyfCsQh62HumoyZoZvqzQd6e
AbN11nq6aViMe2Q3je1AbiBnRnQSHxt1Tc8X4IshO3MQK1Sk7oPI6LA5oQARAQAB
tCJHaXRlYSBTZWN1cml0eSA8c2VjdXJpdHlAZ2l0ZWEuaW8+iQJXBBMBCABBAhsD
BQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheAFiEEPeA9HhRKfwaTWZncqv0jgW/N
LVsFAmhoHmkFCQeT6esACgkQqv0jgW/NLVuFLRAAmjBQSKRAgs2bFIEj7HLAbDp4
f+XkdH+GsT3jRPOZ9QZgmtM+TfoE4yNgIVfOl+s4RdjM/W4QzqZuPQ55hbEHd056
cJmm7B+6GsHFcdrPmh65sOCEIyh4+t45dUfeWpFsDPqm9j1UHXAJQIpB8vDEVAPH
t+3wLCk8GMPJs1o5tIyMmaO23ngvkwn8eG7KgY+rp2PzObrb5g7ppci0ILzILkrp
HVjZsEfUWRgSVF7LuU5ppqDKrlcqwUpQq6n3kGMZcLrCp6ACKP04TBmTfUxNwdL7
I0N7apI2Pbct9T1Gv/lYAUFWyU2c3gh/EBLbO6BukaLOFRQHrtNfdJV/YnMPlcXr
LUJjK9K4eAH9DsrZqrisz/LthsC2BaNIN3KRMTk5YTYgmIh8GXzSgihORmtDFELC
RroID3pTuS0zjXh+wpY9GuPTh7UW23p42Daxca4fAT4k5EclvDRUrL21xMopPMiL
HuNdELz4FVchRTy05PjzKVyjVInDNojE2KUxnjxZDzYJ6aT/g+coD5yfntYm8BEj
+ZzL0ndZES54hzKLpv7zwBQwFzam68clZYmDPILOPTflQDfpGEWmJK4undFU5obz
ZsQRz0R3ulspChATbZxO0d5LX2obLpKO9X3b5VoO1KF+R8Vjw1Y0KxrNZ6rIcfqH
Z50QVQKSe9dm08K0ON+5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
LVsFAmpi3IkFCQmOqAsACgkQqv0jgW/NLVsgyxAAuTyj4+PImOFr7ZkEJcseWUy+
gWQrJDagj+Ks2SKGneVroJ63q5Vngx44b7JNabauKel4IJJCScLdwA7k0DKFHPOM
rUS6aavW5+961CMQxJ13vWz7qxLxhI4FXle255xFSXgOo9T1W7+wtm/wtze8+sws
8XKAVBZGuVI1/KzVpE6bWxpIIYK79DC07NWGDAZzMbuO92TFHBRblXsicPWExBjC
/oFmT70YMcA0wOwU/YKYMg2eK2XYUr5+uYX6FnP7iUhDj1vZmwkF33Cj9v9D3Hv9
NzAquj97uLht4wL2sqsIgZd4Vc37uipPDFaddzTb/lon7lPbJPMLObcd4HjSTkIo
nEMM0wdOQZU7mIIgRM9o3mr/gyVigl3uARmUKaP9PhBaOGTjkni0nrv90hSHBHeu
ayM76vl2tzAB2KpDxHIHbI2SEdytV28l/rxWrsbk2kG7s7QB6VKiThTatYevh1kg
6Ujgl2xNSntG6t6PWjQ65OMSq5Iiwe7RfOZkNmPRGP/rbzMbgWXB3iCR2gVQvsxA
ctYdDZdUYAeQ5g6xSKJBloygDQdLzk9m0hqNUyl6gxhRNRw69hqp9E4mxFF3L1Gi
Z2LspLX1ZZWMh0bp0AniWzZuzLuNI0KYhlok6fnshjidJ1qYM/lbz5eJTYWxoJZW
rczn8gB3+MZbULc/iiO5Ag0EYrVn/gEQALrFLQjCR3GjuHSindz0rd3Fnx/t7Sen
T+p07yCSSoSlmnJHCQmwh4vfg1blyz0zZ4vkIhtpHsEgc+ZAG+WQXSsJ2iRz+eSN
GwoOQl4XC3n+QWkc1ws+btr48+6UqXIQU+F8TPQyx/PIgi2nZXJB7f5+mjCqsk46
XvH4nTr4kJjuqMSR/++wvre2qNQRa/q/dTsK0OaN/mJsdX6Oi+aGNaQJUhIG7F+E
@@ -65,21 +91,20 @@ s+GsP9I3cmWWQcKYxWHtE8xTXnNCVPFZQj2nwhJzae8ypfOtulBRA3dUKWGKuDH/
axFENhUsT397aOU3qkP/od4a64JyNIEo4CTTSPVeWd7njsGqli2U3A4xL2CcyYvt
D/MWcMBGEoLSNTswwKdom4FaJpn5KThnK/T0bQcmJblJhoCtppXisbexZnCpuS0x
Zdlm2T14KJ3LABEBAAGJAjwEGAEIACYCGwwWIQQ94D0eFEp/BpNZmdyq/SOBb80t
WwUCaGgeJAUJB5PppgAKCRCq/SOBb80tW/NWEACB6Jrf0gWlk7e+hNCdnbM0ZVWU
f2sHNFfXxxsdhpcDgKbNHtkZb8nZgv8AX+5fTtUwMVa3vKcdw30xFiIM5N7cCIPV
vg/5z5BtfEaitnabEUG2iiVDIy8IHXIcK10rX+7BosA3QDl2PsiBHwyi5G13lRk8
zGTSNDuOalug33h5/lr2dPigamkq74Aoy29q8Rjad6GfWHipL2bFimgtY+Zdi0BH
NLk4EJXxj1SgVx5dtkQzWJReBA5M+FQ4QYQZBO+f4TDoOLmjui152uhkoLBQbGAa
WWJFTVxm0bG5MXloEL3gA8DfU7XDwuW/sHJC5pBko8RpQViooOhckMepZV3Y83DK
bwLYa3JmPgj2rEv4993dvrJbQhpGd082HOxOsllCs8pgNq1SnXpWYfcGTgGKC3ts
U8YZUUJUQ7mi2L8Tv3ix20c9EiGmA30JAmA8eZTC3cWup91ZkkVBFRml2czTXajd
RWZ6GbHV5503ueDQcB8yBVgF3CSixs67+dGSbD3p86OqGrjAcJzM5TFbNKcnGLdE
kGbZpNwAISy750lXzXKmyrh5RTCeTOQerbwCMBvHZO+HAevA/LXDTw2OAiSIQlP5
sYA4sFYLQ30OAkgJcmdp/pSgVj/erNtSN07ClrOpDb/uFpQymO6K2h0Pst3feNVK
9M2VbqL9C51z/wyHLg==
=SfZA
WwUCamLcngUJCY6oIAAKCRCq/SOBb80tWxilD/41xF1pwD2lGqjWLmlXcd0Ok5xm
67TSGiKiFZUgI3ftkRVlqmeJqjVmRjpc2dLfCuVBzlaP0Oyv5Jn+2CPqgmWIk5tw
pqlxqmsXiIZ9Uv2wWItxrXIwQ6DHc1qcIOu7feTnt1HVhxp2GqlXl3tA/+MTTDmn
4rqrHffI7ps9eCbK/SfSuoQk3+x/x4GPqWDbsNw1E9dDjPPhsKfdwzZ6dFR2Yhzu
2U0GDc2aeBA0Gjy6VFBj1ksmGxQCL3TqtGEHOonoPpTIjmDd35Xq6BSu119onOQS
THZ7+3JQRpizBVrIklKf33UOikZy9WHuffFddP46m8qC2Alh1aB7xcD8K3P9gzd5
OJO9Zr8JR+rvi8eOsCHvJqGhe0JMMf9bgXKjQ1tMTDTe6r0Jnw7HbkDghjpj9xG7
GYmDB0CL4kP5nAZ1OPfIasIu3Llhcy6mS52ECw0X6PNwUINwBn7F6EKAiciw8/OS
CXoGJDI5alwNhh+3DL4oBH1aq1lnmxmhFNpwpRDvtbRGmZeeASTeC4LMAtqP85Er
8W9ulIgQ4Ub8QuoxvE5hzWEB1QUkJgozI8vO4DoGlrsz7P8cPzrOT7WqUfUPTYE6
oZEfEYDSj1IZHnPYPS+yLBJMJ/Lc1h8OCetmbJZx4QWaX3Fgar7Zks8FfJQOTN6i
HnOlrsaMDYNyO76uBQ==
=4kVJ
-----END PGP PUBLIC KEY BLOCK-----
```
Security reports are greatly appreciated and we will publicly thank you for it, although we keep your name confidential if you request it.

View File

@@ -436,6 +436,7 @@
"jsonl",
"mcmeta",
"sarif",
"slnlaunch",
"tact",
"tfstate",
"topojson",
@@ -452,6 +453,8 @@
".htmlhintrc",
".imgbotconfig",
".nycrc",
".releaserc",
".secrets.baseline",
".tern-config",
".tern-project",
".watchmanconfig",
@@ -691,10 +694,17 @@
"extensions": [
"ini",
"cnf",
"container",
"dof",
"lektorproject",
"mount",
"network",
"prefs",
"properties",
"service",
"socket",
"target",
"timer",
"url",
"conf"
],
@@ -702,6 +712,7 @@
".buckconfig",
".coveragerc",
".flake8",
".gitreview",
".pylintrc",
"HOSTS",
"buildozer.spec",
@@ -776,9 +787,12 @@
"extensions": [
"r",
"rd",
"rhistory",
"rsx"
],
"filenames": [
".Rapp.history",
".Rhistory",
".Rprofile",
"expr-dist"
]
@@ -898,6 +912,7 @@
"bats",
"command",
"ksh",
"pacscript",
"sbatch",
"slurm",
"tmux",
@@ -1161,6 +1176,7 @@
"kml",
"launch",
"mdpolicy",
"meta4",
"mjml",
"mod",
"mojo",
@@ -1181,6 +1197,7 @@
"pt",
"pubxml",
"qhelp",
"rbxmx",
"rdf",
"res",
"resx",
@@ -1267,7 +1284,9 @@
".clang-format",
".clang-tidy",
".clangd",
".eslintrc",
".gemrc",
".releaserc",
"CITATION.cff",
"glide.lock",
"pixi.lock",

11483
assets/emoji.json generated

File diff suppressed because it is too large Load Diff

307
assets/go-licenses.json generated

File diff suppressed because one or more lines are too long

View File

@@ -1,21 +0,0 @@
acounts,accounts
canidate,candidate
comfirm,confirm
converage,coverage
currrently,currently
delimeter,delimiter
differrent,different
exclusing,excluding
finshed,finished
formated,formatted
inderect,indirect
insuficient,insufficient
likly,likely
mergable,mergeable
overrided,overridden
priortized,prioritized
registeration,registration
reuqest,request
reviwer,reviewer
superceded,superseded
underlaying,underlying
1 acounts accounts
2 canidate candidate
3 comfirm confirm
4 converage coverage
5 currrently currently
6 delimeter delimiter
7 differrent different
8 exclusing excluding
9 finshed finished
10 formated formatted
11 inderect indirect
12 insuficient insufficient
13 likly likely
14 mergable mergeable
15 overrided overridden
16 priortized prioritized
17 registeration registration
18 reuqest request
19 reviwer reviewer
20 superceded superseded
21 underlaying underlying

View File

@@ -7,213 +7,129 @@
package main
import (
"flag"
"bufio"
"errors"
"fmt"
"go/format"
"io"
"log"
"net/http"
"os"
"regexp"
"sort"
"strconv"
"slices"
"strings"
"unicode/utf8"
"sync"
"gitea.dev/modules/json"
)
const (
gemojiURL = "https://raw.githubusercontent.com/rhysd/gemoji/537ff2d7e0496e9964824f7f73ec7ece88c9765a/db/emoji.json"
maxUnicodeVersion = 16
emojiTestURL = "https://www.unicode.org/Public/17.0.0/emoji/emoji-test.txt"
jsonFile = "public/assets/emoji.json"
)
var flagOut = flag.String("o", "modules/emoji/emoji_data.go", "out")
// Gemoji is a set of emoji data.
type Gemoji []Emoji
// Emoji represents a single emoji and associated data.
type Emoji struct {
Emoji string `json:"emoji"`
Description string `json:"description,omitempty"`
Aliases []string `json:"aliases"`
UnicodeVersion string `json:"unicode_version,omitempty"`
SkinTones bool `json:"skin_tones,omitempty"`
type emoji struct {
Emoji string `json:"emoji"`
Aliases []string `json:"aliases"`
description string
}
// Don't include some fields in JSON
func (e Emoji) MarshalJSON() ([]byte, error) {
type emoji Emoji
x := emoji(e)
x.UnicodeVersion = ""
x.Description = ""
x.SkinTones = false
return json.Marshal(x)
}
var slugRe = sync.OnceValue(func() *regexp.Regexp {
return regexp.MustCompile(`[^a-z0-9]+`)
})
func main() {
flag.Parse()
// generate data
buf, err := generate()
if err != nil {
log.Fatalf("generate err: %v", err)
}
// write
err = os.WriteFile(*flagOut, buf, 0o644)
if err != nil {
log.Fatalf("WriteFile err: %v", err)
if err := generate(); err != nil {
log.Fatal(err)
}
}
var replacer = strings.NewReplacer(
"main.Gemoji", "Gemoji",
"main.Emoji", "\n",
"}}", "},\n}",
", Description:", ", ",
", Aliases:", ", ",
", UnicodeVersion:", ", ",
", SkinTones:", ", ",
)
func generate() error {
// the existing file is also the alias source, so existing aliases stay stable
existing, err := os.ReadFile(jsonFile)
if err != nil {
return err
}
var existingEmojis []*emoji
if err := json.Unmarshal(existing, &existingEmojis); err != nil {
return err
}
existingAliases := make(map[string][]string, len(existingEmojis))
for _, e := range existingEmojis {
existingAliases[e.Emoji] = e.Aliases
}
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
emojis, err := fetchEmojis(existingAliases)
if err != nil {
return err
}
func generate() ([]byte, error) {
// load gemoji data
res, err := http.Get(gemojiURL)
lines := make([]string, len(emojis))
for i, e := range emojis {
line, err := json.Marshal(e)
if err != nil {
return err
}
lines[i] = string(line)
}
return os.WriteFile(jsonFile, []byte("[\n"+strings.Join(lines, ",\n")+"\n]\n"), 0o644)
}
func isSkinTone(r rune) bool {
return r >= 0x1f3fb && r <= 0x1f3ff
}
func fetchEmojis(existingAliases map[string][]string) ([]*emoji, error) {
res, err := http.Get(emojiTestURL)
if err != nil {
return nil, err
}
defer res.Body.Close()
// read all
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("fetching %s: %s", emojiTestURL, res.Status)
}
// unmarshal
var data Gemoji
err = json.Unmarshal(body, &data)
if err != nil {
return nil, err
}
skinTones := make(map[string]string)
skinTones["\U0001f3fb"] = "Light Skin Tone"
skinTones["\U0001f3fc"] = "Medium-Light Skin Tone"
skinTones["\U0001f3fd"] = "Medium Skin Tone"
skinTones["\U0001f3fe"] = "Medium-Dark Skin Tone"
skinTones["\U0001f3ff"] = "Dark Skin Tone"
var tmp Gemoji
// filter out emoji that require greater than max unicode version
for i := range data {
val, _ := strconv.ParseFloat(data[i].UnicodeVersion, 64)
if int(val) <= maxUnicodeVersion {
tmp = append(tmp, data[i])
}
}
data = tmp
sort.Slice(data, func(i, j int) bool {
return data[i].Aliases[0] < data[j].Aliases[0]
})
aliasMap := make(map[string]int, len(data))
for i, e := range data {
if e.Emoji == "" || len(e.Aliases) == 0 {
var emojis []*emoji
scanner := bufio.NewScanner(res.Body)
for scanner.Scan() {
// e.g. "1F44D ; fully-qualified # 👍 E0.6 thumbs up"
_, rest, _ := strings.Cut(scanner.Text(), ";")
status, comment, _ := strings.Cut(rest, "#")
if strings.TrimSpace(status) != "fully-qualified" {
continue
}
for _, a := range e.Aliases {
if a == "" {
continue
fields := strings.SplitN(strings.TrimSpace(comment), " ", 3)
if strings.ContainsFunc(fields[0], isSkinTone) {
continue
}
emojis = append(emojis, &emoji{Emoji: fields[0], description: fields[2]})
}
if err := scanner.Err(); err != nil {
return nil, err
}
var errs []error
aliasOwners := map[string]string{}
for _, e := range emojis {
e.Aliases = existingAliases[e.Emoji]
if e.Aliases == nil {
e.Aliases = []string{strings.Trim(slugRe().ReplaceAllString(strings.ReplaceAll(strings.ToLower(e.description), "’", ""), "_"), "_")}
}
delete(existingAliases, e.Emoji)
for _, alias := range e.Aliases {
if owner, ok := aliasOwners[alias]; ok {
errs = append(errs, fmt.Errorf("alias %q used by both %q and %q", alias, owner, e.Emoji))
}
aliasMap[a] = i
aliasOwners[alias] = e.Emoji
}
}
// gitea customizations
i, ok := aliasMap["tada"]
if ok {
data[i].Aliases = append(data[i].Aliases, "hooray")
for code, aliases := range existingAliases {
errs = append(errs, fmt.Errorf("emoji %q with aliases %v is missing from Unicode data", code, aliases))
}
i, ok = aliasMap["laughing"]
if ok {
data[i].Aliases = append(data[i].Aliases, "laugh")
if len(errs) > 0 {
return nil, errors.Join(errs...)
}
// write a JSON file to use with tribute (write before adding skin tones since we can't support them there yet)
file, _ := json.MarshalIndent(data, "", " ")
_ = os.WriteFile("assets/emoji.json", append(file, '\n'), 0o644)
// Add skin tones to emoji that support it
var (
s []string
newEmoji string
newDescription string
newData Emoji
)
for i := range data {
if data[i].SkinTones {
for k, v := range skinTones {
s = strings.Split(data[i].Emoji, "")
if utf8.RuneCountInString(data[i].Emoji) == 1 {
s = append(s, k)
} else {
// insert into slice after first element because all emoji that support skin tones
// have that modifier placed at this spot
s = append(s, "")
copy(s[2:], s[1:])
s[1] = k
}
newEmoji = strings.Join(s, "")
newDescription = data[i].Description + ": " + v
newAlias := data[i].Aliases[0] + "_" + strings.ReplaceAll(v, " ", "_")
newData = Emoji{newEmoji, newDescription, []string{newAlias}, "12.0", false}
data = append(data, newData)
}
}
}
sort.Slice(data, func(i, j int) bool {
return data[i].Aliases[0] < data[j].Aliases[0]
slices.SortFunc(emojis, func(a, b *emoji) int {
return strings.Compare(a.Aliases[0], b.Aliases[0])
})
// add header
str := replacer.Replace(fmt.Sprintf(hdr, gemojiURL, data))
// change the format of the unicode string
str = emojiRE.ReplaceAllStringFunc(str, func(s string) string {
var err error
s, err = strconv.Unquote(s[len("{Emoji:"):])
if err != nil {
panic(err)
}
return "{" + strconv.QuoteToASCII(s)
})
// format
return format.Source([]byte(str))
return emojis, nil
}
const hdr = `
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package emoji
// Code generated by build/generate-emoji.go. DO NOT EDIT.
// Sourced from %s
var GemojiData = %#v
`

View File

@@ -15,17 +15,24 @@ import (
"slices"
"sort"
"strings"
"sync"
)
// regexp is based on go-license, excluding README and NOTICE
// https://github.com/google/go-licenses/blob/master/licenses/find.go
// also defined in vite.config.ts
var licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
// primaryLicenseRe matches exact primary license filenames without suffixes.
// When a directory has both primary and variant files (e.g. LICENSE and
// LICENSE.docs), only the primary files are kept.
var primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
var globalVars = sync.OnceValue(func() (ret struct {
licenseRe *regexp.Regexp
primaryLicenseRe *regexp.Regexp
},
) {
// regexp is based on go-license, excluding README and NOTICE
// https://github.com/google/go-licenses/blob/master/licenses/find.go
// also defined in vite.config.ts
ret.licenseRe = regexp.MustCompile(`^(?i)((UN)?LICEN(S|C)E|COPYING).*$`)
// primaryLicenseRe matches exact primary license filenames without suffixes.
// When a directory has both primary and variant files (e.g. LICENSE and
// LICENSE.docs), only the primary files are kept.
ret.primaryLicenseRe = regexp.MustCompile(`^(?i)(LICEN[SC]E|COPYING)$`)
return ret
})
// ignoredNames are LicenseEntry.Name values to exclude from the output.
var ignoredNames = map[string]bool{
@@ -144,7 +151,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
continue
}
name := entry.Name()
if !licenseRe.MatchString(name) {
if !globalVars().licenseRe.MatchString(name) {
continue
}
if excludedExt[strings.ToLower(filepath.Ext(name))] {
@@ -181,7 +188,7 @@ func scanDirForLicenses(dir, modulePath, moduleRoot string) []LicenseEntry {
var primary []LicenseEntry
for _, e := range entries {
fileName := e.Path[strings.LastIndex(e.Path, "/")+1:]
if primaryLicenseRe.MatchString(fileName) {
if globalVars().primaryLicenseRe.MatchString(fileName) {
primary = append(primary, e)
}
}

View File

@@ -10,7 +10,7 @@
// cleaner SDK output with proper enum types instead of anonymous strings.
//
// Run: go run build/generate-openapi.go
// Output: templates/swagger/v1_openapi3_json.tmpl
// Output: templates/swagger/v1-openapi3.generated.json
//go:build ignore
@@ -21,35 +21,21 @@ import (
"fmt"
"log"
"os"
"regexp"
"sort"
"strings"
"gitea.dev/build/openapi3gen"
"github.com/getkin/kin-openapi/openapi3"
)
const (
swaggerSpecPath = "templates/swagger/v1_json.tmpl"
openapi3OutPath = "templates/swagger/v1_openapi3_json.tmpl"
appSubUrlVar = "{{.SwaggerAppSubUrl}}"
appVerVar = "{{.SwaggerAppVer}}"
appSubUrlPlaceholder = "GITEA_APP_SUB_URL_PLACEHOLDER"
appVerPlaceholder = "0.0.0-gitea-placeholder"
swaggerSpecPath = "templates/swagger/v1-swagger.generated.json"
openapi3OutPath = "templates/swagger/v1-openapi3.generated.json"
)
var (
appSubUrlRe = regexp.MustCompile(regexp.QuoteMeta(appSubUrlVar))
appVerRe = regexp.MustCompile(regexp.QuoteMeta(appVerVar))
enumScanDirs = []string{
"modules/structs",
"modules/commitstatus",
}
)
var enumScanDirs = []string{
"modules/structs",
"modules/commitstatus",
}
func main() {
astEnumMap, err := openapi3gen.ScanSwaggerEnumTypes(enumScanDirs)
@@ -68,28 +54,17 @@ func main() {
log.Fatalf("reading swagger spec: %v", err)
}
cleaned := appSubUrlRe.ReplaceAll(data, []byte(appSubUrlPlaceholder))
cleaned = appVerRe.ReplaceAll(cleaned, []byte(appVerPlaceholder))
oas3, err := openapi3gen.Convert(cleaned, astEnumMap)
oas3, err := openapi3gen.Convert(data, astEnumMap)
if err != nil {
log.Fatalf("converting to openapi 3.0: %v", err)
}
oas3.Servers = openapi3.Servers{
{URL: appSubUrlPlaceholder + "/api/v1"},
}
out, err := json.MarshalIndent(oas3, "", " ")
if err != nil {
log.Fatalf("marshaling openapi 3.0: %v", err)
}
result := strings.ReplaceAll(string(out), appSubUrlPlaceholder, appSubUrlVar)
result = strings.ReplaceAll(result, appVerPlaceholder, appVerVar)
result = strings.TrimSpace(result)
if err := os.WriteFile(openapi3OutPath, []byte(result), 0o644); err != nil {
if err := os.WriteFile(openapi3OutPath, out, 0o644); err != nil {
log.Fatalf("writing openapi 3.0 spec: %v", err)
}

View File

@@ -8,6 +8,7 @@ import (
"regexp"
"sort"
"strings"
"sync"
"gitea.dev/modules/json"
@@ -16,11 +17,19 @@ import (
"github.com/getkin/kin-openapi/openapi3"
)
// rxDeprecated matches "deprecated" as a word at the start of a description
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
// like "not deprecated" or "previously deprecated, now supported".
var rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
var globalVars = sync.OnceValue(func() (ret struct {
rxDeprecated *regexp.Regexp
rxSwaggerEnum *regexp.Regexp
},
) {
// rxDeprecated matches "deprecated" as a word at the start of a description
// or preceded by whitespace/punctuation that indicates a leading marker (e.g.
// "Deprecated: true", "deprecated (use X instead)"). Rejects negated phrases
// like "not deprecated" or "previously deprecated, now supported".
ret.rxDeprecated = regexp.MustCompile(`(?i)(?:^|[\n.;])\s*deprecated\b`)
ret.rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
return ret
})
// Convert parses a Swagger 2.0 spec and returns an OAS3 spec, applying
// Gitea-specific post-processing: file-schema fixups, URI formats,
@@ -158,7 +167,7 @@ func addDeprecatedFlags(doc *openapi3.T) {
if propRef == nil || propRef.Value == nil || propRef.Ref != "" {
continue
}
if rxDeprecated.MatchString(propRef.Value.Description) {
if globalVars().rxDeprecated.MatchString(propRef.Value.Description) {
propRef.Value.Deprecated = true
}
}

View File

@@ -42,27 +42,19 @@ func TestDeriveEnumName_miss(t *testing.T) {
func TestExtractSharedEnums_usesASTMap(t *testing.T) {
doc := &openapi3.T{
Components: &openapi3.Components{
Schemas: openapi3.Schemas{
"A": {Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green", "blue"},
}},
},
}},
"B": {Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green", "blue"},
}},
},
Schemas: openapi3.Schemas{},
},
}
for _, name := range []string{"A", "B"} {
doc.Components.Schemas[name] = &openapi3.SchemaRef{Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green", "blue"},
}},
},
},
}}
}
astMap := map[string][]string{EnumKey([]any{"red", "green", "blue"}): {"Color"}}
if err := extractSharedEnums(doc, astMap); err != nil {
@@ -190,27 +182,19 @@ func TestExtractEnumTypeName_rejectsIncidentalPrefix(t *testing.T) {
func TestExtractSharedEnums_missReturnsError(t *testing.T) {
doc := &openapi3.T{
Components: &openapi3.Components{
Schemas: openapi3.Schemas{
"A": {Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green"},
}},
},
}},
"B": {Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green"},
}},
},
Schemas: openapi3.Schemas{},
},
}
for _, name := range []string{"A", "B"} {
doc.Components.Schemas[name] = &openapi3.SchemaRef{Value: &openapi3.Schema{
Type: &openapi3.Types{"object"},
Properties: openapi3.Schemas{
"color": {Value: &openapi3.Schema{
Type: &openapi3.Types{"string"},
Enum: []any{"red", "green"},
}},
},
},
}}
}
if err := extractSharedEnums(doc, map[string][]string{}); err == nil {
t.Fatal("expected miss error")

View File

@@ -13,7 +13,6 @@ import (
"go/token"
"os"
"path/filepath"
"regexp"
"sort"
"strconv"
"strings"
@@ -31,8 +30,6 @@ func EnumKey(values []any) string {
return strings.Join(strs, "|")
}
var rxSwaggerEnum = regexp.MustCompile(`swagger:enum\s+(\w+)`)
// ScanSwaggerEnumTypes walks .go files under each dir and returns a map from
// a canonical value-set key (see EnumKey) to the Go type names declared with
// // swagger:enum TypeName. Multiple type names per key are allowed (e.g.
@@ -146,7 +143,7 @@ func registerEnumAnnotation(doc *ast.CommentGroup, specs []ast.Spec, enumTypes m
if doc == nil {
return nil
}
matches := rxSwaggerEnum.FindStringSubmatch(doc.Text())
matches := globalVars().rxSwaggerEnum.FindStringSubmatch(doc.Text())
if len(matches) < 2 {
return nil
}

View File

@@ -33,9 +33,17 @@ func TestEnumKey_handlesNonStringValues(t *testing.T) {
}
}
func TestScanSwaggerEnumTypes_basic(t *testing.T) {
dir := t.TempDir()
src := `package fixture
func TestScanSwaggerEnumTypes(t *testing.T) {
scan := func(t *testing.T, filename, src string) (map[string][]string, error) {
dir := t.TempDir()
if err := os.WriteFile(filepath.Join(dir, filename), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
return ScanSwaggerEnumTypes([]string{dir})
}
t.Run("basic", func(t *testing.T) {
got, err := scan(t, "color.go", `package fixture
// Color is a primary color.
// swagger:enum Color
@@ -46,24 +54,18 @@ const (
ColorGreen Color = "green"
ColorBlue Color = "blue"
)
`
if err := os.WriteFile(filepath.Join(dir, "color.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
`)
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
wantKey := EnumKey([]any{"red", "green", "blue"})
if single(got, wantKey) != "Color" {
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color")
}
})
got, err := ScanSwaggerEnumTypes([]string{dir})
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
wantKey := EnumKey([]any{"red", "green", "blue"})
if single(got, wantKey) != "Color" {
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Color")
}
}
func TestScanSwaggerEnumTypes_orphanAnnotation(t *testing.T) {
dir := t.TempDir()
src := `package fixture
t.Run("orphanAnnotation", func(t *testing.T) {
_, err := scan(t, "typo.go", `package fixture
// swagger:enum Sttype
type StateType string
@@ -71,23 +73,17 @@ type StateType string
const (
StateOpen StateType = "open"
)
`
if err := os.WriteFile(filepath.Join(dir, "typo.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
`)
if err == nil {
t.Fatal("expected error for annotation referencing a non-matching type name")
}
if !strings.Contains(err.Error(), "Sttype") {
t.Fatalf("error %q should mention the typo'd name Sttype", err.Error())
}
})
_, err := ScanSwaggerEnumTypes([]string{dir})
if err == nil {
t.Fatal("expected error for annotation referencing a non-matching type name")
}
if !strings.Contains(err.Error(), "Sttype") {
t.Fatalf("error %q should mention the typo'd name Sttype", err.Error())
}
}
func TestScanSwaggerEnumTypes_collision(t *testing.T) {
dir := t.TempDir()
src := `package fixture
t.Run("collision", func(t *testing.T) {
got, err := scan(t, "dup.go", `package fixture
// swagger:enum Alpha
type Alpha string
@@ -102,52 +98,90 @@ const (
BetaX Beta = "x"
BetaY Beta = "y"
)
`
if err := os.WriteFile(filepath.Join(dir, "dup.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
`)
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
key := EnumKey([]any{"x", "y"})
names := got[key]
if !slices.Equal(names, []string{"Alpha", "Beta"}) {
t.Fatalf("map[%q] = %v, want [Alpha Beta]", key, names)
}
})
got, err := ScanSwaggerEnumTypes([]string{dir})
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
key := EnumKey([]any{"x", "y"})
names := got[key]
if !slices.Equal(names, []string{"Alpha", "Beta"}) {
t.Fatalf("map[%q] = %v, want [Alpha Beta]", key, names)
}
}
t.Run("parseFailure", func(t *testing.T) {
_, err := scan(t, "bad.go", "package fixture\nfunc Foo() {")
if err == nil {
t.Fatal("expected parse error, got nil")
}
})
func TestScanSwaggerEnumTypes_parseFailure(t *testing.T) {
dir := t.TempDir()
if err := os.WriteFile(filepath.Join(dir, "bad.go"), []byte("package fixture\nfunc Foo() {"), 0o644); err != nil {
t.Fatal(err)
}
_, err := ScanSwaggerEnumTypes([]string{dir})
if err == nil {
t.Fatal("expected parse error, got nil")
}
}
func TestScanSwaggerEnumTypes_annotationWithoutConsts(t *testing.T) {
dir := t.TempDir()
src := `package fixture
t.Run("annotationWithoutConsts", func(t *testing.T) {
_, err := scan(t, "lonely.go", `package fixture
// swagger:enum Lonely
type Lonely string
`
if err := os.WriteFile(filepath.Join(dir, "lonely.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
`)
if err == nil {
t.Fatal("expected error for annotation without consts")
}
if !strings.Contains(err.Error(), "Lonely") {
t.Fatalf("error %q should mention Lonely", err.Error())
}
})
_, err := ScanSwaggerEnumTypes([]string{dir})
if err == nil {
t.Fatal("expected error for annotation without consts")
}
if !strings.Contains(err.Error(), "Lonely") {
t.Fatalf("error %q should mention Lonely", err.Error())
}
t.Run("constsBeforeType", func(t *testing.T) {
got, err := scan(t, "shade.go", `package fixture
const (
ShadeDark Shade = "dark"
ShadeLight Shade = "light"
)
// swagger:enum Shade
type Shade string
`)
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
wantKey := EnumKey([]any{"dark", "light"})
if single(got, wantKey) != "Shade" {
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade")
}
})
t.Run("groupedTypeDecl", func(t *testing.T) {
got, err := scan(t, "grouped.go", `package fixture
type (
// swagger:enum Color
Color string
// swagger:enum Shade
Shade string
)
const (
ColorRed Color = "red"
ColorBlue Color = "blue"
)
const (
ShadeDark Shade = "dark"
ShadeLight Shade = "light"
)
`)
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
colorKey := EnumKey([]any{"red", "blue"})
shadeKey := EnumKey([]any{"dark", "light"})
if single(got, colorKey) != "Color" {
t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color")
}
if single(got, shadeKey) != "Shade" {
t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade")
}
})
}
func TestScanSwaggerEnumTypes_constsAndTypeInDifferentFiles(t *testing.T) {
@@ -182,68 +216,3 @@ type Hue string
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Hue")
}
}
func TestScanSwaggerEnumTypes_constsBeforeType(t *testing.T) {
dir := t.TempDir()
src := `package fixture
const (
ShadeDark Shade = "dark"
ShadeLight Shade = "light"
)
// swagger:enum Shade
type Shade string
`
if err := os.WriteFile(filepath.Join(dir, "shade.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
got, err := ScanSwaggerEnumTypes([]string{dir})
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
wantKey := EnumKey([]any{"dark", "light"})
if single(got, wantKey) != "Shade" {
t.Fatalf("map[%q] = %q, want %q", wantKey, got[wantKey], "Shade")
}
}
func TestScanSwaggerEnumTypes_groupedTypeDecl(t *testing.T) {
dir := t.TempDir()
src := `package fixture
type (
// swagger:enum Color
Color string
// swagger:enum Shade
Shade string
)
const (
ColorRed Color = "red"
ColorBlue Color = "blue"
)
const (
ShadeDark Shade = "dark"
ShadeLight Shade = "light"
)
`
if err := os.WriteFile(filepath.Join(dir, "grouped.go"), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
got, err := ScanSwaggerEnumTypes([]string{dir})
if err != nil {
t.Fatalf("ScanSwaggerEnumTypes: %v", err)
}
colorKey := EnumKey([]any{"red", "blue"})
shadeKey := EnumKey([]any{"dark", "light"})
if single(got, colorKey) != "Color" {
t.Fatalf("Color: map[%q] = %q, want %q", colorKey, got[colorKey], "Color")
}
if single(got, shadeKey) != "Shade" {
t.Fatalf("Shade: map[%q] = %q, want %q", shadeKey, got[shadeKey], "Shade")
}
}

10
cliff.toml Normal file
View File

@@ -0,0 +1,10 @@
[git]
commit_parsers = [
{ message = "^(chore|ci)(\\([\\w/.-]+\\))?!?: ", skip = true },
{ message = "^feat", group = "Features" },
{ message = "^enhance", group = "Enhancements" },
{ message = "^perf", group = "Performance" },
{ message = "^fix", group = "Bug Fixes" },
{ message = "^docs", group = "Documentation" },
{ message = ".*", group = "Miscellaneous" },
]

View File

@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
"gitea.dev/modules/private"
"gitea.dev/modules/setting"
@@ -49,6 +48,6 @@ func runGenerateActionsRunnerToken(ctx context.Context, c *cli.Command) error {
if extra.HasError() {
return handleCliResponseExtra(extra)
}
_, _ = fmt.Printf("%s\n", respText.Text)
cprintln(c, respText.Text)
return nil
}

View File

@@ -11,7 +11,6 @@ import (
"gitea.dev/models/db"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
"gitea.dev/modules/gitrepo"
"gitea.dev/modules/log"
repo_module "gitea.dev/modules/repository"
@@ -55,6 +54,9 @@ func newAuthCommand() *cli.Command {
return &cli.Command{
Name: "auth",
Usage: "Modify external auth providers",
Before: func(ctx context.Context, _ *cli.Command) (context.Context, error) {
return cliAuditContext(ctx), nil
},
Commands: []*cli.Command{
microcmdAuthAddOauth(),
microcmdAuthUpdateOauth(),
@@ -128,8 +130,8 @@ func runRepoSyncReleases(ctx context.Context, _ *cli.Command) error {
}
log.Trace("Processing next %d repos of %d", len(repos), count)
for _, repo := range repos {
log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RelativePath())
gitRepo, err := gitrepo.OpenRepository(ctx, repo)
log.Trace("Synchronizing repo %s", repo.FullName())
gitRepo, err := git.OpenRepository(ctx, repo)
if err != nil {
log.Warn("OpenRepository: %v", err)
continue

View File

@@ -10,6 +10,7 @@ import (
"gitea.dev/models/auth"
"gitea.dev/modules/util"
auth_service "gitea.dev/services/auth"
"gitea.dev/services/auth/source/ldap"
"github.com/urfave/cli/v3"
@@ -48,11 +49,11 @@ func commonLdapCLIFlags() []cli.Flag {
},
&cli.StringFlag{
Name: "host",
Usage: "The address where the LDAP server can be reached.",
Usage: "Comma-separated LDAP hosts or ldap://, ldaps:// or ldap+starttls:// URLs, tried in order.",
},
&cli.IntFlag{
Name: "port",
Usage: "The port to use when connecting to the LDAP server.",
Usage: "The port to use when connecting to hosts given without a URL.",
},
&cli.StringFlag{
Name: "user-search-base",
@@ -221,8 +222,8 @@ func microcmdAuthUpdateLdapSimpleAuth() *cli.Command {
func newAuthService() *authService {
return &authService{
initDB: initDB,
createAuthSource: auth.CreateSource,
updateAuthSource: auth.UpdateSource,
createAuthSource: auth_service.CreateSource,
updateAuthSource: auth_service.UpdateSource,
getAuthSourceByID: auth.GetSourceByID,
}
}
@@ -354,13 +355,10 @@ func findLdapSecurityProtocolByName(name string) (ldap.SecurityProtocol, bool) {
return 0, false
}
// getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
authSource, err := a.getAuthSourceByID(ctx, c.Int64("id"))
// getAuthSourceOfType gets the login source by id.
// It returns an error if the id does not match any source or if the source is not of the expected type.
func (a *authService) getAuthSourceOfType(ctx context.Context, id int64, authType auth.Type) (*auth.Source, error) {
authSource, err := a.getAuthSourceByID(ctx, id)
if err != nil {
return nil, err
}
@@ -372,6 +370,15 @@ func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authTyp
return authSource, nil
}
// getAuthSource gets the login source by its id defined in the command line flags.
// It returns an error if the id is not set, does not match any source or if the source is not of expected type.
func (a *authService) getAuthSource(ctx context.Context, c *cli.Command, authType auth.Type) (*auth.Source, error) {
if err := argsSet(c, "id"); err != nil {
return nil, err
}
return a.getAuthSourceOfType(ctx, c.Int64("id"), authType)
}
// addLdapBindDn adds a new LDAP via Bind DN authentication source.
func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
if err := argsSet(c, "name", "security-protocol", "host", "port", "user-search-base", "user-filter", "email-attribute"); err != nil {
@@ -381,16 +388,17 @@ func (a *authService) addLdapBindDn(ctx context.Context, c *cli.Command) error {
return err
}
ldapConfig := &ldap.Source{
Enabled: true, // always true
}
authSource := &auth.Source{
Type: auth.LDAP,
IsActive: true, // active by default
Cfg: &ldap.Source{
Enabled: true, // always true
},
Cfg: ldapConfig,
}
parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
if err := parseLdapConfig(c, ldapConfig); err != nil {
return err
}
@@ -407,9 +415,10 @@ func (a *authService) updateLdapBindDn(ctx context.Context, c *cli.Command) erro
if err != nil {
return err
}
ldapConfig := auth.MustSourceCfg[*ldap.Source](authSource)
parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
if err := parseLdapConfig(c, ldapConfig); err != nil {
return err
}
@@ -426,16 +435,17 @@ func (a *authService) addLdapSimpleAuth(ctx context.Context, c *cli.Command) err
return err
}
ldapConfig := &ldap.Source{
Enabled: true, // always true
}
authSource := &auth.Source{
Type: auth.DLDAP,
IsActive: true, // active by default
Cfg: &ldap.Source{
Enabled: true, // always true
},
Cfg: ldapConfig,
}
parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
if err := parseLdapConfig(c, ldapConfig); err != nil {
return err
}
@@ -452,9 +462,10 @@ func (a *authService) updateLdapSimpleAuth(ctx context.Context, c *cli.Command)
if err != nil {
return err
}
ldapConfig := auth.MustSourceCfg[*ldap.Source](authSource)
parseAuthSourceLdap(c, authSource)
if err := parseLdapConfig(c, authSource.Cfg.(*ldap.Source)); err != nil {
if err := parseLdapConfig(c, ldapConfig); err != nil {
return err
}

View File

@@ -489,13 +489,20 @@ func TestUpdateLdapBindDn(t *testing.T) {
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
type testCase struct {
args []string
id int64
existingAuthSource *auth.Source
authSource *auth.Source
errMsg string
}{
}
withConfig := func(config *ldap.Source, args ...string) testCase {
return testCase{
args: append([]string{"ldap-test", "--id", "1"}, args...),
authSource: &auth.Source{Type: auth.LDAP, Cfg: config},
}
}
cases := []testCase{
// case 0
{
args: []string{
@@ -574,16 +581,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
},
},
// case 1
{
args: []string{
"ldap-test",
"--id", "1",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{},
},
},
withConfig(&ldap.Source{}),
// case 2
{
args: []string{
@@ -618,215 +616,44 @@ func TestUpdateLdapBindDn(t *testing.T) {
},
},
// case 4
{
args: []string{
"ldap-test",
"--id", "1",
"--security-protocol", "LDAPS",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
SecurityProtocol: ldap.SecurityProtocol(1),
},
},
},
withConfig(&ldap.Source{SecurityProtocol: ldap.SecurityProtocol(1)}, "--security-protocol", "LDAPS"),
// case 5
{
args: []string{
"ldap-test",
"--id", "1",
"--skip-tls-verify",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
SkipVerify: true,
},
},
},
withConfig(&ldap.Source{SkipVerify: true}, "--skip-tls-verify"),
// case 6
{
args: []string{
"ldap-test",
"--id", "1",
"--host", "ldap-server",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
Host: "ldap-server",
},
},
},
withConfig(&ldap.Source{Host: "ldap-server"}, "--host", "ldap-server"),
// case 7
{
args: []string{
"ldap-test",
"--id", "1",
"--port", "389",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
Port: 389,
},
},
},
withConfig(&ldap.Source{Port: 389}, "--port", "389"),
// case 8
{
args: []string{
"ldap-test",
"--id", "1",
"--user-search-base", "ou=Users,dc=domain,dc=org",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
UserBase: "ou=Users,dc=domain,dc=org",
},
},
},
withConfig(
&ldap.Source{UserBase: "ou=Users,dc=domain,dc=org"},
"--user-search-base", "ou=Users,dc=domain,dc=org",
),
// case 9
{
args: []string{
"ldap-test",
"--id", "1",
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
},
},
},
withConfig(
&ldap.Source{Filter: "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)"},
"--user-filter", "(memberOf=cn=user-group,ou=example,dc=domain,dc=org)",
),
// case 10
{
args: []string{
"ldap-test",
"--id", "1",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
},
},
},
withConfig(
&ldap.Source{AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)"},
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
),
// case 11
{
args: []string{
"ldap-test",
"--id", "1",
"--username-attribute", "uid",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributeUsername: "uid",
},
},
},
withConfig(&ldap.Source{AttributeUsername: "uid"}, "--username-attribute", "uid"),
// case 12
{
args: []string{
"ldap-test",
"--id", "1",
"--firstname-attribute", "givenName",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributeName: "givenName",
},
},
},
withConfig(&ldap.Source{AttributeName: "givenName"}, "--firstname-attribute", "givenName"),
// case 13
{
args: []string{
"ldap-test",
"--id", "1",
"--surname-attribute", "sn",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributeSurname: "sn",
},
},
},
withConfig(&ldap.Source{AttributeSurname: "sn"}, "--surname-attribute", "sn"),
// case 14
{
args: []string{
"ldap-test",
"--id", "1",
"--email-attribute", "mail",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributeMail: "mail",
},
},
},
withConfig(&ldap.Source{AttributeMail: "mail"}, "--email-attribute", "mail"),
// case 15
{
args: []string{
"ldap-test",
"--id", "1",
"--attributes-in-bind",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributesInBind: true,
},
},
},
withConfig(&ldap.Source{AttributesInBind: true}, "--attributes-in-bind"),
// case 16
{
args: []string{
"ldap-test",
"--id", "1",
"--public-ssh-key-attribute", "publickey",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
AttributeSSHPublicKey: "publickey",
},
},
},
withConfig(&ldap.Source{AttributeSSHPublicKey: "publickey"}, "--public-ssh-key-attribute", "publickey"),
// case 17
{
args: []string{
"ldap-test",
"--id", "1",
"--bind-dn", "cn=readonly,dc=domain,dc=org",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
BindDN: "cn=readonly,dc=domain,dc=org",
},
},
},
withConfig(&ldap.Source{BindDN: "cn=readonly,dc=domain,dc=org"}, "--bind-dn", "cn=readonly,dc=domain,dc=org"),
// case 18
{
args: []string{
"ldap-test",
"--id", "1",
"--bind-password", "secret",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
BindPassword: "secret",
},
},
},
withConfig(&ldap.Source{BindPassword: "secret"}, "--bind-password", "secret"),
// case 19
{
args: []string{
@@ -841,19 +668,7 @@ func TestUpdateLdapBindDn(t *testing.T) {
},
},
// case 20
{
args: []string{
"ldap-test",
"--id", "1",
"--page-size", "12",
},
authSource: &auth.Source{
Type: auth.LDAP,
Cfg: &ldap.Source{
SearchPageSize: 12,
},
},
},
withConfig(&ldap.Source{SearchPageSize: 12}, "--page-size", "12"),
// case 21
{
args: []string{
@@ -964,13 +779,20 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
defer test.MockVariableValue(&cli.OsExiter, func(code int) {})()
// Test cases
cases := []struct {
type testCase struct {
args []string
id int64
existingAuthSource *auth.Source
authSource *auth.Source
errMsg string
}{
}
withConfig := func(config *ldap.Source, args ...string) testCase {
return testCase{
args: append([]string{"ldap-test", "--id", "1"}, args...),
authSource: &auth.Source{Type: auth.DLDAP, Cfg: config},
}
}
cases := []testCase{
// case 0
{
args: []string{
@@ -1020,16 +842,7 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
},
},
// case 1
{
args: []string{
"ldap-test",
"--id", "1",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{},
},
},
withConfig(&ldap.Source{}),
// case 2
{
args: []string{
@@ -1064,187 +877,43 @@ func TestUpdateLdapSimpleAuth(t *testing.T) {
},
},
// case 4
{
args: []string{
"ldap-test",
"--id", "1",
"--security-protocol", "starttls",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
SecurityProtocol: ldap.SecurityProtocol(2),
},
},
},
withConfig(&ldap.Source{SecurityProtocol: ldap.SecurityProtocol(2)}, "--security-protocol", "starttls"),
// case 5
{
args: []string{
"ldap-test",
"--id", "1",
"--skip-tls-verify",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
SkipVerify: true,
},
},
},
withConfig(&ldap.Source{SkipVerify: true}, "--skip-tls-verify"),
// case 6
{
args: []string{
"ldap-test",
"--id", "1",
"--host", "ldap-server",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
Host: "ldap-server",
},
},
},
withConfig(&ldap.Source{Host: "ldap-server"}, "--host", "ldap-server"),
// case 7
{
args: []string{
"ldap-test",
"--id", "1",
"--port", "987",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
Port: 987,
},
},
},
withConfig(&ldap.Source{Port: 987}, "--port", "987"),
// case 8
{
args: []string{
"ldap-test",
"--id", "1",
"--user-search-base", "ou=Users,dc=domain,dc=org",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
UserBase: "ou=Users,dc=domain,dc=org",
},
},
},
withConfig(
&ldap.Source{UserBase: "ou=Users,dc=domain,dc=org"},
"--user-search-base", "ou=Users,dc=domain,dc=org",
),
// case 9
{
args: []string{
"ldap-test",
"--id", "1",
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
Filter: "(&(objectClass=posixAccount)(cn=%s))",
},
},
},
withConfig(
&ldap.Source{Filter: "(&(objectClass=posixAccount)(cn=%s))"},
"--user-filter", "(&(objectClass=posixAccount)(cn=%s))",
),
// case 10
{
args: []string{
"ldap-test",
"--id", "1",
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
},
},
},
withConfig(
&ldap.Source{AdminFilter: "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)"},
"--admin-filter", "(memberOf=cn=admin-group,ou=example,dc=domain,dc=org)",
),
// case 11
{
args: []string{
"ldap-test",
"--id", "1",
"--username-attribute", "uid",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AttributeUsername: "uid",
},
},
},
withConfig(&ldap.Source{AttributeUsername: "uid"}, "--username-attribute", "uid"),
// case 12
{
args: []string{
"ldap-test",
"--id", "1",
"--firstname-attribute", "givenName",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AttributeName: "givenName",
},
},
},
withConfig(&ldap.Source{AttributeName: "givenName"}, "--firstname-attribute", "givenName"),
// case 13
{
args: []string{
"ldap-test",
"--id", "1",
"--surname-attribute", "sn",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AttributeSurname: "sn",
},
},
},
withConfig(&ldap.Source{AttributeSurname: "sn"}, "--surname-attribute", "sn"),
// case 14
{
args: []string{
"ldap-test",
"--id", "1",
"--email-attribute", "mail",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AttributeMail: "mail",
},
},
},
withConfig(&ldap.Source{AttributeMail: "mail"}, "--email-attribute", "mail"),
// case 15
{
args: []string{
"ldap-test",
"--id", "1",
"--public-ssh-key-attribute", "publickey",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
AttributeSSHPublicKey: "publickey",
},
},
},
withConfig(&ldap.Source{AttributeSSHPublicKey: "publickey"}, "--public-ssh-key-attribute", "publickey"),
// case 16
{
args: []string{
"ldap-test",
"--id", "1",
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
},
authSource: &auth.Source{
Type: auth.DLDAP,
Cfg: &ldap.Source{
UserDN: "cn=%s,ou=Users,dc=domain,dc=org",
},
},
},
withConfig(
&ldap.Source{UserDN: "cn=%s,ou=Users,dc=domain,dc=org"},
"--user-dn", "cn=%s,ou=Users,dc=domain,dc=org",
),
// case 17
{
args: []string{

View File

@@ -221,12 +221,11 @@ func (a *authService) runUpdateOauth(ctx context.Context, c *cli.Command) error
return err
}
source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
source, err := a.getAuthSourceOfType(ctx, c.Int64("id"), auth_model.OAuth2)
if err != nil {
return err
}
oAuth2Config := source.Cfg.(*oauth2.Source)
oAuth2Config := auth_model.MustSourceCfg[*oauth2.Source](source)
if c.IsSet("name") {
source.Name = c.String("name")

View File

@@ -16,10 +16,11 @@ import (
func TestAddOauth(t *testing.T) {
testCases := []struct {
name string
args []string
source *auth_model.Source
errMsg string
name string
args []string
config *oauth2.Source
twoFactorPolicy string
errMsg string
}{
{
name: "valid config",
@@ -29,17 +30,11 @@ func TestAddOauth(t *testing.T) {
"--key", "some_key",
"--secret", "some_secret",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Scopes: []string{},
Provider: "github",
ClientID: "some_key",
ClientSecret: "some_secret",
},
TwoFactorPolicy: "",
config: &oauth2.Source{
Scopes: []string{},
Provider: "github",
ClientID: "some_key",
ClientSecret: "some_secret",
},
},
{
@@ -51,18 +46,12 @@ func TestAddOauth(t *testing.T) {
"--secret", "some_secret",
"--auto-discover-url", "https://example.com",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Scopes: []string{},
Provider: "openidConnect",
ClientID: "some_key",
ClientSecret: "some_secret",
OpenIDConnectAutoDiscoveryURL: "https://example.com",
},
TwoFactorPolicy: "",
config: &oauth2.Source{
Scopes: []string{},
Provider: "openidConnect",
ClientID: "some_key",
ClientSecret: "some_secret",
OpenIDConnectAutoDiscoveryURL: "https://example.com",
},
},
{
@@ -91,35 +80,30 @@ func TestAddOauth(t *testing.T) {
"--ssh-public-key-claim-name", "attr_ssh_pub_key",
"--full-name-claim-name", "attr_full_name",
},
source: &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "gitlab",
ClientID: "some_key",
ClientSecret: "some_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://example.com/token",
AuthURL: "https://example.com/auth",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "some_tenant",
},
IconURL: "https://example.com/icon",
Scopes: []string{"scope1", "scope2"},
RequiredClaimName: "claim_name",
RequiredClaimValue: "claim_value",
GroupClaimName: "group_name",
AdminGroup: "admin",
RestrictedGroup: "restricted",
GroupTeamMap: `{"group1": [1,2]}`,
GroupTeamMapRemoval: true,
SSHPublicKeyClaimName: "attr_ssh_pub_key",
FullNameClaimName: "attr_full_name",
config: &oauth2.Source{
Provider: "gitlab",
ClientID: "some_key",
ClientSecret: "some_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://example.com/token",
AuthURL: "https://example.com/auth",
ProfileURL: "https://example.com/profile",
EmailURL: "https://example.com/email",
Tenant: "some_tenant",
},
TwoFactorPolicy: "skip",
IconURL: "https://example.com/icon",
Scopes: []string{"scope1", "scope2"},
RequiredClaimName: "claim_name",
RequiredClaimValue: "claim_value",
GroupClaimName: "group_name",
AdminGroup: "admin",
RestrictedGroup: "restricted",
GroupTeamMap: `{"group1": [1,2]}`,
GroupTeamMapRemoval: true,
SSHPublicKeyClaimName: "attr_ssh_pub_key",
FullNameClaimName: "attr_full_name",
},
twoFactorPolicy: "skip",
},
}
@@ -150,7 +134,13 @@ func TestAddOauth(t *testing.T) {
assert.EqualError(t, err, tc.errMsg)
} else {
assert.NoError(t, err)
assert.Equal(t, tc.source, createdSource)
assert.Equal(t, &auth_model.Source{
Type: auth_model.OAuth2,
Name: "test",
IsActive: true,
Cfg: tc.config,
TwoFactorPolicy: tc.twoFactorPolicy,
}, createdSource)
}
})
}
@@ -158,12 +148,10 @@ func TestAddOauth(t *testing.T) {
func TestUpdateOauth(t *testing.T) {
testCases := []struct {
name string
args []string
id int64
existingAuthSource *auth_model.Source
authSource *auth_model.Source
errMsg string
name string
args []string
authSource *auth_model.Source
errMsg string
}{
{
name: "missing id",
@@ -174,19 +162,6 @@ func TestUpdateOauth(t *testing.T) {
},
{
name: "valid config",
id: 1,
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "old name",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "github",
ClientID: "old_key",
ClientSecret: "old_secret",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",
@@ -210,37 +185,6 @@ func TestUpdateOauth(t *testing.T) {
},
{
name: "valid config with options",
id: 1,
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.OAuth2,
Name: "old name",
IsActive: true,
Cfg: &oauth2.Source{
Provider: "gitlab",
ClientID: "old_key",
ClientSecret: "old_secret",
CustomURLMapping: &oauth2.CustomURLMapping{
TokenURL: "https://old.example.com/token",
AuthURL: "https://old.example.com/auth",
ProfileURL: "https://old.example.com/profile",
EmailURL: "https://old.example.com/email",
Tenant: "old_tenant",
},
IconURL: "https://old.example.com/icon",
Scopes: []string{"old_scope1", "old_scope2"},
RequiredClaimName: "old_claim_name",
RequiredClaimValue: "old_claim_value",
GroupClaimName: "old_group_name",
AdminGroup: "old_admin",
RestrictedGroup: "old_restricted",
GroupTeamMap: `{"old_group1": [1,2]}`,
GroupTeamMapRemoval: true,
SSHPublicKeyClaimName: "old_ssh_pub_key",
FullNameClaimName: "old_full_name",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",

View File

@@ -175,12 +175,11 @@ func (a *authService) runUpdateSMTP(ctx context.Context, c *cli.Command) error {
return err
}
source, err := a.getAuthSourceByID(ctx, c.Int64("id"))
source, err := a.getAuthSourceOfType(ctx, c.Int64("id"), auth_model.SMTP)
if err != nil {
return err
}
smtpConfig := source.Cfg.(*smtp.Source)
smtpConfig := auth_model.MustSourceCfg[*smtp.Source](source)
if err := parseSMTPConfig(c, smtpConfig); err != nil {
return err

View File

@@ -132,11 +132,10 @@ func TestAddSMTP(t *testing.T) {
func TestUpdateSMTP(t *testing.T) {
testCases := []struct {
name string
args []string
existingAuthSource *auth_model.Source
authSource *auth_model.Source
errMsg string
name string
args []string
authSource *auth_model.Source
errMsg string
}{
{
name: "missing id",
@@ -149,17 +148,6 @@ func TestUpdateSMTP(t *testing.T) {
},
{
name: "valid config",
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "old name",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "old host",
Port: 26,
},
},
args: []string{
"--id", "1",
"--name", "test",
@@ -180,20 +168,6 @@ func TestUpdateSMTP(t *testing.T) {
},
{
name: "valid config with options",
existingAuthSource: &auth_model.Source{
ID: 1,
Type: auth_model.SMTP,
Name: "old name",
IsActive: true,
Cfg: &smtp.Source{
Auth: "PLAIN",
Host: "old host",
Port: 26,
HeloHostname: "old.example.com",
AllowedDomains: "old.example.com",
},
TwoFactorPolicy: "",
},
args: []string{
"--id", "1",
"--name", "test",

View File

@@ -4,13 +4,34 @@
package cmd
import (
"context"
"strings"
user_model "gitea.dev/models/user"
"gitea.dev/modules/util"
"github.com/urfave/cli/v3"
)
// parseUserTypeFlag parses "--user-type", keeping the lowercase "individual" and "bot" the flag shipped with
func parseUserTypeFlag(s string) (user_model.UserType, error) {
switch strings.ToLower(s) {
case "user", "individual":
return user_model.UserTypeIndividual, nil
case "bot":
return user_model.UserTypeBot, nil
default:
return 0, util.NewInvalidArgumentErrorf("invalid user type %q (expected User, Bot)", s)
}
}
func newUserCommand() *cli.Command {
return &cli.Command{
Name: "user",
Usage: "Modify users",
Before: func(ctx context.Context, _ *cli.Command) (context.Context, error) {
return cliAuditContext(ctx), nil
},
Commands: []*cli.Command{
microcmdUserCreate(),
newUserListCommand(),
@@ -18,6 +39,8 @@ func newUserCommand() *cli.Command {
microcmdUserDelete(),
newUserGenerateAccessTokenCommand(),
microcmdUserMustChangePassword(),
microcmdUserDisableTwoFactor(),
microcmdUserChangeType(),
},
}
}

View File

@@ -73,6 +73,6 @@ func runChangePassword(ctx context.Context, c *cli.Command) error {
}
}
fmt.Printf("%s's password has been successfully updated!\n", user.Name)
cprintf(c, "%s's password has been successfully updated!\n", user.Name)
return nil
}

View File

@@ -0,0 +1,61 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
user_model "gitea.dev/models/user"
"gitea.dev/modules/optional"
"gitea.dev/modules/setting"
user_service "gitea.dev/services/user"
"github.com/urfave/cli/v3"
)
func microcmdUserChangeType() *cli.Command {
return &cli.Command{
Name: "change-type",
Usage: "Convert an account between the user and bot types",
Action: runChangeUserType,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "The user to convert",
Required: true,
},
&cli.StringFlag{
Name: "user-type",
Usage: "New user type: User or Bot",
Required: true,
},
},
}
}
func runChangeUserType(ctx context.Context, c *cli.Command) error {
targetType, err := parseUserTypeFlag(c.String("user-type"))
if err != nil {
return err
}
if !setting.IsInTesting {
if err := initDB(ctx); err != nil {
return err
}
}
user, err := user_model.GetUserByName(ctx, c.String("username"))
if err != nil {
return err
}
if err := user_service.UpdateUser(ctx, user, &user_service.UpdateOptions{UserType: optional.Some(targetType)}); err != nil {
return err
}
cprintf(c, "%s's type has been successfully changed to %s!\n", user.Name, targetType.DisplayName())
return nil
}

View File

@@ -44,8 +44,8 @@ func microcmdUserCreate() *cli.Command {
Flags: []cli.Flag{
&cli.StringFlag{
Name: "user-type",
Usage: "Set user's type: individual or bot",
Value: "individual",
Usage: "Set user's type: User or Bot",
Value: "User",
},
&cli.StringFlag{
Name: "password",
@@ -105,20 +105,16 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings()
userTypes := map[string]user_model.UserType{
"individual": user_model.UserTypeIndividual,
"bot": user_model.UserTypeBot,
}
userType, ok := userTypes[c.String("user-type")]
if !ok {
return fmt.Errorf("invalid user type: %s", c.String("user-type"))
userType, err := parseUserTypeFlag(c.String("user-type"))
if err != nil {
return err
}
if userType != user_model.UserTypeIndividual {
// Some other commands like "change-password" also only support individual users.
// Some other commands like "change-password" also only support regular user accounts.
// It needs to clarify the "password" behavior for bot users in the future.
// At the moment, we do not allow setting password for bot users.
if c.IsSet("password") || c.IsSet("random-password") {
return errors.New("password can only be set for individual users")
return errors.New("password can only be set for user accounts")
}
}
@@ -152,7 +148,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
return err
}
// codeql[disable-next-line=go/clear-text-logging]
fmt.Printf("generated random password is '%s'\n", password)
cprintf(c, "generated random password is '%s'\n", password)
} else if userType == user_model.UserTypeIndividual {
return errors.New("must set either password or random-password flag")
}
@@ -162,7 +158,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
mustChangePassword := userType == user_model.UserTypeIndividual
if c.IsSet("must-change-password") {
if userType != user_model.UserTypeIndividual {
return errors.New("must-change-password flag can only be set for individual users")
return errors.New("must-change-password flag can only be set for user accounts")
}
// if the flag is set, use the value provided by the user
mustChangePassword = c.Bool("must-change-password")
@@ -228,7 +224,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
if err := user_model.CreateUser(ctx, u, &user_model.Meta{}, overwriteDefault); err != nil {
return fmt.Errorf("CreateUser: %w", err)
}
fmt.Printf("New user '%s' has been successfully created!\n", username)
cprintf(c, "New user '%s' has been successfully created!\n", username)
// create the access token
if accessTokenScope != "" {
@@ -236,7 +232,7 @@ func runCreateUser(ctx context.Context, c *cli.Command) error {
if err := auth_model.NewAccessToken(ctx, t); err != nil {
return err
}
fmt.Printf("Access token was successfully created... %s\n", t.Token)
cprintf(c, "Access token was successfully created... %s\n", t.Token)
}
return nil
}

View File

@@ -56,14 +56,21 @@ func TestAdminUserCreate(t *testing.T) {
t.Run("UserType", func(t *testing.T) {
reset()
assert.ErrorContains(t, createUser("u", "--user-type", "invalid"), "invalid user type")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for individual users")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for individual users")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--password", "123"), "can only be set for user accounts")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--must-change-password"), "can only be set for user accounts")
assert.ErrorContains(t, createUser("u", "--user-type", "bot", "--admin"), "bot user can not be a site administrator")
assert.NoError(t, createUser("u", "--user-type", "bot"))
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"})
assert.Equal(t, user_model.UserTypeBot, u.Type)
assert.Empty(t, u.Passwd)
assert.False(t, u.MustChangePassword, "bot users should not be forced to change password")
changeType := func(userType string) error {
return microcmdUserChangeType().Run(t.Context(), []string{"change-type", "--username", "u", "--user-type", userType})
}
assert.NoError(t, changeType("User"))
assert.True(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "u"}).IsIndividual())
})
t.Run("AccessToken", func(t *testing.T) {

View File

@@ -24,42 +24,29 @@ func TestAdminUserDelete(t *testing.T) {
require.NoError(t, db.TruncateBeans(t.Context(), &auth_model.AccessToken{}))
}()
setupTestUser := func(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
for _, tc := range []struct {
name string
args []string
}{
{"delete user by id", []string{"delete-test", "--id", ""}},
{"delete user by username", []string{"delete-test", "--username", "testuser"}},
{"delete user by email", []string{"delete-test", "--email", "testuser@gitea.local"}},
{"delete user by all 3 attributes", []string{"delete", "--id", "", "--username", "testuser", "--email", "testuser@gitea.local"}},
} {
t.Run(tc.name, func(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
if tc.args[1] == "--id" {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
tc.args[2] = strconv.FormatInt(user.ID, 10)
}
err = microcmdUserDelete().Run(ctx, tc.args)
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
}
t.Run("delete user by id", func(t *testing.T) {
setupTestUser(t)
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--id", strconv.FormatInt(u.ID, 10)})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by username", func(t *testing.T) {
setupTestUser(t)
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--username", "testuser"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by email", func(t *testing.T) {
setupTestUser(t)
err := microcmdUserDelete().Run(ctx, []string{"delete-test", "--email", "testuser@gitea.local"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
t.Run("delete user by all 3 attributes", func(t *testing.T) {
setupTestUser(t)
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
err := microcmdUserDelete().Run(ctx, []string{"delete", "--id", strconv.FormatInt(u.ID, 10), "--username", "testuser", "--email", "testuser@gitea.local"})
require.NoError(t, err)
unittest.AssertNotExistsBean(t, &user_model.User{LowerName: "testuser"})
})
}
func TestAdminUserDeleteFailure(t *testing.T) {

View File

@@ -0,0 +1,72 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
"errors"
"fmt"
"strings"
auth_model "gitea.dev/models/auth"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
"github.com/urfave/cli/v3"
)
func microcmdUserDisableTwoFactor() *cli.Command {
return &cli.Command{
Name: "disable-2fa",
Usage: "Disable two-factor authentication for a user",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "username",
Aliases: []string{"u"},
Usage: "Username of the user to disable 2FA for",
},
&cli.Int64Flag{
Name: "id",
Usage: "ID of the user to disable 2FA for",
},
},
Action: runDisableTwoFactor,
}
}
func runDisableTwoFactor(ctx context.Context, c *cli.Command) error {
if !c.IsSet("id") && !c.IsSet("username") {
return errors.New("either --id or --username must be provided")
}
if !setting.IsInTesting {
if err := initDB(ctx); err != nil {
return err
}
}
var user *user_model.User
var err error
if c.IsSet("id") {
user, err = user_model.GetUserByID(ctx, c.Int64("id"))
} else {
user, err = user_model.GetUserByName(ctx, c.String("username"))
}
if err != nil {
return err
}
// When both selectors are given, make sure they refer to the same user.
if c.IsSet("id") && c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
return fmt.Errorf("the user with id %d is %q, which does not match the provided username %q", user.ID, user.Name, c.String("username"))
}
totp, webAuthn, err := auth_model.DisableTwoFactor(ctx, user.ID)
if err != nil {
return err
}
cprintf(c, "Disabled 2FA for user %q (removed %d TOTP and %d WebAuthn credential(s))\n", user.Name, totp, webAuthn)
return nil
}

View File

@@ -0,0 +1,119 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"io"
"strconv"
"testing"
auth_model "gitea.dev/models/auth"
"gitea.dev/models/db"
"gitea.dev/models/unittest"
user_model "gitea.dev/models/user"
"github.com/go-webauthn/webauthn/webauthn"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDisableTwoFactorCommand(t *testing.T) {
ctx := t.Context()
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}, &auth_model.TwoFactor{}, &auth_model.WebAuthnCredential{}))
}()
t.Run("disable TOTP and WebAuthn", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "tfuser", "--email", "tfuser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "tfuser"})
// Enroll TOTP.
tf := &auth_model.TwoFactor{UID: user.ID}
require.NoError(t, tf.SetSecret("test-secret"))
_, err := tf.GenerateScratchToken()
require.NoError(t, err)
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
// Register a WebAuthn credential.
_, err = auth_model.CreateCredential(ctx, user.ID, "test-key", &webauthn.Credential{ID: []byte("test-cred-id")})
require.NoError(t, err)
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
require.True(t, has)
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "tfuser"}))
// Both factors must be gone afterwards.
has, err = auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
assert.False(t, has)
})
t.Run("disable by id", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "iduser", "--email", "iduser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "iduser"})
tf := &auth_model.TwoFactor{UID: user.ID}
require.NoError(t, tf.SetSecret("test-secret"))
require.NoError(t, auth_model.NewTwoFactor(ctx, tf))
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", strconv.FormatInt(user.ID, 10)}))
has, err := auth_model.HasTwoFactorOrWebAuthn(ctx, user.ID)
require.NoError(t, err)
assert.False(t, has)
})
t.Run("no enrollment is a no-op", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "plainuser", "--email", "plainuser@gitea.local", "--random-password"}))
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--username", "plainuser"}))
})
t.Run("id and username must match when both given", func(t *testing.T) {
require.NoError(t, microcmdUserCreate().Run(ctx, []string{"create", "--username", "matchuser", "--email", "matchuser@gitea.local", "--random-password"}))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "matchuser"})
id := strconv.FormatInt(user.ID, 10)
// Matching id + username is accepted.
require.NoError(t, microcmdUserDisableTwoFactor().Run(ctx, []string{"disable-2fa", "--id", id, "--username", "matchuser"}))
// Mismatched id + username is rejected.
cmd := microcmdUserDisableTwoFactor()
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
err := cmd.Run(ctx, []string{"disable-2fa", "--id", id, "--username", "someotheruser"})
require.Error(t, err)
require.Contains(t, err.Error(), "does not match the provided username")
})
t.Run("failure cases", func(t *testing.T) {
testCases := []struct {
name string
args []string
expectedErr string
}{
{
name: "user does not exist",
args: []string{"disable-2fa", "--username", "nonexistentuser"},
expectedErr: "user does not exist",
},
{
name: "neither id nor username",
args: []string{"disable-2fa"},
expectedErr: "either --id or --username must be provided",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cmd := microcmdUserDisableTwoFactor()
cmd.Writer, cmd.ErrWriter = io.Discard, io.Discard
err := cmd.Run(ctx, tc.args)
require.Error(t, err)
require.Contains(t, err.Error(), tc.expectedErr)
})
}
})
}

View File

@@ -88,9 +88,9 @@ func runGenerateAccessToken(ctx context.Context, c *cli.Command) error {
}
if c.Bool("raw") {
fmt.Printf("%s\n", t.Token)
cprintln(c, t.Token)
} else {
fmt.Printf("Access token was successfully created: %s\n", t.Token)
cprintf(c, "Access token was successfully created: %s\n", t.Token)
}
return nil

View File

@@ -33,7 +33,7 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
return err
}
users, err := user_model.GetAllUsers(ctx)
users, err := user_model.GetAllUsersForAdminListing(ctx)
if err != nil {
return err
}
@@ -41,17 +41,17 @@ func runListUsers(ctx context.Context, c *cli.Command) error {
w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
if c.IsSet("admin") {
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tType\n")
for _, u := range users {
if u.IsAdmin {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.Type.DisplayName())
}
}
} else {
twofa := user_model.UserList(users).GetTwoFaStatus(ctx)
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\tType\n")
for _, u := range users {
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\t%s\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID], u.Type.DisplayName())
}
}

View File

@@ -6,7 +6,6 @@ package cmd
import (
"context"
"errors"
"fmt"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
@@ -59,6 +58,6 @@ func runMustChangePassword(ctx context.Context, c *cli.Command) error {
}
// codeql[disable-next-line=go/clear-text-logging]
fmt.Printf("Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
cprintf(c, "Updated %d users setting MustChangePassword to %t\n", n, mustChangePassword)
return nil
}

View File

@@ -18,61 +18,28 @@ func TestMustChangePassword(t *testing.T) {
defer func() {
require.NoError(t, db.TruncateBeans(t.Context(), &user_model.User{}))
}()
err := microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuser", "--email", "testuser@gitea.local", "--random-password"})
require.NoError(t, err)
err = microcmdUserCreate().Run(t.Context(), []string{"create", "--username", "testuserexclude", "--email", "testuserexclude@gitea.local", "--random-password"})
require.NoError(t, err)
// Reset password change flag
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset"})
require.NoError(t, err)
testUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Make all users change password
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.True(t, testUserExclude.MustChangePassword)
// Reset password change flag but exclude all tested users
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.True(t, testUserExclude.MustChangePassword)
// Reset password change flag by listing multiple users
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser", "testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Exclude a user from all user
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--all", "--exclude", "testuserexclude"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.True(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
// Unset a flag for single user
err = microcmdUserMustChangePassword().Run(t.Context(), []string{"change-test", "--unset", "testuser"})
require.NoError(t, err)
testUser = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"})
assert.False(t, testUser.MustChangePassword)
testUserExclude = unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"})
assert.False(t, testUserExclude.MustChangePassword)
for _, name := range []string{"testuser", "testuserexclude"} {
require.NoError(t, microcmdUserCreate().Run(t.Context(), []string{"create", "--username", name, "--email", name + "@gitea.local", "--random-password"}))
}
for _, tc := range []struct {
args []string
assertUser, assertExcludedUser assert.BoolAssertionFunc
}{
// Reset password change flag
{[]string{"change-test", "--all", "--unset"}, assert.False, assert.False},
// Make all users change password
{[]string{"change-test", "--all"}, assert.True, assert.True},
// Reset password change flag but exclude all tested users
{[]string{"change-test", "--all", "--unset", "--exclude", "testuser,testuserexclude"}, assert.True, assert.True},
// Reset password change flag by listing multiple users
{[]string{"change-test", "--unset", "testuser", "testuserexclude"}, assert.False, assert.False},
// Exclude a user from all user
{[]string{"change-test", "--all", "--exclude", "testuserexclude"}, assert.True, assert.False},
// Unset a flag for single user
{[]string{"change-test", "--unset", "testuser"}, assert.False, assert.False},
} {
require.NoError(t, microcmdUserMustChangePassword().Run(t.Context(), tc.args))
tc.assertUser(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuser"}).MustChangePassword)
tc.assertExcludedUser(t, unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: "testuserexclude"}).MustChangePassword)
}
}

17
cmd/audit.go Normal file
View File

@@ -0,0 +1,17 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"context"
audit_model "gitea.dev/models/audit"
user_model "gitea.dev/models/user"
"gitea.dev/services/audit"
)
func cliAuditContext(ctx context.Context) context.Context {
ctx = audit.WithOrigin(ctx, audit_model.OriginCLI)
return audit.WithDoer(ctx, user_model.NewCliUser())
}

View File

@@ -76,110 +76,82 @@ func TestCliCmd(t *testing.T) {
cli.SubcommandHelpTemplate = "(subcommand help template)"
cases := []struct {
env map[string]string
cmd string
exp string
env map[string]string
cmds []string
exp string
}{
// help commands
{
cmd: "./gitea -h",
exp: "DEFAULT CONFIGURATION:",
cmds: []string{"./gitea -h", "./gitea help"},
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea help",
exp: "DEFAULT CONFIGURATION:",
},
{
cmd: "./gitea -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea help -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null -h",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -h -c /dev/null",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea -c /dev/null test-cmd help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd -c /dev/null help",
exp: "ConfigFile: /dev/null",
},
{
cmd: "./gitea test-cmd help -c /dev/null",
cmds: []string{
"./gitea -c /dev/null -h",
"./gitea -c /dev/null help",
"./gitea help -c /dev/null",
"./gitea -c /dev/null test-cmd -h",
"./gitea test-cmd -c /dev/null -h",
"./gitea test-cmd -h -c /dev/null",
"./gitea -c /dev/null test-cmd help",
"./gitea test-cmd -c /dev/null help",
"./gitea test-cmd help -c /dev/null",
},
exp: "ConfigFile: /dev/null",
},
// parse paths
{
cmd: "./gitea test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
cmds: []string{"./gitea test-cmd"},
exp: makePathOutput(defaultWorkPath, defaultCustomPath, defaultCustomConf),
},
{
cmd: "./gitea -c /tmp/app.ini test-cmd",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
cmds: []string{"./gitea -c /tmp/app.ini test-cmd"},
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
cmd: "./gitea test-cmd -c /tmp/app.ini",
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
cmds: []string{"./gitea test-cmd -c /tmp/app.ini"},
exp: makePathOutput(defaultWorkPath, defaultCustomPath, "/tmp/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmds: []string{"./gitea test-cmd"},
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --work-path /tmp/other",
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmds: []string{"./gitea test-cmd --work-path /tmp/other"},
exp: makePathOutput("/tmp/other", "/tmp/other/custom", "/tmp/other/custom/conf/app.ini"),
},
{
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmd: "./gitea test-cmd --config /tmp/app-other.ini",
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
env: map[string]string{"GITEA_WORK_DIR": "/tmp"},
cmds: []string{"./gitea test-cmd --config /tmp/app-other.ini"},
exp: makePathOutput("/tmp", "/tmp/custom", "/tmp/app-other.ini"),
},
}
for _, c := range cases {
t.Run(c.cmd, func(t *testing.T) {
app := newTestApp(cli.Command{
Action: func(ctx context.Context, cmd *cli.Command) error {
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
},
for _, command := range c.cmds {
t.Run(command, func(t *testing.T) {
app := newTestApp(cli.Command{
Action: func(ctx context.Context, cmd *cli.Command) error {
_, _ = fmt.Fprint(cmd.Root().Writer, makePathOutput(setting.AppWorkPath, setting.CustomPath, setting.CustomConf))
return nil
},
})
for k, v := range c.env {
t.Setenv(k, v)
}
args := strings.Split(command, " ") // for test only, "split" is good enough
r, err := runTestApp(app, args...)
assert.NoError(t, err, command)
assert.NotEmpty(t, c.exp, command)
if !assert.Contains(t, r.Stdout, c.exp, command) {
t.Log("Full output:\n" + r.Stdout)
t.Log("Expected:\n" + c.exp)
}
})
for k, v := range c.env {
t.Setenv(k, v)
}
args := strings.Split(c.cmd, " ") // for test only, "split" is good enough
r, err := runTestApp(app, args...)
assert.NoError(t, err, c.cmd)
assert.NotEmpty(t, c.exp, c.cmd)
if !assert.Contains(t, r.Stdout, c.exp, c.cmd) {
t.Log("Full output:\n" + r.Stdout)
t.Log("Expected:\n" + c.exp)
}
})
}
}
}

View File

@@ -12,9 +12,9 @@ import (
"strings"
"text/tabwriter"
"gitea.dev/modelmigration"
"gitea.dev/modelmigration/base"
"gitea.dev/models/db"
"gitea.dev/models/migrations"
migrate_base "gitea.dev/models/migrations/base"
"gitea.dev/modules/container"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
@@ -114,8 +114,8 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
setting.Database.LogSQL = debug
if err := db.InitEngine(ctx); err != nil {
fmt.Println(err)
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
cprintln(cmd, err)
cprintln(cmd, "Check if you are using the right config file. You can use a --config directive to specify one.")
return nil
}
@@ -129,10 +129,10 @@ func runRecreateTable(ctx context.Context, cmd *cli.Command) error {
if err != nil {
return err
}
recreateTables := migrate_base.RecreateTables(beans...)
recreateTables := base.RecreateTables(beans...)
return db.InitEngineWithMigration(context.Background(), func(ctx context.Context, x db.EngineMigration) error {
if err := migrations.EnsureUpToDate(ctx, x); err != nil {
if err := modelmigration.EnsureUpToDate(ctx, x); err != nil {
return err
}
return recreateTables(x)

View File

@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
"gitea.dev/models/db"
"gitea.dev/modules/log"
@@ -40,15 +39,15 @@ func runDoctorConvert(ctx context.Context, cmd *cli.Command) error {
log.Fatal("Failed to convert database & table: %v", err)
return err
}
fmt.Println("Converted successfully, please confirm your database's character set is now utf8mb4")
cprintln(cmd, "Converted successfully, please confirm your database's character set is now utf8mb4")
case setting.Database.Type.IsMSSQL():
if err := db.ConvertVarcharToNVarchar(); err != nil {
log.Fatal("Failed to convert database from varchar to nvarchar: %v", err)
return err
}
fmt.Println("Converted successfully, please confirm your database's all columns character is NVARCHAR now")
cprintln(cmd, "Converted successfully, please confirm your database's all columns character is NVARCHAR now")
default:
fmt.Println("This command can only be used with a MySQL or MSSQL database")
cprintln(cmd, "This command can only be used with a MySQL or MSSQL database")
}
return nil

View File

@@ -13,13 +13,11 @@ import (
"gitea.dev/models/db"
"gitea.dev/modules/dump"
"gitea.dev/modules/json"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
"gitea.dev/modules/storage"
"gitea.dev/modules/util"
"gitea.com/go-chi/session"
"github.com/urfave/cli/v3"
)
@@ -92,7 +90,7 @@ func newDumpCommand() *cli.Command {
},
&cli.StringFlag{
Name: "type",
Usage: `Dump output format, default to "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
Usage: `Dump output format, default to the --file extension if supported, else "zip", supported types: ` + strings.Join(dump.SupportedOutputTypes, ", "),
},
},
}
@@ -198,7 +196,7 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
}
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
if err := os.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
}
}()
@@ -249,12 +247,8 @@ func runDump(ctx context.Context, cmd *cli.Command) error {
log.Info("Packing data directory...%s", setting.AppDataPath)
var excludes []string
if setting.SessionConfig.OriginalProvider == "file" {
var opts session.Options
if err = json.Unmarshal([]byte(setting.SessionConfig.ProviderConfig), &opts); err != nil {
return err
}
excludes = append(excludes, opts.ProviderConfig)
if setting.SessionConfig.Provider == "file" {
excludes = append(excludes, setting.SessionConfig.ProviderConfig)
}
if cmd.IsSet("skip-index") && cmd.Bool("skip-index") {

View File

@@ -17,6 +17,7 @@ import (
"gitea.dev/modules/structs"
"gitea.dev/modules/util"
"gitea.dev/services/convert"
"gitea.dev/services/gitproxy"
"gitea.dev/services/migrations"
"github.com/urfave/cli/v3"
@@ -32,7 +33,7 @@ func newDumpRepositoryCommand() *cli.Command {
&cli.StringFlag{
Name: "git_service",
Value: "",
Usage: "Git service, git, github, gitea, gitlab. If clone_addr could be recognized, this could be ignored.",
Usage: "Git service, git, github, gitea, forgejo, gitlab. If clone_addr could be recognized, this could be ignored.",
},
&cli.StringFlag{
Name: "repo_dir",
@@ -94,6 +95,9 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
if err := git.InitSimple(); err != nil {
return err
}
if err := gitproxy.Run(ctx); err != nil {
return err
}
log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath)
@@ -113,6 +117,8 @@ func runDumpRepository(ctx context.Context, cmd *cli.Command) error {
serviceStr = "gitlab"
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://gitea.com/") {
serviceStr = "gitea"
} else if strings.HasPrefix(strings.ToLower(cloneAddr), "https://codeberg.org/") {
serviceStr = "forgejo"
}
if serviceStr == "" {
return errors.New("git_service missed or clone_addr cannot be recognized")

View File

@@ -18,7 +18,6 @@ import (
"gitea.dev/modules/public"
"gitea.dev/modules/setting"
"gitea.dev/modules/templates"
"gitea.dev/modules/util"
"github.com/urfave/cli/v3"
)
@@ -151,7 +150,7 @@ func runListDo(c *cli.Command) error {
}
for _, a := range matchedAssetFiles {
fmt.Println(a.path)
cprintln(c, a.path)
}
return nil
@@ -195,7 +194,7 @@ func runExtractDo(c *cli.Command) error {
destdir = c.String("destination")
} else if c.Bool("custom") {
destdir = setting.CustomPath
fmt.Println("Using app.ini at", setting.CustomConf)
cprintln(c, "Using app.ini at", setting.CustomConf)
}
fi, err := os.Stat(destdir)
@@ -214,13 +213,13 @@ func runExtractDo(c *cli.Command) error {
return fmt.Errorf("destination %q is not a directory", destdir)
}
fmt.Printf("Extracting to %s:\n", destdir)
cprintf(c, "Extracting to %s:\n", destdir)
overwrite := c.Bool("overwrite")
rename := c.Bool("rename")
for _, a := range matchedAssetFiles {
if err := extractAsset(destdir, a, overwrite, rename); err != nil {
if err := extractAsset(c, destdir, a, overwrite, rename); err != nil {
// Non-fatal error
_, _ = fmt.Fprintf(os.Stderr, "%s: %v\n", a.path, err)
}
@@ -229,7 +228,7 @@ func runExtractDo(c *cli.Command) error {
return nil
}
func extractAsset(d string, a assetFile, overwrite, rename bool) error {
func extractAsset(c *cli.Command, d string, a assetFile, overwrite, rename bool) error {
dest := filepath.Join(d, filepath.FromSlash(a.path))
dir := filepath.Dir(dest)
@@ -250,29 +249,23 @@ func extractAsset(d string, a assetFile, overwrite, rename bool) error {
return fmt.Errorf("%s: %w", dest, err)
}
} else if !overwrite && !rename {
fmt.Printf("%s already exists; skipped.\n", dest)
cprintf(c, "%s already exists; skipped.\n", dest)
return nil
} else if !fi.Mode().IsRegular() {
return fmt.Errorf("%s already exists, but it's not a regular file", dest)
} else if rename {
if err := util.Rename(dest, dest+".bak"); err != nil {
if err := os.Rename(dest, dest+".bak"); err != nil {
return fmt.Errorf("error creating backup for %s: %w", dest, err)
}
// Attempt to respect file permissions mask (even if user:group will be set anew)
perms = fi.Mode()
}
file, err := os.OpenFile(dest, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perms)
if err != nil {
return fmt.Errorf("%s: %w", dest, err)
}
defer file.Close()
if _, err = file.Write(data); err != nil {
if err := os.WriteFile(dest, data, perms); err != nil {
return fmt.Errorf("%s: %w", dest, err)
}
fmt.Println(dest)
cprintln(c, dest)
return nil
}
@@ -307,7 +300,7 @@ func compileCollectPatterns(args []string) (_ []glob.Glob, err error) {
pat := make([]glob.Glob, len(args))
for i := range args {
if pat[i], err = glob.Compile(args[i], '/'); err != nil {
return nil, fmt.Errorf("invalid glob patterh %q: %w", args[i], err)
return nil, fmt.Errorf("invalid glob pattern %q: %w", args[i], err)
}
}
return pat, nil

View File

@@ -106,10 +106,10 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
return err
}
fmt.Printf("%s", internalToken)
cprintf(c, "%s", internalToken)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
cprintf(c, "\n")
}
return nil
@@ -117,10 +117,10 @@ func runGenerateInternalToken(_ context.Context, c *cli.Command) error {
func runGenerateLfsJwtSecret(_ context.Context, c *cli.Command) error {
_, jwtSecretBase64 := generate.NewJwtSecretWithBase64()
fmt.Printf("%s", jwtSecretBase64)
cprintf(c, "%s", jwtSecretBase64)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
cprintf(c, "\n")
}
return nil
@@ -133,10 +133,10 @@ func runGenerateSecretKey(_ context.Context, c *cli.Command) error {
}
// codeql[disable-next-line=go/clear-text-logging]
fmt.Printf("%s", secretKey)
cprintf(c, "%s", secretKey)
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Printf("\n")
cprintf(c, "\n")
}
return nil
@@ -168,14 +168,14 @@ func runGenerateKeyPair(_ context.Context, c *cli.Command) error {
// Check if file exists to prevent overwriting
if _, err := os.Stat(file); err == nil {
if !confirm(c.Reader, c.Writer, "%s already exists.\nOverwrite (y/n)? ", file) {
fmt.Println("Aborting")
cprintln(c, "Aborting")
return nil
}
}
bits := c.Int("bits")
err := ssh.GenKeyPair(file, generate.SSHKeyType(keyType), bits)
if err == nil {
fmt.Printf("Your SSH key has been saved in %s\n", file)
cprintf(c, "Your SSH key has been saved in %s\n", file)
}
return err
}

View File

@@ -1,8 +1,6 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Package cmd provides subcommands to the gitea binary - such as "web" or
// "admin".
package cmd
import (
@@ -22,8 +20,8 @@ import (
"github.com/urfave/cli/v3"
)
// argsSet checks that all the required arguments are set. args is a list of
// arguments that must be set in the passed Context.
// argsSet checks that all the required arguments are set.
// args is a list of arguments that must be set in the command context.
func argsSet(c *cli.Command, args ...string) error {
for _, a := range args {
if !c.IsSet(a) {
@@ -141,3 +139,11 @@ func isValidDefaultSubCommand(cmd *cli.Command) (string, bool) {
}
return "", true
}
func cprintf(c *cli.Command, format string, args ...any) {
_, _ = fmt.Fprintf(c.Writer, format, args...)
}
func cprintln(c *cli.Command, args ...any) {
_, _ = fmt.Fprintln(c.Writer, args...)
}

View File

@@ -169,10 +169,6 @@ func parseGitHookCommitRefLine(line string) (oldCommitID, newCommitID string, re
}
func runHookPreReceive(ctx context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
setup(ctx, c.Bool("debug"))
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
@@ -186,23 +182,24 @@ Gitea or set your environment appropriately.`, "")
// the environment is set by serv command
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
username := os.Getenv(repo_module.EnvRepoUsername)
reponame := os.Getenv(repo_module.EnvRepoName)
ownerName := os.Getenv(repo_module.EnvRepoUsername)
repoName := os.Getenv(repo_module.EnvRepoName)
userID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
deployKeyID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvDeployKeyID), 10, 64)
actionsTaskID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvActionsTaskID), 10, 64)
hookOptions := private.HookOptions{
UserID: userID,
IsWiki: isWiki,
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
GitPushOptions: pushOptions(),
PullRequestID: prID,
DeployKeyID: deployKeyID,
ActionsTaskID: actionsTaskID,
IsWiki: isWiki,
PullRequestID: prID,
UserID: userID,
UserName: os.Getenv(repo_module.EnvPusherName),
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
}
scanner := bufio.NewScanner(os.Stdin)
@@ -212,7 +209,6 @@ Gitea or set your environment appropriately.`, "")
refFullNames := make([]git.RefName, hookBatchSize)
count := 0
total := 0
lastline := 0
out := io.Discard
if setting.Git.VerbosePush {
@@ -225,8 +221,6 @@ Gitea or set your environment appropriately.`, "")
}
}
supportProcReceive := git.DefaultFeatures().SupportProcReceive
for scanner.Scan() {
// TODO: support news feeds for wiki
if isWiki {
@@ -239,37 +233,23 @@ Gitea or set your environment appropriately.`, "")
}
total++
lastline++
oldCommitIDs[count] = oldCommitID
newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName
count++
fmt.Fprintf(out, "*")
// If the ref is a branch or tag, check if it's protected
// if supportProcReceive all ref should be checked because
// permission check was delayed
if supportProcReceive || refFullName.IsBranch() || refFullName.IsTag() {
oldCommitIDs[count] = oldCommitID
newCommitIDs[count] = newCommitID
refFullNames[count] = refFullName
count++
fmt.Fprintf(out, "*")
if count >= hookBatchSize {
fmt.Fprintf(out, " Checking %d references\n", count)
if count >= hookBatchSize {
fmt.Fprintf(out, " Checking %d references\n", count)
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
hookOptions.RefFullNames = refFullNames
extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
if extra.HasError() {
return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
}
count = 0
lastline = 0
hookOptions.OldCommitIDs = oldCommitIDs
hookOptions.NewCommitIDs = newCommitIDs
hookOptions.RefFullNames = refFullNames
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
if extra.HasError() {
return fail(ctx, extra.UserMsg, "HookPreReceive(batch) failed: %v", extra.Error)
}
} else {
fmt.Fprintf(out, ".")
}
if lastline >= hookBatchSize {
fmt.Fprintf(out, "\n")
lastline = 0
count = 0
}
}
if err := scanner.Err(); err != nil {
@@ -283,12 +263,10 @@ Gitea or set your environment appropriately.`, "")
fmt.Fprintf(out, " Checking %d references\n", count)
extra := private.HookPreReceive(ctx, username, reponame, hookOptions)
extra := private.HookPreReceive(ctx, ownerName, repoName, hookOptions)
if extra.HasError() {
return fail(ctx, extra.UserMsg, "HookPreReceive(last) failed: %v", extra.Error)
}
} else if lastline > 0 {
fmt.Fprintf(out, "\n")
}
fmt.Fprintf(out, "Checked %d references in total\n", total)
@@ -298,10 +276,6 @@ Gitea or set your environment appropriately.`, "")
// runHookUpdate avoid to do heavy operations on update hook because it will be
// invoked for every ref update which does not like pre-receive and post-receive
func runHookUpdate(_ context.Context, c *cli.Command) error {
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
// Update is empty and is kept only for backwards compatibility
if len(os.Args) < 3 {
return nil
@@ -322,11 +296,6 @@ func runHookPostReceive(ctx context.Context, c *cli.Command) error {
return fmt.Errorf("failed to call 'git update-server-info': %w", err)
}
// Now if we're an internal don't do anything else
if isInternal, _ := strconv.ParseBool(os.Getenv(repo_module.EnvIsInternal)); isInternal {
return nil
}
if len(os.Getenv("SSH_ORIGINAL_COMMAND")) == 0 {
if setting.OnlyAllowPushIfGiteaEnvironmentSet {
return fail(ctx, `Rejecting changes as Gitea environment not set.
@@ -353,18 +322,20 @@ Gitea or set your environment appropriately.`, "")
repoName := os.Getenv(repo_module.EnvRepoName)
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
prID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPRID), 10, 64)
pusherName := os.Getenv(repo_module.EnvPusherName)
hookOptions := private.HookOptions{
UserName: pusherName,
UserID: pusherID,
IsWiki: isWiki,
GitAlternativeObjectDirectories: os.Getenv(private.GitAlternativeObjectDirectories),
GitObjectDirectory: os.Getenv(private.GitObjectDirectory),
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
GitPushOptions: pushOptions(),
PullRequestID: prID,
PushTrigger: repo_module.PushTrigger(os.Getenv(repo_module.EnvPushTrigger)),
IsWiki: isWiki,
PullRequestID: prID,
UserID: pusherID,
UserName: os.Getenv(repo_module.EnvPusherName),
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
}
oldCommitIDs := make([]string, 0, hookBatchSize)
@@ -472,16 +443,11 @@ Gitea or set your environment appropriately.`, "")
return nil
}
if !git.DefaultFeatures().SupportProcReceive {
return fail(ctx, "No proc-receive support", "current git version doesn't support proc-receive.")
}
reader := bufio.NewReader(os.Stdin)
repoUser := os.Getenv(repo_module.EnvRepoUsername)
isWiki, _ := strconv.ParseBool(os.Getenv(repo_module.EnvRepoIsWiki))
repoName := os.Getenv(repo_module.EnvRepoName)
pusherID, _ := strconv.ParseInt(os.Getenv(repo_module.EnvPusherID), 10, 64)
pusherName := os.Getenv(repo_module.EnvPusherName)
// 1. Version and features negotiation.
// S: PKT-LINE(version=1\0push-options atomic...) / PKT-LINE(version=1\n)
@@ -553,10 +519,13 @@ Gitea or set your environment appropriately.`, "")
// S: ... ...
// S: flush-pkt
hookOptions := private.HookOptions{
UserName: pusherName,
UserID: pusherID,
IsWiki: isWiki,
GitPushOptions: make(map[string]string),
IsWiki: isWiki,
UserID: pusherID,
UserName: os.Getenv(repo_module.EnvPusherName),
UserExtDoerData: os.Getenv(repo_module.EnvPusherExtDoerData),
}
hookOptions.OldCommitIDs = make([]string, 0, hookBatchSize)
hookOptions.NewCommitIDs = make([]string, 0, hookBatchSize)
@@ -752,7 +721,7 @@ func writeFlushPktLine(ctx context.Context, out io.Writer) error {
func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error {
hexchar := []byte("0123456789abcdef")
hex := func(n uint64) byte {
return hexchar[(n)&15]
return hexchar[n&15]
}
length := uint64(len(data) + 4)

View File

@@ -74,7 +74,7 @@ func runKeys(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
authorizedString, extra := private.AuthorizedPublicKeyByContent(ctx, content)
authorizedString, extra := private.AuthorizedPublicKeyForSSH(ctx, content)
// do not use handleCliResponseExtra or cli.NewExitError, if it exists immediately, it breaks some tests like Test_CmdKeys
if extra.Error != nil {
return extra.Error

View File

@@ -5,7 +5,6 @@ package cmd
import (
"context"
"fmt"
"gitea.dev/modules/private"
"gitea.dev/modules/setting"
@@ -22,11 +21,11 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
if !confirmSkipped {
if len(body) == 0 {
fmt.Println("warning: Content is empty")
cprintln(c, "warning: Content is empty")
}
if !confirm(c.Reader, c.Writer, "Proceed with sending email? [Y/n] ") {
fmt.Println("The mail was not sent")
cprintln(c, "The mail was not sent")
return nil
}
}
@@ -35,6 +34,6 @@ func runSendMail(ctx context.Context, c *cli.Command) error {
if extra.HasError() {
return handleCliResponseExtra(extra)
}
_, _ = fmt.Printf("Sent %s email(s) to all users\n", respText.Text)
cprintf(c, "Sent %s email(s) to all users\n", respText.Text)
return nil
}

View File

@@ -5,60 +5,14 @@ package cmd
import (
"context"
"errors"
"fmt"
"os"
"gitea.dev/modules/log"
"gitea.dev/modules/private"
"github.com/urfave/cli/v3"
)
func defaultLoggingFlags() []cli.Flag {
return []cli.Flag{
&cli.StringFlag{
Name: "logger",
Usage: `Logger name - will default to "default"`,
},
&cli.StringFlag{
Name: "writer",
Usage: "Name of the log writer - will default to mode",
},
&cli.StringFlag{
Name: "level",
Usage: "Logging level for the new logger",
},
&cli.StringFlag{
Name: "stacktrace-level",
Aliases: []string{"L"},
Usage: "Stacktrace logging level",
},
&cli.StringFlag{
Name: "flags",
Aliases: []string{"F"},
Usage: "Flags for the logger",
},
&cli.StringFlag{
Name: "expression",
Aliases: []string{"e"},
Usage: "Matching expression for the logger",
},
&cli.StringFlag{
Name: "prefix",
Aliases: []string{"p"},
Usage: "Prefix for the logger",
},
&cli.BoolFlag{
Name: "color",
Usage: "Use color in the logs",
},
&cli.BoolFlag{
Name: "debug",
},
}
}
func newLoggingCommand() *cli.Command {
return &cli.Command{
Name: "logging",
@@ -91,92 +45,6 @@ func newLoggingCommand() *cli.Command {
},
},
Action: runReleaseReopenLogging,
}, {
Name: "remove",
Usage: "Remove a logger",
ArgsUsage: "[name] Name of logger to remove",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "debug",
}, &cli.StringFlag{
Name: "logger",
Usage: `Logger name - will default to "default"`,
},
},
Action: runRemoveLogger,
}, {
Name: "add",
Usage: "Add a logger",
Commands: []*cli.Command{
{
Name: "file",
Usage: "Add a file logger",
Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.StringFlag{
Name: "filename",
Aliases: []string{"f"},
Usage: "Filename for the logger - this must be set.",
},
&cli.BoolFlag{
Name: "rotate",
Aliases: []string{"r"},
Usage: "Rotate logs",
},
&cli.Int64Flag{
Name: "max-size",
Aliases: []string{"s"},
Usage: "Maximum size in bytes before rotation",
},
&cli.BoolFlag{
Name: "daily",
Aliases: []string{"d"},
Usage: "Rotate logs daily",
},
&cli.IntFlag{
Name: "max-days",
Aliases: []string{"D"},
Usage: "Maximum number of daily logs to keep",
},
&cli.BoolFlag{
Name: "compress",
Aliases: []string{"z"},
Usage: "Compress rotated logs",
},
&cli.IntFlag{
Name: "compression-level",
Aliases: []string{"Z"},
Usage: "Compression level to use",
},
}...),
Action: runAddFileLogger,
}, {
Name: "conn",
Usage: "Add a net conn logger",
Flags: append(defaultLoggingFlags(), []cli.Flag{
&cli.BoolFlag{
Name: "reconnect-on-message",
Aliases: []string{"R"},
Usage: "Reconnect to host for every message",
},
&cli.BoolFlag{
Name: "reconnect",
Aliases: []string{"r"},
Usage: "Reconnect to host when connection is dropped",
},
&cli.StringFlag{
Name: "protocol",
Aliases: []string{"P"},
Usage: "Set protocol to use: tcp, unix, or udp (defaults to tcp)",
},
&cli.StringFlag{
Name: "address",
Aliases: []string{"a"},
Usage: "Host address and port to connect to (defaults to :7020)",
},
}...),
Action: runAddConnLogger,
},
},
}, {
Name: "log-sql",
Usage: "Set LogSQL",
@@ -195,107 +63,6 @@ func newLoggingCommand() *cli.Command {
}
}
func runRemoveLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
logger := c.String("logger")
if len(logger) == 0 {
logger = log.DEFAULT
}
writer := c.Args().First()
extra := private.RemoveLogger(ctx, logger, writer)
return handleCliResponseExtra(extra)
}
func runAddConnLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "conn"
vals["net"] = "tcp"
if c.IsSet("protocol") {
switch c.String("protocol") {
case "udp":
vals["net"] = "udp"
case "unix":
vals["net"] = "unix"
}
}
if c.IsSet("address") {
vals["address"] = c.String("address")
} else {
vals["address"] = ":7020"
}
if c.IsSet("reconnect") {
vals["reconnect"] = c.Bool("reconnect")
}
if c.IsSet("reconnect-on-message") {
vals["reconnectOnMsg"] = c.Bool("reconnect-on-message")
}
return commonAddLogger(ctx, c, mode, vals)
}
func runAddFileLogger(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
vals := map[string]any{}
mode := "file"
if c.IsSet("filename") {
vals["filename"] = c.String("filename")
} else {
return errors.New("filename must be set when creating a file logger")
}
if c.IsSet("rotate") {
vals["rotate"] = c.Bool("rotate")
}
if c.IsSet("max-size") {
vals["maxsize"] = c.Int64("max-size")
}
if c.IsSet("daily") {
vals["daily"] = c.Bool("daily")
}
if c.IsSet("max-days") {
vals["maxdays"] = c.Int("max-days")
}
if c.IsSet("compress") {
vals["compress"] = c.Bool("compress")
}
if c.IsSet("compression-level") {
vals["compressionLevel"] = c.Int("compression-level")
}
return commonAddLogger(ctx, c, mode, vals)
}
func commonAddLogger(ctx context.Context, c *cli.Command, mode string, vals map[string]any) error {
if len(c.String("level")) > 0 {
vals["level"] = log.LevelFromString(c.String("level")).String()
}
if len(c.String("stacktrace-level")) > 0 {
vals["stacktraceLevel"] = log.LevelFromString(c.String("stacktrace-level")).String()
}
if len(c.String("expression")) > 0 {
vals["expression"] = c.String("expression")
}
if len(c.String("prefix")) > 0 {
vals["prefix"] = c.String("prefix")
}
if len(c.String("flags")) > 0 {
vals["flags"] = log.FlagsFromString(c.String("flags"))
}
if c.IsSet("color") {
vals["colorize"] = c.Bool("color")
}
logger := log.DEFAULT
if c.IsSet("logger") {
logger = c.String("logger")
}
writer := mode
if c.IsSet("writer") {
writer = c.String("writer")
}
extra := private.AddLogger(ctx, logger, writer, mode, vals)
return handleCliResponseExtra(extra)
}
func runPauseLogging(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
userMsg := private.PauseLogging(ctx)

View File

@@ -9,7 +9,6 @@ import (
"fmt"
"net/url"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
@@ -29,6 +28,7 @@ import (
"gitea.dev/modules/process"
repo_module "gitea.dev/modules/repository"
"gitea.dev/modules/setting"
"gitea.dev/services/agit"
"gitea.dev/services/lfs"
"github.com/kballard/go-shellquote"
@@ -139,13 +139,13 @@ func runServ(ctx context.Context, c *cli.Command) error {
setup(ctx, c.Bool("debug"))
if setting.SSH.Disabled {
println("Gitea: SSH has been disabled")
cprintln(c, "Gitea: SSH has been disabled")
return nil
}
if c.NArg() < 1 {
if err := cli.ShowSubcommandHelp(c); err != nil {
fmt.Printf("error showing subcommand help: %v\n", err)
cprintf(c, "error showing subcommand help: %v\n", err)
}
return nil
}
@@ -171,15 +171,19 @@ func runServ(ctx context.Context, c *cli.Command) error {
if err != nil {
return fail(ctx, "Key check failed", "Failed to check provided key: %v", err)
}
var authSuccessMsg string
switch key.Type {
case asymkey_model.KeyTypeDeploy:
println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Gitea does not provide shell access.")
authSuccessMsg = "Hi there! You've successfully authenticated with an SSH deploy key."
case asymkey_model.KeyTypePrincipal:
println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Gitea does not provide shell access.")
authSuccessMsg = "Hi there! You've successfully authenticated with the SSH principal " + key.Content + "."
default:
println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Gitea does not provide shell access.")
authSuccessMsg = "Hi there, " + user.Name + "! You've successfully authenticated with the SSH key named " + key.Name + "."
}
println("If this is unexpected, please log in with password and setup Gitea under another user.")
_, _ = fmt.Fprintf(c.ErrWriter, "%s\n%s",
authSuccessMsg,
"Gitea does not provide shell access. If this is unexpected, please setup Gitea under another SSH user or use container to deploy.",
)
return nil
} else if c.Bool("debug") {
log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND"))
@@ -191,45 +195,35 @@ func runServ(ctx context.Context, c *cli.Command) error {
}
if len(sshCmdArgs) < 2 {
if git.DefaultFeatures().SupportProcReceive {
// for AGit Flow
if cmd == "ssh_info" {
fmt.Print(`{"type":"agit","version":1}`)
return nil
}
// for AGit Flow
if cmd == "ssh_info" {
cprintf(c, "%s", agit.SshInfoJson)
return nil
}
return fail(ctx, "Too few arguments", "Too few arguments in cmd: %s", cmd)
}
repoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
repoPathFields := strings.SplitN(repoPath, "/", 2)
if len(repoPathFields) != 2 {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath)
var reqOwnerName, reqRepoName string
{
var ok bool
reqRepoPath := strings.TrimPrefix(sshCmdArgs[1], "/")
reqOwnerName, reqRepoName, ok = strings.Cut(reqRepoPath, "/")
if !ok {
return fail(ctx, "Invalid repository path", "Invalid repository path: %v", reqRepoPath)
}
reqRepoName = strings.TrimSuffix(reqRepoName, ".git") // "the-repo-name" or "the-repo-name.wiki"
}
username := repoPathFields[0]
reponame := strings.TrimSuffix(repoPathFields[1], ".git") // “the-repo-name" or "the-repo-name.wiki"
if !repo_model.IsValidSSHAccessRepoName(reponame) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame)
if !repo_model.IsValidSSHAccessRepoName(reqRepoName) {
return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reqRepoName)
}
if c.Bool("enable-pprof") {
if err := os.MkdirAll(setting.PprofDataPath, os.ModePerm); err != nil {
return fail(ctx, "Error while trying to create PPROF_DATA_PATH", "Error while trying to create PPROF_DATA_PATH: %v", err)
}
stopCPUProfiler, err := pprof.DumpCPUProfileForUsername(setting.PprofDataPath, username)
stopProfiler, err := pprof.DumpPprofForUsername(setting.PprofDataPath, reqOwnerName)
if err != nil {
return fail(ctx, "Unable to start CPU profiler", "Unable to start CPU profile: %v", err)
return fail(ctx, "Unable to start pprof profiler", "Unable to start pprof profile: %v", err)
}
defer func() {
stopCPUProfiler()
err := pprof.DumpMemProfileForUsername(setting.PprofDataPath, username)
if err != nil {
_ = fail(ctx, "Unable to dump Mem profile", "Unable to dump Mem Profile: %v", err)
}
}()
defer stopProfiler()
}
verb, lfsVerb := sshCmdArgs[0], ""
@@ -254,39 +248,38 @@ func runServ(ctx context.Context, c *cli.Command) error {
return fail(ctx, "Unknown git command", "Unknown git command %s %s", verb, lfsVerb)
}
results, extra := private.ServCommand(ctx, keyID, username, reponame, requestedMode, verb, lfsVerb)
results, extra := private.ServCommand(ctx, keyID, reqOwnerName, reqRepoName, requestedMode, verb, lfsVerb)
if extra.HasError() {
return fail(ctx, extra.UserMsg, "ServCommand failed: %s", extra.Error)
}
// because the original repoPath maybe redirected, we need to use the returned actual repository information
if results.IsWiki {
repoPath = repo_model.RelativeWikiPath(results.OwnerName, results.RepoName)
} else {
repoPath = repo_model.RelativePath(results.OwnerName, results.RepoName)
}
// LFS SSH protocol
if verb == git.CmdVerbLfsTransfer {
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
if results.IsWiki {
return fail(ctx, "LFS Transfer is not supported for wikis", "")
}
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID})
if err != nil {
return err
}
return lfstransfer.Main(ctx, repoPath, lfsVerb, token)
return lfstransfer.Main(ctx, results.OwnerName, results.RepoName, lfsVerb, token)
}
// LFS token authentication
if verb == git.CmdVerbLfsAuthenticate {
url := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
if results.IsWiki {
return fail(ctx, "LFS Authenticate is not supported for wikis", "")
}
lfsTokenHref := fmt.Sprintf("%s%s/%s.git/info/lfs", setting.AppURL, url.PathEscape(results.OwnerName), url.PathEscape(results.RepoName))
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, RepoID: results.RepoID})
token, err := lfs.GetLFSAuthTokenWithBearer(lfs.AuthTokenOptions{Op: lfsVerb, UserID: results.UserID, UserExtDoerData: results.UserExtDoerData, RepoID: results.RepoID})
if err != nil {
return err
}
tokenAuthentication := &git_model.LFSTokenResponse{
Header: make(map[string]string),
Href: url,
Href: lfsTokenHref,
}
tokenAuthentication.Header["Authorization"] = token
@@ -298,7 +291,7 @@ func runServ(ctx context.Context, c *cli.Command) error {
return nil
}
var command *exec.Cmd
var command *process.Cmd
gitBinPath := filepath.Dir(gitcmd.GitExecutable) // e.g. /usr/bin
gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack
if _, err := os.Stat(gitBinVerb); err != nil {
@@ -307,15 +300,14 @@ func runServ(ctx context.Context, c *cli.Command) error {
verbFields := strings.SplitN(verb, "-", 2)
if len(verbFields) == 2 {
// use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ...
command = exec.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], repoPath)
command = process.CommandContext(ctx, gitcmd.GitExecutable, verbFields[1], results.RepoStoragePath)
}
}
if command == nil {
// by default, use the verb (it has been checked above by allowedCommands)
command = exec.CommandContext(ctx, gitBinVerb, repoPath)
command = process.CommandContext(ctx, gitBinVerb, results.RepoStoragePath)
}
process.SetSysProcAttribute(command)
command.Dir = setting.RepoRootPath
command.Stdout = os.Stdout
command.Stdin = os.Stdin
@@ -323,15 +315,20 @@ func runServ(ctx context.Context, c *cli.Command) error {
command.Env = append(command.Env, os.Environ()...)
command.Env = append(command.Env,
repo_module.EnvRepoIsWiki+"="+strconv.FormatBool(results.IsWiki),
repo_module.EnvRepoName+"="+results.RepoName,
repo_module.EnvRepoUsername+"="+results.OwnerName,
repo_module.EnvRepoName+"="+results.RepoName,
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
repo_module.EnvKeyID+"="+strconv.FormatInt(results.PublicKeyID, 10),
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
repo_module.EnvPusherName+"="+results.UserName,
repo_module.EnvPusherEmail+"="+results.UserEmail,
repo_module.EnvPusherID+"="+strconv.FormatInt(results.UserID, 10),
repo_module.EnvRepoID+"="+strconv.FormatInt(results.RepoID, 10),
repo_module.EnvPusherExtDoerData+"="+results.UserExtDoerData,
repo_module.EnvPRID+"="+strconv.Itoa(0),
repo_module.EnvDeployKeyID+"="+strconv.FormatInt(results.DeployKeyID, 10),
repo_module.EnvKeyID+"="+strconv.FormatInt(results.KeyID, 10),
repo_module.EnvAppURL+"="+setting.AppURL,
)
// to avoid breaking, here only use the minimal environment variables for the "gitea serv" command.
@@ -343,9 +340,9 @@ func runServ(ctx context.Context, c *cli.Command) error {
}
// Update user key activity.
if results.KeyID > 0 {
if err = private.UpdatePublicKeyInRepo(ctx, results.KeyID, results.RepoID); err != nil {
return fail(ctx, "Failed to update public key", "UpdatePublicKeyInRepo: %v", err)
if results.PublicKeyID > 0 {
if err = private.UpdatePublicKeyLastUsed(ctx, results.PublicKeyID, results.RepoID); err != nil {
return fail(ctx, "Failed to update public key", "UpdatePublicKeyLastUsed: %v", err)
}
}

View File

@@ -15,6 +15,7 @@ import (
"strings"
"time"
audit_model "gitea.dev/models/audit"
"gitea.dev/modules/container"
"gitea.dev/modules/graceful"
"gitea.dev/modules/gtprof"
@@ -26,6 +27,7 @@ import (
"gitea.dev/modules/util"
"gitea.dev/routers"
"gitea.dev/routers/install"
"gitea.dev/services/audit"
"github.com/felixge/fgprof"
"github.com/urfave/cli/v3"
@@ -226,7 +228,14 @@ func serveInstalled(c *cli.Command) error {
// Set up Chi routes
webRoutes := routers.NormalRoutes()
auditCtx := cliAuditContext(context.Background())
log.Info("Audit record output: %s", setting.Audit.RecordOutput)
audit.Record(auditCtx, audit_model.SystemStartup, nil, "version", setting.AppVer)
err := listen(webRoutes, true)
audit.Record(auditCtx, audit_model.SystemShutdown, nil)
<-graceful.GetManager().Done()
log.Info("PID: %d Gitea Web Finished", os.Getpid())
return err
@@ -244,7 +253,8 @@ func servePprof() {
_, _, finished := process.GetManager().AddTypedContext(context.TODO(), "Web: PProf Server", process.SystemProcessType, true)
// The pprof server is for debug purpose only, it shouldn't be exposed on public network. At the moment, it's not worth introducing a configurable option for it.
log.Info("Starting pprof server on localhost:6060")
log.Info("Stopped pprof server: %v", http.ListenAndServe("localhost:6060", mux))
server := &http.Server{Addr: "localhost:6060", Handler: mux, ReadHeaderTimeout: 10 * time.Second}
log.Info("Stopped pprof server: %v", server.ListenAndServe())
finished()
}

View File

@@ -83,6 +83,7 @@ func runACME(listenAddr string, m http.Handler) error {
TrustedRoots: certPool,
Email: setting.AcmeEmail,
Agreed: setting.AcmeTOS,
Profile: setting.AcmeProfile,
DisableHTTPChallenge: !enableHTTPChallenge,
DisableTLSALPNChallenge: !enableTLSALPNChallenge,
ListenHost: setting.HTTPAddr,
@@ -99,37 +100,20 @@ func runACME(listenAddr string, m http.Handler) error {
// takes HTTPS down on restart (https://github.com/go-gitea/gitea/issues/38519).
// Prefer keeping the existing cert and retrying renewals asynchronously.
ctx := graceful.GetManager().ShutdownContext()
err := magic.ManageSync(ctx, []string{setting.Domain})
err := magic.ManageSync(ctx, []string{setting.AppDomain})
if err != nil {
cert, cacheErr := magic.CacheManagedCertificate(ctx, setting.Domain)
cert, cacheErr := magic.CacheManagedCertificate(ctx, setting.AppDomain)
if cacheErr != nil || cert.Expired() {
return errors.Join(err, cacheErr)
}
log.Error("ACME certificate manage failed; continuing with existing certificate: %v", err)
if err := magic.ManageAsync(ctx, []string{setting.Domain}); err != nil {
if err := magic.ManageAsync(ctx, []string{setting.AppDomain}); err != nil {
log.Error("Failed to start async ACME management: %v", err)
}
}
tlsConfig := magic.TLSConfig()
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2")
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
tlsConfig.MinVersion = version
}
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
tlsConfig.MaxVersion = version
}
// Set curve preferences
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
tlsConfig.CurvePreferences = curves
}
// Set cipher suites
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
tlsConfig.CipherSuites = ciphers
}
// certmagic only advertises its own ACME challenge protocol, applyTLSSettings appends ours
tlsConfig := applyTLSSettings(magic.TLSConfig())
if enableHTTPChallenge {
go func() {

View File

@@ -12,122 +12,82 @@ import (
"gitea.dev/modules/graceful"
"gitea.dev/modules/log"
"gitea.dev/modules/setting"
"github.com/klauspost/cpuid/v2"
)
var tlsVersionStringMap = map[string]uint16{
"": tls.VersionTLS12, // Default to tls.VersionTLS12
"tlsv1.0": tls.VersionTLS10,
"tlsv1.1": tls.VersionTLS11,
"tlsv1.2": tls.VersionTLS12,
"tlsv1.3": tls.VersionTLS13,
}
// toTLSVersion returns 0 when unset or unknown, which keeps the crypto/tls default
func toTLSVersion(version string) uint16 {
tlsVersion, ok := tlsVersionStringMap[strings.TrimSpace(strings.ToLower(version))]
if !ok {
version = strings.TrimSpace(strings.ToLower(version))
tlsVersion, ok := tlsVersionStringMap[version]
if !ok && version != "" {
log.Warn("Unknown tls version: %s", version)
return 0
}
return tlsVersion
}
var curveStringMap = map[string]tls.CurveID{
"x25519": tls.X25519,
"p256": tls.CurveP256,
"p384": tls.CurveP384,
"p521": tls.CurveP521,
"x25519": tls.X25519,
"p256": tls.CurveP256,
"p384": tls.CurveP384,
"p521": tls.CurveP521,
"mlkem1024": tls.MLKEM1024,
"x25519mlkem768": tls.X25519MLKEM768,
"secp256r1mlkem768": tls.SecP256r1MLKEM768,
"secp384r1mlkem1024": tls.SecP384r1MLKEM1024,
}
func toCurvePreferences(preferences []string) []tls.CurveID {
ids := make([]tls.CurveID, 0, len(preferences))
for _, pref := range preferences {
id, ok := curveStringMap[strings.TrimSpace(strings.ToLower(pref))]
// lookupAll resolves configured names, warning about and skipping the ones crypto/tls does not know
func lookupAll[T comparable](names []string, table map[string]T, kind string) []T {
values := make([]T, 0, len(names))
for _, name := range names {
value, ok := table[strings.TrimSpace(strings.ToLower(name))]
if !ok {
log.Warn("Unknown curve: %s", pref)
}
if id != 0 {
ids = append(ids, id)
log.Warn("Unknown %s: %s", kind, name)
continue
}
values = append(values, value)
}
return ids
return values
}
var cipherStringMap = map[string]uint16{
"rsa_with_rc4_128_sha": tls.TLS_RSA_WITH_RC4_128_SHA,
"rsa_with_3des_ede_cbc_sha": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
"rsa_with_aes_128_cbc_sha": tls.TLS_RSA_WITH_AES_128_CBC_SHA,
"rsa_with_aes_256_cbc_sha": tls.TLS_RSA_WITH_AES_256_CBC_SHA,
"rsa_with_aes_128_cbc_sha256": tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
"rsa_with_aes_128_gcm_sha256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
"rsa_with_aes_256_gcm_sha384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
"ecdhe_ecdsa_with_rc4_128_sha": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
"ecdhe_ecdsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
"ecdhe_ecdsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
"ecdhe_rsa_with_rc4_128_sha": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
"ecdhe_rsa_with_3des_ede_cbc_sha": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
"ecdhe_rsa_with_aes_128_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
"ecdhe_rsa_with_aes_256_cbc_sha": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
"ecdhe_ecdsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
"ecdhe_rsa_with_aes_128_cbc_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
"ecdhe_rsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
"ecdhe_ecdsa_with_aes_128_gcm_sha256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
"ecdhe_rsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
"ecdhe_ecdsa_with_aes_256_gcm_sha384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
"ecdhe_rsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
"ecdhe_ecdsa_with_chacha20_poly1305_sha256": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
"ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
"aes_128_gcm_sha256": tls.TLS_AES_128_GCM_SHA256,
"aes_256_gcm_sha384": tls.TLS_AES_256_GCM_SHA384,
"chacha20_poly1305_sha256": tls.TLS_CHACHA20_POLY1305_SHA256,
}
// cipherStringMap is derived from crypto/tls so that suites Go adds or removes are picked up automatically
var cipherStringMap = buildCipherStringMap()
func toTLSCiphers(cipherStrings []string) []uint16 {
ciphers := make([]uint16, 0, len(cipherStrings))
for _, cipherString := range cipherStrings {
cipher, ok := cipherStringMap[strings.TrimSpace(strings.ToLower(cipherString))]
if !ok {
log.Warn("Unknown cipher: %s", cipherString)
}
if cipher != 0 {
ciphers = append(ciphers, cipher)
}
func buildCipherStringMap() map[string]uint16 {
ciphers := map[string]uint16{
// aliases for the two suites Go later renamed with a _SHA256 suffix
"ecdhe_rsa_with_chacha20_poly1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
"ecdhe_ecdsa_with_chacha20_poly1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
for _, cipher := range append(tls.CipherSuites(), tls.InsecureCipherSuites()...) {
ciphers[strings.ToLower(strings.TrimPrefix(cipher.Name, "TLS_"))] = cipher.ID
}
return ciphers
}
// defaultCiphers uses hardware support to check if AES is specifically
// supported by the CPU.
//
// If AES is supported AES ciphers will be preferred over ChaCha based ciphers
// (This code is directly inspired by the certmagic code.)
func defaultCiphers() []uint16 {
if cpuid.CPU.Supports(cpuid.AESNI) {
return defaultCiphersAESfirst
// applyTLSSettings applies the configured [server] TLS options. Every option
// left unset keeps the crypto/tls default, which Go keeps current.
func applyTLSSettings(tlsConfig *tls.Config) *tls.Config {
// the listener is already TLS wrapped when it reaches http.Server, so it never sets ALPN for us
tlsConfig.NextProtos = append(tlsConfig.NextProtos, "h2", "http/1.1")
tlsConfig.MinVersion = toTLSVersion(setting.SSLMinimumVersion)
tlsConfig.MaxVersion = toTLSVersion(setting.SSLMaximumVersion)
if curves := lookupAll(setting.SSLCurvePreferences, curveStringMap, "curve"); len(curves) > 0 {
tlsConfig.CurvePreferences = curves
}
return defaultCiphersChaChaFirst
if ciphers := lookupAll(setting.SSLCipherSuites, cipherStringMap, "cipher suite"); len(ciphers) > 0 {
tlsConfig.CipherSuites = ciphers
}
return tlsConfig
}
var (
defaultCiphersAES = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}
defaultCiphersChaCha = []uint16{
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
}
defaultCiphersAESfirst = append(defaultCiphersAES, defaultCiphersChaCha...)
defaultCiphersChaChaFirst = append(defaultCiphersChaCha, defaultCiphersAES...)
)
// runHTTPS listens on the provided network address and then calls
// Serve to handle requests on incoming TLS connections.
//
@@ -136,33 +96,7 @@ var (
// certFile should be the concatenation of the server's certificate followed by the
// CA's certificate.
func runHTTPS(network, listenAddr, name, certFile, keyFile string, m http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
tlsConfig := &tls.Config{}
if tlsConfig.NextProtos == nil {
tlsConfig.NextProtos = []string{"h2", "http/1.1"}
}
if version := toTLSVersion(setting.SSLMinimumVersion); version != 0 {
tlsConfig.MinVersion = version
}
if version := toTLSVersion(setting.SSLMaximumVersion); version != 0 {
tlsConfig.MaxVersion = version
}
// Set curve preferences
tlsConfig.CurvePreferences = []tls.CurveID{
tls.X25519,
tls.CurveP256,
}
if curves := toCurvePreferences(setting.SSLCurvePreferences); len(curves) > 0 {
tlsConfig.CurvePreferences = curves
}
// Set cipher suites
tlsConfig.CipherSuites = defaultCiphers()
if ciphers := toTLSCiphers(setting.SSLCipherSuites); len(ciphers) > 0 {
tlsConfig.CipherSuites = ciphers
}
tlsConfig := applyTLSSettings(&tls.Config{})
tlsConfig.Certificates = make([]tls.Certificate, 1)
certPEMBlock, err := os.ReadFile(certFile)

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