Before publishing a release:
- Confirm
LICENSEis the Apache License 2.0 and the README license section points to it. - Confirm
CONTRIBUTING.md,SECURITY.md,CODE_OF_CONDUCT.md, and the.githubissue / pull-request templates are present. - Confirm the README still states the at-least-once delivery guarantee, Java 25 requirement, PostgreSQL 18+ requirement, Redis AOF durability note, and Testcontainers requirement for real backend tests.
Before merging a 1.0 release candidate:
- Complete and review the soak qualification plan, including final drain/counter reconciliation, baseline-relative performance and resource stability. An interrupted run or unexplained growth does not pass.
- Record which datastore versions and topologies were qualified and resolve every release-blocking finding. Requalify affected behavior after fixes.
- Align
ThreadmillVersion.CURRENT, installation examples, the changelog, compatibility/migration guide and release notes at the release version. Historical 0.3.0 references and immutable migration fixtures retain their original versions. - Check the final PR head, required CI, review disposition and merge result.
Run the final candidate gate:
./gradlew productionCheck verifyReleaseTag -PreleaseTag=v<version> -PdependencyScanRequired=trueproductionCheck owns the clean-all-projects boundary, every subproject check,
real-store tests, soak and correctness simulations, Javadoc, the example,
dependency scanning, and artifact inspection. If it fails, fix the cause and
re-run it from a clean tree — no failure is acceptable for a release build.
No freely available container image provides Oracle Database 19c, the oldest supported Oracle release; CI covers 23ai Free and 21c XE. Run the Oracle store suite against a real 19c database before release and record the server release in the validation notes:
./gradlew :threadmill-store-oracle:test -PoracleJdbcUrl=jdbc:oracle:thin:@//host:1521/SERVICE -PoracleUser=... -PoraclePassword=...It deletes all Threadmill rows in that account's schema: use a disposable
schema. Grant it SELECT ANY DICTIONARY (or access to V$SESSION, V$SQL, and
V$SQL_PLAN) so the execution-plan tests run instead of skipping.
Then inspect artifacts:
jar tf threadmill-core/build/libs/threadmill-core-*.jarConfirm no test classes, no local development material, no dynamic dependency
versions, no unresolved security findings, and a non-SNAPSHOT release version.
The pull-request, nightly, and tag-release workflows install pinned Node/npm
and the pinned, checksum-verified OSV Scanner, then run
dependencySecurityScan. The task scans every Git-tracked Gradle/npm lockfile
and runs npm audit at the low threshold; either scanner fails the build on
any known advisory. See
Dependency security for the exact policy,
reachability record, and the strict process for temporary exceptions.
RELEASING.md is the canonical publishing runbook:
- Set and commit the release version.
- Tag that exact commit with the matching
v<version>tag. - Push
mainand the tag. - The GitHub Release workflow validates the tag against the project version,
runs
productionCheck, signs the same verified artifacts from that task graph, and submits one aggregated bundle throughpublishAggregationToCentralPortal. - Central Portal validates and publishes the bundle automatically because
publishingTypeisAUTOMATIC. - Verify every published module and a fresh consumer installation, then create the GitHub release with reviewed notes and the migration link. The workflow does not create that GitHub release itself.
Do not run the obsolete unconfigured ./gradlew publish path and do not wait
for a manual Central Portal promotion. If the automated deployment fails,
correct the source and cut a new version; never patch a published release.