Backport #37327 by @prettysunflower
Nyallo~
In pull request #36901, a change is made so that the link to
authentication sources is now escaped with the QueryEscape filter.
https://github.com/go-gitea/gitea/pull/36901/changes#diff-34c39c9736a8b62e293c0c0b24c4b5b8c1c792790018c5809f9ff2cbc12b16b1R4
The problem is that [QueryEscape replace spaces with the `+`
character](https://cs.opensource.google/go/go/+/refs/tags/go1.26.2:src/net/url/url.go;l=234;drc=917949cc1d16c652cb09ba369718f45e5d814d8f),
and this is not unescaped when a user tries to log in with an
authentication source that contains a space, which throws an error.
This commit fixes that by unescaping the provider name in the URL.
---
Example of the error, on my instance, when I try to log in with
`prettysunflower's auth`
```
2026/04/21 00:11:41 routers/web/auth/oauth.go:42:SignInOAuth() [E] SignIn: oauth2 source not found, name: "prettysunflower's+auth"
/go/src/code.gitea.io/gitea/routers/web/auth/oauth.go:42 (0x2cfa5c5)
/usr/local/go/src/reflect/value.go:586 (0x51e245)
/usr/local/go/src/reflect/value.go:369 (0x51d0f8)
/go/src/code.gitea.io/gitea/modules/web/handler.go:181 (0x1a6aaf6)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:188 (0x1a6ab65)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:188 (0x1a6ab65)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:188 (0x1a6ab65)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/services/context/context.go:217 (0x2df1b23)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/gitea.com/go-chi/session@v0.0.0-20251124165456-68e0254e989e/session.go:258 (0x197eb82)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/chain.go:31 (0x1a61d05)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:479 (0x1a64fae)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:73 (0x1a628c2)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:321 (0x1a6421a)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/chain.go:31 (0x1a61d05)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:479 (0x1a64fae)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/middleware/get_head.go:37 (0x2c33a67)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:73 (0x1a628c2)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:321 (0x1a6421a)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/routers/common/maintenancemode.go:50 (0x2b752da)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/chain.go:31 (0x1a61d05)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:479 (0x1a64fae)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/routing/logger_manager.go:124 (0x127d1ec)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/chi-middleware/proxy@v1.1.1/middleware.go:37 (0x2b76acf)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/routers/common/middleware.go:89 (0x2b78cd6)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/routers/common/middleware.go:104 (0x2b7890f)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/src/code.gitea.io/gitea/modules/web/handler.go:145 (0x1a6afb5)
/usr/local/go/src/net/http/server.go:2286 (0x94dc88)
/go/pkg/mod/github.com/go-chi/chi/v5@v5.2.5/mux.go:90 (0x1a62881)
/go/src/code.gitea.io/gitea/modules/web/router.go:286 (0x1a6d2a2)
/go/src/code.gitea.io/gitea/modules/web/router.go:221 (0x1a6cbc6)
/usr/local/go/src/net/http/server.go:3311 (0x96e36d)
/usr/local/go/src/net/http/server.go:2073 (0x94bd6f)
/usr/local/go/src/runtime/asm_amd64.s:1771 (0x49af20)
```
Signed-off-by: prettysunflower <me@prettysunflower.moe>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: prettysunflower <me@prettysunflower.moe>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Backport #37311 by @silverwind
## Problem
Workflow-level concurrency groups were evaluated — and jobs were parsed
— before the run was persisted, so `run.ID` was `0` and `github.run_id`
in the expression context resolved to an empty string. Expressions like:
```yaml
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
```
collapsed to `<workflow>-` on every push event (`head_ref` is empty on
push), so `cancel-in-progress` cancelled in-progress runs across
**unrelated branches**, not just the current one.
Reproduced on a 1.26 instance:
- push to `master` → `ci` run starts
- push to `feature-branch` → the `master` run gets cancelled
GitHub Actions' documented semantic: on push events `github.run_id` is
unique per run, so the group is unique → no cancellation; on PR events
`github.head_ref` is the source branch → cancellation is per-PR.
## Fix
Insert the run **before** parsing jobs or evaluating workflow-level
concurrency, so `run.ID` is populated in time for every expression that
reads `github.run_id` — not just the concurrency group, but also
`run-name`, job names, and `runs-on`.
`jobparser.Parse` now runs inside the `InsertRun` transaction, after
`db.Insert(ctx, run)`. Workflow-level concurrency evaluation runs next
and only mutates `run` in memory. All concurrency-derived fields
(`raw_concurrency`, `concurrency_group`, `concurrency_cancel`) plus
`status` and `title` are persisted in a single final `UpdateRun` at
end-of-transaction — one `INSERT` + one `UPDATE` per run in both the
concurrency and non-concurrency paths (matches pre-branch parity, one
fewer `UpdateRepoRunsNumbers` `COUNT` than the interim state).
`GenerateGiteaContext` now sets `run_id` from `run.ID` unconditionally;
every caller passes a persisted run.
**Verification**: tested end-to-end on a 1.26 deployment. Before the
patch, two successive `ci` pushes (one to master, one to a feature
branch) cross-cancelled each other. After the patch, the same pushes —
in both orders (master→branch, branch→master) — run to completion
simultaneously across 15+ runs with zero cancellations.
**Regression tests** in `services/actions/context_test.go`:
- `TestEvaluateRunConcurrency_RunIDFallback` — unit check that
`EvaluateRunConcurrencyFillModel` resolves `github.run_id` from
`run.ID`.
- `TestPrepareRunAndInsert_ExpressionsSeeRunID` — full-flow check: calls
`PrepareRunAndInsert` with `${{ github.run_id }}` in both `run-name` and
the concurrency group, then asserts the persisted `Title`,
`ConcurrencyGroup`, and `RawConcurrency` contain / survive the run's ID.
Re-ordering `db.Insert` relative to either parse or concurrency eval
fails this test.
## Relation to #37119
[#37119](https://github.com/go-gitea/gitea/pull/37119) also moves
concurrency evaluation into `InsertRun` but keeps it **before**
`db.Insert`, then tries to populate `run_id` only when `run.ID > 0` —
which is still `0` at that call site, so the cross-branch leak would
survive that PR as written. This PR fixes the ordering so that `run.ID`
is actually populated at eval time, and broadens it to cover parse-time
expression interpolation too.
---
This PR was written with the help of Claude Opus 4.7
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Backport #37325 by @lunny
Fix a bug the checkout command line hint become `git fetch -u
https://gitea.combircni/tea`
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Backport #37279 by @silverwind
Moves the manifest patching from `closeBundle` to `writeBundle`. Thrown
errors in `writeBundle` work correctly and exit the build.
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: wxiaoguang <wxiaoguang@gmail.com>
Backport #37290 by wxiaoguang
Fix#37289
Don't tell container client that the instance needs basic auth if the
public access is available.
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37189 by @bircni
If a workflow is not in default branch the hooks could not be detected
Fixes#37169
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Backport of go-git upgrade to v5.18.0 for the v1.26 release branch.
Fixes GHSA-3xc5-wrhm-f963 (credential exposure on HTTP redirects).
---
This PR was written with the help of Claude Opus 4.6
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Backport #37244 by @JoeGruffins
Patterns starting with "/" (e.g. /docs/.*\.md) never matched because git
returns relative paths without a leading slash. Strip the leading "/"
before compiling the regex since the ^...$ anchoring already provides
root-relative semantics.
closes#28107
Co-authored-by: JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37256 by wxiaoguang
1. Make sure OmitEmail won't panic
2. SSH principal keys are not for signing or authentication
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37193 by @Mohit25022005
fix(api): handle missing base branch in PR commits API
Closes#36366
Previously, the PR commits API returned a 500 Internal Server Error
when the base branch was missing due to an unhandled git "bad revision"
error.
This change:
- Checks for base branch existence before performing git operations
- Returns 404 when the base branch does not exist
- Prevents git errors from surfacing as 500 responses
This improves API robustness and aligns behavior with expected error
handling.
Tested locally by:
- Creating a pull request
- Deleting the base branch
- Verifying that the API returns 404 instead of 500
Co-authored-by: Mohit Swarnkar <mohitswarnkar13@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Backport #37190 by @bircni
`url.PathEscape` unnecessarily encodes ! to %21, causing Matrix
homeservers to reject the request with 401. Replace %21 back to ! after
escaping.
Fixes#36012
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37172 by @xingxing21
Fixes: https://github.com/go-gitea/gitea/issues/36677
The fix is a template-only change in
[templates/repo/diff/compare.tmpl:16-25](vscode-webview://1ca9j6f1e3qtaf59o0cr4ind65ulf8mevvbbbq88int1gg2lncar/templates/repo/diff/compare.tmpl#L16-L25).
Before, the display names were built with conditional logic:
All four variables defaulted to just the owner name (Examples)
$HeadCompareName was only upgraded to owner/repo format in two narrow
cases:
Same org, different repos
A root repo exists with the same owner as the head repo
All other cases (same-repo PRs, cross-org PRs) got owner-only labels
After, all four variables are unconditionally set to owner/repo format
using printf "%s/%s":
```
- {{$BaseCompareName := printf "%s/%s" $.BaseName $.Repository.Name -}}
- {{- $HeadCompareName := printf "%s/%s" $.HeadRepo.OwnerName $.HeadRepo.Name -}}
- {{- $OwnForkCompareName := "" -}}
- {{- if .OwnForkRepo -}}
- {{- $OwnForkCompareName = printf "%s/%s" .OwnForkRepo.OwnerName .OwnForkRepo.Name -}}
- {{- end -}}
- {{- $RootRepoCompareName := "" -}}
- {{- if .RootRepo -}}
- {{- $RootRepoCompareName = printf "%s/%s" .RootRepo.OwnerName .RootRepo.Name -}}
- {{- end -}}
+ {{$BaseCompareName := $.Repository.FullName -}}
+ {{$HeadCompareName := $.HeadRepo.FullName -}}
+ {{$OwnForkCompareName := "" -}}
+ {{if $.OwnForkRepo -}}
+ {{$OwnForkCompareName = $.OwnForkRepo.FullName -}}
+ {{end -}}
+ {{$RootRepoCompareName := "" -}}
+ {{if $.RootRepo -}}
+ {{$RootRepoCompareName = $.RootRepo.FullName -}}
+ {{end -}}
```
These variables drive the labels in the base and head branch selector
buttons and their dropdown items. No backend changes were needed —
$.BaseName, $.Repository.Name, $.HeadRepo.OwnerName, and $.HeadRepo.Name
were already available in the template context.
Co-authored-by: Xing Hong <39619359+xingxing21@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37185 by @Mohit25022005
Fix 500 error when comparing branches across fork repositories
## Problem
The compare API returns a 500 Internal Server Error when comparing
branches where the head commit exists only in the fork repository.
## Cause
The API was using the base repository's GitRepo and repository context
when converting commits. This fails when the commit does not exist in
the base repository, resulting in a "fatal: bad object" error.
## Solution
Use the head repository and HeadGitRepo when available to ensure commits
are resolved in the correct repository context.
## Result
* Fixes "fatal: bad object" error
* Enables proper comparison between base and fork repositories
* Prevents 500 Internal Server Error
Fixes#37168
Co-authored-by: Mohit Swarnkar <mohitswarnkar13@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37175 by @silverwind
The `Run As Username` field on the install page was a `readonly` input
that looked editable but wasn't, confusing users. Style `readonly`
inputs with a subtle background, matching other frameworks.
<img width="735" height="131" alt="image"
src="https://github.com/user-attachments/assets/cb76ce71-faab-4300-811e-e4c503b59f9a"
/>
Backport #37180
The comment "so just use current one if config says default" is not
right anymore: "git" isn't the "default" value of RunUser (Comment out
app.example.ini #15807). The RunUser's value is from current session's
username.
Fixes#37174
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37173 by @silverwind
Remove CSS rules whose HTML classes/IDs are no longer referenced in any
template, Go source, or JavaScript/TypeScript file:
- `.archived-icon`: removed from templates in c85bb62635
- `.bottom-line`: removed from blame rendering in 9c6aeb47f7
- `.commit-status-link`: removed from templates in f3c4baa84b
- `.instruct-toggle`: removed from templates in 75e85c25c1
- `.runner-new-text`, `#runner-new`: never referenced outside CSS
- `.ap-terminal`: stale, asciinema-player uses `.ap-term`, still not
needed
- `.scrolling.dimmable.dimmed`: dimmer stand-in never adds this class
- `.markup span.align-center/align-right/float-left/float-right`: never
produced by any renderer, sanitizer strips class attributes
- `.markup ul.no-list`, `.markup ol.no-list`: same as above
---
This PR was written with the help of Claude Opus 4.6
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37159 by @silverwind
`TestCatFileBatch/QueryTerminated` relied on timing to distinguish
`os.ErrClosed` vs `io.EOF` error paths. Replace `time.Sleep`-based
synchronization with a channel-based hook on pipe close, making both
error paths fully deterministic regardless of CI runner speed.
Ref:
https://github.com/go-gitea/gitea/actions/runs/24193070536/job/70615366804
---
This PR was written with the help of Claude Opus 4.6
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #36085 by @eliroca
When authentication is handled externally by a reverse proxy SSO
provider, users can be redirected to an external logout URL or relative
path defined on the reverse proxy.
Co-authored-by: Elisei Roca <eroca@suse.de>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37162 by @silverwind
When running `golangci-lint` without `GOEXPERIMENT=jsonv2`, a lint error
`import 'encoding/json' is not allowed` is seen.
All other files in the module that import `encodings/json` have
`//nolint` already, so add it.
---
This PR was written with the help of Claude Opus 4.6
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Backport #37130. Only one merge conflict in lockfile.
---
This PR was written with the help of Claude Opus 4.6
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Backport #37116 by @bircni
`model.ReadWorkflow` succeeds for YAML that is syntactically valid but
fails deeper parsing in `jobparser.Parse` (e.g. blank lines inside `run:
|` blocks cause a SetJob round-trip error). Add
`ValidateWorkflowContent` which runs the full `jobparser.Parse` to catch
these cases, and use it in the file view, the actions workflow list, and
the workflow detection loop so users see the error instead of silently
getting a 500 or a dropped workflow.
Fixes#37115
Signed-off-by: Nicolas <bircni@icloud.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #37148 by @silverwind
1. Filter out errors that contain `chrome-extension://` etc protocols
2. Extract filtering into its own function and test it
3. Fix the `window.config.assetUrlPrefix` mock, guaranteed to end with
`/assets`
4. Remove useless `??` and `?.` for properties that always exist
---
This PR was written with the help of Claude Opus 4.6
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
* Fix#37128
* Manually tested with various cases (issue, pr) X (close, reopen)
* Fix#36792
* Fix the comment
* Fix#36755
* Add a "sleep 3"
* Follow up #36697
* Clarify the "attachment uploading" problem and function call
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
`LoadSettingsForInstall` only ran `loadMailerFrom`, not
_loadRegisterMailFrom_ or _loadNotifyMailFrom_, so
Service.RegisterEmailConfirm and Service.EnableNotifyMail were never
read from app.ini on the install page.
Full startup runs those through loadMailsFrom; the install path was a
narrower subset and never included that step—an oversight from when
install-specific loading was added
Fixes#37112
Workflow run, job, task, and step durations could show **negative**
values (e.g. `-50s`) when `Stopped` was missing, zero (epoch), or
**before** `Started` (clock skew, races, reruns). The UI used
`calculateDuration` with no validation.
This change:
- Uses each row`s **Updated** timestamp as a **fallback end time** when
`Stopped` is invalid but the status is terminal, so duration
approximates elapsed time instead of `0s` or a negative.
- Keeps **`ActionRun.Duration()`** clamped to **≥ 0** when
`PreviousDuration` plus the current segment would still be negative
(legacy bad data).
Fixes#34582.
Co-authored-by: Composer <composer@cursor.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Follow-up to #37078.
- Use Unicode Control Pictures](U+2400-U+2421) to render C0 control characters
- Make it work in diff view too
- Replace escape warning emoji with SVG
- Align escape warning button with code lines
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Unties settings page from package version and adds button to delete the
package version
Settings page now allows for deletion of entire package and it's
versions as opposed to a single version
Adds an API endpoint to delete the entire package with all versions from
registry
fixes: https://github.com/go-gitea/gitea/issues/36904
Co-Authored-By: gemini-3-flash
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Keep `swagger` and `external-render-helper` as a standalone entries for
external render.
- Move `devtest.ts` to `modules/` as init functions
- Make external renders correctly load its helper JS and Gitea's current theme
- Make external render iframe inherit Gitea's iframe's background color to avoid flicker
- Add e2e tests for external render and OpenAPI iframe
---------
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>