Commit Graph
21795 Commits
Author SHA1 Message Date
silverwind fb79898c28 fix(api): return language of starred and watched repositories (#40127) (#40157)
Backport https://github.com/go-gitea/gitea/pull/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 09:05:28 -08:00
96b2997c06 ci: use GitHub merge queue (#40130) (#40162)
Backport #40130 by @bircni

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: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-11-01 10:40:02 +00:00
Giteabotandsilverwind 4f5a3e13e5 fix(repo): don't get stuck generating contributor stats (#40125) (#40156)
Backport #40125 by @silverwind

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.

Co-authored-by: silverwind <me@silverwind.io>
v28.3.0
2026-11-01 01:20:24 -07:00
c50d5fc20d fix: list bot users in gitea admin user list and admin users API (#40060) (#40147)
Backport #40060 by @soujasK

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

The admin web UI lists every account type except organizations, but
`gitea admin user list` and `GET /api/v1/admin/users` only returned
individual users. That hid bots created for CI, even though the CLI and
the admin API can both create and manage them.

- The admin web UI, the admin API and the CLI share one type list:
individual, reserved, bot and remote accounts.
- `gitea admin user list` (and `--admin`) gains a `Type` column showing
`User`, `Bot`, `Reserved` or `Remote`.

Signed-off-by: Soujas K <kudchadkarsoujas@gmail.com>
Co-authored-by: soujasK <121026960+soujasK@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-11-01 07:27:30 +00:00
dfdb0438ad fix(auth): only auto-link the account the external identity points to (#40121) (#40137)
Backport https://github.com/go-gitea/gitea/pull/40121

With `[oauth2_client].ACCOUNT_LINKING = auto`, the link account sign-up
form linked the external identity to an existing account matching the
submitted user name or email and signed in as it. Automatic linking now
only happens when the existing user matches the external identity,
otherwise the linking login page asks for the account's password.

`release/v28` lacks `TestOAuth2AvatarFromPicture`, so only the new test
is added.

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 22:33:10 +00:00
7868ade8f7 perf(pull): load the repository once for all commit statuses (#39930) (#40135)
Backport #39930 by @lunny

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: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 19:12:36 +00:00
67904da6a5 perf: speed up repository issue and pull request lists (#40114) (#40132)
Backport #40114 by @silverwind

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: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-31 18:24:15 +01:00
Giteabotandsilverwind 6f5d66f6dd fix(pull): keep review body in sync when its comment is edited (#40058) (#40133)
Backport #40058 by @silverwind

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: silverwind <me@silverwind.io>
2026-10-31 18:21:03 +01:00
a1c56dd96a fix(projects): hide linked pull requests the viewer cannot read (#40016) (#40134)
Backport #40016 by @basavaraj-sm05

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: Basavaraj S m <basavaraj@digiscrypt.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: bircni <bircni@icloud.com>
2026-10-31 18:14:26 +01:00
5a3f06430d fix: harden context user access scope (#39945) (#40115)
Backport https://github.com/go-gitea/gitea/pull/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`.

`release/v28` lacks https://github.com/go-gitea/gitea/pull/39967, so
`/{username}` enables basic auth only for feed requests through a
middleware. The profile page token tests from
https://github.com/go-gitea/gitea/pull/39853 are folded into
`TestFeedUser`, because profile pages no longer accept tokens.

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-31 17:40:49 +01:00
bircniandsilverwind 6c8eaaf9e7 fix(actions): don't pick up jobs of repos with Actions disabled (#40106) (#40122)
Backport https://github.com/go-gitea/gitea/pull/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`.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 15:56:26 +00:00
GiteabotandLazizbek Ergashev c9e1805cc9 fix(pull): respect max approvers in default merge message (#40118) (#40119)
Backport #40118 by @lazerg

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

Co-authored-by: Lazizbek Ergashev <lazerg2@gmail.com>
2026-10-31 15:07:42 +00:00
Giteabotandsilverwind 5ba69f9af4 fix(actions): run review-triggered workflows on the pull request head (#40099) (#40117)
Backport #40099 by @silverwind

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.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 13:13:39 +00:00
e395b4d9dc fix(mail): preserve outer spacing in notification emails (#40104) (#40111)
Backport #40104 by @0xJacky

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: Jacky <jacky-943572677@qq.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-31 09:29:48 +00:00
308dd93484 perf: speed up the issue and pull request dashboards and fix table indices (#40001) (#40096)
Backport #40001 by @silverwind

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: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-31 07:38:45 +00:00
3162dc4cbf fix(api): answer 409 when repository files already exist on disk (#40095) (#40098)
Backport #40095 by @hktitof

when a repository directory exists on disk without a database row, for
example after a failed delete, POST /user/repos answered 500 with an
empty message

the migrate endpoint already answers 409 for this error and the fork API
got the same treatment in https://github.com/go-gitea/gitea/pull/39909,
so the create handler now maps ErrRepoFilesAlreadyExist to 409 too. this
covers POST /user/repos, POST /orgs/{org}/repos and the admin user route
since they all go through CreateUserRepo

the integration test creates the leftover directory and asserts 409, on
master it gets 500. runs on sqlite as usual

this also touches routers/api/v1/repo/repo.go, which several open PRs
modify, but none of them changes this error mapping

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: Abdellatif Anaflous <62770500+hktitof@users.noreply.github.com>
Co-authored-by: hktitof <hktitof@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 22:26:30 +01:00
51736a9706 fix(pull): refuse review comments on locked pull requests (#39960) (#40084)
Backport https://github.com/go-gitea/gitea/pull/39960

Locking a pull request stops users without write access from commenting,
but the review paths never checked the lock, so any signed-in user could
still post reviews, code comments and replies on a locked pull request.

The cherry-pick conflicted on the `repo-diff.ts` imports, because
`release/v28` has no `DiffFileTreeLocale`.

Co-authored-by: Basavaraj S m <basavaraj@digiscrypt.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 19:36:51 +00:00
silverwind 40079c7c09 fix(ui): ignore code line anchors below 1, show JS errors in vite dev mode (#39432) (#40087)
Backport https://github.com/go-gitea/gitea/pull/39432

- A `#L0` URL hash made `selectRange` index the line list at -1 and
throw inside `initRepoCodeView`, which aborted every init function after
it on the page. Line numbers below 1 now select nothing.
- The JS error filter only accepted stacks under the asset prefix, but
the vite dev server serves the sources from `/web_src/`, so dev mode
dropped every error without showing a banner. Outside production, the
filter now also accepts stacks from `/web_src/`.
2026-10-30 19:07:17 +00:00
Giteabot 2b11ba861f fix(repo): honor ALLOW_FORK_INTO_SAME_OWNER for organizations (#40025) (#40094)
Backport #40025 by @somaz94

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
Co-authored-by: somaz <112675579+somaz94@users.noreply.github.com>
2026-10-30 18:06:59 +00:00
9ad9f99060 fix(user): answer the follow of a bot user with the profile card (#39980) (#40086)
Backport https://github.com/go-gitea/gitea/pull/39980

Following a bot user from its profile succeeded but answered with an
error, because the follow handler only rendered a response for
individuals and organizations.

The cherry-pick conflicted in `OwnerProfile`, which still serves the
user feeds on `release/v28`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 07:30:13 +00:00
0964106988 fix: add missing access checks and size limits (#39942) (#40085)
Backport https://github.com/go-gitea/gitea/pull/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`

`release/v28` lacks two helpers this needs, so they are included:
`insertInvalidWorkflowRun`, split out of `handleInvalidWorkflows` by
https://github.com/go-gitea/gitea/pull/39480, and
`packages.NewLimitedReader` from
https://github.com/go-gitea/gitea/pull/39366.

Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-30 08:08:32 +01:00
silverwindandBasavaraj S m 8cf0f9798f fix(feed): require code read access for the repo home branch feed (#39964) (#40067)
Backport https://github.com/go-gitea/gitea/pull/39964

The repo home URL served the branch or file commit feed for RSS or Atom
`Accept` headers without the code unit check of the explicit
`/rss/branch/*` routes, exposing commit messages and authors to users
without code access.

Co-authored-by: Basavaraj S m <basavaraj@digiscrypt.com>
2026-10-29 10:04:51 +00:00
5d737d660b perf(actions): is_reusable_caller index makes SQLite read nearly every job row (#40055) (#40064)
Backport #40055 by @myers

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: Myers Carpenter <myers@maski.org>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-29 07:40:30 +01:00
silverwind 5be73fa93a fix: size mermaid iframe by the diagram's aspect ratio (#40046) (#40052)
Backport https://github.com/go-gitea/gitea/pull/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. Diagrams that don't scale (`useMaxWidth: false`) keep
the fixed height.

The cherry-pick conflicted only on the `srcdoc` line, which uses an
`html` template on `main` but a plain string on `release/v28`.

Fixes: https://github.com/go-gitea/gitea/issues/40045
2026-10-28 19:10:25 +00:00
silverwindandGiteabot 3ed5886066 fix(deps): update module golang.org/x/net to v0.62.0 [security] (#40050) (#40051)
Backport https://github.com/go-gitea/gitea/pull/40050

`release/v28` is still on x/net v0.60.0, so the cherry-pick conflicted.
x/net v0.62.0 raises the minimum x/crypto, x/sync, x/sys, x/text and
x/tools versions, which `go get` and `make tidy` bump to what `main`
already uses.

Co-authored-by: Giteabot <teabot@gitea.io>
2026-10-28 09:57:35 +01:00
12b5af068f fix(repo): use the viewed ref's editorconfig in file view, blame and editor (#39973) (#40019)
Backport #39973 by @Hello-Liar

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: Hello-Liar <80651389+Hello-Liar@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-26 22:39:01 +00:00
Giteabotandleo 8e83bb0e61 fix(markup): keep commit title emoji inside links (#39986) (#40032)
Backport #39986 by @Hello-Liar

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

Verified failing-before/passing-after native, shortcode, custom, and
emoji-only cases; markup/template tests and Go lint pass.
Browser-checked actual renderer output; blue outlines show link
boundaries.


![Before](https://github.com/user-attachments/assets/f56a4607-3265-4536-9fa2-ecc297dada6f)

![After](https://github.com/user-attachments/assets/7e0ad96d-6732-4d41-b7c6-cc28e90b747a)

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

Co-authored-by: leo <80651389+Hello-Liar@users.noreply.github.com>
2026-10-26 20:07:13 +00:00
Giteabotandpetervavrinecz 2042caf945 fix(issue): prevent redirect to external tracker when built-in tracker is enabled (#39997) (#40031)
Backport #39997 by petervavrinecz

Signed-off-by: Vavrinecz Péter <98645804+petervavrinecz@users.noreply.github.com>
Co-authored-by: petervavrinecz <98645804+petervavrinecz@users.noreply.github.com>
2026-10-26 19:38:30 +00:00
wxiaoguang 79189a959f fix: replace null char with space for issue/comment content (#40014) (#40034)
A simple workaround to fix
https://github.com/go-gitea/gitea/issues/40003

Backport of https://github.com/go-gitea/gitea/pull/40014
2026-10-27 03:14:59 +08:00
Giteabotandsilverwind 1b8a7c4d85 fix(git): default to the gc maintenance strategy (#40022) (#40029)
Backport #40022 by @silverwind

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.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-26 19:43:11 +01:00
dbe80ac05c fix: Fix OneDev migration with OneDev 16.8 and newer (#40017) (#40026)
Backport #40017 by @lunny

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: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-26 13:54:28 +00:00
silverwind 4b669ca620 fix(migrations): update gitea sdk to fix nil pointer panic (#39999)
`gitea.dev/sdk` v1.2.0 loses the error of a failed server version lookup
after the first call, so Gitea-to-Gitea migrations panic on the next
version check. v1.3.0 (https://gitea.com/gitea/go-sdk/pulls/855) keeps
returning the error. `main` already has v1.3.0 via
https://github.com/go-gitea/gitea/pull/39742.

Fixes: https://github.com/go-gitea/gitea/issues/39989
2026-10-24 23:57:14 +02:00
9d335e8783 fix(diff): load both blobs of renamed files for diff rendering (#39969) (#39992)
Backport #39969

Co-authored-by: schop-rob <49682405+schop-rob@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-24 14:05:09 +00:00
85a85878c7 fix: get valid UTF-8 for diff_hunk and composer readme (#39932) (#40000)
Backport #39932 by @fixfon

* Fixes #39929
* Fixes #39994

Co-authored-by: fixfon <12512757+fixfon@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-24 13:38:33 +00:00
ef2ceff6cd fix(actions): drop untrusted Content-Encoding from v1 artifact uploads (#39855) (#39996)
Backport #39855 by @basavaraj-sm05

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: Basavaraj S m <basavaraj@digiscrypt.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-24 15:06:46 +02:00
35a6f3d4e0 enhance(project): filter project milestones by linked issues (#39958) (#39984)
Backport #39958 by @SHIVANSHGARG07

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: Shivansh Garg <shivanshgarg587@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-24 06:24:46 +00:00
Giteabotandsilverwind cf3d325e45 ci(release): stop creating deployments for release tags (#39966) (#39971)
Backport #39966 by @silverwind

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.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-23 12:12:33 -07:00
Lunny Xiaoandsilverwind b140311d54 perf(actions): cache successful runner token verification (#39934) (#39959)
Backport of https://github.com/go-gitea/gitea/pull/39934 to release/v28.

Runner token verification (PBKDF2) dominated CPU on instances with many
runners. Successful verifications are now cached in a dedicated LRU.
Conflict resolved by keeping v28's connect v1 interceptor and adding
only the cache.

_Backport prepared with Codet._

Co-authored-by: silverwind <me@silverwind.io>
2026-10-23 02:36:43 -07:00
Giteabotandsilverwind 2b0aa6cfd2 docs: clarify that the team packages permission covers all org packages (#39953) (#39956)
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.

Co-authored-by: silverwind <me@silverwind.io>
2026-10-23 00:13:33 +00:00
silverwindandbircni 9d1d5ceddb fix(actions): bound requested artifact retention (#39894) (#39946)
`actions/upload-artifact` v4 and later send an absolute `expires_at`
computed with the runner's clock, which Gitea stored unchecked. A runner
clock running behind made artifacts expire at the next cleanup, and
workflows could keep artifacts longer than `ARTIFACT_RETENTION_DAYS`.

- Requested expiries are bounded by Gitea's clock, at least 1 day and at
most `ARTIFACT_RETENTION_DAYS` when it is not `0`. Longer
`retention-days` requests now get the instance limit.
- `github.retention_days` now carries `ARTIFACT_RETENTION_DAYS`, so
upload actions cap `retention-days` themselves and log a warning, like
on GitHub.

Co-authored-by: bircni <bircni@icloud.com>
2026-10-22 23:21:37 +00:00
silverwind 9141a6386f ci: update giteabot to v1.3.1 (#39947) (#39948)
Backport https://github.com/go-gitea/gitea/pull/39947

PRs targeting `release/v28` run this branch's `giteabot.yml`, and
`v1.3.1` runs the merge queue when such a PR merges, so the branch needs
the same pin. `giteabot-backport.yml` gets it too to keep the branches
in sync.
2026-10-22 22:56:30 +00:00
a814cf7567 fix(api): adding issue labels by name attaches both the repo and the org label (#39873) (#39951)
Backport #39873 by @nazem-droubi

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.

With this change the repo label is used when the names collide, and org
labels are only looked up for the names that didn't match a repo label.
Names that only exist at the org level still resolve to the org label as
before. I documented the behavior on `IssueLabelsOption.Labels` and
regenerated the swagger files.

Picking the repo label seemed the least surprising choice since it's the
more specific one, but I'm happy to change it (for example to return a
422 on ambiguous names) if you'd prefer that.

Tests: added `TestAPIAddIssueLabelsWithSharedRepoAndOrgLabelName`, which
covers both POST and PUT. It fails on `main` (3 labels attached instead
of 2) and passes with this change. I ran the existing add/replace label
integration tests locally on sqlite, plus `go vet` and gofmt.

AI disclosure: the code and test in this PR were written primarily by an
AI coding agent working on my behalf. I'm happy to walk through it or
revise anything during review.

Signed-off-by: Nazem <214302554+nazem-droubi@users.noreply.github.com>
Co-authored-by: Nazem <nazem98@gmail.com>
Co-authored-by: Nazem <214302554+nazem-droubi@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 21:40:40 +00:00
GiteabotandBasavaraj S m 2fd12d5f76 fix(feed): hide release notes in tags feed without releases access (#39944) (#39949)
Backport #39944 by @basavaraj-sm05

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.

Co-authored-by: Basavaraj S m <basavaraj@digiscrypt.com>
2026-10-22 14:06:27 -07:00
640b528580 fix(migrations): use cursor pagination for GitHub issue migration too (#39888) (#39941)
Backport #39888 by @yuhongwei380

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
Assisted-by: Codex:GPT-6

Signed-off-by: Phoenix <44333391+yuhongwei380@users.noreply.github.com>
Co-authored-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 13:33:39 +00:00
3e5256b6ca fix(api): check unit read permission in issue sub-resource endpoints (#39885) (#39938)
Backport #39885 by @basavaraj-sm05

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: Basavaraj S m <basavaraj@digiscrypt.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-22 05:27:46 -07:00
3996c2c520 fix(issues): keep one row per issue when sorting by exclusive label order (#39364) (#39928)
Backport #39364 by @shoemoney

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

This base-branch update was prepared with AI assistance.

Co-authored-by: Jeremy Schoemaker <jeremy@shoemoney.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 00:22:16 -07:00
716295015b fix(oauth2): decode form-encoded client credentials in HTTP Basic auth (#39926) (#39931)
Backport #39926 by @fixfon

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: fixfon <12512757+fixfon@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-22 06:22:15 +00:00
Giteabotandsilverwind f50a5d0dab test(e2e): enable async clipboard testing pref in Firefox (#39838) (#39927)
Backport #39838 by @silverwind

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
Co-authored-by: silverwind <me@silverwind.io>
2026-10-21 22:14:01 -07:00
Shudhanshu Singh bc3e228e78 fix(api): keep milestone due date when it is omitted in edit request (#39869) (#39921)
Backport https://github.com/go-gitea/gitea/pull/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.

Only the milestone fix is backported. The issue and pull request
deadline changes of https://github.com/go-gitea/gitea/pull/39869 build
on https://github.com/go-gitea/gitea/pull/38677, which isn't on v28.
2026-10-21 17:59:21 +02:00
193c676946 fix(lfs): send the default branch as ref in batch download requests (#39891) (#39920)
Backport #39891

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

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Shudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-10-21 10:59:03 +00:00