Use the new "form-fetch-action" for better user experience, and use
JSONError to show error messages.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This PR simplifies URL validation by removing `IsValidExternalURL` and
`IsAPIURL` from `modules/validation/helpers.go` and switching repository
settings/API callers to `IsValidURL`.
It also aligns tracker-format validation and tests with the new helper
surface.
- **Validation helpers**
- Removed `IsValidExternalURL` and `IsAPIURL`.
- Updated `IsValidExternalTrackerURLFormat` to depend on `IsValidURL`.
- **Caller updates**
- Replaced `validation.IsValidExternalURL(...)` with
`validation.IsValidURL(...)` in:
- `routers/web/repo/setting/setting.go`
- `routers/api/v1/repo/repo.go`
- **Tests**
- Removed tests dedicated to `IsValidExternalURL`.
- Updated tracker-format test expectations to match `IsValidURL`-based
behavior.
```go
// before
if !validation.IsValidExternalURL(form.ExternalTrackerURL) { ... }
// after
if !validation.IsValidURL(form.ExternalTrackerURL) { ... }
```
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com>
Follow up #37327. See the comments.
* Root problem: the design of OAuth2 providers is a mess, the display
name is used as provider's name and used in the URL directly
* The regressions:
* When trying to fix https://github.com/go-gitea/gitea/issues/36409 , it
introduced inconsistent URL escaping for the "path" part.
* This fix: always use "path escaping" for the path part, add more tests
to cover all escaping cases.
Now, frontend "pathEscape" and "pathEscapeSegments" generate exactly the
same result as backend.
The link to authentication sources is now escaped with the QueryEscape.
This commit fixes that by unescaping the provider name in the URL.
---------
Signed-off-by: prettysunflower <me@prettysunflower.moe>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
While editing frontend, I found some inconsistencies while testing
transferring repositories:
- No button for accepting/rejecting/cancelling the transfer of an empty
repository.
- The `redirect_to` in `templates/repo/header.tmpl` is useless.
- There's no redirection when there's an error from `handleActionError`
in `routers/web/repo/repo.go`. Therefore, instead of flash message, a
blank page will be displayed.
This pr adds some commits to resolve all these issues.
Update: see the new changes
https://github.com/go-gitea/gitea/pull/37277#issuecomment-4276150232
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Introduces a frontend external-render framework that runs renderer
plugins inside an `iframe` (loaded via `srcdoc` to keep the CSP
`sandbox` directive working without origin-related console noise), and
migrates the 3D viewer and OpenAPI/Swagger renderers onto it. PDF and
asciicast paths are refactored to share the same `data-render-name`
mechanism.
Adds e2e coverage for 3D, PDF, asciicast and OpenAPI render paths, plus
a regression for the `RefTypeNameSubURL` double-escape on non-ASCII
branch names.
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Use `golangci-lint fmt` to format code, replacing the previous custom
formatter tool. https://github.com/daixiang0/gci is used to order the
imports.
`make fmt` performs ~13% faster while consuming ~57% less cpu while
formatting for me.
`GOFUMPT_PACKAGE` is gone because it's using the builtin package from
golangci-lint.
Co-authored-by: Claude (claude-opus-4-6) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This PR adds an External ID Claim Name configuration field to the OIDC
auth source. When set, Gitea uses the specified JWT claim as the user's
`ExternalID` instead of the default `sub` claim.
This PR fixes the bug when migrating from Azure AD V2 to OIDC. When an
admin migrates the same auth source to OIDC, goth's `openidConnect`
provider defaults to using the `sub` claim as `UserID`. However, Azure
AD's `sub` is a pairwise identifier:
> `sub`: The subject is a pairwise identifier and is unique to an
application ID. If a single user signs into two different apps using two
different client IDs, those apps receive two different values for the
subject claim.
https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#payload-claims
As a result, every existing user appears as a new account after
migration.
To fix this issue, Gitea should use `oid` claim for `UserID`.
> `oid`: This ID uniquely identifies the user across applications - two
different applications signing in the same user receives the same value
in the oid claim.
Note: The `oid` claim is not included in Azure AD tokens by default. The
`profile` scope must be added to the Scopes field of the auth source.
Close#35059
Slightly improved the "fetch action" framework and started adding tests for it.
---------
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
The only remaining (hard) part is "templates/repo/editor/edit.tmpl", see the FIXME
By the way:
* Make "user unfollow" use basic color but not red color, indeed it is not dangerous
* Fix "org folllow" layout (use block gap instead of inline gap)
This is the first step (the hardest part):
* repo file list last commit message lazy load
* admin server status monitor
* watch/unwatch (normal page, watchers page)
* star/unstar (normal page, watchers page)
* project view, delete column
* workflow dispatch, switch the branch
* commit page: load branches and tags referencing this commit
The legacy "data-redirect" attribute is removed, it only makes the page
reload (sometimes using an incorrect link).
Also did cleanup for some devtest pages.
`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: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
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: wxiaoguang <wxiaoguang@gmail.com>
That logic is from 2014~2015, it unclear why it is necessary or
whether it is still needed (whether Windows is still special)
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.
1. Fix the "flash message" layout problem for different cases
* I am sure most of the users should have ever seen the ugly
center-aligned error message with multiple lines.
2. Fix inconsistent "Details" flash message EOL handling, sometimes
`\n`, sometimes `<br>`
* Now, always use "\n" and use `<pre>` to render
3. Remove SanitizeHTML template func because it is not useful and can be
easily abused.
* But it is still kept for mail templates, for example:
https://github.com/go-gitea/gitea/issues/36049
4. Clarify PostProcessCommitMessage's behavior and add FIXME comment
By the way: cleaned up some devtest pages, move embedded style block to
CSS file
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: wxiaoguang <wxiaoguang@gmail.com>
`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: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.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>
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>
1. `origin-url` was introduced in the past when there was no good
framework support to detect current host url
* It is not needed anymore
* Removing it makes the code clearer
2. Separate template helper functions for different templates (web
page/mail)
3. The "AppURL" info is removed from admin config page: it doesn't
really help.
* We already have various app url checks at many places
Fixes#37086, fix the bug in MatchPath, and swap the order of
overlapping routes in api.go to make it look better.
---------
Signed-off-by: Rohan Guliani <rohansguliani@google.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Quick fix for 1.26.
* Slightly refactor NewComment to fix incorrect responses, remove
incorrect defer (still far from ideal)
* Avoid `const` causes js error in global scope
* Don't process markup contents on user's home activity feed, to avoid
js error due to broken math/mermaid code
* Fix#36582
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Follow up #36842
Migration `326` can be prohibitively slow on large instances because it
scans and rewrites all commit status target URLs generated by Gitea
Actions in the database. This PR refactors migration `326` to perform a
partial update instead of rewriting every legacy target URL. The reason
for this partial rewrite is that **smaller legacy run/job indexes are
the most likely to be ambiguous with run/job ID-based URLs** during
runtime resolution, so this change prioritizes that subset while
avoiding the cost of rewriting all legacy records.
To preserve access to old links, this PR introduces
`resolveCurrentRunForView` to handle both ID-based URLs and index-based
URLs:
- For job pages (`/actions/runs/{run}/jobs/{job}`), it first tries to
confirm that the URL is ID-based. It does so by checking whether `{job}`
can be treated as an existing job ID in the repository and whether that
job belongs to `{run}`. If that match cannot be confirmed, it falls back
to treating the URL as legacy `run index + job index`, resolves the
corresponding run and job, and redirects to the correct ID-based URL.
- When both ID-based and index-based interpretations are valid at the
same time, the resolver **prefers the ID-based interpretation by
default**. For example, if a repository contains one run-job pair
(`run_id=3, run_index=2, job_id=4`), and also another run-job pair
(`run_id=1100, run_index=3, job_id=1200, job_index=4`), then
`/actions/runs/3/jobs/4` is ambiguous. In that case, the resolver treats
it as the ID-based URL by default and shows the page for `run_id=3,
job_id=4`. Users can still explicitly force the legacy index-based
interpretation with `?by_index=1`, which would resolve the same URL to
`/actions/runs/1100/jobs/1200`.
- For run summary pages (`/actions/runs/{run}`), it uses a best-effort
strategy: by default it first treats `{run}` as a run ID, and if no such
run exists in the repository, it falls back to treating `{run}` as a
legacy run index and redirects to the ID-based URL. Users can also
explicitly force the legacy interpretation with `?by_index=1`.
- This summary-page compatibility is best-effort, not a strict ambiguity
check. For example, if a repository contains two runs: runA (`id=7,
index=3`) and runB (`id=99, index=7`), then `/actions/runs/7` will
resolve to runA by default, even though the old index-based URL
originally referred to runB.
The table below shows how valid legacy index-based target URLs are
handled before and after migration `326`. Lower-range legacy URLs are
rewritten to ID-based URLs, while higher-range legacy URLs remain
unchanged in the database but are still handled correctly by
`resolveCurrentRunForView` at runtime.
| run_id | run_index | job_id | job_index | old target URL | updated by
migration 326 | current target URL | can be resolved correctly |
|---|---|---|---|---|---|---|---|
| 3 | 2 | 4 | 1 | `/user2/repo2/actions/runs/2/jobs/1` | true |
`/user2/repo2/actions/runs/3/jobs/4` | true |
| 4 | 3 | 8 | 4 | `/user2/repo2/actions/runs/3/jobs/4` | true |
`/user2/repo2/actions/runs/4/jobs/8` | true (without migration 326, this
URL will resolve to run(`id=3`)) |
| 80 | 20 | 170 | 0 | `/user2/repo2/actions/runs/20/jobs/0` | true |
`/user2/repo2/actions/runs/80/jobs/170` | true |
| 1500 | 900 | 1600 | 0 | `/user2/repo2/actions/runs/900/jobs/0` | false
| `/user2/repo2/actions/runs/900/jobs/0` | true |
| 2400 | 1500 | 2600 | 0 | `/user2/repo2/actions/runs/1500/jobs/0` |
false | `/user2/repo2/actions/runs/1500/jobs/0` | true |
| 2400 | 1500 | 2601 | 1 | `/user2/repo2/actions/runs/1500/jobs/1` |
false | `/user2/repo2/actions/runs/1500/jobs/1` | true |
For users who already ran the old migration `326`, this change has no
functional impact. Their historical URLs are already stored in the
ID-based form, and ID-based URLs continue to resolve correctly.
For users who have not run the old migration `326`, only a subset of
legacy target URLs will now be rewritten during upgrade. This avoids the
extreme runtime cost of the previous full migration, while all remaining
legacy target URLs continue to work through the web-layer compatibility
logic.
Many thanks to @wxiaoguang for the suggestions.
Fixes: #36846
1. When there is only on OAuth2 login method, automatically direct to it
2. Fix legacy problems in code, including:
* Rename template filename and fix TODO comments
* Fix legacy variable names
* Add missing SSPI variable for template
* Fix unnecessary layout, remove garbage styles
* Only do AppUrl(ROOT_URL) check when it is needed (avoid unnecessary
warnings to end users)
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Add an optional Name field to webhooks so users can give them
human-readable labels instead of relying only on URLs. The webhook
overview page now displays names when available, or falls back to the
URL for unnamed webhooks.
Fixes#37025
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
- Replace monaco-editor with CodeMirror 6
- Add `--color-syntax-*` CSS variables for all syntax token types,
shared by CodeMirror, Chroma and EasyMDE
- Consolidate chroma CSS into a single theme-independent file
(`modules/chroma.css`)
- Syntax colors in the code editor now match the code view and
light/dark themes
- Code editor is now 12px instead of 14px font size to match code view
and GitHub
- Use a global style for kbd elements
- When editing existing files, focus will be on codemirror instead of
filename input.
- Keyboard shortcuts are roughtly the same as VSCode
- Add a "Find" button, useful for mobile
- Add context menu similar to Monaco
- Add a command palette (Ctrl/Cmd+Shift+P or F1) or via button
- Add clickable URLs via Ctrl/Cmd+click
- Add e2e test for the code editor
- Remove `window.codeEditors` global
- The main missing Monaco features are hover types and semantic rename
but these were not fully working because monaco operated only on single
files and only for JS/TS/HTML/CSS/JSON.
| | Monaco (main) | CodeMirror (cm) | Delta |
|---|---|---|---|
| **Build time** | 7.8s | 5.3s | **-32%** |
| **JS output** | 25 MB | 14 MB | **-44%** |
| **CSS output** | 1.2 MB | 1012 KB | **-17%** |
| **Total (no maps)** | 23.3 MB | 12.1 MB | **-48%** |
Fixes: #36311Fixes: #14776Fixes: #12171
<img width="1333" height="555" alt="image"
src="https://github.com/user-attachments/assets/f0fe3a28-1ed9-4f22-bf25-2b161501d7ce"
/>
---------
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>