Technical Documentation: theonedev/onedev
โน๏ธ Provenance: Multi-Tier Ingestion ยท CodeWiki Reference
Overview (`readme.md`)
**NOTE: ** We develop OneDev at code.onedev.io for sake of dogfooding. Please submit issues and pull requests there
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 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
๐ฆ 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
๐ฆ 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
๐ฌ 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
๐ Versatile code protection rules
Set rule to require review or CI/CD verification when certain users touch certain
files in certain branches.
Tutorial
๐ 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
๐ 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
๐จ 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
โฐ Time tracking and reporting
Track estimated/spent time on tasks. Aggregate time from subtasks automatically.
Generate time sheets for work statistics and billing.
Tutorial
๐ก 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
๐ 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
Example2
๐ 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
Tutorial2
๐ฆ Built-in package registries
Built-in registry to manage binary packages. Link packages with
CI/CD jobs.
Tutorial
๐ 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
๐งฉ 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
Example2
๐ฒ 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
๐ 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
๐ค 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
๐งโ๐ป 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
๐คน 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
๐ค 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
๐๏ธ 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
๐ฏ Effortless high availability and scalability
Easy cluster setup. Replicate projects across different servers
for high availability, or distribute projects for horizontal scalability.
More Info
๐ธ 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
๐๏ธ 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
Overview (`e2e-test/README.md`)
OneDev end-to-end tests
This project uses Playwright Test 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.
cd e2e-test
npm install
npm run install:browsers
npm run install:mail-serverRun the tests
npm testBy 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:
E2E_SERVER_STDOUT=1 npm testBefore 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
environment variables so the first-run wizard is skipped: admin user/passwordadmin/admin, email [email protected], and initial_server_url set tohttp://<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:
E2E_SKIP_WEBSERVER=1 npm test
E2E_SKIP_WEBSERVER=1 E2E_BASE_URL=https://onedev.example.com npm testFor local debugging, npm run test:headed opens the browser andnpm 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.
npm test -- tests/ssh-clone.spec.jsLogin 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 uninitializedwicket:enclosure error. To include this regression check, add the following
logger to the test server's conf/logback.xml and restart it:
<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:
E2E_SKIP_WEBSERVER=1 \
E2E_SERVER_LOG=../server-product/target/sandbox/logs/server.log \
npm test -- tests/login-redirect.spec.jsThe 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
beforeElementReplaceseeing the attached old node andafterElementReplace
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 thatWicketWebSocketJQueryResourceReference 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 andE2E_ADMIN_PASSWORD if its credentials differ from admin/admin.
npm test -- tests/websocket-*.spec.jsJev 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 tohttps://api.typesafe.ai/v1; custom URLs can include a port, and /systemone is
appended automatically.
npm test -- tests/jev-issue-suggestions.spec.jsThese tests also work with an already-running local OneDev server:
E2E_SKIP_WEBSERVER=1 npm test -- tests/jev-issue-suggestions.spec.jsUse 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 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:
npm run install:mail-server
npm test -- tests/jev-*.spec.jsThe 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
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
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:
stopAllServers: defaults totrue; 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.
This directory contains OneDev's JVM defaults, prepared distribution files, andupdate.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,
run from this repository (requires Bash, curl, tar, a SHA-256 utility, and network access):
./server-product/wrapper/update.shOn Windows, use Windows PowerShell 5.1 and tar.exe (included with current
Windows versions):
server-product\wrapper\update.batThe 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:
(cd ../wrapper && ./build.sh)
./server-product/wrapper/update.sh ../wrapperThe equivalent Windows commands, run from the server repository, are:
..\wrapper\build.bat
server-product\wrapper\update.bat ..\wrapperupdate.bat also accepts update, import, --checksum, --destination, and--sandbox with the same meanings as update.sh. For example:
server-product\wrapper\update.bat ..\wrapper --sandbox
server-product\wrapper\update.bat import C:\downloads\wrapper-VERSION.tar.gzPass /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:
./server-product/wrapper/update.sh --sandbox
# Or import a local build and then refresh the sandbox:
./server-product/wrapper/update.sh ../wrapper --sandboxAfter successfully updating the prepared distribution, this copies the server and
embedded agent launchers, native executables, and wrapper.jar intoserver-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:
./server-product/wrapper/update.sh import /path/to/wrapper-VERSION.tar.gzThe importer verifies the archive's .sha256 checksum. It accepts bothwrapper-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/, andconf/contain the server's wrapper files.agent/boot/,agent/bin/, andagent/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 andonedev.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):
./server-product/wrapper/update.sh test
./server-product/wrapper/update.sh check-package server-product/target/onedev-VERSION.zipRun ./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, withrestartAndReturn() and stopAndReturn(int). The launcher class isio.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 toorg.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 andbin from a new agent distribution while retaining configuration. Setwrapper.java.mainclass=io.onedev.wrapper.WrapperSimpleApp and, on macOS, setwrapper.launchd.label to the installed JSW label (normallyorg.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 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 realValidatorFactory 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 soBranchProtection#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. @ClassValidatingruns 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 underserver-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, 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 producesCould 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 optionalE2E_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 redirectsproject/~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 callsRestartResponseAtInterceptPageException.replaceSessionPreservingOriginalDestination().
This helper saves only the intercepted URL and POST parameters, calls the normalWebSession.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 threeWebSocketRequestHandler 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-producingtoString(). Pagination state changes remain a UI verification scenario.
Verification
./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
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