20 KiB
Ta4j Release Process
This document is the operator guide for shipping ta4j releases safely and repeatedly.
Default branch: master
Core release invariant:
- the release tag must point to the intended release commit
- that release commit must be reachable from
master
1. At a Glance
Preferred path (PR mode):
- Scheduler (
release-scheduler.yml) builds a release dossier, validates the configured GitHub Models catalog entry, asks the model for a SemVer decision, and dispatches prepare when appropriate. - Prepare release (
prepare-release.yml) creates release + next-snapshot commits onrelease/<version>. - Maintainer reviews and merges release PR into
masterusing a merge commit. - Publish release (
publish-release.yml) validates metadata/ancestry, runs the release-candidate gate, validates the artifact manifest, tags, deploys to Maven Central, pushes tag, and explicitly dispatches snapshot publication. - GitHub release (
github-release.yml) runs on tag push. - Release health (
release-health.yml) audits drift, verifies the current snapshot version is published once snapshot publication is authoritative, and posts summary.
Emergency path (direct push mode):
prepare-release.ymlpushes both commits directly tomasterwhenRELEASE_DIRECT_PUSH=true.- It then dispatches
publish-release.yml.
Validation path (dry run):
- Manually run any mutating release workflow and keep its default
dryRun=true. - Inspect the computed release/tag/snapshot values, workflow summary, and audit artifacts.
- Rerun manually with
dryRun=falseonly when intentionally mutating, or let the scheduled/push/merge release path run with its explicitdryRun=false. - Prepare dry-runs still run the read-only release-ready and next-cycle deprecation scans and upload their reports.
- No release commits, managed cleanup issue mutations, branch/tag push, Maven Central deploy, GitHub Release creation, snapshot deploy, or discussion/comment mutation occur.
2. Release Modes
| Mode | How it starts | Git changes | Tag/deploy | Typical use |
|---|---|---|---|---|
| PR mode (default) | scheduler or manual prepare | release branch + merged PR | yes | normal production releases |
| Direct push | manual prepare + RELEASE_DIRECT_PUSH=true |
commits land directly on master |
yes | emergency/unblocked maintenance |
| Dry run | manual mutating release workflow, default dryRun=true |
none | no | inspection-first preflight validation |
3. Preconditions (Do This Before Releasing)
Code and docs:
- Update
CHANGELOG.mdunderUnreleased. - Keep README version references current.
- Run
scripts/docs-integrity-check.shand resolve failures before preparing a release.- This gate verifies canonical docs presence, link validity, command references, and TODO-free user-facing entry docs.
- Ensure release notes can be generated cleanly (
release/<version>.md). - Confirm release PRs are visible to release owner:
- release PR must be labeled
release. - release PR is auto-assigned to
TheCookieLab. TheCookieLabis auto-requested as reviewer.
- release PR must be labeled
- While any labeled release PR is open, only that release PR can be merged.
- Open PRs will receive an automatic release-freeze notice comment while the freeze is active. The workflow removes that notice once no release PR remains open.
Repository settings:
- Actions workflow permissions must allow write operations (dispatch, PR updates, tag push).
- Use merge-commit discipline for release PRs (no squash/rebase for those PRs).
- Add
Release Merge Freezeas a required status check onmasterin branch protection.
Secrets and variables:
- Required secrets for publish/snapshot must be configured.
- Scheduler-related variables must be set intentionally (especially
RELEASE_SCHEDULER_ENABLED).
4. Production Runbook (PR Mode)
- Trigger Prepare Release
- Via scheduler or manual
workflow_dispatch. - Inputs:
releaseVersion(optional if auto-detected),nextVersion(optional),dryRun. - Manual runs default
dryRun=true. SetdryRun=falseonly for an intentional mutating run after reviewing a dry-run. Scheduled release automation dispatches prepare with explicitdryRun=false. - For scheduled production runs, confirm
release-scheduler.ymllogsaudit:dry_run_normalized=falseandaudit:dispatch_workflow workflow=prepare-release.yml ... dryRun=false, then confirmprepare-release.ymllogsaudit:dry_run_input_raw='false'andaudit:dry_run_normalized=false. - If
nextVersionis omitted andreleaseVersionis a plainX.Y.Z, it is auto-generated as<major>.<minor>.<patch+1>-SNAPSHOT(for example0.22.2->0.22.3-SNAPSHOT). - For RC/non-plain release versions, provide
nextVersionexplicitly. - Before the workflow commits the next snapshot version, it runs the Java-based
ta4jexamples.doc.RemovalReadyDeprecationScanneragainst the release version and fails if any@Deprecated(forRemoval = true)symbols are due or overdue for removal. This read-only scan also runs in dry-run mode. - After the next snapshot version is known, it scans sources scheduled for that planned snapshot or any earlier removal version because ta4j versions may jump across major, minor, or patch positions. Non-dry-run runs then sync deduplicated GitHub cleanup issues, including reopening still-valid managed issues and closing stale managed issues for the same removal version; dry-runs only upload the scan report.
- The workflow uploads release-gate and next-snapshot removal-ready deprecation report artifacts with grouped findings, symbols, lifecycle status, replacement hints when available, and synced issue links when issue sync runs.
- The scanner JSON is the stable handoff contract for future automation: it includes
schemaVersion,automationNamespace, grouped issueplanKind, and per-symboltrackingKeyfields so a later AI-driven planner can split work into one issue per deprecated item while remaining restart-safe by searching managed markers before mutation. - The workflow auto-labels the PR with
release, assigns it toTheCookieLab, and requests review fromTheCookieLab. - In PR mode, the successful handoff is the release branch push plus release PR creation. In direct-push mode, the successful handoff is
prepare-release.ymldispatchingpublish-release.ymlwithdryRun=false. - Opening a release PR automatically triggers freeze notices on other open PRs.
- Review generated release PR
- Confirm release commit + next snapshot commit are present.
- Confirm release notes file exists (
release/<version>.md). - Confirm docs delta is complete for changed APIs/examples (README/wiki/examples index/changelog consistency).
- Confirm canonical documentation artifacts are present and linked (
Home,Getting-started, journey/runbook/checklist/troubleshooting, decision matrix, migration map, expected outputs, performance characterization). - If a release PR is open, wait for it to merge before merging any non-release PRs to
master.
- Merge release PR to
master
- Use a merge commit.
- Do not squash/rebase release PRs.
- This merge is allowed because the
Release Merge Freezecheck allows only release PRs to merge during freeze.
- Observe Publish Release
- Workflow validates release metadata and ancestry.
- Creates annotated tag.
- Deploys to Maven Central.
- Pushes tag only after successful deploy.
- Verifies pushed tag points to expected release commit and is reachable from default branch.
- Observe post-publish workflows
github-release.ymlshould run from tag push.release-health.ymlmay first report snapshot publication as pending during the async handoff, then should reportOKaftersnapshot.ymlcompletes.
5. Dry-Run Runbook
Manual release workflows are inspection-first. release-scheduler.yml, prepare-release.yml, publish-release.yml, github-release.yml, snapshot.yml, and release-health.yml all default manual workflow_dispatch runs to dryRun=true.
Operator flow:
- Run the workflow manually and leave
dryRun=true. - Inspect the computed values in the workflow summary and audit artifacts: release version, next snapshot, tag, publish target, snapshot version, and planned mutation steps.
- If the computed values are correct and mutation is intended, rerun the same workflow with
dryRun=false. - If no manual mutation is needed, let the official scheduled, push, merge, tag-push, or workflow-run trigger continue; those paths normalize to
dryRun=false.
Prepare dry-runs may leave releaseVersion and nextVersion blank where auto-detection is supported. Publish dry-runs require releaseVersion; releaseCommit remains optional and can be auto-detected.
Expected behavior:
- dry-run can warn about missing deploy secrets/resources.
- no managed cleanup issue sync, release PR creation, branch push, tag push, Maven Central deployment, GitHub Release creation, snapshot deployment, or discussion/comment mutation.
- prepare dry-runs still run push capability probes with
git push --dry-run. - prepare dry-runs run deprecation scans and upload report artifacts, but skip managed GitHub cleanup issue sync. If the release-ready gate finds due or overdue removals, the dry-run fails after the reports are available.
- publish dry-runs run the same metadata, ancestry, release-candidate, and artifact manifest checks without deploying. If the release tag already exists, the dry-run records a warning and continues so historical release validation remains possible.
- GitHub Release dry-runs build and validate the exact release artifact manifest from the selected tag while using workflow support files from the workflow commit.
- release-candidate checks use the repository default
integration,slowtest-tag exclusions and log that policy in the workflow output. - workflows upload audit artifacts such as release dossiers, decisions, manifests, logs, and tag-resolution files so failures can be diagnosed from the exact phase that produced them.
6. Workflow Map (Trigger + Responsibility)
| Workflow | Trigger(s) | Primary responsibility | Critical guardrails |
|---|---|---|---|
release-scheduler.yml |
schedule, manual | decide whether/how to release | manual runs default dry-run; schedule normalizes to production; binary-impact gate, model catalog preflight, release dossier, semver safety, tag collision checks |
prepare-release.yml |
manual (or scheduler dispatch) | generate release artifacts and release PR/direct-push commits | manual runs default dry-run; scheduler passes dryRun; docs-integrity checks, version validation, metadata validation, dry-run push capability probes |
publish-release.yml |
merged release PR close, manual | release-candidate verification + tag + Maven Central deploy + snapshot dispatch + release summary | manual runs default dry-run; merged release PRs normalize to production; merge discipline + ancestry checks, artifact manifest checks, post-push tag integrity/reachability checks |
release-health.yml |
push to master, publish workflow completion, snapshot workflow completion, schedule, manual |
detect drift in release state | manual runs default dry-run; non-manual triggers normalize to production; fails on tag reachability drift, snapshot version drift, missing snapshot publication once snapshot publication is authoritative, missing notes, stale release PRs |
github-release.yml |
semver-like tag push, manual | GitHub release publication | manual runs default dry-run; tag pushes normalize to production; semver tag validation, exact artifact manifest using current workflow support files |
snapshot.yml |
push to master, publish workflow dispatch, manual |
publish snapshots | manual runs default dry-run; master pushes and publish handoff normalize to production; build/test/deploy prechecks and source-release audit fields |
Tag metrics used by release automation:
latest tag: newest release tag by tag creation date, preferring bare numeric tags beforev-prefixed tags.last reachable tag: newest release tag merged intomaster;release-scheduler.ymluses this as its diff and version baseline.last first-parent tag: nearest release tag visible onmaster's first-parent chain; informational only for diagnosing merge topology.latest tag reachable: whether the newest release tag is an ancestor ofmaster.
7. Verification Checklist (Post-Release)
prepare-release.ymlandpublish-release.ymlsucceeded.- Tag exists for released version.
- Tag reachability from
masteris true. - Maven Central artifacts are visible (allow propagation time).
- GitHub release exists with expected notes/artifacts.
- Release-ready deprecation gate report exists for the released version and did not find due or overdue removals.
- Removal-ready deprecation report artifact exists for the new snapshot version, due or overdue removal versions were included, and any matching cleanup issues were created, refreshed, reopened, or closed as stale successfully.
- The chained
snapshot.ymlrun succeeded for the next-SNAPSHOTversion. masteris on next-SNAPSHOTversion.release-health.ymlreports no drift and confirms the current snapshot version is published aftersnapshot.ymlcompletes.
Quick checks:
git fetch origin --tags
VERSION=0.22.3
git rev-list -n 1 "refs/tags/${VERSION}"
git merge-base --is-ancestor "${VERSION}" origin/master && echo "reachable"
8. Troubleshooting
8.1 Publish succeeded, Health failed
Is this possible?
- Yes.
publish-release.ymlandrelease-health.ymlare separate workflows.
What should fail earlier now?
- If the just-pushed tag is wrong/unreachable, publish should fail first due to post-push tag checks.
Why health can still fail afterward:
pom.xmlsnapshot not ahead of latest tag.- current snapshot version is missing from the Maven snapshot repository after snapshot publication becomes authoritative.
- missing
release/<version>.mdfor latest tag. - stale release PRs.
- existing repository drift not introduced by this publish run.
Why a large last first-parent tag gap can still be healthy:
- release PRs merge back to
masterwith merge commits, so the tagged release commit often lives on the merge's second parent rather than the first-parent spine. release-health.ymlonly fails when the newest release tag is not reachable frommaster; a lagging first-parent tag is diagnostic context, not drift by itself.
Remediation playbook:
- Open the failed
release-health.ymlrun and readDrift reasons. - Apply targeted fix:
latest tag not reachable from <branch>: make tagged commit reachable frommaster(typically a reachability merge commit; avoid retagging published releases).pom.xml snapshot version not ahead of latest tag: bumpmasterto next-SNAPSHOT.current snapshot version not published to Maven snapshot repository: inspect the latestsnapshot.ymlrun, fix the publication failure, and rerun health after the snapshot version appears in metadata.missing release notes for latest tag: addrelease/<version>.md.stale release PRs detected: merge or close stale release PRs.
- Re-run health via
workflow_dispatch. - If Maven deploy already succeeded, treat this as repo-state remediation, not republish.
8.2 Release PR waiting for merge
- Ensure required checks pass.
- Ensure required maintainer approval exists.
- Merge with merge commit.
8.3 Tag exists error
- Version already tagged.
- Use a new version unless you have an explicit rollback/recovery plan.
8.4 Release notes missing
- Ensure
release/<version>.mdexists in the release commit.
8.5 Branch advanced during prepare/push
mastermoved while workflow was preparing/pushing.- Re-run prepare once branch is stable.
8.6 Dry-run warnings
- Missing deploy secrets in dry-run are expected warnings.
- Fix before production run.
8.7 Finding the failure point
Release workflows use grouped log sections and upload audit artifacts on every run.
Look for these files first:
release-dossier.md: scheduler context sent to the model.release-decision.json: normalized AI release decision.release-audit.json: workflow-local release metadata.tag-resolution.txt: resolved latest/reachable/first-parent tag state.artifact-manifest.txt: exact jars expected for publish/GitHub Release.javadoc-warnings.txt: Javadoc warning baseline comparison from release artifact/deploy logs; new warnings beyond the trackedscripts/release/javadoc-warning-baseline.txtdebt fail publish..agents/logs/full-build-*.log: release-candidate full build log.
If a grouped section fails, inspect the matching artifact before rerunning. Avoid rerunning publish until tag state, artifact state, and Central deployment state are understood.
9. Discussion Posts (Markers and Cleanup)
Non-dry-run release-related workflows post to GitHub Discussions with machine-readable markers:
<!-- ta4j:post-type=<type>;run=<real|dry-run> -->
Post types:
release-schedulerpublish-releaserelease-health
Cleanup rules:
- manual dry-runs do not create, update, or delete discussion comments; their dry-run details remain in workflow summaries and audit artifacts.
release-health: removes prior real health posts before posting the latest real summary.release-scheduler: posts real scheduler summaries only.publish-release: posts real publish summaries and keeps historical posts as an audit trail.
Do not key automation off author/body heuristics; key off marker metadata.
10. Required Resources
10.1 Required secrets
| Secret | Used by | Purpose |
|---|---|---|
MAVEN_CENTRAL_TOKEN_USER |
publish, snapshot | Maven Central auth user/token |
MAVEN_CENTRAL_TOKEN_PASS |
publish, snapshot | Maven Central auth password/token |
GPG_PRIVATE_KEY |
publish, snapshot | artifact signing key |
GPG_PASSPHRASE |
publish, snapshot | signing key passphrase |
GH_MODELS_TOKEN |
scheduler | model API access |
GH_TA4J_REPO_TOKEN |
prepare, publish, github-release | PAT for release pushes/release creation |
10.2 Optional secrets
| Secret | Used by | Purpose |
|---|---|---|
MAVEN_MASTER_PASSPHRASE |
publish, snapshot | optional Maven settings-security |
10.3 Variables
| Variable | Used by | Purpose |
|---|---|---|
RELEASE_DIRECT_PUSH |
prepare | direct-push mode switch |
RELEASE_NOTIFY_USER |
publish, scheduler, health | discussion mention target |
RELEASE_DISCUSSION_NUMBER |
publish | publish summary discussion |
RELEASE_SCHEDULER_DISCUSSION_NUMBER |
scheduler, health | scheduler/health discussion |
RELEASE_SCHEDULER_ENABLED |
scheduler | enable scheduled release decisions |
RELEASE_AI_MODEL |
scheduler | GitHub Models model override; default should be openai/gpt-4.1 |
RELEASE_PR_STALE_DAYS |
health | stale release PR threshold |
10.4 Environment
| Environment | Purpose |
|---|---|
major-release |
manual approval gate for major bumps |
release-recovery |
manual approval gate for publish-release.yml recovery mode when an explicit release commit is not reachable from master |
11. Repository Policy Notes
- Keep release tags reachable from
master. - Use merge commits for release PRs.
- Avoid force-retagging already-published versions.
- Prefer PR mode; use direct push only when justified.
- Keep
RELEASE_DIRECT_PUSH=falsefor normal releases. - Use
recoveryMode=trueonly for manual publish recovery when the release commit is intentionally not reachable frommaster; this path is protected by therelease-recoveryenvironment.
12. Responsibilities Summary
| Task | Maintainer/manual | GitHub Actions |
|---|---|---|
| maintain changelog/readme release readiness | yes | no |
| generate release notes | yes (via prepare script/workflow) | yes |
| set release version + next snapshot | no | yes |
| create/push release tag | no | yes (non-dry-run) |
| deploy to Maven Central | no | yes (non-dry-run) |
| publish GitHub release | no | yes (non-dry-run) |
| release drift auditing | no | yes |
13. RC Builds
RC versions are supported if they are Maven-valid (for example 0.22.2-rc1).
Use the same workflows and verification checklist.