Commit Graph
100 Commits
Author SHA1 Message Date
bircni 70a5ff725c enhance(oauth): pre-register a builtin OAuth2 application for gitea-vscode (#39785)
The [Gitea VS Code extension](https://gitea.com/gitea/gitea-vscode)
currently signs in through the builtin `tea` OAuth2 application. Gitea
keeps one grant per user and application, so anyone who has already
signed in with the tea CLI gets `a grant exists with different scope`
when the extension requests its own scopes, and sign-in fails
(gitea/gitea-vscode#24).

This adds a dedicated builtin application for the extension, following
the same mechanism as the existing CLI entries:
2026-10-16 06:48:13 +00:00
bircni 4b5f1f46de enhance(actions): show the steps the runner reports (#39720)
Gitea derives the steps of a job from the workflow file, so the `pre`
and `post` steps of actions only show up inside "Set up job" and
"Complete job".
Fixes https://github.com/go-gitea/gitea/issues/24604.

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

Related:
- Proto: https://gitea.com/gitea/actions-proto-def/pulls/19
- Runner: https://gitea.com/gitea/runner/pulls/1293
2026-10-12 22:36:25 +02:00
bircniandwxiaoguang 8a9e8c4e8c fix(actions): harden log cursor, activity type and actor filter handling (#39721)
Fixes a few Actions bugs where user input or nondeterminism caused
failures:

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

---------

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

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

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-10-10 18:21:28 +00:00
c30ddc3e14 refactor(actions): small cleanups across models, services and routers (#39722)
Collection of small, behaviour-preserving cleanups in the Actions code.

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

---------

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

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

---------

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

Co-authored-by: silverwind <me@silverwind.io>
2026-10-07 03:08:08 +02:00
43fedd662a ci(release): automate signed release tags and release notes (#39544)
Automate release tagging as proposed in
https://github.com/go-gitea/gitea/pull/39544#issuecomment-5955939142,
part of https://github.com/go-gitea/gitea/issues/39550.

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

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


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

---------

Co-authored-by: bircni <bircni@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-06 05:21:07 +00:00
bircni 66546045b5 fix(actions): refresh reusable caller status when children are skipped (#39589) 2026-10-05 17:58:42 +02:00
bircni 7641fc3a8c fix(actions): restore pushes to protected branches (#39564)
Use the Actions token's loaded write permission when checking
protected-branch pushes. Preserve push and force-push allowlists and add
regression coverage.

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

Fixes: https://github.com/go-gitea/gitea/issues/39542
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-10-03 17:22:07 +00:00
bircni e0095af8c3 ci: Also release for other versions than 1 majors (#39475) 2026-09-29 21:47:12 +02:00
bircni 5a96cc454a docs: Add changelog for 28.0.0 (#39474) 2026-09-29 19:06:21 +00:00
bircniandsilverwind f44e64be81 fix(actions): harden fork pull request run approval (#39399)
Fixes several gaps in the approval of fork pull request runs:

1. Approving a run that was cancelled while awaiting approval revived
its cancelled jobs. Such a run is no longer treated as awaiting approval
by the merge box, run page, approve actions and API, and rerunning it
approves it.
2. Approval no longer revives jobs cancelled while the run was pending,
no longer lets two jobs sharing a concurrency group cancel each other,
and re-emits the run so jobs needing a cancelled job get resolved.
3. An unapproved run applies its workflow-level concurrency only once
approved.
4. For workflows from the pull request, both the event actor and the
pull request author must be trusted to skip approval. Workflows from the
default branch, like `issue_comment`, still only check the actor.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-26 22:52:19 +00:00
3875db1974 feat(actions): add build queue view (#38585)
Adds a read-only Actions job queue: running jobs first, then waiting
jobs in the order a runner picks them up. It is shown instance-wide in
the admin Actions section with owner, repository and status filters, and
per repository in the Actions tab. Both lists refresh in place.

Pending work is currently only visible per repository and newest-first,
so nothing shows what is queued, in which order, or what occupies the
runners. Reordering the queue will be proposed separately.

A migration adds indexes for the runner pickup query and
repository-scoped status lookups.

* Fix #34198

<img width="1345" height="451" alt="image"
src="https://github.com/user-attachments/assets/7d52ff76-81b4-44e8-b583-d7d89c9dffcd"
/>
<img width="1809" height="1134" alt="image"
src="https://github.com/user-attachments/assets/4d56c0cb-bae7-4ce2-8f3c-75163b2bc7f4"
/>

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-26 08:01:41 +00:00
5a56e118e4 feat(actions): Add artifact preview in Actions run view (#36754)
Closes https://github.com/go-gitea/gitea/issues/33579.

Adds browser previews for Actions artifacts. Selecting an artifact opens
its file browser; selecting a file renders it in the same tab. The ZIP
download remains available separately.

Previews require sign-in and read access to the run. Text, image and PDF
files are supported; rendered HTML and JavaScript run in a sandboxed
frame and are labeled as automatically generated. The frame loads files
from a signed link that expires after an hour, because its requests
carry no session cookie. `[actions] ARTIFACT_PREVIEW_MAX_SIZE` limits
total previewable artifact size (`0` disables previews; `-1` removes the
limit); individual files also follow `[ui] MAX_DISPLAY_FILE_SIZE`.

<img width="1803" height="913" alt="image"
src="https://github.com/user-attachments/assets/a38fd704-2244-44fa-9181-c695ecbe0276"
/>

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

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-09-26 07:42:36 +00:00
bircniandsilverwind d42128d71d fix: preserve SNI for local internal API (#39412)
https://github.com/go-gitea/gitea/pull/38406 stopped setting
`ServerName` on the internal API client, which
https://github.com/go-gitea/gitea/pull/5820 had added for ACME. Internal
requests to a local `LOCAL_ROOT_URL` now send SNI `localhost` (or none
for IPs). The ACME listener selects its certificate by SNI, finds none
and aborts the handshake with `tls: internal error`, breaking SSH access
and git hooks.

Send the `ROOT_URL` host as SNI again for local targets, and treat
unspecified addresses (`0.0.0.0`, `::`) as local since dialing them
reaches the local host. Remote targets are still verified against their
own hostname.

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

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-09-25 13:46:52 +00:00
bircniandsilverwind 05f049e8bb fix(issues): don't render team review requests as Ghost (#39416)
Closes https://github.com/go-gitea/gitea/issues/39414

`CommentList.loadAssignees` set a Ghost assignee on every comment
without an `AssigneeID` whenever another comment in the same batch had
one. Since https://github.com/go-gitea/gitea/pull/38413 the timeline
prefers `Assignee` over `AssigneeTeam`, so team review requests, from
CODEOWNERS or added manually, rendered as "Ghost" whenever the timeline
also had a user assignee or user review request. Skip comments without
an `AssigneeID`.

Co-authored-by: silverwind <me@silverwind.io>
2026-09-24 19:01:34 +00:00
da37b7916b feat: Add audit logging (#38189)
Co-authored-by: bircni <bircni@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-09-12 08:15:23 +00:00
bircni 8ef7a9e46d docs: Update CHANGELOG for release 1.27.3 (#39170) 2026-08-30 12:53:19 +00:00
3c0bfe9463 fix(actions): run every due schedule exactly once per occurrence (#39078)
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-29 06:44:51 +00:00
bircniandsilverwind 3b6ce586a7 fix(actions): keep step-level continue-on-error expressions unevaluated (#39141)
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-27 05:46:46 +00:00
bircniandwxiaoguang e21c37703e fix(repo): centralize repository-scoped authorization (#39063)
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-26 20:12:53 +08:00
38747d48fe fix(pull): keep the merged state in sync with git (#39062)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-26 01:04:49 +08:00
bircniandsilverwind 3a806a58d0 fix(attachments): enforce owning repository path (#39048)
Reject attachment requests routed through a repository other than the
attachment owner.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-24 17:32:23 +00:00
bircniandsilverwind 0bed1232ee fix(packages): restrict limited owner package access (#39043)
Apply restricted-viewer visibility rules when resolving package access.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 15:57:51 +08:00
bircniandsilverwind adc3db1f27 fix(api): enforce organization listing token scope (#39041)
Enforce organization token scope before listing organizations and retain
public-only filtering.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 07:23:16 +00:00
bircniandsilverwind 55a5f50961 fix(actions): enforce workflow badge token scope (#39044)
Apply repository token-scope and public-only checks to workflow badges.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 06:59:58 +00:00
bircniandsilverwind 204c0bafd3 fix(repo): require organization owners for team access (#39046)
Require organization ownership before changing repository team
associations when team access is restricted.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-23 06:35:55 +00:00
bircniandsilverwind bedd2afb47 fix(org): hide limited organizations from restricted users (#39047)
Do not expose limited organization memberships to restricted viewers.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 23:11:04 -07:00
e6af4c341c fix(repo): preserve transfer recipient collaboration (#39042)
Remove temporary recipient access after a transfer ends while preserving
existing collaboration.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 19:43:42 +00:00
9251eeb66b fix(markup): enforce same-repository issue access (#39045)
Enforce Issues and Pull Requests access for references within the
current repository.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 19:13:53 +00:00
bircniandsilverwind 5fc3cec87f fix(actions): verify raw artifact signatures first (#39049)
Validate raw-artifact signatures before resolving the requested
artifact.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 18:47:55 +00:00
bircniandwxiaoguang 89c7019a3c fix(repo): limit gitignore template selections (#39027)
Bound gitignore template selections at both web and API request
boundaries before repository initialization.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-22 10:03:17 +00:00
bircniandsilverwind 6bb6ce678b fix(api): hide limited users from restricted viewers (#39004)
Use the canonical profile-visibility check for user API content and
prevent restricted users from enumerating public repositories owned by
limited users.

This keeps feeds, heatmaps, keys, and issue search consistent with
profile visibility.

---------

Co-authored-by: silverwind <me@silverwind.io>
2026-08-22 08:08:36 +00:00
bircni 1f349eb1eb fix(migrations): cancel GitLab version probes (#39023)
Bind the GitLab version probe to the migration context so a cancelled
migration does not remain blocked on a remote response.
2026-08-22 01:54:07 +00:00
bircni 5eba4f92ce fix(migrations): bound OneDev version responses (#39024)
Limit OneDev version responses before parsing so a remote server cannot
make a migration retain an unbounded response.
2026-08-22 00:54:20 +00:00
bircni aa96725ae7 fix(packages): limit Swift package manifests (#39025)
Bound the number and aggregate size of Swift manifests retained from an
uploaded archive.
2026-08-22 00:11:29 +00:00
bircni fa4c0b1cf6 fix(packages): limit Maven checksum uploads (#39028)
Bound checksum uploads to the maximum usable digest length before
buffering their content.
2026-08-21 23:41:12 +00:00
bircni 2b81cbbe70 fix(packages): bound Alpine metadata entries (#39026) 2026-08-22 01:17:04 +02:00
bircni ad05aaee80 fix(actions): enforce fork pull request trust boundaries (#39005)
Preserve fork pull request restrictions across review-triggered
workflows, reusable workflow access, job scheduling, and filtered
workflow statuses.

This prevents untrusted fork workflow content from bypassing approval,
accessing private reusable workflows, or satisfying protected status
checks.


_Assisted-by: Codex:GPT-5_
2026-08-21 12:35:28 +00:00
bircni a52e5f53c0 fix(git): restrict hook permissions (#39008)
Create delegate hook files and directories without group or other write
access, including correcting existing hook directories.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:52:23 +00:00
bircni db24633e6d fix(api): enforce repository creation token authorization (#39007)
Reject public-only tokens for repository migrations and require
repository scope for canonical organization repository creation. This
aligns both routes with the existing token authorization boundaries.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:28:23 +00:00
bircni 920b5f1e68 fix(api): enforce public-only scope for compare heads (#39006)
Enforce public-only token scope for repositories resolved as compare
heads.

_Assisted-by: Codex:GPT-5_
2026-08-21 07:06:56 +00:00
bircni 7306d5aff8 fix(repo): hide repositories of hidden owners (#39009)
Exclude public repositories owned by hidden individual accounts from
broad repository listings, while preserving visibility through explicit
access and ownership.

_Assisted-by: Codex:GPT-5_
2026-08-21 06:32:57 +00:00
bircni ffd982c7ab fix(actions): show "Complete job" logs when the last step is skipped (#38939)
`FullSteps` only gave the synthetic "Complete job" step the remaining
log range when the last step that had run was also the final step of the
job. A skipped step does not count as having run, so any job ending in a
skipped step left the post step with an empty range: its logs were
stored but never rendered, and the duration showed as `0s`.

Reproducible with any job whose last step is skipped, which is common
for failure notifications:

```yaml
steps:
  - run: echo hello
  - run: echo never
    if: failure()
```

The gate now checks whether the final step is done, which preserves the
behaviour from https://github.com/go-gitea/gitea/pull/29926 of showing
the post step as waiting while steps are still pending.
--> Regression from https://github.com/go-gitea/gitea/pull/29926

---------

Signed-off-by: bircni <bircni@icloud.com>
2026-08-20 19:06:21 +00:00
bircniandwxiaoguang e8e1973e16 fix: allow anonymous theme switching when REQUIRE_SIGNIN_VIEW is set (#38956)
Fixes https://github.com/go-gitea/gitea/issues/38950


Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-18 04:02:52 +00:00
bircniandwxiaoguang ed4a23e893 enhance: inherit team access for all units (#38938)
Admin and write team authorize now grant that mode on every unit,
including units added later, instead of only rows present in
`team_unit`. Granular teams keep `authorize=none` and explicit unit
rows.

Closes the `TEAM-UNIT-PERMISSION` design gap from
https://github.com/go-gitea/gitea/pull/34128.

Maybe also fix #15962 (actually maybe it had been fixed before, the root
cause is out-of-sync "access" table)


## Screenshots

only writing selected:
<img width="1399" height="1007" alt="image"
src="https://github.com/user-attachments/assets/1d1b4c49-a59a-47b6-998f-0464a067395b"
/>


_Created with the help of AI_

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 20:30:24 +00:00
bircniandwxiaoguang 1cf904f101 feat(repo): add quick repository switcher to repo header (#38188)
Add a GitHub-style quick repo switcher: a caret next to the owner/repo
breadcrumb
opens a dropdown that lists and searches the current owner's
repositories and
navigates to the selected one. The current repository is marked with a
check, and
private/fork repos show an icon.

Also, fix various bugs in fomtantic dropdown remote query

## Screenshots

<img width="505" height="198" alt="image"
src="https://github.com/user-attachments/assets/9f673d1b-fe60-41f0-b9e2-b00dc43720b5"
/>

Fixes #38187

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-17 20:08:15 +00:00
bircni 2b8ea5476c fix(ui): respect FEED_PAGING_NUM on the dashboard feed (#38935)
The dashboard activity feed was paginated with `[ui.user]
REPO_PAGING_NUM`
instead of `[ui] FEED_PAGING_NUM`.

The wrong setting was picked up when the page size was hoisted into a
local
variable in https://github.com/go-gitea/gitea/pull/34994, most likely
copied
from the `dashboardRepoList` block a few lines above. `REPO_PAGING_NUM`
should
only control repository lists.

Fixes https://github.com/go-gitea/gitea/issues/38925
2026-08-15 19:31:10 +08:00
bircni a96a73c364 docs: Update CHANGELOG for version 1.27.2 (#38923)
Signed-off-by: bircni <bircni@icloud.com>
2026-08-14 22:06:05 +02:00
bircniandwxiaoguang dbe311197c enhance(admin): show impersonation banner and keep password change with the user (#38924)
Follow-up to https://github.com/go-gitea/gitea/pull/38614

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-14 22:23:30 +08:00
01e9febbea fix(actions): keep github.event.inputs as strings for workflow_dispatch (#38899)
`github.event.inputs` must mirror the raw `workflow_dispatch` payload,
where
GitHub keeps every input as a string. Only the separate `inputs` context
preserves declared types, e.g. booleans. A previous fix coerced boolean
inputs in the single map that fed both contexts, so
`github.event.inputs.someBool` became a real boolean and comparisons
like
`== 'true'` stopped matching.

`github.event.inputs` now stays string-only again. The `inputs` context
used
for server-side `if:` evaluation of needs-gated/matrix-deferred jobs
re-coerces booleans independently, from the job's own workflow
declaration,
so that path keeps working correctly.

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

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-08-13 09:36:41 +02:00
bircniandZettat 8161479fde fix(actions): resolve pull_request_target reusable workflows at the base commit (#38886)
For a `pull_request_target` (PRT) run, Gitea loads the top-level
workflow from the trusted base branch, but any local reusable workflow
it calls (`uses: ./...`) was read from the PR **head** commit, which the
fork author controls.

## Fix

**Record the source commit where the content is read.**
`DetectedWorkflow` now carries a `SourceCommitSHA` filled in next to
`Content`, so the PRT detection pass at the base commit records the base
SHA automatically.

**Defense in depth.** `loadReusableWorkflowSource` pins the PR base
commit for a PRT run's local `uses: ./...` rather than trusting the
stored SHA. This also covers runs recorded before this change, whose
rows still hold the head SHA and would otherwise resolve from the fork
on rerun.

Existing run rows are not migrated.

---------

Co-authored-by: Zettat <zettat123@gmail.com>
2026-08-12 19:03:44 +02:00
bircniandwxiaoguang 2551f9949a enhance(repo): add default object format setting (#38877)
Adds `[repository] DEFAULT_OBJECT_FORMAT` to default new repositories to
`sha1` or `sha256`.

Applies the setting to repository creation defaults in the UI and API,
reducing repeated manual selection.

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

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

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-08-12 16:12:57 +00:00
bircni 300331313b docs: Adjust Changelog to include all Security relevant details (#38876)
Signed-off-by: bircni <bircni@icloud.com>
2026-08-12 11:56:51 +00:00
11d0ed699b fix(actions): dynamic matrix expansion correctness fixes (#38690)
Follow-up to https://github.com/go-gitea/gitea/pull/36564 (dynamic
matrix) and https://github.com/go-gitea/gitea/pull/36357 (max-parallel),
fixing issues found reviewing the two features together.

- **A placeholder could stall its run forever.** Its payload keeps the
raw matrix but loses its `needs`, so `ParseJob` re-expanded it instead
of reading it back — fatal for `include: ${{ fromJson(needs.*.outputs.*)
}}`.
- **An `if:` reading `matrix.*` skipped the whole job**, with or without
the `${{ }}`. It now reduces to the needs gate, except under
`always()`/`failure()`/`cancelled()`, and each combination is decided on
its own values once the matrix expands.
- **Dependents could be skipped before the combinations ran**, since
inserted siblings are absent from the resolver's job set. The pass now
stops after an insert and defers to the re-emit it schedules.
- **Expansion failures stranded the placeholder.** A retryable one is
returned so the queue retries it; a malformed payload fails the job
instead of requeueing forever.
- **Rerun could rewind a pass-through row** into a raw placeholder
keeping its old terminal status, which nothing expands. Now gated on the
anchor itself.

Plus: `max-parallel` distinguishes an unevaluated `${{ }}` (debug) from
a non-numeric literal (warn — it silently drops the cap).

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-30 09:13:47 +00:00
bircniandsilverwind a12634fd8d docs: Update Changelog for release v1.27.1 (#38670)
Add changelog for version 1.27.1 with security, API, enhancements, bug
fixes, build updates, and miscellaneous changes.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-27 12:31:04 -07:00
1b18b25c8e fix(actions): cancel tasks immediately when the runner stopped reporting (#38616)
Fixes jobs that get stuck in `cancelling` after Gitea is restarted while
a job is running.

Reproduction:

1. Run a job with `sleep 100`
2. Stop Gitea and wait 120s (> 100s)
3. Start Gitea — the job is still `running`; cancel it, and it stays in
`cancelling`

## Cause

A cancellation is only ever delivered to a runner as the *response* to
its `UpdateTask`
RPC. A runner that has already given up on the task — it crashed, or it
failed to report
the final state while Gitea was unreachable — never calls `UpdateTask`
again, so it never
learns about the cancellation. The task then sits in `cancelling` until
`stop_zombie_tasks`
reaps it, which needs `ZOMBIE_TASK_TIMEOUT` (10m) of silence and only
runs every 5 minutes.

Cancelling actually made this worse. xorm rewrites the `updated` column
on every `UPDATE`,
even when `Cols()` restricts the update to `status`, so persisting the
`cancelling` status
reset the zombie clock. Pressing cancel pushed the cleanup a full
`ZOMBIE_TASK_TIMEOUT`
into the future instead of bringing it forward.

## Change

`StopTask` now skips the `cancelling` handshake when the task has had no
state report from
its runner for longer than `TaskReportTimeout` (1 minute) and cancels it
directly. This
joins the two existing fallbacks — runner deleted, and runner without
cancelling support —
so every cancel path (web UI, API, concurrency, rerun) is covered.

Runners report the state of a running task every few seconds, so a
minute of silence means
the runner is gone. The value is a constant rather than a setting
because it only decides
whether the runner is still reachable, not whether a task should be
killed —
`ZOMBIE_TASK_TIMEOUT` still owns that.

### Tradeoff

If a runner is alive but has been silent for over a minute and is
cancelled in that window,
it skips the graceful post-step cleanup added in #37275. No work is
lost: the runner still
learns the outcome on its next report, because `UpdateTask` returns the
task status as the
result and the runner stops there. That is the behaviour that existed
before #37275.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2026-07-26 13:50:06 +00:00
bircniandZettat123 396ec646f6 fix(actions): improve runner list status sorting, labels and task job links (#38586)
Several small fixes to the Actions runner management UI.

### Runner task list links to the job, not the workflow run
Relabeled the first column from "Run" to "Job"; it now shows the job ID
and links to the specific job (`/actions/runs/{runID}/jobs/{jobID}`).
Renamed locale key `task_list.run` to `task_list.job`.

### Missing "Disabled" translation
The runner list rendered a grey label via `actions.runners.disabled`,
but that key
did not exist in `locale_en-US.json`, so the raw key string leaked into
the UI.
Replaced `"actions.runners.disabled"` with `"disabled"`.

### Status column sorting ignored active vs idle
Sorting by status ordered purely on `last_online`, but the displayed
status is
computed from both `last_online` (offline) and `last_active` (idle vs
active).
As a result idle runners were interleaved with active ones. Sorting now
ranks by
the computed status (active → idle → offline). Disabled runners sink to
the bottom
of their status group (`is_disabled` as a secondary key), with
`last_online`/`id`
as stable tiebreakers so pagination stays deterministic.

### Status label colors
Active and idle both rendered green. Idle is now yellow, active green,
and
offline/unknown grey; the separate grey "Disabled" badge is unchanged.
This keeps
connectivity visible even for disabled runners (e.g. a disabled runner
still shows
whether it is idle or offline).

<img width="715" height="406" alt="image"
src="https://github.com/user-attachments/assets/9ef06aa8-a870-4de5-9d94-603a58186908"
/>

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-25 21:31:19 +00:00
69f0a10364 fix(actions): correctness and hardening fixes (#38518)
Various fixes to actions

1. **Cap total jobs per run in reusable-workflow expansion** — only
nesting depth was capped, so fan-out + nested reusable workflows could
explode job-row inserts and exhaust the DB from a single push. Now
enforces `MaxJobNumPerRun` in the insert path.
2. **Reject rerun-failed when a run has no failed jobs** — an empty job
list meant "re-run everything", so `rerun-failed` on a green run re-ran
all jobs. Now errors (web + API).
3. **Don't adopt external commit statuses into the legacy hash** — the
pre-#35699 Context-only hash matched API-posted statuses too, collapsing
two same-named workflows into one check. Now limited to Actions-user
rows.
4. **Don't cut post-cancel cleanup short in `StopEndlessTasks`** — the
sweep force-stopped just-cancelled jobs mid-cleanup. Now targets
`StatusRunning` only; stalled cancels stay covered by `StopZombieTasks`.
5. **Avoid redundant run reload in `GenerateGiteaContext`** — resolving
`github.triggering_actor` reloaded the run already passed in. Now loads
only the trigger user via new `ActionRunAttempt.LoadTriggerUser`.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-25 16:07:40 +00:00
608c248638 docs: Update Security docs (#38422)
Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
2026-07-24 21:28:35 +02:00
bircni e8befe0268 fix(actions): coerce workflow_dispatch boolean inputs to native types (#38472)
A `workflow_dispatch` job that has `needs:` **and** an `if:` comparing a
boolean
input against a boolean literal never runs — it stays `Blocked` forever
even
though its needs succeed. Minimal repro:

```yaml
on:
  workflow_dispatch:
    inputs:
      deploy:
        type: boolean
        default: true
jobs:
  build:
    runs-on: ubuntu-latest
    steps: [{ run: echo build }]
  deploy:
    needs: build
    if: ${{ inputs.deploy == true }}   # never true on Gitea
    runs-on: ubuntu-latest
    steps: [{ run: echo deploy }]
```

On GitHub this runs; on Gitea `deploy` is stuck. Jobs **without**
`needs` are
unaffected, which makes it look like a matrix/`needs` bug — it isn't.

## Root cause

`workflow_dispatch` stores boolean inputs as the strings
`"true"`/`"false"`
(`ctx.FormBool` → `strconv.FormatBool` in the web path, plain strings in
the API
path). Since #37478 (shipped in **v1.27.0**), `evaluateJobIf` runs
**server-side**
as part of the job-emitter resolver passes. For a `needs`-gated job the
server
therefore evaluates `inputs.deploy == true` with `inputs.deploy` being
the string
`"true"`; comparing a string to a boolean coerces to `NaN == 1` →
`false`, so the
job is never dispatched.

Jobs without `needs` skip this server-side gate and are evaluated by the
runner,
which coerces the string to a real bool — that's why they keep working,
and why
the same input yields opposite results in the two paths.

## Fix

Normalize `type: boolean` dispatch inputs to native JSON booleans in the
shared
`DispatchActionWorkflow` path, so it covers both the web and API entry
points in
one place. The coerced value flows into both the run's `EventPayload`
(read back
by the server-side `if` evaluation and by the runner) and the
creation-time
parse, so all consumers agree.

This matches GitHub, whose `inputs` context "preserves Boolean values as
Booleans
instead of converting them to strings", and mirrors the native JSON
types Gitea
already sends for `workflow_call`. Only booleans are coerced; other
input types
are left untouched, and a value that is already a bool (JSON API
request) passes
through.

Note: this makes dispatch payloads carry native booleans, which the
runner
consumes correctly as of gitea/runner#1087 (it accepts a native bool and
keeps
the string fallback) — the same expectation `workflow_call` already
relies on.

Fixes https://github.com/go-gitea/gitea/issues/38466
2026-07-18 17:08:09 +02:00
bircniandwxiaoguang ba7c84673b docs: Update Changelog for 1.27 (#38440)
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-17 15:31:40 +00:00
bircni 9d3f04eb7e fix(actions): explain why a blocked or waiting job has not started (#38476)
When an Actions job is blocked or waiting, the job view only shows the
generic **Blocked** / **Waiting** label. Users have no way to tell *why*
a job is stuck — whether it's waiting on dependencies, waiting for a
runner that doesn't exist, waiting for a runner whose labels don't
match, or simply queued behind busy runners.

## Change

The current-job detail line now surfaces the actual cause:

- **Blocked** → lists the dependency jobs (`needs`) that haven't
finished yet, e.g. *"Waiting for the following jobs to complete:
build."*
- **Waiting**, no online runner → *"No runner is online to pick up this
job."*
- **Waiting**, online runners but none match `runs-on` → *"No matching
online runner with label: X"* (reuses the existing string)
- **Waiting**, a matching runner exists but hasn't claimed the job →
*"Waiting for a matching runner to become available."*

The runner lookup reuses the same available-online-runner query the run
list already performs, and only runs while a selected job is actually
pending. Dependency resolution is scoped to the same parent job and
treats matrix expansions of a `need` as pending until all of them
complete.
2026-07-16 23:10:43 +02:00
bircniandwxiaoguang 02f82a2054 fix(actions): show retention info on hover for expired artifacts (#38477)
The artifact info hover tooltip (#37100) only worked for live artifacts.
Hovering an expired/deleted artifact showed nothing, and even on live
artifacts the retention date was missing on real runs.

Two root causes:

1. **Expired artifacts had no tooltip.** In the run view sidebar,
expired artifacts render in a separate branch that omitted
`data-tooltip-content` entirely, so there was nothing to hover.
2. **`ExpiresUnix` was no longer sent.** The `ActionRunAttempt` refactor
(#37119) dropped `ExpiresUnix` from `fillViewRunResponseArtifacts`, so
real runs always returned `0` and the tooltip fell back to size-only.
Only the devtest mock still populated it, which masked the regression.

Artifacts without a recorded expiry (`expiresUnix <= 0`) still degrade
gracefully to a size-only tooltip. Both states can be previewed on the
`/devtest/mock/*` actions run page.

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-16 15:58:31 +02:00
bircni 1850a108e5 fix(actions): group reusable-workflow matrix legs in the workflow graph (#38475)
Fixes the UI issue reported in #38466: matrix jobs that invoke a
reusable workflow (`build-call (linux)`, `build-call (windows)`, …) were
rendered as separate nodes in the run's workflow graph, while an
equivalent regular matrix
(`build (linux)`, `build (windows)`, …) collapsed neatly into a single
matrix node.

Before: three loose `build-call (...)` boxes next to one grouped `build` box.
After: both matrices collapse into a single node.
2026-07-16 20:58:05 +08:00
f69e15afe7 fix: various security fixes (#38406)
Addresses a batch of privately reported security issues, grouped by
area:

- **SSRF** - migration PR-patch/asset fetches, OAuth2 avatar & OpenID
discovery, pull-mirror URL re-validation, and the outbound proxy path.
- **Access-token scope** - prevent scope escalation on token creation;
keep public-only tokens confined (feeds, packages, Actions listings,
star/watch lists, limited/private owners).
- **Access control / disclosure** - go-get default-branch leak, webhook
authorization-header leak, watch clearing on private transitions,
label/attachment scoping.
- **Denial of service** - input bounds for npm dist-tags, Debian control
files, Arch file lists, and SSH keys.

### 📌 Attention for site admins

Not breaking - existing configs keep working - but two changes are worth
a look:

- **New SSRF protection** Outbound requests (migrations, OAuth2 avatars,
OpenID discovery, pull mirrors, proxy path) are now validated against
the allow/block host lists. If your instance legitimately reaches
internal hosts, you may need to add them to
`[security].ALLOWED_HOST_LIST` (and the relevant `ALLOW_LOCALNETWORKS`
settings).
- **Deprecation** `[webhook].ALLOWED_HOST_LIST` is deprecated and will
be removed in a future release. Use `[security].ALLOWED_HOST_LIST`
instead; the old key still works for now.

---------

Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-12 17:14:09 +00:00
bircniandwxiaoguang f803f8e269 fix(actions): keep workflow run trailing on one row with long branch names (#38382)
The flex-list refactor (#37505) raised the shared `.item-trailing`
selector's specificity, so its `flex-wrap: wrap` started overriding the
run list's intended `flex-wrap: nowrap` — a long branch name pushed the
trailing content past its fixed 280px width and wrapped the kebab menu
onto its own line.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-10 23:43:32 +02:00
bircni 8401fe7c54 fix(pull): re-evaluate review official flag on target branch change (#38319)
The `official` flag of a pull request review is computed against the
target branch's protection rules at submit time and stored on the review
record. `ChangeTargetBranch` updated the base branch but never
re-evaluated it, so an `official` approval obtained against an
unprotected branch could be retargeted onto a protected branch and
satisfy its required approvals, bypassing the branch protection.

This re-evaluates the `official` flag of the latest approve/reject
reviews against the new base branch whenever the target branch changes.
2026-07-10 19:18:47 +00:00
bircni aab3242f7b fix(security): harden access checks and migration validation (#38324)
Harden access checks for issue dependencies, team repository membership,
notifications, stars, tracked times and repository migrations.
2026-07-10 19:30:43 +02:00
bircni f452c369ac fix: enforce public-only token scope and harden push options / locale parsing (#38323)
- **Locale DoS:** the `Locale` middleware passed the raw
`Accept-Language` header to `ParseAcceptLanguage`, whose guard only
counts `-` while the scanner aliases `_` to `-` — a large `_`-separated
header on an unauthenticated request burned CPU. The header is now
length-bounded before parsing.
- **Public-only token scope:** `GET /teams/{id}/repos`,
`.../repos/{org}/{repo}`, `/teams/{id}/activities/feeds`, and
`/users/{username}/orgs/{org}/permissions` still returned private
repo/activity/permission data to a public-only token. They now filter
via `TokenCanAccessRepo` / `ApplyPublicOnly` and reject non-public org
permissions.
- **Push-option visibility:** `repo.private` / `repo.template` push
options were applied to any existing repo, letting an owner/admin
silently flip visibility bypassing audit, webhooks, and notifications.
They are now honored only on push-to-create.
2026-07-10 16:39:01 +00:00
bircni 362539b78e fix(api): stop leaking private repo metadata after access revocation (#38321)
The `/user/starred` and `/user/subscriptions` endpoints returned private
repositories a user had starred/watched even after their access to those
repositories was revoked, still exposing the repository name,
description and visibility (including later metadata changes).

Private repositories in the starred/watched queries are now gated on the
actor's current access via `AccessibleRepositoryCondition`, so users who
no longer have access no longer receive the metadata. Public
repositories and public-only tokens are unaffected.
2026-07-09 22:21:44 +00:00
bircni 66a3723cbb fix(lfs): require proof of possession for cross-repo objects (#38322)
The LFS batch and upload handlers linked an object that already existed
in the content store but was not linked to the current repo whenever the
token's user could access it in another repo. Deploy-key tokens carry
the repo owner's identity, so a single-repo write deploy key could link
and then download objects from any repo the owner can see.

This drops the cross-repo access check: the batch handler now makes the
client upload (hash-verified) any object not yet linked to the repo, and
the upload handler skips proof of possession only when the object is
already linked to the current repo.
2026-07-09 21:43:08 +00:00
bircni 27e33b7ba1 fix: incorrect co-author detection on commit page (#38386)
The commit page built its "co-authored by" list from
`AllParticipantIdentities()[1:]`, which only drops the author and leaves
the committer in the list even though the committer is already shown
separately as "committed by". This caused the committer to be wrongly
rendered as a co-author (issue #38384): a commit authored by
`silverwind`, committed by `bircni`, with a `Co-authored-by: silverwind`
trailer displayed "co-authored by bircni" instead of the actual trailer
identity. This adds a `Commit.CoAuthorIdentities()` method that excludes
both the author and the committer, uses it on the commit page, and
covers the fix with a regression test.

Closes #38384
2026-07-09 22:31:14 +02:00
bircniandZettat123 308a6f12ae perf(actions): debounce runner heartbeat writes and throttle task picks (#38281)
3 reductions in the DB load generated by many runners polling
`FetchTask`:

**1. Debounce runner heartbeat writes**
Every poll wrote `last_online`, and every `UpdateTask`/`UpdateLog` wrote
`last_active` — while a runner streams logs that is many writes per
second per runner. These are now persisted only when stale enough to
actually affect the active/offline status (`ShouldPersistLastOnline` /
`ShouldPersistLastActive`), using the existing columns.

**2. Throttle concurrent task picks**
A new in-process semaphore (`MAX_CONCURRENT_TASK_PICKS`) bounds how many
runners run the task-assignment transaction at once, so a fleet polling
together cannot stampede the query. Throttled polls retry on their next
poll without advancing the runner's tasks version.

**3. Paginate the task-pick query**
`CreateTaskForRunner` previously loaded every waiting job in the
runner's scope into memory on each poll (no `LIMIT`). Now it pages
through the waiting backlog oldest-first with `LIMIT`, claiming the
first label-matching job.

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
2026-07-07 19:16:20 +00:00
bircni 97078b96cf fix(mirror): disable HTTP redirects on pull mirror sync (#38320)
Pull mirror sync ran `git fetch` / `remote update` / `remote prune`
without disabling HTTP redirects. A mirror remote that later starts
redirecting to an otherwise-blocked or internal address could be used as
an SSRF/exfiltration vector on scheduled syncs, bypassing the
allow/block validation applied at migration time.

This sets `http.followRedirects=false` on all three remote-contacting
commands in the pull mirror path, matching the existing guard already
present on the clone path.
2026-07-07 17:35:21 +00:00
bircni d4c4142123 fix(actions): make runner list pagination order deterministic (#38313)
The runner-listing API endpoints (`admin`, `org`, `user`, `repo`, and
`shared`) return runners in a non-deterministic order across pages. When
paging through runners, some runners from page 1 could reappear on page
2 (and others get skipped entirely).

The cause is in `FindRunnerOptions.ToOrders()`. Most sort modes order by
a **non-unique** column only:

- default / `online` / `offline` → `last_online`
- `alphabetically` / `reversealphabetically` → `name`

When multiple runners tie on the sort key (e.g. every offline runner
shares `last_online = 0`, or two runners have the same name), the
database is free to return the tied rows in any order between separate
queries. Combined with `LIMIT`/`OFFSET` pagination, this means the same
runner can land on more than one page.

## Fix

Append the unique primary key `id` as a stable tiebreaker to each
non-unique sort order:

The `newest`/`oldest` modes already sort by the unique `id`, so they are
left unchanged.
2026-07-03 20:35:47 +00:00
bircni f7fd510224 fix(release): gate draft release attachments on web download endpoints (#38318)
Draft-release access control was enforced only on the API release
endpoints (`/api/v1/repos/{owner}/{repo}/releases/...`) but not on the
UUID-based web attachment endpoints (`/attachments/{uuid}`,
`/{owner}/{repo}/attachments/{uuid}`,
`/{owner}/{repo}/releases/attachments/{uuid}`).

Anyone who obtained an attachment UUID — including unauthenticated
callers — could download files belonging to a hidden draft release,
since `ServeAttachment` only checked repo-level read permission and
never the release's draft state.
2026-07-03 20:07:37 +02:00
bircni b6ef881a9f docs: Welcome Zettat to TOC (#38303) 2026-07-01 20:07:44 +00:00
bircniandwxiaoguang 458c11bd68 fix(actions): allow Actions bot to push to protected branches (#38284)
Fixes #38278

## Problem

When branch protection matches the branch an Actions workflow pushes to,
the runner's `git push` is rejected — even though the workflow token has
`contents: write` and the same push performed with a PAT (write access)
succeeds. Disabling protection or changing the pattern so it no longer
matches makes the push work.

## Root cause

In `preReceiveBranch` (`routers/private/hook_pre_receive.go`), the "can
the doer push to this protected branch" check resolves the pusher with
`user_model.GetUserByID(ctx, ctx.opts.UserID)`. For an Actions push the
user ID is `-2` (the virtual `ActionsUserID`), which has no database
row, so the lookup fails. Even past that, `CanUserPush` →
`HasAccessUnit`/whitelist membership cannot evaluate a virtual user and
returns `false`. As a result the Actions bot was rejected on every
matching protected branch, despite the earlier `assertCanWriteRef`
already confirming the token's code-write via
`GetActionsUserRepoPermission`.

This was inconsistent: a PAT with identical write access passed the
exact same check.

## Fix

Evaluate the Actions bot against its already-computed token permission
instead of a user lookup, mirroring the existing
`IsUserMergeWhitelisted` pattern:

- Add `CanActionsUserPush` / `CanActionsUserForcePush` on
`ProtectedBranch`, which take the precomputed `access_model.Permission`.
- Allow the push when push is enabled, **no** push whitelist is
enforced, and the token has code-write.
- Keep the bot blocked when a whitelist is enforced — it cannot be added
to one, so it must use a pull request. This preserves the whitelist as a
real security boundary.

Force-push, signed-commit and protected-file-path checks are untouched.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-07-01 09:19:47 +00:00
bircni d46d0540d0 fix(actions): include all aggregable run statuses in status filter (#38280)
The **Status** filter dropdown on the repository Actions run list does
not let you filter for **Blocked** runs (nor **Cancelled** or
**Skipped**). These statuses are missing from the dropdown even though a
run can legitimately end up in any of them.

A run's status is computed by `aggregateJobStatus`, which can return
`Blocked`, `Cancelled` and `Skipped`. Because the filter dropdown only
offered Success, Failure, Waiting, Running and Cancelling, runs in those
other states existed but were impossible to filter for.
2026-06-30 19:59:30 +00:00
bircni cc1df1976b fix: codemirror regressions (#38248) 2026-06-28 20:29:34 +02:00
bircni 1c718da16c fix(api): support HEAD requests on all API GET endpoints (#38245)
Fixes #38226

## Summary

Add `chi_middleware.GetHead` as the first `BeforeRouting` middleware on
the API router. This makes every API `GET` endpoint automatically handle
`HEAD` requests, as required by RFC 9110 §9.3.2.

Previously, `HEAD` requests to endpoints like `GET
/repos/{owner}/{repo}/git/commits/{sha}` returned `405 Method Not
Allowed`.

The web router already used this same middleware (see
`routers/web/web.go:261`), so this aligns API behaviour with the web
router.

## Changes

- `routers/api/v1/api.go`: add `chi_middleware.GetHead` middleware to
the API router
- `tests/integration/api_repo_git_commits_test.go`: add
`TestAPIReposGitCommitsHEAD` verifying HEAD returns 200 on a valid ref
and 404 (not 405) on a missing ref
2026-06-28 12:14:39 +00:00
bircniandwxiaoguang ce8cf22af9 fix(actions): don't swallow HTML entities into linkified URLs (#38239)
In the Actions log viewer, a double-quoted URL renders with a stray
extra `;` after it.

Reported in `gitea/runner#1046`

Remove the buggy AI slop `linkifyURLs` and use new approach to process
URLs in text

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-06-28 19:37:16 +08:00
bircni 1d43b736b5 fix(actions): deny fork-PR cross-repo access via collaborative owner (#38214)
### What

`GetActionsUserRepoPermission` (`models/perm/access/repo_permission.go`)
decides whether an Actions task token may access a target repo. Its
cross-repo branches each enforce a fork-PR discriminator — except the
collaborative-owner branch, which was missing the
`!task.IsForkPullRequest` guard that its sibling
`checkSameOwnerCrossRepoAccess` has.

As a result, when a private repo **B** lists owner **A** as a
collaborative owner, an attacker-controlled fork pull-request workflow
whose base repo is owned by A was granted code-read on B — i.e. the
fork's workflow could clone a third private repository it has no rights
to (read-only confidentiality breach).

### Fix

Add the same fork-PR guard the sibling path already enforces:

```go
if taskRepo.IsPrivate && !task.IsForkPullRequest {
    actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)
    if actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID) {
        return maxPerm, nil
    }
}
```
2026-06-28 10:25:56 +00:00
bircni 0319358e5e fix(web): Correctly align the "disabled" label on larger workflow names (#38240) 2026-06-28 07:58:29 +02:00
bircni 0f5102427e fix(actions): ensure all waiting jobs get runners in large workflows (#38200)
## Summary

Fixes two related bugs that cause jobs in large workflows (50+ parallel
jobs) to never get a runner assigned even though runners are free.

### Bug 1 — Concurrent runner race

When N runners all poll `FetchTask` with a stale `tasksVersion`
simultaneously, they all query the same waiting job list sorted by
`(updated, id)` and all pick **job #1**. Only one wins the `UPDATE WHERE
task_id=0` optimistic lock; the rest return empty-handed but still
receive `latestVersion` in the response. They then consider themselves
"up to date" and skip `PickTask` on every subsequent poll, leaving jobs
#2–50 permanently unassigned.

**Fix:** `CreateTaskForRunner` now iterates through all matching waiting
jobs. When the optimistic lock fails on job #1, it immediately tries job
#2, then #3, etc., each in its own independent transaction so a failed
attempt rolls back cleanly before the next candidate is tried.
`PickTask` no longer wraps this call in an outer `db.WithTx` (which
caused `halfCommitter` entanglement that prevented per-attempt
rollbacks).

### Bug 2 — Idle runner doesn't re-check after finishing a task

`tasks_version` only bumps when a job transitions **to** waiting (new
workflow triggered, blocked→unblocked). After a runner finishes its
current task it polls `FetchTask` with `tasksVersion == latestVersion`,
so the server skips `PickTask` entirely — the remaining 45 waiting jobs
are invisible to the now-idle runner.

**Fix:** Also call `IncreaseTaskVersion` in `UpdateRunJob` when a
(non-reusable-caller) job transitions to a **done** state. Idle runners
then see a version mismatch on their next poll and attempt `PickTask`,
picking up the remaining jobs.
2026-06-27 17:56:12 +00:00
bircniandwxiaoguang d5e6f273f0 fix(migrations): prevent path traversal in repository restore (#38215)
## Problem

The repository restorer (`services/migrations/restore.go`) builds
`file://` URLs for release attachments and PR patches by joining
user-supplied paths from `release.yml` and `pull_request.yml` onto the
dump directory:

```go
*asset.DownloadURL = "file://" + filepath.Join(r.baseDir, *asset.DownloadURL)
pr.PatchURL        = "file://" + filepath.Join(r.baseDir, pr.PatchURL)
```

`filepath.Join` cleans the path, so a crafted relative value such as
`../../../../etc/passwd` resolves to an absolute path **outside** the
dump directory. `uri.Open` then reads it via `os.Open` and stores the
content as a release attachment, which is retrievable through the API —
an arbitrary file read (Local File Inclusion) from a dump archive
supplied to `restore-repo`.

## Fix

Add a `localFileURL` helper that resolves the relative path against
`baseDir` and rejects anything that escapes it. Malicious entries are
skipped with a warning so a legitimate restore still completes; in-dump
files keep working unchanged.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-06-27 14:50:30 +00:00
bircni 122ebcf0a8 fix(api): deny private org member enumeration via /members (#38213) 2026-06-26 18:35:13 +00:00
bircni c2f130d352 fix(mssql): convert legacy DATETIME columns to DATETIME2 (#38216)
## Problem

On MSSQL databases created by old Gitea versions, the real datetime
columns `external_login_user.expires_at` and `lfs_lock.created` were
created as `DATETIME`. `DATETIME` parses datetime literals in a
locale-dependent way, so the ISO string `'YYYY-MM-DD HH:MM:SS'` that
xorm sends fails to convert when the session language is not English
(e.g. German defaults to `dmy`):

```
mssql: Bei der Konvertierung eines nvarchar-Datentyps in einen datetime-Datentyp liegt der Wert außerhalb des gültigen Bereichs.
```

This breaks linking an external (OAuth/Keycloak) account to an existing
user, and LFS lock creation, with a 500 error.

## Fix

Current xorm already maps `time.Time` to the locale-independent
`DATETIME2` for new installs, so only legacy databases are affected.
This adds migration `341` that converts these columns to `DATETIME2` on
legacy MSSQL databases (no-op on other databases and on columns already
using `DATETIME2`).

A full audit of persisted `time.Time` columns in `models/` confirmed
these two are the only real datetime columns affected — every other time
value is stored as a unix-timestamp integer.

A regression test (MSSQL-only, mirroring the existing v338 pattern)
downgrades the columns to legacy `DATETIME`, runs the migration, asserts
the type becomes `DATETIME2`, and verifies an ISO datetime insert
succeeds under `SET LANGUAGE German`.

Fixes #38211
2026-06-25 14:38:39 +02:00
bircni 649cb6ff3e fix(actions): show run index in run view and fix summary graph height (#38165)
- Display the per-repository run number as `#N` next to the run title in
the run view, matching the runs list and GitHub
- Add the run `Index` to the run view API response (and the devtest
mock) to support that
- Restore the summary panel's `flex: 1` so the workflow graph fills the
right-column height even when a run has no job summaries
- Keep the job-summary section content-sized so it doesn't compete with
the graph for height
- Gate the devtest mock job summaries to a subset of runs so the devtest
page also exercises the no-summary layout

<img width="521" height="232" alt="image"
src="https://github.com/user-attachments/assets/a1f2f20b-65bd-4d98-ba6a-b8135580a6de"
/>
2026-06-22 06:16:09 +00:00
bircniandLunny Xiao 7684221ed4 feat(actions): implement jobs.<job_id>.continue-on-error (#38100)
Support `continue-on-error` for workflow jobs when aggregating an
Actions workflow run status.

Previously, `continue-on-error` was parsed from workflow YAML but was
not persisted or used when calculating the overall run result. As a
result, a failed job could incorrectly fail the entire workflow even
when the workflow explicitly allowed that job to fail.

This PR stores the parsed `continue-on-error` value on each action run
job and treats failed jobs with `continue-on-error: true` as successful
when computing the workflow run status, matching GitHub Actions
behavior.

## Changes

- Add `ContinueOnError` to `jobparser.Job`.
- Add `continue_on_error` to `ActionRunJob` with a `NOT NULL DEFAULT
FALSE` migration.
- Populate `ActionRunJob.ContinueOnError` when creating workflow run
jobs.
- Update workflow status aggregation so failed `continue-on-error` jobs
do not fail the overall run.
- Leave `resolveCheckNeeds` unchanged so dependent jobs still see the
job result as `failure` and are skipped by default.

## Compatibility

This is backward compatible.

If only the runner or only the server is updated, `continue-on-error`
continues to degrade to the previous behavior and is effectively ignored
until both sides support it.

Related runner PR: https://gitea.com/gitea/runner/pulls/1032

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-06-22 04:51:16 +00:00
bircni 685b62c60f fix(api): don't expose private org membership via public_members (#38145) 2026-06-22 05:50:02 +02:00
bircniandLauris B 180af33f86 perf: Various performance regression fixes (#38078)
Fixes five N+1 / O(n) query patterns found across common user paths.
Each uses a bulk query that already existed elsewhere in the codebase.

| Location | Problem | Introduced in |
| -------------------------------- |
-------------------------------------------------------------------------------------------------------------------------------
| ------------- |
| `IssueList.LoadIsRead` | `.In("issue_id")` missing its arg — xorm
generates `WHERE 0=1`, so `IsRead` is **never** set; every issue always
appears unread | #29515 |
| `ParseCommitsWithStatus` | `GetLatestCommitStatus` called once per
commit (O(n) queries on commit list / PR commits tab) | #33605 |
| `getReleaseInfos` (release list) | `GetLatestCommitStatus` called once
per release for CI badges | #29149 |
| User milestone dashboard | O(n×m) nested loop matching milestones to
repos | #26300 |
| `findCodeComments` (PR diff) | `LoadResolveDoer` + `LoadReactions`
called per inline comment — up to ~150 queries on a PR with 50 comments
| #20821 |

---------

Co-authored-by: Lauris B <lauris@nix.lv>
2026-06-21 14:34:07 +00:00
bircni 5368542f8e fix(cli): default must-change-password to false for bot users (#38175) 2026-06-20 22:26:22 +02:00
bircni 240d0efa7e perf: extend action c_u index to include created_unix for faster dashboard feeds (#38076)
Adds `created_unix` as the third column of the `c_u` composite index on
the `action` table, changing it from `(user_id, is_deleted)` to
`(user_id, is_deleted, created_unix)`.

Migration 337 drops and recreates the index. No data is touched.

## Root causes

#32333 introduced the `c_u` index to speed up dashboard queries, but
defined it as `(user_id, is_deleted)` — without `created_unix`.

#3368 The simple query is now efficient enough for the database to
actually use `c_u`, but because `created_unix` is absent from the index,
the database must load and sort **every** matching row before returning
the first page of 20.

The existing `c_u_d` index `(created_unix, user_id, is_deleted)` does
not help because its leading column is `created_unix`, which can't be
used for an equality seek on `user_id`.

Those two caused this issue:
https://github.com/go-gitea/gitea/issues/38075

With the fix, the database seeks directly to `(user_id=X,
is_deleted=false)` and walks `created_unix` in descending order,
stopping after 20 rows.

Fixes https://github.com/go-gitea/gitea/issues/38075
2026-06-17 23:37:55 +03:00
bircniandwxiaoguang 68692e19d4 fix: Various security fixes (#38103)
- Enforce org visibility on organization label read endpoints (private
org labels no longer leak to non-members).
- Block fork sync (`merge-upstream`) when the base repo is no longer
readable (stops pulling commits after a parent goes private).
- Remove `REVERSE_PROXY_LIMIT` / `REVERSE_PROXY_TRUSTED_PROXIES` from
the Docker `app.ini` templates (the `= *` default allowed
`X-WEBAUTH-USER` impersonation; reverse-proxy auth is now opt-in and
admin-configured).
- Enforce single-use TOTP passcodes across web login, password-reset,
and Basic-Auth `X-Gitea-OTP` (fixes a TOCTOU race and a stateless
replay).
- Re-check branch write permission for every ref in a push (the
pre-receive hook cached the first ref's result, letting a per-branch
maintainer-edit grant escalate to full repo write).

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2026-06-17 16:06:51 +00:00
c68925152b docs: add development setup guide (#37960)
Moves the "Hacking on Gitea" page out of the documentation website and into the repository as `docs/development.md`, so contributors find build and test instructions next to the code. The content has been cleaned up and corrected for in-repo use.

---------

Signed-off-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-06-17 06:39:22 +00:00
bircniandLunny Xiao 9e84deb969 fix: Various sec fixes 2 (#38108)
- Enforce repository token scope on RSS/Atom feed endpoints so a PAT
without repo scope can no longer read private repo commit data.
- Block HTTP redirects during repository migration clones to prevent
SSRF reaching internal addresses via an attacker-controlled redirect.
- Redact the notification subject after repo access is revoked so
private issue/PR metadata is no longer leaked through the notification
API.

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-06-17 06:50:25 +02:00