- Hash emails with sha256. Gravatar moved to sha256, and both it and
libravatar.org serve the same image for either hash.
- Drop `strk.kbt.io/projects/go/libravatar` for a 46 line inline SRV
lookup. It could not bound or cancel its DNS query and panicked on an
unexpected resolver error. The replacement carries the request context
and a 3s timeout.
- Fix federated avatars querying DNS for every avatar on every render.
`loadAvatarSetting` compared a cache field that was never assigned, so
each call rebuilt the resolver and dropped its cache. That cache is
gone, both settings are read where they are used.
- Migration 348 recreates `email_hash` with a 64 char hash column and a
`hash_type` column, so a later algorithm change can tell old rows apart.
The MD5 rows are unreachable and their `UNIQUE` email index would reject
the SHA256 replacements.
- Fix a re-saved avatar form replacing an uploaded avatar with a random
one.
- Remove the `duoshuo` `GRAVATAR_SOURCE` alias, that service shut down
in 2017.
- Remove dead i18n key.
Fixes: https://github.com/go-gitea/gitea/issues/34284
Fixes: https://github.com/go-gitea/gitea/issues/28110
Docs: https://gitea.com/gitea/docs/pulls/499
Signed-off-by: silverwind <me@silverwind.io>
Follow-up to https://github.com/go-gitea/gitea/pull/37571.
"Participating and mentions" deleted the watch row, so choosing it
dropped you out of the watcher count. It is a watch like the others, so
it now keeps a row and simply subscribes to no events.
The dashboard feed ignored the per-event options, so a "Custom: issues"
watcher still got pull request activity there. It now gates on the same
options as mail and notifications. That also closes a gap where pull
request reviews bypassed the permission check.
Also, address
https://github.com/go-gitea/gitea/pull/37571#discussion_r3740487363 and
reword a UI text for clarity.
---------
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Enable [`forcetypeassert`](https://github.com/gostaticanalysis/forcetypeassert)
linter to prevent unchecked type assertions. ~650 issues fixed, most
fixes were clean, some use `setting.PanicInDevOrTesting`.
The only behaviour changes are where code would previously send a 500 error
or panic, a 4xx error is now emitted.
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Pairs with https://gitea.com/gitea/runner/pulls/1143.
Gitea depends on `gitea.com/gitea/runner` for exactly two packages:
`act/model` and `act/exprparser`, the workflow model and the expression
evaluator it needs to parse workflows and to build the task payload the
runner consumes. Pulling the whole runner module in for that is heavy
and puts shared code in the repository of one of the two consumers.
Both packages now live in `gitea.dev/actionslib` (`pkg/model`,
`pkg/exprparser`), the module Gitea and the runner already share for the
runner API, so the dependency on the runner repository is dropped here.
### Changes
- `gitea.com/gitea/runner/act/model` ->
`gitea.dev/actionslib/pkg/model`, `.../act/exprparser` ->
`gitea.dev/actionslib/pkg/exprparser` (22 files, import paths only).
- `routers/api/actions/runner/interceptor.go` takes the `x-runner-uuid`
/ `x-runner-token` names from `gitea.dev/actionslib/pkg/protocol`
instead of repeating the literals the runner also has.
- `go.mod`: `gitea.com/gitea/runner` removed.
---------
Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Zettat123 <zettat123@gmail.com>
1. add correct "refs/heads" prefix to the branch name for commit graph
2. fix incorrect cache key in GetCommitGraphsCount
3. remove the "--" trim for the tag name, there is no security vulnerability, we never do so anywhere else
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1. always use "last commit cache"
2. correctly build the cache key for any input (SafeCacheKey)
3. fix the git note "last commit cache FIXME" and avoid OOM
extends the current license detection to support two modes:
- legacy which is using classification and was expanded to handle more
paths (extensions, different spelling or GNU copying file)
- REUSE which avoids classification by relying on the spec dictating
that license must be named as SPDX-ID.extension. Newly created
repositories will default to REUSE based paths
Use of styles at the same time is not allowed by design.
Extends the UI to show all the detected licenses and paths to them,
deduplicating them per SPDX-ID in database as is in github
closes: https://github.com/go-gitea/gitea/issues/28672
---------
Assisted-By: omp:glm5.2
Assisted-By: omp:mimo-v2.5-pro
Assisted-By: omp:mimimax-m3
Assisted-By: omp:kimi-k3
Assisted-By: omp:deepseek-v4-flash
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Implements the missing REST API endpoints for Actions workflow run
management:
1. `POST /actions/runs/{run}/cancel` cancels a run and its jobs, `409`
when it already finished
1. `POST /actions/runs/{run}/approve` approves a run awaiting approval,
idempotent, `409` when it never awaited one
1. `GET /actions/runs/{run}/logs` downloads the latest attempt's job
logs as a zip archive
`ActionWorkflowRun` gains `created_at`, `updated_at` and the `jobs_url`,
`logs_url`, `artifacts_url`, `cancel_url` and `rerun_url` fields, and
now always emits `conclusion` and `head_branch`.
Cancellation is shared with the web handler in `services/actions`.
Fixes https://github.com/go-gitea/gitea/issues/35176
Fixes https://github.com/go-gitea/gitea/issues/36554
---------
Co-authored-by: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Co-authored-by: OpenCode Agent <opencode@rossgolder.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This pull request optimizes code diff highlighting in the PR Files
Changed view, which is a major CPU bottleneck and hot path in
production.
Previously, Gitea executed the expensive `DiffMatchPatch` algorithm
twice for every matching deleted/added line pair (once for the deleted
line, and once for the added line). We now run the diff algorithm once
and cache the resulting `DiffInline` on the `DiffLine` struct itself,
allowing the second line to render via a 0-cost cache lookup.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Show only author and co-authors without committer, and deduplicate the
same user with multiple email addresses.
The commit list "Author" column should not show the committer. This is
somewhat misleading, and arguably showing it on the individual commit
page is sufficient and consistent with other forges.
The same user with multiple email addresses often happens when
DEFAULT_KEEP_EMAIL_PRIVATE is enabled and Gitea does not use an actual
email address by default for edits. Showing the same avatar and name
twice is not helpful then.
Ref #37594Fix#38488
---
Before
<img width="4088" height="2138" alt="before"
src="https://github.com/user-attachments/assets/a9e919f4-72d0-495f-a0b5-ec094f148f5a"
/>
After
<img width="4124" height="2142" alt="after"
src="https://github.com/user-attachments/assets/7212e527-ab83-440a-a8a4-95b5a79b3580"
/>
---------
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Unfortunately, we can't completely remove the ctx from git.Repository,
because the CatFileBatch still heavily depends on a parent context.
If we remove the Repository ctx, the CatFileBatch will become a mess and
create a lot of unnecessary git processes.
http://localhost:3000/-/admin/monitor/perftrace
* Before: open a repo home, dozens of git processes (duplicate cat-file)
* After: only a few (no duplicate cat-file)
Adds dynamic matrix evaluation to Gitea Actions: a job's
`strategy.matrix` can be built from the outputs of the jobs it needs.
```yaml
jobs:
generate:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.result }}
steps:
- id: set
run: echo "result=[1,2,3]" >> $GITHUB_OUTPUT
build:
needs: [generate]
runs-on: ubuntu-latest
strategy:
matrix:
version: ${{ fromJson(needs.generate.outputs.matrix) }}
steps:
- run: echo "building ${{ matrix.version }}"
```
Such a matrix cannot be expanded at planning time, so the job is planned
as a single placeholder and expanded by the job emitter once its needs
finish. Each combination is then gated by `if:` and concurrency as
usual.
- A matrix that resolves to no combination fails the job, as on GitHub.
- Expansion is capped at `MaxJobNumPerRun`.
- Workflows without a needs-dependent matrix are unaffected.
Fixes https://github.com/go-gitea/gitea/issues/25179
---------
Signed-off-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Signed-off-by: ZPascal <pascal.zimmermann@theiotstudio.com>
Co-authored-by: Claude <claude-sonnet-4-5@anthropic.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.8) <noreply@anthropic.com>
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Regression of the ctx removal from "git.Repository" struct (the old code
was already wrong and can still cause 500, the "ctx removal" just makes
the problem easier to reproduce).
Merge duplicate code.
Reviewd by codex: no actionable findings.
`GetBranch` silently returned soft-deleted branches, contradicting
`IsBranchExist` and forcing callers to manually check `branch.IsDeleted`
everywhere.
Refactored it to `GetBranchExisting` to have a clear behavior: it only
returns the existing branch.
---------
Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
* Closes#36942
* Fixes#19265
Replaces the SSE-based push channel (`/user/events`) with a WebSocket
endpoint (`/-/ws`).
### What changes
- **New `/-/ws` endpoint** (authenticated). One WebSocket per origin,
shared across tabs via a single `SharedWorker`.
- **Pubsub broker** (`services/pubsub`) for fan-out by topic, behind a
`Broker` interface. `MemoryBroker` is the default (single process); a
Redis backend is available for multi-process setups, configured via
`[websocket].PUBSUB_TYPE` / `PUBSUB_CONN_STR`. The internal Gitea queue
was not usable here because it has FIFO/single-consumer semantics.
- **Push-only event production.** Events are emitted by write-triggered
notifiers — `NotificationCountChange`, `PublishStopwatchesForUser`, and
the logout publisher — wired into the existing `notify.Notifier`
interface. No server-side pollers.
- **Typed pub/sub on the client.** `web_src/js/modules/worker.ts` is a
singleton transport; features subscribe per event type via
`onUserEvent('notification-count', cb)` instead of branching on
`event.data.type`.
- **Wire contract** (`UserEventType` union) is shared between the worker
and consumers via `web_src/js/types.ts`, kept in sync with
`services/websocket/events.go`.
- **Client-side periodic polling fallback** kicks in only when the
WebSocket cannot be established (e.g. proxy blocks WS, browser lacks
module-SharedWorker support).
### What's removed
- `modules/eventsource` (SSE manager, run loop, messenger).
- `/user/events` route and `tests/integration/eventsource_test.go`.
- All server-side polling for stopwatches and notification counts.
### Stopwatch multi-tab fix
The navbar stopwatch icon was previously rendered conditionally on `{{if
$activeStopwatch}}`, so tabs loaded before the timer started had no DOM
element to update. The icon and popup are now always rendered (toggled
with `tw-hidden`), and the start/stop/cancel handlers POST silently so
all open tabs reflect the change in real time.
### Deployment note
WebSocket needs the upgrade headers to pass through a reverse proxy,
e.g. for nginx:
```nginx
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
```
Without them the WebSocket cannot be established, and after 3
consecutive failed opens the shared worker signals `push-unavailable`:
the notification count and stopwatch fall back to periodic polling on
the existing `[ui.notification]` timeouts. Real-time push is lost, the
features keep working. The reverse-proxy docs need the same note (see
the `docs-update-needed` label).
---------
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Epid <rexmrj@gmail.com>
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>
1. fix `AddDependency` and ` RemoveDependency` to respond correctly
2. refactor the form to use "form-fetch-action"
3. fix the issue dependency icon layout regression
---------
Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Batch operations in the issues list (labels, milestones, projects,
assignees, etc.) could fail silently because `updateIssuesMeta` caught
and suppressed fetch errors internally. The caller would then proceed to
reload the page, clearing all selected issue checkboxes and giving the
impression that the operation had succeeded.
- Remove updateIssuesMeta and use our "fetch-action" framework to handle errors and page reloading
- Refactor backend code to use ctx.JSONError
---------
Signed-off-by: Sudhanshu Singh <sudhanshuwriterblc@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
by the way, remove some unnecessary "models" imports from model
migration package, fix migration test model init bug
(modelmigration/migrationtest/tests.go)
This PR adjusts the repository README discovery logic to mirror GitHub's
priority order, while retaining support for Gitea's specific `.gitea/`
directory.
Previously, Gitea would prioritize a `README.md` at the root of the
repository over any READMEs in `.gitea/` or `.github/`. With this
change, well-known subdirectories are evaluated first when viewing the
repository root.
**New Priority Order:**
1. `.gitea/README.md`
2. `.github/README.md`
3. `/README.md` (root directory)
4. `docs/README.md`
This allows repository maintainers to use `.github/README.md` (or
`.gitea/README.md`) as the repository homepage without having to remove
or rename generic root `/README.md` files required by package managers
(like NPM, Crates.io, Docker, etc.).
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
The "path" details should be hidden to other packages
By the way, fix a resource leaking in gogit's CommitNodeIndex
(the file was not closed in CacheCommit)
before: gitrepo vs git packages
after: git package fully handle all git operations
by the way, use `WithRepo(repo)` instead of `WithDir(repo.Path)` to hide
path details.
benefits:
1. remove all unnecessary wrappers, developers no need to struggle with
"which package should be used"
2. simplify code, RepositoryFacade can (will) be used everywhere, all
"path" details are (will be) hidden
Removed `gitrepo.RunCmd*` functions, because gitcmd.Command works with
Repository directly.
Move some "local filesystem" related function into "localfs.go"
Blob.Size requires a context after the repository context removal
(5b078f72aa).
Actually the template code should just render, it should not depend on
the fragile dynamic calls to backend functions.
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Updating a branch by merge produced an unsigned commit even when merges
are configured to be signed. Update by rebase was unaffected.
`Update()` builds a fake reverse PR to switch head and base, and it has
no `Index`, so `pr.GetGitHeadRefName()` resolves `refs/pull/0/head`.
Since #36186 `SignMerge` looks that ref up in the base repository
instead of the temporary merge repo. That lookup fails. The caller
dropped the error, so `sign` stayed false.
Sync fork goes through the same fake-PR path.
Pass both sides of the merge to `SignMerge` as refs and evaluate them in
the temp repo, where `base` and `tracking` always exist.
Tests cover a signed and an unsigned update by merge.
Fixes#38066
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.
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>
1. Storing "ctx" in a long-living object is wrong
2. Make the commit & tree cacheable (for the future performance
optimization)
3. Also fix some bad designs like `// FIXME: bad design, this field can
be nil if the commit is from "last commit cache"`
ref:
* #33893
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>
### Description
This PR implements an optimized, adaptive client-side auto-refresh
mechanism for the Gitea Actions workflow runs list page. It allows users
to see workflow progress updates dynamically without having to reload
the page.
Fixes https://github.com/go-gitea/gitea/issues/37457
---------
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
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
Adds a new `branch-name` value for the `[repository.pull-request]`
`DEFAULT_TITLE_SOURCE` setting that always uses the normalized branch
name as the PR title, regardless of commit count.
Fix#38317
---------
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>