4884 Commits
Author SHA1 Message Date
Lunny Xiaoandsilverwind e867cfbf0d fix(repo): rank exact repository name matches first (#39038)
The quick repository switcher orders results alphabetically, so a
repository whose name exactly matches the query can appear below names
that merely contain it.

- Rank exact repository name matches first when no explicit sort order
is requested
- For "owner/repo" queries, rank the exact pair first, then the owner's
other repositories
- Match the owner of "owner/repo" queries case-insensitively, mixed-case
owners were missed

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-11 19:01:43 +00:00
silverwind 03fc2bdcc8 fix(repo): require the unit in AccessibleRepositoryCondition (#40059)
`AccessibleRepositoryCondition(user, unitType)` now requires the repo to
have the requested unit enabled and not globally disabled, consistent
with the per-repo permission checks. This affects code search, Actions
run listings, LFS object reuse, recently pushed branches and org project
milestones.

Written by Claude.
2026-11-11 12:55:04 +00:00
wxiaoguangandsilverwind d1d702bea9 refactor: user error system (#40308)
Introduce "FlashErrorAuto" for "XxxErrorAuto" family and simplify the
error handling logic.

* Use ErrorUnwrapForHTML for web error messages 
* Use ErrorUnwrapForUser for api error messages
* Make Flash message can take up to about 3000 bytes

Next step: completely refactor "ctx.ServerError"

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-11-10 16:29:36 +00:00
Shivansh Gargandwxiaoguang 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
Lunny Xiaoandsilverwind 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
Lunny Xiaoandsilverwind 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
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
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
Lazizbek Ergashevandsilverwind 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
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 Heheandsilverwind 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
silverwindandwxiaoguang 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
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
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
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 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 Gargandsilverwind 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
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](https://gitea.com/gitea/docs/src/commit/955ae7d75c3377a15fafe465d85e484fc8dc7d2a/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
Lazizbek Ergashevandsilverwind 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
silverwindandwxiaoguang 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
Myers Carpenterandsilverwind 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
wxiaoguangandsilverwind 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
Jon Fullerandwxiaoguang 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
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
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
silverwindandwxiaoguang 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
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
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
Lunny Xiaoandsilverwind 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
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
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
silverwindandGiteabot 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
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
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
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 Gargandsilverwind 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
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
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
silverwindandwxiaoguang 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
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
Lunny Xiaoandwxiaoguang 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
Myers Carpenterandwxiaoguang 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
Jorgeandwxiaoguang 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
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
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
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
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
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