Fix#36234
## Bug
Logs show `PrepareRunAndInsert: InsertRun: Error 1213: Deadlock found`,
which `handleWorkflows` silently swallows via `log.Error + continue`, so
the triggered run is dropped.
## Root cause
The path `UpdateRun -> UpdateRepoRunsNumbers` runs the following SQL
inside every status-changing transaction:
```sql
UPDATE repository
SET num_action_runs = (SELECT count(*) FROM action_run WHERE repo_id = N),
num_closed_action_runs = (SELECT count(*) FROM action_run WHERE repo_id = N AND status IN (...))
WHERE id = N;
```
On any DB that treats subqueries inside an UPDATE as locking reads, this
statement takes locks in two steps:
1. The outer UPDATE acquires an X lock on `repository[id=N]`
2. The embedded SELECT subqueries are evaluated as locking reads, taking
S locks on every `action_run` row matching `repo_id = N`
Two such concurrent transactions form a cycle via `repository[N]`:
| Tx | Holds | Wants | Blocked by |
|---|---|---|---|
| A: `PrepareRunAndInsert` (push trigger) | X on inserted `action_run`
row R_A; X on `repository[N]` (outer UPDATE already through step 1) | S
on `action_run` rows for repo N (subquery, step 2) | B's X lock on R_B |
| B: `UpdateTaskByState` (runner callback) | X on `action_run` row R_B
(from `UpdateRun`) | X on `repository[N]` (outer UPDATE, step 1) | A's X
lock on `repository[N]` |
| **Cycle** | A waits for R_B; B waits for `repository[N]` | | deadlock
error -> `handleWorkflows` swallows -> run lost |
PostgreSQL's MVCC reads do not take these locks and SQLite serializes
writers, so the symptom only surfaces on MySQL/MSSQL.
## Fix
Split `UpdateRepoRunsNumbers` into small SQLs to avoid locking reads and
move it out of DB transactions.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
This PR fixes two permission-checking gaps in Git and LFS request
handling.
## What it changes
- keep wiki Git HTTP pushes on the normal write-permission path, even
when proc-receive support is enabled
- revalidate LFS bearer token requests against the current user state
and current repository permissions before allowing access
- add regression coverage for unauthorized wiki HTTP pushes
- add LFS tests for blocked users, revoked repository access, read-only
upload attempts, and valid write access
## Why
- wiki repositories should not inherit the relaxed refs/for handling
used for normal code repositories
- LFS authorization tokens should not remain usable after a user is
disabled or loses repository access
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Two bugs in GenerateGitContent, the function behind
`POST /api/v1/repos/{owner}/{template}/generate`:
1. The new repository's refs were not written `branch` DB table
2. The function re-fetched the new repo row from the database
but reassigned its local pointer
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
## Description
As mentioned in #37654 `AWSSecretAccessKey` are not encrypted and stored as is.
## Update
Follow the existing `AuthToken` flow of setting the `Encrypted` fields,
`Decrypting` them later and `Clearing` them at the end.
Closes#37654
---------
Signed-off-by: Kausthubh J Rao <105716675+Exgene@users.noreply.github.com>
Co-authored-by: Lauris B <lauris@nix.lv>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This PR adds a new repository API field, `mirror_last_sync_at`, to
expose the timestamp of the last successful pull mirror sync.
Unlike `mirror_updated`, this field does not affect mirror scheduling
and is updated only after a successful pull sync. Failed sync attempts
leave the value unchanged.
What changed
- added `mirror_last_sync_at` to the repository API response
- updated pull mirror sync flow to persist the timestamp only on
successful sync
- kept `mirror_updated` behavior unchanged for queue/scheduling purposes
`mirror_updated` is currently tied to mirror queue behavior, so it
cannot safely represent the last successful sync time. The new field
makes that state explicit for API consumers without changing scheduling
semantics.
---------
Signed-off-by: pomidorry <106489913+Pomidorry@users.noreply.github.com>
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>
Co-authored-by: Giteabot <teabot@gitea.io>
Action runs, jobs and steps have 8 statuses but the UI only showed 5
(from the commit status api) for the latter two. Align all 8 to GitHub
as closely as possible:
- waiting — `octicon-circle` (hollow circle), gray
- blocked — `octicon-blocked` (slashed circle), yellow
- running — `gitea-running` (rotating spinner), yellow
- cancelled — `octicon-stop` (gray), was `octicon-x` (red)
Descriptions also aligned with GitHub:
- "Has started running" → "In progress"
- "Has been cancelled" → "Cancelled after {dur}"
- "Has been skipped" → "Skipped"
Fixes: https://github.com/go-gitea/gitea/issues/32228
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
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: Nicolas <bircni@icloud.com>
1. Pin all makefile go deps to exact version, renovate will bump them in the future
2. Bump all deps and golangci-lint and fix all new issues, most are from modernize
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
When a workflow job failed, the API response reported all steps as
failed — even steps that had completed successfully before the failing
step. `ToActionWorkflowJob` was calling `ToActionsStatus(job.Status)`
for every step instead of `ToActionsStatus(step.Status)`, so the job's
overall conclusion was propagated to each step.
Each `ActionTaskStep` has its own `Status` field that tracks the actual
outcome of that step independently of the job result.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
fixes adding collaborative owners in Actions settings when the user or
organization name contains capital letters.
Fixes#37548
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Fixes#37446.
The job-status resolver in `checkJobsOfCurrentRunAttempt` only
considered `needs` and job-level concurrency when transitioning jobs out
of `Blocked`. When something drove the resolver against a run blocked
solely by workflow-level concurrency — for example, a sibling run in the
same group entering the queue and triggering `EmitJobsIfReadyByRun` —
the run's job silently became `Waiting` while another run still held the
concurrency group, and the runner could pick it up, defeating the
concurrency guarantee.
The fix bails out of the resolver when the run's latest attempt is still
blocked by run-level concurrency. `checkRunConcurrency` re-evaluates
when the holding run finishes.
Covered by a unit test
(`Test_checkJobsOfCurrentRunAttempt_RunLevelConcurrencyKeepsJobsBlocked`
in `services/actions/job_emitter_test.go`) that sets up a Running holder
attempt and a Blocked sibling attempt in the same concurrency group
directly in the DB, calls `checkJobsOfCurrentRunAttempt`, and asserts
the blocked job stays `Blocked`. Fails on master, passes with the fix.
---
This PR was written with the help of Claude Opus 4.7
---------
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
The migration repository model now carries Website alongside Description, the GitHub/Gitea downloaders populate it, and mirror finalization persists both description and website so the About section is not empty after creating a mirror.
Fixes#37495
---------
Signed-off-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
After the Webpack-to-Vite migration (#37002), mCaptcha stopped working
entirely on the registration page, throwing an error:
`TypeError: setting getter-only property "INPUT_NAME"`
This fix stops trying to mutate the read-only INPUT_NAME export. Instead
it probes for the Widget constructor at module.default (direct) or
module.default.default (CJS-wrapped), constructs the widget, and then
renames the hidden input element it creates to m-captcha-response which
is the field name Gitea's backend reads from the submitted form.
Generative AI was used to help with making this PR.
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>
1. Rename CompareInfo.MergeBase to CompareBase, it is not merge base
2. Remove unused template variables `ctx.Data["Username"]` and
`ctx.Data["Reponame"]`
3. Decouple some template variable accesses, use typed struct
---------
Co-authored-by: Nicolas <bircni@icloud.com>
Add a build-time conversion step that transforms the existing Swagger
2.0 spec into an OpenAPI 3.0 spec. The OAS3 spec is served alongside the
existing Swagger 2.0 spec, enabling API clients that require OAS3 to
generate code directly from Gitea's API.
This is not to be an answer to how gitea handles OAS3 long term,
but a way to use what we have to move a step forward.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Avoid per-item DB queries in ListRuns, ListJobs, and ListActionTasks by
batch-loading trigger users, repositories, and task attributes before
the conversion loop. Remove ReferencesGitRepo from the /actions route
group since no task/run endpoints use it.
Added tests for these endpoints as well.
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This fixes the scheduled action panic when an event payload is JSON
`null` by initializing the payload map before adding `schedule`. It also
adds regression coverage for the null-payload case.
Fixes#37447.
Testing:
- `go test -tags 'sqlite sqlite_unlock_notify' ./services/actions -run
'^TestWithScheduleInEventPayload$' -count=1`
- Local note: this agent ran the command as root with a temporary
`GITEA_TEST_CONF=custom/conf/app-test-root.ini` file that only set
`I_AM_BEING_UNSAFE_RUNNING_AS_ROOT = true`.
Authorship: cyphercodes; AI assistance disclosed: Hermes Agent
(GPT-5.5).
---------
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
Co-authored-by: Hermes Agent (GPT-5.5) <hermes-agent@users.noreply.github.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: Giteabot <teabot@gitea.io>
After using CSP nonce, the "onerror" doesn't work anymore. Change it to
use a global variable to detect
Also help users like #37379 to catch errors more easily.
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
The `Repository` struct in `services/context/repo.go` embedded
`access_model.Permission` anonymously, causing all permission methods to
be promoted directly onto `Repository`. This made it unclear at call
sites whether a method belonged to `Repository` itself or to its
embedded `Permission`.
### Changes
- **`services/context/repo.go`**: Replace anonymous
`access_model.Permission` with named field `Permission
access_model.Permission`
- **49 files** updated to route permission method calls through the
named field:
```go
// Before
ctx.Repo.IsAdmin()
ctx.Repo.CanWrite(unit.TypeCode)
ctx.Repo.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.CanWrite)
// After
ctx.Repo.Permission.IsAdmin()
ctx.Repo.Permission.CanWrite(unit.TypeCode)
ctx.Repo.Permission.CanReadIssuesOrPulls(isPull)
slices.ContainsFunc(unitTypes, ctx.Repo.Permission.CanWrite)
```
Methods defined directly on `*Repository` (`CanWriteToBranch`,
`CanCreateBranch`, etc.) are unchanged.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Refactor preparePullViewPullInfo and related functions, split them into
small ones:
* preparePullViewPullInfo creates PullRequestViewInfo struct
* if the PR is merged: prepareView**Merged**PullInfo
* if the PR is open: prepareView**Open**PullInfo
In prepareViewMergedPullInfo and preparePullViewFillInfo: call
preparePullView**FillInfo** consistnently
preparePullViewFillInfo calls preparePullViewFill**CompareInfo** and
preparePullViewFill**CommitStatusInfo**
Fast-forward-only creates no Gitea commit, so skip the "can Gitea sign"
precheck for it. Pre-check head-commit verification for styles that
preserve user commits on the target (merge, fast-forward-only) so a PR
with unsigned commits surfaces a localized error instead of a 500 at the
pre-receive hook. The dropdown still shows every configured style; the
avatar and signing warning toggle per selection via
data-pull-merge-style.
Fixes#12272
**Note**: Admin force-merge does not bypass the new head-commits check.
This matches the existing `isSignedIfRequired` behavior.
Signed-off-by: Nikita Vakula <programmistov.programmist@gmail.com>
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>
This PR introduces a new `ActionRunAttempt` model and makes Actions
execution attempt-scoped.
**Main Changes**
- Each workflow run trigger generates a new `ActionRunAttempt`. The
triggered jobs are then associated with this new `ActionRunAttempt`
record.
- Each rerun now creates:
- a new `ActionRunAttempt` record for the workflow run
- a full new set of `ActionRunJob` records for the new
`ActionRunAttempt`
- For jobs that need to be rerun, the new job records are created as
runnable jobs in the new attempt.
- For jobs that do not need to be rerun, new job records are still
created in the new attempt, but they reuse the result of the previous
attempt instead of executing again.
- Introduce `rerunPlan` to manage each rerun and refactored rerun flow
into a two-phase plan-based model:
- `buildRerunPlan`
- `execRerunPlan`
- `RerunFailedWorkflowRun` and `RerunFailed` no longer directly derives
all jobs that need to be rerun; this step is now handled by
`buildRerunPlan`.
- Converted artifacts from run-scoped to attempt-scoped:
- uploads are now associated with `RunAttemptID`
- listing, download, and deletion resolve against the current attempt
- Added attempt-aware web Actions views:
- the default run page shows the latest attempt
(`/actions/runs/{run_id}`)
- previous attempt pages show jobs and artifacts for that attempt
(`/actions/runs/{run_id}/attempts/{attempt_num}`)
- New APIs:
- `/repos/{owner}/{repo}/actions/runs/{run}/attempts/{attempt}`
- `/repos/{owner}/{repo}/actions/runs/{run}/attempts/{attempt}/jobs`
- New configuration `MAX_RERUN_ATTEMPTS`
- https://gitea.com/gitea/docs/pulls/383
**Compatibility**
- Existing legacy runs use `LatestAttemptID = 0` and legacy jobs use
`RunAttemptID = 0`. Therefore, these fields can be used to identify
legacy runs and jobs and provide backward compatibility.
- If a legacy run is rerun, an `ActionRunAttempt` with `attempt=1` will
be created to represent the original execution. Then a new
`ActionRunAttempt` with `attempt=2` will be created for the real rerun.
- Existing artifact records are not backfilled; legacy artifacts
continue to use `RunAttemptID = 0`.
**Improvements**
- It is now easier to inspect and download logs from previous attempts.
-
[`run_attempt`](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#github-context)
semantics are now aligned with GitHub.
- > A unique number for each attempt of a particular workflow run in a
repository. This number begins at 1 for the workflow run's first
attempt, and increments with each re-run.
- Rerun behavior is now clearer and more explicit.
- Instead of mutating the status of previous jobs in place, each rerun
creates a new attempt with a full new set of job records.
- Artifacts produced by different reruns can now be listed separately.
Signed-off-by: Zettat123 <zettat123@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>
Co-authored-by: Giteabot <teabot@gitea.io>
Fix team members missing from assignee list when `team_unit.access_mode`
is 0 but the doer is owner.
Fix #34871
1. Use `GetTeamUserIDsWithAccessToAnyRepoUnit` for repo assignee list
2. Load assignee list for project issues directly
3. Use `GetTeamUserIDsWithAccessToAnyRepoUnit` for repo reviewer list
Signed-off-by: Jakub Pisarczyk <pisarz77@gmail.com>
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-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: Giteabot <teabot@gitea.io>
Fix#36859
Replace live third-party API calls in migration tests with a
fixture-based HTTP mock server. Fixtures are committed so tests run
offline by default; live recording is gated per service on an API-token
env var.
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Fix#34349
By the way, remove `(ctx *APIContext) HasAPIError() ` and `(ctx
*APIContext) GetErrMsg()` because they do nothing, the error handling
has been done in API's middeware
The existing OAuth2 tests were not quite right, refactored them together
## Summary
Fixes#37252
The `/api/v1/repos/{owner}/{repo}/actions/runs` endpoint was returning
`event: "push"` for workflow runs triggered by `schedule:` (cron),
instead
of `event: "schedule"`.
## Root Cause
`ActionRun` has two separate fields:
- `Event` — the workflow registration event (e.g. `push`, set when the
workflow file was first pushed)
- `TriggerEvent` — the actual event that triggered the run (e.g.
`schedule`)
`ToActionWorkflowRun` in `services/convert/action.go` was serializing
`run.Event` into the API response instead of `run.TriggerEvent`, causing
scheduled runs to be indistinguishable from push events via the API.
This was already asymmetric — the tasks/jobs API correctly used
`TriggerEvent`.
## Fix
Changed `ToActionWorkflowRun` to use `run.TriggerEvent` for the `event`
field in the API response, consistent with how the jobs API works.
## Before
`event: "push"` returned for all scheduled runs:
<img width="1112" height="191" alt="Screenshot 2026-04-19 115642"
src="https://github.com/user-attachments/assets/c0a169f5-bbd9-4f5d-9474-e4c3795110e4"
/>
## After
`event: "schedule"` correctly returned for scheduled runs:
<img width="890" height="166" alt="Screenshot 2026-04-19 121723"
src="https://github.com/user-attachments/assets/860e99ac-0935-4a43-86a1-7b60f8113480"
/>
## Testing
- Added unit test `TestToActionWorkflowRun_UsesTriggerEvent` in
`services/convert/action_test.go` that explicitly verifies the API
returns `TriggerEvent` and not `Event` for a scheduled run.
- Manually verified via the API against a live Gitea instance with a
`cron: "* * * * *"` workflow.
---------
Co-authored-by: Nicolas <bircni@icloud.com>
A quick fix#37317
---
The current behavior for forks when an organization or repository is
changed to private differs from GitHub.
On GitHub, when a parent repository becomes private, the fork
relationship is removed, which keeps the behavior simple and avoids
visibility conflicts.
I think we need a similar solution to handle cases where the parent
repository becomes private while a fork remains public and the fork
relationship is still preserved.
---------
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Giteabot <teabot@gitea.io>