# Technical Documentation: theonedev/onedev

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/theonedev/onedev)

### Overview (`readme.md`)

<div class='d-none'>

> **NOTE: ** We develop OneDev at <a href="https://code.onedev.io">code.onedev.io</a> for sake of dogfooding. Please submit issues and pull requests there

</div>

<div align="center">
<h1>The Unified and Autonomous Development Platform</h1>

<h2>
<a href="https://docs.onedev.io">Get Started</a> 
&nbsp;&nbsp;
<a href="https://onedev.io/blogs/ai-teammates">AI Teammates</a> 
</h2>

<p style="margin-bottom: 20px;">
</div>

## Supported platforms

OneDev server and agent distributions support Linux and Windows on amd64/arm64, and macOS on Apple Silicon.
Starting with OneDev 16.9.0, Java Service Wrapper (JSW) has been replaced with OneDev Wrapper. Intel Macs, FreeBSD, and other platforms are no longer supported.
See the [installation guide](https://docs.onedev.io/installation-guide/run-on-bare-metal) for setup instructions.

## 🔎 Out-of-box code search and navigation

Language aware symbol search and navigation in any commit.
Click symbol to show occurrences in current file.
Fast code search with regular expression. 
[**Try It**](https://code.onedev.io/demo/kubernetes)



## 📦 Renovate integration to update project dependencies

Integrate with Renovate to update project dependencies via pull requests. 
Merge pull requests automatically when approved by required reviewers, or pass required tests.
[**Tutorial**](https://docs.onedev.io/tutorials/cicd/dependency-update)



## 🚦 Annotate code with coverage and problems

Code will be annotated with coverage info and problems found in 
CI/CD pipeline, to facilitate code review. 
[**Demo**](https://code.onedev.io/demo/react/~files/6039030814aedeaa6ebac706c0886e3675160666/packages/react-dom/src/client/ReactDOMSelect.js?position=source-202.1-202.36-1)



## 💬 Code discussion anywhere anytime

Select any code or diff to start discussion. Suggest and apply changes.
Discussions stay with code to help code understanding.
[**See It In Action**](https://code.onedev.io/onedev/server/~compare?left=160:f96d82a3fa12800b4040cc9ea62af09233307ae9&right=160:e55d152b9cc783fd7e64dc752a6c2b3c5613212c&compare-with-merge-base=false&comment=149&mark=e55d152b9cc783fd7e64dc752a6c2b3c5613212c~server-product/docker/build.sh~22.1-22.148-1&tab=FILE_CHANGES)



## 🔒 Versatile code protection rules

Set rule to require review or CI/CD verification when certain users touch certain
files in certain branches. 
[**Tutorial**](https://docs.onedev.io/tutorials/code/pullrequest-approval)



## 📋 Automated Kanban to keep team organized

Move tasks manually in Kanban, or define rules to move them automatically
when related work is committed/tested/released/deployed.
[**See It In Action**](https://code.onedev.io/onedev/server/~boards/State?iteration=4.2.0&backlog=true)



## 🛠 Customizable and flexible issue workflow

Custom issue states and fields. Manual or automatic state transition rules.
Issue links to sync operations and states. Confidential issues in public projects.
[**Tutorial**](https://docs.onedev.io/tutorials/issue/state-auto-transition)



## 📨 Service desk to link emails with issues

Use issues as ticket system to support customers via email, without requiring
them to register accounts. Assign different support contacts for different
projects or customers.
[**Tutorial**](https://docs.onedev.io/tutorials/issue/service-desk)



## ⏰ Time tracking and reporting

Track estimated/spent time on tasks. Aggregate time from subtasks automatically.
Generate time sheets for work statistics and billing.
[**Tutorial**](https://docs.onedev.io/tutorials/issue/time-tracking)



## 💡 CI/CD as code without writing code

An intuitive GUI to create CI/CD jobs. Template for typical frameworks.
Typed parameters. Matrix jobs. CI/CD logic reuses. Cache management.
[**Tutorial**](https://docs.onedev.io/category/cicd)



## 🚀 Versatile CI/CD executors from simple to scale

Run CI/CD out-of-box in container or on bare metal. Run massive jobs concurrently
with Kubernetes or agents.
[**Example1**](https://docs.onedev.io/tutorials/cicd/agent-farm)
[**Example2**](https://docs.onedev.io/tutorials/cicd/k8s-farm)



## 🛠 Tools to debug CI/CD jobs

Command to pause job execution. Web terminal to check job execution environment.
Run job locally against uncommitted changes.
[**Tutorial1**](https://docs.onedev.io/tutorials/cicd/diagnose-with-web-terminal)
[**Tutorial2**](https://docs.onedev.io/tutorials/cicd/run-job-against-local-change)



## 📦 Built-in package registries

Built-in registry to manage binary packages. Link packages with
CI/CD jobs.
[**Tutorial**](https://docs.onedev.io/category/packages)



## 📖 Wiki for versioned project documentation

Keep Markdown documentation alongside your code. Edit pages in the browser or with Git,
review changes in pull requests, and browse the wiki for any branch or release tag.
[**Tutorial**](https://docs.onedev.io/tutorials/wiki/working-with-wiki)



## 🧩 Deep integration and information cross-reference

Transit issue state via commit, CI/CD, or pull request.
Show fixing builds of issue. Query fixed issues or code changes between build/package versions.
[**Example1**](https://code.onedev.io/onedev/server/~builds/4799/fixed-issues?query=%22State%22+is+%22Released%22+order+by+%22Priority%22+desc+and+%22Type%22+asc)
[**Example2**](https://code.onedev.io/onedev/server/~issues/1794/builds)



## 🌲 Project tree for easy maintenance

Use tree to organize projects clearly and efficiently.
Define common settings in parent project and inherit in child projects.
[**See It In Action**](https://code.onedev.io/~projects?query=%22Path%22+is+%22onedev%22)



## 🐒 Smart query that can be saved and subscribed

Powerful and intuitive query for everything. Save query for quick access. Subscribe to
query to get notified of interesting events.
[**Try It**](https://code.onedev.io/onedev/server/~issues)



## 🤖 Built-in AI for DevOps Intelligence

Query with natural language. Explain code snippets, help understand codebase, review commits and pull requests, assist with CI/CD specs, investigate build errors. 
[**Tutorial**](https://docs.onedev.io/tutorials/ai/working-with-built-in-ai)



## 🧑‍💻 Workspaces for Vibe Coding

Work on any branch with preconfigured dev containers on server. Use OpenCode, Claude Code, Codex, or other terminal agents in browser without setting up local environment.
[**Tutorial**](https://docs.onedev.io/tutorials/ai/working-with-workspaces)



## 🤹 Skills and CLI to interact with OneDev in AI agents

TOD CLI to query and edit issues, pull requests, and builds, run CI/CD jobs against local changes, and check out pull requests locally. Shipped skills to drive OneDev workflows in coding agents.
[**Skills and CLI**](https://onedev.io/blogs/onedev-cli-and-agent-skills)



## 🤖 AI for DevOps intelligence

AI users work autonomously in your issue and pull request flow: implement assigned issues, review and improve pull requests, fix CI/CD failures, and resolve merge conflicts. They can also explain code, summarize long discussions, help write CI/CD specs, and answer project questions.
[**Tutorial**](https://docs.onedev.io/category/ai)



## 🎛️ Dashboard for teams and users

Arrange gadgets in custom dashboard to get important information
at a glance. Share dashboard with users or groups, or make it public
for everyone.
[**See It In Action**](https://code.onedev.io/~dashboards)



## 👯 Effortless high availability and scalability

Easy cluster setup. Replicate projects across different servers
for high availability, or distribute projects for horizontal scalability.
[**More Info**](https://docs.onedev.io/administration-guide/high-availabilty-scalabilty)



## 🛸 Command palette for quick access

Use cmd/ctrl-k to bring up command palette from anywhere.
Search anything and jump to it without digging through menus.
[**Try It**](https://code.onedev.io)



## 🕊️ Fast, lightweight, and reliable

Crafted with resource usage and performance in mind. Get all features above with a 1 core 2G mem box
for medium-sized projects. Intensively used for more than 5 years, with battle-proven reliability.
[**Performance Comparison**](https://faun.pub/performance-compasion-of-onedev-and-gitlab-c11fc27b25be#:~:text=Git%20Push%3A%20OneDev%20is%2040,50%25%20less%20memory%20than%20GitLab)

---

### Overview (`e2e-test/README.md`)

# OneDev end-to-end tests

This project uses [Playwright Test](https://playwright.dev/docs/intro) to test
the OneDev web interface in Chromium.

## Setup

Node.js 20 or newer is required. The service desk tests also require Java and
Maven to install and run the local GreenMail server.

```bash
cd e2e-test
npm install
npm run install:browsers
npm run install:mail-server
```

## Run the tests

```bash
npm test
```

By default Playwright starts `./dev.sh run` from the repository root and waits
up to ten minutes for `http://127.0.0.1:6610`. If a server is already listening
there, Playwright reuses it.

Server stdout is hidden by default, including the stack traces deliberately
triggered by Jev failure tests. Server stderr and test failures remain visible.
Full application logs remain in `server-product/target/sandbox/logs/server.log`.
To show server stdout when Playwright starts the server, run:

```bash
E2E_SERVER_STDOUT=1 npm test
```

Before running tests, each worker polls `~api/server/ready` without
authentication until it returns `true`, allowing up to ten minutes for server
initialization to finish.

When Playwright starts the server, it passes OneDev
[unattended setup](https://docs.onedev.io/installation-guide/run-as-docker-container)
environment variables so the first-run wizard is skipped: admin user/password
`admin`/`admin`, email `admin@example.com`, and `initial_server_url` set to
`http://<host-ip>:6610` (SSH root URL left unset). These only apply on a fresh
data directory.

To test a server started separately, or a server at another URL, use:

```bash
E2E_SKIP_WEBSERVER=1 npm test
E2E_SKIP_WEBSERVER=1 E2E_BASE_URL=https://onedev.example.com npm test
```

For local debugging, `npm run test:headed` opens the browser and
`npm run test:ui` starts Playwright's interactive test runner.

## SSH cloning

`tests/ssh-clone.spec.js` requires Git, OpenSSH (`ssh` and `ssh-keygen`),
and a reachable SSH service at the server's advertised SSH clone URL (port 6611
by default). Its SSH fixture waits up to 60 seconds for a TCP connection to the
host and port in that URL, after HTTP readiness. This also supports custom SSH
ports without making other tests depend on SSH.

It tests Ed25519 and 3072-bit RSA keys, with separate RSA cases
forcing SHA-256 and SHA-512 signatures. Each case generates a temporary key,
checks that cloning fails before registration, registers the key for a test user
with the Code Reader role, then clones and verifies the checked-out content and
commit. SSH is isolated from local keys, agents and configuration. Temporary keys, checkouts,
users and projects are cleaned up.

```bash
npm test -- tests/ssh-clone.spec.js
```

## Login redirects and page-store warnings

`tests/login-redirect.spec.js` checks protected system settings and private
projects with fresh and existing anonymous sessions. It verifies session
replacement, return to the requested URL with its query parameters, recovery
from an incorrect password, and authenticated access after reload. A second-tab
login must close the previous session's WebSocket; replaying the old session
cookie after login must still require authentication.

It also opens a private issue comment link while logged out, verifies the
fragment is saved during login and restored afterward, and checks that the
comment is scrolled into view. The saved fragment is consumed, so a later visit
without a fragment does not jump back to the comment.

Wicket catches exceptions from its page-store statelessness check, so the
browser can complete a redirect while the server logs an uninitialized
`wicket:enclosure` error. To include this regression check, add the following
logger to the test server's `conf/logback.xml` and restart it:

```xml
<logger name="org.apache.wicket.pageStore.RequestPageStore" level="WARN"/>
```

The default `org.apache.wicket` logger is set to `ERROR`, which hides these
warnings. Pass the server's local log file when running the test:

```bash
E2E_SKIP_WEBSERVER=1 \
E2E_SERVER_LOG=../server-product/target/sandbox/logs/server.log \
npm test -- tests/login-redirect.spec.js
```

The test attaches the log entries written during each case and fails on
statelessness-check or enclosure errors. Use a dedicated test server and a log
file that will not rotate during the run. Without `E2E_SERVER_LOG`, only the
browser assertions run, which also supports remote test servers.

## WebSocket partial updates

`tests/websocket-request-handler.spec.js` covers the OneDev customizations in
Wicket's `WebSocketRequestHandler` through real browser WebSocket connections:

- An issue title change made through REST replaces its list row, with
  `beforeElementReplace` seeing the attached old node and `afterElementReplace`
  seeing the attached replacement.
- A link added after the issue page loads introduces a new state badge through
  a WebSocket update. Changing only the linked issue then updates that badge
  twice on the same connection, exercising subscription registration after rendering.
- Submitting the iteration form in the same browser session, without following
  its redirect, leaves a pending success message. An independent issue change
  delivers that message in the WebSocket response and displays the feedback panel.

`tests/websocket-resource-reference.spec.js` checks that
`WicketWebSocketJQueryResourceReference` selects OneDev's adjacent JavaScript
and that its Ajax coordination works. It holds a real issue-query Ajax response,
changes the issue through REST, and waits for the resulting WebSocket frame.
The DOM must retain the old title while Ajax is pending, then show the newer
pushed title after the held response completes. This protects against an older
Ajax response overwriting a newer WebSocket update (OD-2661).

The tests create and clean up their own projects and use the default `Related`
issue link and `Open`/`Closed` states. They do not require changes to server code
or logging configuration. For an existing server, set `E2E_ADMIN_USER` and
`E2E_ADMIN_PASSWORD` if its credentials differ from `admin`/`admin`.

```bash
npm test -- tests/websocket-*.spec.js
```

## Jev issue suggestions

`tests/jev-issue-suggestions.spec.js` configures a fake API key and tests the real
new-issue form against a local HTTP mock. It verifies the outgoing authorization,
model, title, description and choice questions, with no project path or other
metadata in the request state; saving suggestions at the 75% confidence threshold;
ignoring lower-confidence/unknown choices; preserving a manual choice;
discarding a response after the title changes; and recovering from an API failure
on a later edit.

Jev requests originate in the Java server, so browser request interception cannot
mock them. The fixture starts a local HTTP server on an automatically assigned
port, then saves its URL and a fake API key through the Jev settings page. It
restores the original settings afterward. Jev's normal base URL defaults to
`https://api.typesafe.ai/v1`; custom URLs can include a port, and `/systemone` is
appended automatically.

```bash
npm test -- tests/jev-issue-suggestions.spec.js
```

These tests also work with an already-running local OneDev server:

```bash
E2E_SKIP_WEBSERVER=1 npm test -- tests/jev-issue-suggestions.spec.js
```

Use one worker and a dedicated test server with the default Type and Priority
fields: the fixture temporarily removes their defaults and changes Jev settings,
restoring both after each case. No special server startup options are needed.

The mock listens on the test runner's loopback interface, so run the Java server
on the same host. No real Jev key or external API calls are needed.

## Jev service desk fields

`tests/jev-service-desk.spec.js` sends a generated email over SMTP to a local
[GreenMail](https://greenmail-mail-test.github.io/greenmail/) mailbox. OneDev's
real SMTP/IMAP connector polls that mailbox and creates the issue through the
service desk. The tests reuse the Jev mock in `tests/jev-fixtures.js`.

Install the pinned standalone mail server once (requires Maven and Java), then
run both Jev suites:

```bash
npm run install:mail-server
npm test -- tests/jev-*.spec.js
```

The mail fixture starts and stops GreenMail automatically on local ports. It
waits for OneDev to poll the empty inbox before sending, because OneDev skips
pre-existing messages on its first connection. Outgoing notifications also stay
inside GreenMail. Its protocol log is attached to each test result.

The tests verify that request state contains only the email title and description,
without the project path. They check single and multiple choices at the 75%
confidence threshold, assignee scripts using the resolved values, and configured
fallbacks for lower-confidence or unknown answers and API failures.

Use a dedicated server and one worker, as with the new-issue form tests. Test
settings are restored, and test projects and users are removed. No external
mailbox, SMTP credentials or real Jev key is
required. Both Jev suites run as part of the normal test run.

---

### Overview (`server-product/helm/README.md`)

# OneDev Helm Chart

Git Server with CI/CD, Kanban, and Packages

### Homepage
https://onedev.io

### Documentation 
https://docs.onedev.io/installation-guide/deploy-to-k8s

### Chart Values
https://code.onedev.io/onedev/server/~files/main/server-product/helm/values.yaml

### Upgrading

Follow the [Kubernetes upgrade guide](https://docs.onedev.io/upgrade-guide/deploy-to-k8s)
and use a chart version containing the upgrade hook.

Image upgrades require downtime. The hook shuts down all OneDev pods in this
release before Helm applies the new image, then Helm restores `onedev.replicas`.
Unchanged images skip shutdown. Shut down any other servers sharing the database
separately, and keep PVC retention on scale-down set to `Retain` (the default).
Keep the StatefulSet name unchanged and any rolling-update partition at zero.

Configure the hook under `onedev.upgrade` in [values.yaml](values.yaml):

- `stopAllServers`: defaults to `true`; disable only when external tooling stops the servers.
- `stopTimeoutSeconds`: wait for servers to stop, default 240 seconds. Also allow enough time with Helm's `--timeout`.
- `image`: override the hook image when using a private registry.
- `podLabels` / `podAnnotations`: configure the hook pod separately; disable sidecar injection here when needed.

If the hook fails, inspect its Job logs, resolve the cause, and retry. The
StatefulSet may remain scaled to zero. Rolling back the image does not undo
database migrations.

---

### Overview (`server-product/wrapper/README.md`)

# OneDev packaging integration

The wrapper source now lives in the separate
[wrapper project](https://code.onedev.io/onedev/wrapper).
This directory contains OneDev's JVM defaults, prepared distribution files, and
`update.sh` and `update.bat` for downloading and importing releases, with Unix
packaging verification in `update.sh`. The generic
wrapper project has no OneDev application code or packaging profile.
To update to the latest release from the
[public download site](https://code.onedev.io/onedev/wrapper/~sites/public/),
run from this repository (requires Bash, curl, tar, a SHA-256 utility, and network access):

```sh
./server-product/wrapper/update.sh
```

On Windows, use Windows PowerShell 5.1 and `tar.exe` (included with current
Windows versions):

```bat
server-product\wrapper\update.bat
```

The command downloads the release archive and its SHA-256 checksum and imports
them using the same verification and adaptation described below. The site contains
the latest successful publication. Downloads are removed after the update.
Review and commit the updated files in this directory with the server changes.

Every remote or local update imports the files and regenerates the prepared
server and agent configurations, even when the version is unchanged.

For local testing, build in the sibling wrapper checkout and import its
output directly, without creating or downloading a release archive:

```sh
(cd ../wrapper && ./build.sh)
./server-product/wrapper/update.sh ../wrapper
```

The equivalent Windows commands, run from the server repository, are:

```bat
..\wrapper\build.bat
server-product\wrapper\update.bat ..\wrapper
```

`update.bat` also accepts `update`, `import`, `--checksum`, `--destination`, and
`--sandbox` with the same meanings as `update.sh`. For example:

```bat
server-product\wrapper\update.bat ..\wrapper --sandbox
server-product\wrapper\update.bat import C:\downloads\wrapper-VERSION.tar.gz
```

Pass `/path/to/wrapper` for a checkout elsewhere. This copies the
available binaries and `wrapper.jar` from `dist/boot`, adapts the checkout's
launcher templates, and refreshes both server and agent files.
Platforms absent from the local build retain their bundled binaries, so a
host-only `./build.sh` is sufficient. All binaries already in `dist/boot` are
imported, including those from previous builds. `VERSION` is marked with a
`-local` suffix. Refresh the development sandbox as described below, or repackage
OneDev to update a release archive; `./dev.sh build` alone does not refresh wrapper
files in an existing sandbox. Local imports are for development;
run the updater without arguments to restore a published release before committing
distribution files.

To also refresh wrapper binaries and scripts in an existing development sandbox,
stop its server and add `--sandbox` to the update command:

```sh
./server-product/wrapper/update.sh --sandbox
# Or import a local build and then refresh the sandbox:
./server-product/wrapper/update.sh ../wrapper --sandbox
```

After successfully updating the prepared distribution, this copies the server and
embedded agent launchers, native executables, and `wrapper.jar` into
`server-product/target/sandbox`. It preserves configuration, application JARs,
logs, and site data. If the sandbox does not exist, the command prints an error
and exits before downloading or importing anything. Create it with `./dev.sh build`
first. The flag also works with `import`; when using `--destination`, the sandbox
is located at `../target/sandbox` relative to that destination. Distribution
archives still need to be repackaged separately.

To import an archive downloaded separately, place `wrapper-VERSION.tar.gz`
and its `.sha256` together, then run:

```sh
./server-product/wrapper/update.sh import /path/to/wrapper-VERSION.tar.gz
```

The importer verifies the archive's `.sha256` checksum. It accepts both
`wrapper-latest.tar.gz` and versioned archives, copies all upstream boot files,
and adapts generic launchers for server/agent and one-shot utilities. It renders
server and agent configurations from `wrapper.conf`. These integration rules
live in the server project. The agent configuration retains the `agentVersion` placeholder,
which is resolved when assembling a standalone agent or downloading one.

`distribution/` contains the exact installation-relative files Maven will copy:

- `boot/`, `bin/`, and `conf/` contain the server's wrapper files.
- `agent/boot/`, `agent/bin/`, and `agent/conf/` contain the agent's wrapper files.

The service names (`wrapper.name`) are `onedev.server` and `onedev.agent`.
Windows uses these SCM names, and Linux uses `onedev.server.service` and
`onedev.agent.service` (or the same names without `.service` for SysV).
On macOS, `wrapper.launchd.label` selects `io.onedev.server` and `io.onedev.agent`
as the launchd labels and plist filenames (with `.plist` appended). Stop/remove
an existing service using its old configuration before changing its label, then
install/start with the new configuration.

Keep this prepared tree in version control. The Unix maintenance script uses Bash 3.2+
and standard Unix tools (`sha256sum` on Linux or `shasum` on macOS). The Windows
script uses PowerShell and `tar.exe`, without Bash. Neither requires Python or
Node.js. Ordinary Maven builds consume the checked-in files. Importing recreates
the tree, so removed upstream files cannot survive into a new release. To change
JVM defaults or the adaptation rules, edit `wrapper.conf` or the importer and
reimport the pinned release archive.

The Maven plugin has no wrapper Maven dependency, does not download its
releases, and does not require its source checkout. It copies these prepared files
into the server and agent distributions. Rust is not needed to package OneDev.

The companion maven-plugin 2.9.1 consumes a generic prepared distribution instead
of interpreting JSW/native layouts, launcher templates, platform names, or wrapper
configuration keys. Publish that plugin change before the server change.
`server-product/pom.xml` configures the prepared directory, generated paths to
replace on incremental builds, agent files needing version substitution, and
executable permissions. Future wrapper changes belong in this repository's
importer, inputs, or POM and do not require a Maven plugin release as long as this
prepared-file contract remains the same.

Validate the resulting release with (the package checker requires `unzip`, and
the tests also use `zip` to create package fixtures):

```sh
./server-product/wrapper/update.sh test
./server-product/wrapper/update.sh check-package server-product/target/onedev-VERSION.zip
```

Run `./server-product/wrapper/update.sh help` for all commands and import options.
Running the script with no arguments is equivalent to the `update` command.

On Windows, run `server-product\wrapper\update.bat test` for offline import,
local build, sandbox refresh, and release discovery checks, or use `help` for
usage. Run `update.sh check-package` on Unix to check packaged executable
permissions as well as contents; that command is not provided by `update.bat`.

Existing `server.sh` / `agent.sh` commands and `conf/wrapper.conf` remain supported.
The Java lifecycle API is `io.onedev.wrapper.WrapperManager`, with
`restartAndReturn()` and `stopAndReturn(int)`. The launcher class is
`io.onedev.wrapper.WrapperSimpleApp`. Update the wrapper and application jars
together when migrating from an earlier Java API package.
Server upgrades replace boot files and scripts while retaining `wrapper.conf`
and updating its lifecycle bridge class. When migrating a JSW configuration
(16.8.8 or earlier), the upgrader sets a missing `wrapper.launchd.label` to
`org.tanukisoftware.wrapper.<existing wrapper.name>`. Explicit labels are preserved.
This retains the standard old macOS registration and its `launchdinternal`
launcher argument, so an Apple Silicon service can be stopped before the upgrade
and started afterward without uninstalling/reinstalling it. Existing Linux and
Windows service names are also retained. Fresh installations use the new names
and labels documented above. Intel Macs are not supported by the new wrapper.

Existing agents update only application jars and continue using their installed
wrapper. The agent supports both the new WrapperManager and JSW's WrapperManager
for restart and stop requests, so automatic application updates do not require
migrating the native wrapper or reinstalling the service.

To migrate an existing agent's native wrapper, stop it and replace `boot` and
`bin` from a new agent distribution while retaining configuration. Set
`wrapper.java.mainclass=io.onedev.wrapper.WrapperSimpleApp` and, on macOS, set
`wrapper.launchd.label` to the installed JSW label (normally
`org.tanukisoftware.wrapper.onedevagent`). Keep the old `wrapper.name` and launcher
path to retain the existing service registration. Newly downloaded agents use Wrapper.

Review the standalone project's configuration compatibility list before migrating
custom JSW features. Native SCM, launchd, and systemd acceptance testing is still
required before the initial production release.

The Docker upgrade invocation explicitly disables crash retries for its one-shot
operation. Generic Wrapper does not inspect the application's class name.
Launchers supply their own paths when installing services. When invoking a native
binary directly to install a service, specify `wrapper.script=../bin/server.sh`
or `wrapper.script=../bin/agent.sh` for the corresponding distribution.

---

### Doc Hibernate Customizations (`doc/hibernate-customizations.md`)

# Why OneDev overrides Hibernate

OneDev overrides eight Hibernate classes in `server-core/src/main/java/org/hibernate`.
The reasons below were checked against Git history and the source jars for the
versions in `pom.xml`: Hibernate ORM 7.4.8.Final and Hibernate Validator 9.1.3.Final.
The [library override audit](library-customizations.md) also covers the Jakarta
web stack and the removal of the now-redundant Commons Lang proxy-cloning patch.

| Customized class | Why OneDev needs it | Regression coverage |
| --- | --- | --- |
| `cache.spi.support.SimpleTimestamper` | Replace the clock-based busy loop with an atomic maximum of wall-clock time and the previous counter plus one to prevent excessive CPU usage during cache timestamp generation. Introduced in `8de9b24808` for #711. | `SimpleTimestamperTest`: a counter ahead of the wall clock still advances; concurrent cache users get unique, increasing timestamps. The clock regression uses an isolated counter, not the machine clock. |
| `proxy.pojo.BasicLazyInitializer` | Apply `AbstractEntity` ID equality and hashing without loading the entity. Added with the large-user-count performance improvements in `a32c76862a` (#875). Collection membership checks should not fetch every referenced user. | `BasicLazyInitializerTest`: detached references with equal/different IDs, loaded entities, null/unrelated values, unsaved identity semantics, and ordinary method delegation. Calls the interceptor directly because `AbstractEntity`'s final methods can bypass interception on real proxies. |
| `validator.internal.metadata.aggregated.MetaDataBuilder` | Let editable subclasses replace constraint attributes instead of combining parent and child annotations of the same type. Introduced in `de278c1554`. | `HibernateValidationInheritanceTest`: metadata contains the child's `@Size`, and value validation uses its limit and message. |
| `validator.internal.engine.MethodValidationConfiguration` | Permit `@Valid` on both overridden and overriding getters, consistent with OneDev's getter replacement semantics. Added in `e3b23bd5ba` for Kaniko build-spec validation, OD-2564. | `HibernateValidationInheritanceTest`: redeclared object and collection cascades build metadata successfully and still report invalid children. |
| `validator.internal.engine.ValidatorImpl` | Respect removed/overridden getter constraints and cascades; skip fields and nested beans hidden by `@DependsOn` or `@ShowCondition`; validate properties and nested beans before `@ClassValidating`, and suppress class validation after failures. The changes span `de278c1554`, `8ac7615738`, `77cf1da58c`, and `e3b23bd5ba`. Display-name lookup fixes hidden parameters (OD-2560, `0defe8b62a`); repeated dependencies support multiple visibility conditions (OD-2884, `91457af05e`). | `HibernateValidationInheritanceTest`, `HibernateValidationVisibilityTest`, and `HibernateValidationOrderTest`: inherited/replaced/removed annotations, visible and hidden scalar/object/list properties, repeated/inverse dependencies, display-name lookup, edit-context cleanup, and successful/failing recursive validation through default groups, explicit groups, and sequences. |
| `validator.internal.engine.constraintvalidation.ConstraintTree` | Validate the shape of interpolative settings before runtime variables are available: substitute `@Interpolative.exampleVar` and unescape `@@` before other validators run. Apply this to strings and collections, while leaving syntax errors to `InterpolativeValidator`. Introduced with #222 in `0dd7555781`; the real create-tag scenario was added in #1242 (`52dca1aab9`). | `HibernateValidationInterpolationTest`: real `CreateTagStep` names, escaped at signs, custom example values, invalid literals, malformed interpolation, null values, and collections. Assertions also ensure validation does not modify the bean or the reported invalid value. |
| `validator.internal.metadata.core.MetaConstraint` | Expose the active constraint's getter metadata to `ConstraintTree` with a thread-local stack. Added alongside interpolation in `0dd7555781`; nested validation must restore the previous constraint and exceptions must clean it up. | `HibernateValidationInterpolationTest`: interpolation through the public validator API, nested validation, exception cleanup, and isolation between concurrent validation threads. |
| `validator.internal.engine.valuecontext.BeanValueContext` | Share processed-constraint bookkeeping within one validation context tree. Hibernate 9 otherwise repeats cascaded constraints across group sequences when OneDev separates property, cascade, and class validation. The map is confined to one validation call and is discarded with its context. | `HibernateValidationOrderTest`: nested class validators run once across a group sequence; existing concurrent and nested validation tests remain in place. |

The validation tests are in `server-core/src/test/java/io/onedev/server/validation`.
They use a real
`ValidatorFactory` and small beans so they need neither a running OneDev server
nor a database. These behaviors intentionally differ from standard Bean Validation.

`HibernateValidationContainerCascadeTest` also covers cascades declared on getter
return type arguments, including nested containers and inherited/redeclared/removed
cascades. The override check in `ValidatorImpl` recognizes these annotations so
`BranchProtection#getFileProtections()` can use `List<@Valid FileProtection>` without
losing property or class validation of its entries.

## Consistent validation across entry points

The inheritance and ordering tests enforce these rules in `ValidatorImpl`:

- A child annotation replaces the parent in property metadata, `validateValue`,
  and bean/property validation, including the default group. Overriding a getter
  removes its parent's getter constraints; annotations redeclared on the child
  use the child's attributes. Getters inherited without an override retain their
  constraints.
- `@ClassValidating` runs after properties and nested beans and is skipped when
  earlier validation has reported a violation, for both default and explicit
  non-default groups. Sequences stop after a failing group.

The tests include ordinary validation without explicit groups, as well as explicit
groups and group sequences. These rules replace the previous default-group behavior
that also checked overridden parent attributes and ran class validators after failures.

---

### Doc Missing Issue Fixes (`doc/missing-issue-fixes.md`)

In case we forget to mention fixed issues when commit the change, edit and commit this file with appropriate message so that issues can be linked to builds

issue #417 
issue #34
issue #48 
issue #55 
issue #102 
issue #5 
issue #99 
issue #132 
issue #148 
issue #172
issue #173
issue #191 
issue #212 
issue #223 
issue #255
issue #330
issue #232 
issue #448
issue #489
issue #626
issue #753 
issue #953 
issue #997 
issue #1001 
issue #1134 
issue #1326
issue #1327 
issue #1338 
issue #1471
issue #1533 
issue #1547 
issue #1580
issue #7
issue #1693
issue #1722
issue #1729 
issue OD-2063 
OD-2122
OD-2223 
OD-2248 
OD-2257 
OD-2358 
OD-2403 
OD-2467 
OD-2552 
OD-2694
OD-2870
OD-2963 
OD-2964 
OD-2962 
OD-3016
OD-3050 
OD-2276 
OD-3083 
OD-3162 
OD-3183

---

### Doc Wicket Customizations (`doc/wicket-customizations.md`)

# Why OneDev overrides Wicket 10.11.0

The 17 Java classes and two resources under
`server-core/src/main/java/org/apache/wicket` shadow the Wicket dependencies.
They were compared with the 10.11.0 release source jars, and their application
patches were checked against the former 7.18.0 overrides and Git history.
Formatting, import ordering, and warning suppressions are not customizations.

Baselines are the `wicket-core`, `wicket-extensions`, `wicket-request`,
`wicket-util`, `wicket-native-websocket-core`, and `wicket-guice` source jars in
[Maven Central](https://repo.maven.apache.org/maven2/org/apache/wicket/), at
`<artifact>/10.11.0/<artifact>-10.11.0-sources.jar`.
Paths below are relative to `server-core/src/main/java/org/apache/wicket`.

## Component context and forms

| Override | Retained behavior |
| --- | --- |
| `Component.java` | Push/pop OneDev's `HierarchicalContext` around initialization/re-addition, before-render, render/after-render, and header rendering. Contextual choices, defaults, and visibility resolve against the current editor, with cleanup on exceptions. |
| `core/request/handler/ListenerRequestHandler.java` | Scope both component and behavior callbacks, including authorization, to the originating component. This replaces the removed Wicket 7 `RequestListenerInterface` override. |
| `extensions/ajax/markup/html/autocomplete/AutoCompleteBehavior.java` | Scope the separately scheduled suggestions response to its owning component. The listener's context has already ended at that point. |
| `markup/html/form/FormComponent.java` | Initialize converted input from the model, retain it through detach and serialization, and include errors on ordinary child components when determining validity. Dependent editors can read uncommitted values, and composite editors fail validation when their labels/containers have errors. |

`ComponentContextCustomizationTest` covers initialization/ancestor lookup,
listener failure, authorization, and restoration of enclosing context.
`FormComponentCustomizationTest` covers initial and uncommitted values,
serialization, and nested feedback. Rendering/head and autocomplete callback
scopes remain source-audited contracts, rather than separate integration tests.

## Dynamic classes and dependency injection

| Override | Retained behavior |
| --- | --- |
| `application/AbstractClassResolver.java` | Resolve generated issue-field and parameter beans through `FieldUtils`/`ParamUtils` before Wicket's weak class cache. Generated classes follow the current configuration instead of an obsolete cached definition. |
| `guice/GuiceProxyTargetLocator.java` | Resolve lazy injections using `AppLoader.injector`, including when no Wicket application is bound to the thread. |

These behaviors still differ from Wicket 10 and depend on OneDev's runtime class
and injector lifecycle. The rationale is inferred from the callers; the available
history does not establish a separate original incident for each hook.

## Page storage and request processing

| Override | Retained behavior |
| --- | --- |
| `pageStore/RequestPageStore.java` | Store touched pages without inspecting `isPageStateless()`, which can evaluate an uninitialized enclosure during a login redirect. Suppress only `IllegalStateException` messages containing `Response is committed` during session binding/page storage, with debug logging. Clear pending pages after this failure so detach cannot retry session creation. Other failures propagate. These behaviors replace the former `RequestAdapter` and part of `PageStoreManager`. |
| `request/cycle/RequestCycle.java` | Cache rendered resource and bookmarkable-page URLs within each request (#429), suppress the extra development-mode exception banner, and log nested Jetty EOF failures at debug when error-response handling fails. |

The old `PageStoreManager` session-cache customization is implemented through
OneDev's `web/page/store/PageManagerProvider`: it omits Wicket's session cache of
live pages and writes serialized pages synchronously to disk, loading them on
demand. No serialized-page cache or asynchronous write queue is retained in heap;
page objects and serialization buffers still exist while processing requests.
Requests wait for disk writes to complete. This uses Wicket 10's store extension
points instead of copying its old page manager.
`RequestAdapterCustomizationTest` covers both lifecycle phases,
committed responses, unexpected exceptions (including null messages), and avoiding
statelessness checks.

The Chromium comparison in `e2e-test/tests/login-redirect.spec.js` also reproduces
the enclosure problem with real anonymous requests to system settings. Restoring
Wicket 10.11.0's original `isPageStateless` helper produces
`Could not find child with id: ingressUrl in the wicket:enclosure` in both `end()`
and `detach()`. Wicket catches these exceptions and assumes the page is stateful,
so login can still complete. OneDev's default Wicket log level (`ERROR`) hides
the warnings. The override prevents the exceptions; the optional
`E2E_SERVER_LOG` assertion detects them with the page-store logger at `WARN`.
See `e2e-test/README.md` for the command and logging setup.

`RequestCycleCustomizationTest` covers cache reuse and request/parameter isolation.
The URL caches retain mutable `PageParameters`; their existing contract does not
promise correctness if callers mutate parameters or the renderer's base URL after
caching. Exception logging is source-audited, not asserted by those tests.

## URLs and login

| Override | Retained behavior |
| --- | --- |
| `core/request/mapper/BasicResourceReferenceMapper.java` | Lowercase the scope class segment for agents that lowercase URLs, and maintain the reverse class-name map (`d642b2383f`). The port uses `Locale.ROOT`. The map still depends on the URL having been generated in the process. |
| `request/http/handler/RedirectRequestHandler.java` | Rebase application-relative redirect destinations against the current request with `UrlRenderer.renderRelativeUrl` (`59b517746c`). |
| `RestartResponseAtInterceptPageException.java` | Remove the backend host/port/protocol from the saved URL (#137), and remove behavior callback metadata before returning from login (#1301). Wicket 10 encodes numeric behavior IDs, so the patch now uses `MapperUtils.parsePageComponentInfoParameter` instead of searching for `.IBehaviorListener.`. Page-version and ordinary valued query parameters survive. |
| `protocol/http/servlet/ServletWebResponse.java` | Preserve browser fragments across login using session storage and a scripted redirect (#114). Crawlers retain servlet redirects; Ajax retains upstream Ajax redirects. |

`RedirectRequestHandler` was introduced in `59b517746c` ("Fix the issue that page
redirecting works abnormally"). Wicket 10.11.0 leaves destinations without a
leading slash unchanged. For example, `ProjectSiteFileResource` redirects
`project/~sites/public` to the application-relative `project/~sites/public/`
when the public site directory exists. Passing that string
directly to the browser resolves it as `project/project/~sites/public/`; rendering it
relative to the current request produces the intended `project/~sites/public/` URL.

`InterceptRedirectCustomizationTest` generates callback metadata using Wicket 10's
encoder and verifies removal, ordinary/duplicate parameters, page versions, and
backend-origin removal. It reproduces the old string-matching patch's failure.
`e2e-test/tests/login-redirect.spec.js` verifies a logged-out private issue
comment link through login, including fragment storage/restoration, scrolling
to the comment, and consuming the saved fragment. Proxy/context paths, explicit
destination fragments, and crawler/Ajax requests still need verification when
the redirect script changes.

OneDev's `LoginPage` calls
`RestartResponseAtInterceptPageException.replaceSessionPreservingOriginalDestination()`.
This helper saves only the intercepted URL and POST parameters, calls the normal
`WebSession.replaceSession()`, and restores that one metadata entry. Replacing
the session destroys the old HTTP session, runs WebSocket/chat cleanup listeners,
and clears OneDev's session fields and unrelated metadata. Plain ID rotation
would bypass this cleanup. Wicket 10's unwrapped `replaceSession()` loses the
intercepted destination and returns users to the home page after login.

`InterceptRedirectCustomizationTest` verifies the preserved request and removal
of unrelated metadata, servlet attributes, and OneDev session fields; ordinary
session replacement still clears the destination. The browser tests cover
fresh/existing anonymous sessions, a rejected password followed by successful
login, page reloads, closure of an old tab's WebSocket, and rejection of the old
session cookie after login.

## WebSockets

| Override | Retained behavior |
| --- | --- |
| `protocol/ws/api/AbstractWebSocketProcessor.java` | Replay observable changes missed between rendering and connection through `WebSocketService.onConnect`, and send OneDev's error message when processing fails. Wicket 10 already exposes `getRegistryKey()` to the subclass. Its transport error API can remain: OneDev's Jetty listener handles transport errors directly without calling that broadcast path. |
| `protocol/ws/api/WebSocketRequestHandler.java` | Include session feedback, emit `beforeElementReplace`/`afterElementReplace`, and re-register observables after replacing components. The customization now extends `XmlPartialPageUpdate`; it still assumes a OneDev `BasePage`. |
| `protocol/ws/api/WicketWebSocketJQueryResourceReference.java` | Select the adjacent customized JavaScript instead of the upstream `res/js/` path. |
| `protocol/ws/api/wicket-websocket-jquery.js` | Defer pushed XML Ajax responses while a OneDev Ajax request is in flight (OD-2661), preserving Wicket 10's channel handling and non-XML messages. |

`e2e-test/tests/websocket-request-handler.spec.js` exercises all three
`WebSocketRequestHandler` patches with real browser connections: replacement
events surround an issue row's DOM change, a newly rendered linked-issue badge
receives subsequent state changes, and pending session feedback appears in a
pushed update. REST changes trigger the pushes without an Ajax request or page
reload in the receiving tab.

`e2e-test/tests/websocket-resource-reference.spec.js` checks the selected
JavaScript resource and its Ajax coordination: a received WebSocket update
waits until a held issue-query response completes, then applies the newer
title. Both responses come from the server; the test controls their delivery
order to reproduce the OD-2661 race.

The `ServletRequestCopy` override was removed. `WebSocketProcessor` now passes
Jetty 12's real `HttpServletRequest` to Wicket; the old Jetty adapter that threw
on character encoding, content type, and requested session ID is gone. Wicket 10's
copy implements Jakarta Servlet. `JakartaJettyIntegrationTest` verifies those
metadata getters with a real Jetty request. It does not claim to test the entire
browser WebSocket reconnect/DOM lifecycle.

`protocol/ws/api/registry/SimpleWebSocketConnectionRegistry.java` was removed:
it was identical to Wicket 10.11.0 apart from whitespace, and had no behavioral
patch relative to 7.18.0 either.

## Navigation and diagnostics

| Override | Retained behavior |
| --- | --- |
| `extensions/markup/html/repeater/data/table/NavigationToolbar.java` | Recreate the navigator before rendering and omit the separate count label (OD-1965). OneDev selects a different toolbar when a count is wanted. |
| `extensions/markup/html/repeater/data/table/NavigationToolbar.html` | Render the navigator as a `ul`, matching `OnePagingNavigator` and OneDev's pagination styling. |
| `util/string/Strings.java` | Use `cause.getMessage()` instead of `cause.toString()` when printing exceptions, avoiding whole HTML templates in diagnostics. Stack frames remain. |

`StringsCustomizationTest` covers nested exceptions with template-producing
`toString()`. Pagination state changes remain a UI verification scenario.

## Verification

```sh
./dev.sh test -pl server-core '-Dtest=*CustomizationTest,JakartaJettyIntegrationTest'
```

On JVMs that disallow Mockito self-attachment, set `JAVA_TOOL_OPTIONS` to
`-javaagent:/absolute/path/to/byte-buddy-agent.jar` for the command. The local audit
used the cached 1.12.19 agent. See [the library audit](library-customizations.md)
for the other libraries and retired overrides.

---

--- METRICS ---
- Files Extracted: 7
- Estimated Token Budget: ~12120 tokens
- Canonical Reference: https://codewiki.google/github.com/theonedev/onedev
