The easy4j organization's "single source, three JDK lines, monthly date version" release system.
1. Branch Model (Iron Rule)
1.1 SDK Repository (feature/X.0.x, 3 branches)
| Branch | JDK | Positioning |
|---|---|---|
feature/1.0.x |
8 | Legacy project compatibility line |
feature/2.0.x |
17 | Mainline |
feature/3.0.x |
21 | New feature line |
Source Code Consistency Iron Rule: The source code, tests, and README of the three branches must be byte-for-byte identical; the only allowed differences are pom.xml(JDK version + dependency versions)..gitignore、maven-wrapper.properties and other tool files are allowed to drift.
Synchronization Method: After committing on the source branch, use git checkout <source> -- src/ README.md(excluding pom.xml) to the target branch, then manually correct the branch-specific versions in the pom (java.version, okhttp3/jackson and other dependency versions, the artifact's own version number).
1.2 Starter Repository (xxx-spring-boot-starter, 10 branches)
Branches correspond to Spring Boot version lines:2.3.x、2.7.x、3.0.x、3.1.x、3.2.x、3.3.x、3.4.x、3.5.x、4.0.x、4.1.x(branch names are subject to the actual repository—openclaw-spring-boot-starter uses bare version-number branches; if the repository has a feature/ prefix then adjust accordingly. Before execution, first git branch --list enumerate and confirm). Starter source code remains consistent across branches; pom differences = Spring Boot parent version + JDK + the SDK line version of dependencies.
2. Version Number Specification
- Format:
{major}.0.x.{yyyyMMdd}(official version), with-SNAPSHOTsuffix for development version - Date suffix advances monthly: release
20260630→ branch immediately upgrades to20260730-SNAPSHOT(+1 month, take end of month) - Tag name = official version number (e.g.
2.0.x.20260630), placed on the release commit
3. SDK Release Process (three-branch official release)
3.1 Release Rules (Original Text Must Be Followed)
- Create 3 tags:
/tags/1.0.x.{日期}、/tags/2.0.x.{日期}、/tags/3.0.x.{日期}- Branch snapshot
1.0.x.{日期}-SNAPSHOT→ release official version number1.0.x.{日期}(same for the three branches)
- Branch snapshot
- Publish to Maven Central Repository based on the 3 tags
- Update the version numbers of the 3 branches (feature/1.0.x, feature/2.0.x, feature/3.0.x):
X.0.x.{本月日期}-SNAPSHOT→X.0.x.{下月日期}-SNAPSHOT(e.g.20260630-SNAPSHOT→20260730-SNAPSHOT)
- Jackson Dependency Version Control: see 5.1 BOM Rules
Date parameters are given by the user's release instruction; when not given, derive them from the current branch SNAPSHOT version (this month's date is the current date suffix, next month's date = +1 month, take end of month).
3.2 Orchestration: Sub-agents Execute in Parallel
When the user requests "release official versions for 3 branches":
- Main thread runs gates first(Section 6): CVE scan × 3 branches, tests × 3 branches, consistency diff—if any fails, fix first
- Obtain explicit user authorization(release is irreversible)
- Use the Agent tool to dispatch 3 sub-agents in parallel, one per branch. Central validation takes 5–20 minutes per branch; parallelization can compress the total duration to that of the slowest branch
- Sub-agent prompts must beself-contained: absolute repository path, branch name, release date, next month's date, JDK path, full command sequence, abort conditions on failure (do not retry deploy on your own). Can be executed directly by a subagent
scripts/release-sdk.sh(SDK) orscripts/release-starter.sh(starter) - After all three subagents succeed, the main thread summarizes the report (version/tag/URL matrix)
3.3 Single-Branch Execution Sequence (executed by subagent or script)
修改代码 → commit 到对应分支(如有待发布改动)
→ 在分支去 SNAPSHOT:X.0.x.{日期}-SNAPSHOT → X.0.x.{日期}
→ mvn clean deploy -P release -DskipTests (发布 Maven Central,JDK 须匹配分支)
→ 推 tag:git tag -a X.0.x.{日期} && git push origin X.0.x.{日期}
→ bump SNAPSHOT:X.0.x.{下月日期}-SNAPSHOT
→ 推分支:git push origin feature/X.0.x
Detailed commands:
# 去 SNAPSHOT
mvn versions:set -DnewVersion={major}.0.x.{日期} -DgenerateBackupPoms=false
git add pom.xml && git commit -m "chore(release): bump version to {major}.0.x.{日期}"
# 发布 Maven Central(JDK 必须与分支匹配)
JAVA_HOME=<分支对应JDK> mvn clean deploy -P release -DskipTests
# 打 tag 并推送
git tag -a {major}.0.x.{日期} -m "Release {major}.0.x.{日期}"
git push origin {major}.0.x.{日期}
# bump 到下月 SNAPSHOT
mvn versions:set -DnewVersion={major}.0.x.{下月日期}-SNAPSHOT -DgenerateBackupPoms=false
git add pom.xml && git commit -m "chore: bump version to {major}.0.x.{下月日期}-SNAPSHOT"
git push origin feature/{major}.0.x
JDK resolution(executed automatically by the release script, no need to configure an absolute path):
- Priority:
EASY4J_JDK_HOMEenvironment variable → macOS/usr/libexec/java_home -v <8|17|21>→ abort on resolution failure and prompt - You can also directly
export JAVA_HOMEspecify the matching version at the outer level
One-click script:scripts/release-sdk.sh feature/2.0.x 20260630 20260730(i.e., the above sequence; built-in branch whitelist and starter repository guard, must be confirmed with the user before execution).
Release channel:
- Maven Central: the release profile uses
central-publishing-maven-plugin(publishingServerId=central, settings.xml already has credentials), GPG signing is performed automatically.nexus-stagingis deprecated (oss.sonatype.org is dead, reports 404) - Alibaba Cloud SNAPSHOT:
mvn clean deploy -Dmaven.test.skip=truedirect publish to2624322-snapshot-3eoov3, no release profile needed
3.4 Jackson Dependency Version Control (Release Rule No. 4)
See 5.1 Jackson BOM Rules。
4. Starter Release Process (xxx-spring-boot-starter, 10 branches official release)
Applicable to xxx-spring-boot-starter repositories (such as openclaw-spring-boot-starter).Must be executed after the corresponding SDK official release is completed。
4.1 Prerequisites: dependencies SDK / extension must be switched to official release first
The xxx-java-sdk / xxx-extension version property depended on in each branch pom of the starter must be updated from SNAPSHOT to the official release before release (Maven Central rejects SNAPSHOT transitive dependencies; this step cannot be skipped):
1.0.x.{日期}-SNAPSHOT→1.0.x.{日期}(for 2.3.x / 2.7.x, JDK 8)2.0.x.{日期}-SNAPSHOT→2.0.x.{日期}(for the 3.x line, JDK 17)3.0.x.{日期}-SNAPSHOT→3.0.x.{日期}(for 4.0.x / 4.1.x)
The actual correspondence between dependency lines and starter branches is based on the current value of <xxx-java-sdk.version> in the pom (determined by the JDK ceiling). When bumping its own SNAPSHOT after release,dependencies remain on the official release, and switch back to SNAPSHOT as needed in the next integration cycle.
4.2 Release Rules (original text that must be followed)
- Create 10 tags:
/tags/2.3.x.{日期}、/tags/2.7.x.{日期}、/tags/3.0.x.{日期}、/tags/3.1.x.{日期}、/tags/3.2.x.{日期}、/tags/3.3.x.{日期}、/tags/3.4.x.{日期}、/tags/3.5.x.{日期}、/tags/4.0.x.{日期}、/tags/4.1.x.{日期}- snapshot for each branch
X.Y.x.{日期}-SNAPSHOT→ release official version numberX.Y.x.{日期}
- snapshot for each branch
- Publish to Maven Central based on the 10 tags
- Update the version numbers of the 10 branches:
X.Y.x.{本月日期}-SNAPSHOT→X.Y.x.{下月日期}-SNAPSHOT(e.g.,2.7.x.20260630-SNAPSHOT→2.7.x.20260730-SNAPSHOT)
4.3 Starter Branch × JDK × Spring Boot Matrix
| Branch | Spring Boot | its own java.version | Build JDK | Dependency SDK line (typical) |
|---|---|---|---|---|
| 2.3.x | 2.3.12.RELEASE | 1.8 | 8 | 1.0.x |
| 2.7.x | 2.7.18 | 1.8 | 8 | 1.0.x |
| 3.0.x | 3.0.13 | 17 | 17 | 2.0.x |
| 3.1.x | 3.1.12 | 17 | 17 | 2.0.x |
| 3.2.x | 3.2.12 | 17 | 17 | 2.0.x |
| 3.3.x | 3.3.13 | 17 | 17 | 2.0.x |
| 3.4.x | 3.4.13 | 17 | 17 | 2.0.x |
| 3.5.x | 3.5.9 | 17 | 17 | 2.0.x |
| 4.0.x | 4.0.x | 17 | 21(see below) | 3.0.x |
| 4.1.x | 4.1.x | 17 | 21(see below) | 3.0.x |
⚠️ Key pitfall: when the starter itself java.version=17 but depends on SDK 3.0.x (Java 21 bytecode),the build must use JDK 21, otherwise compilation reports "cannot access io.github.easy4j.xxx". The build JDK is taken from max(自身 java.version, SDK 依赖线字节码版本)。
4.4 Orchestration: 10 subagents in parallel + pre-release vulnerability check
Same as the 3.2 pattern, but before release a per-branch CVE scan is required, fixing vulnerabilities where the corresponding JDK permits(The starter's transitive dependencies mostly come from the Spring Boot parent; focus on scanning dependencies declared in its own properties and the SDK line versions). Then dispatch 10 sub-agents in parallel, each branch executing the sequence:
依赖转正式版(pom 属性去 -SNAPSHOT)→ commit
→ 去 SNAPSHOT(自身版本 X.Y.x.{日期})
→ mvn clean deploy -P release -DskipTests (JDK 按 4.3 矩阵)
→ 推 tag:git tag -a X.Y.x.{日期} && git push origin X.Y.x.{日期}
→ bump 自身 SNAPSHOT:X.Y.x.{下月日期}-SNAPSHOT(依赖保持正式版)
→ 推分支
One-click script:scripts/release-starter.sh 2.7.x 20260630 20260730 <starter仓库路径>(automatically handles dependency conversion to release versions; JDK takes max(own java.version, all dependency lines); built-in branch whitelist and repository type guard).
5. Dependency Management Standards
5.1 Jackson BOM Rules (Release Rule No. 4)
The Jackson version control approach for the three branches (Mandatory for repositories that depend on Jackson):
- Define in properties
jackson-bom.version - Import the corresponding BOM in dependencyManagement
- Use the corresponding components directly in dependencies (without version)
| Branch | properties | dependencyManagement import |
|---|---|---|
| 1.0.x | jackson-bom.version=2.18.9 |
com.fasterxml.jackson:jackson-bom:${jackson-bom.version} |
| 2.0.x | jackson-bom.version=2.22.1 |
com.fasterxml.jackson:jackson-bom:${jackson-bom.version} |
| 3.0.x | jackson-bom.version=3.2.1 |
tools.jackson:jackson-bom:${jackson-bom.version}(groupId has been changed!) |
5.2 JDK Dependency Ceiling
| Component | JDK 8 Ceiling | Reason |
|---|---|---|
| jackson | 2.18.9 | 2.19+ requires JDK 11+ |
| okhttp | 4.12.0 | 5.x requires JDK 11+ |
| junit-jupiter | 5.11.4 | 6.x requires JDK 17+ |
| commons-exec / slf4j / lombok | 1.6.0 / 2.0.18 / 1.18.46 | Applies to all lines |
Upgrade principle:Upgrade to the highest CVE-free version within the range allowed by the corresponding JDK. For the complete matrix and the Jackson 3.x migration API comparison table, see references/dependency-matrix.md。
5.3 In-house Component Alignment
In-house components (such as okhttp3-extension) release three-line versions following the same rules 1.0.x / 2.0.x / 3.0.x.{日期}, corresponding one-to-one with the SDK branches. The depended-upon party mustrelease the official version first—Maven Central rejects any SNAPSHOT transitive dependencies.
6. Pre-release Gates
- CVE Scan:
scripts/cve-scan.sh <分支> <仓库路径>(OSV API, scans all dependencies in the pom; SDK × 3 branches, starter × 10 branches). If a CVE is found, upgrade and fix within the JDK ceiling, then rescan - Testing: all three branches' tests green. When surefire is stuck by jacoco's
${argLine}, add-DargLine=""; when the pom hardcodesskipTests=false, use-Dmaven.test.skip=true(-DskipTestsis ineffective) - Branch Consistency:
git diff feature/2.0.x feature/{1,3}.0.x --statshould only have pom.xml left
7. Coordinate Specifications
| Project | Value |
|---|---|
| groupId | io.github.easy4j |
| Java package | io.github.easy4j.*(aligned with groupId) |
| Repository URL | github.com/easy-4-java/{artifactId} |
| developer | Keep hiwepy / hiwepy@gmail.com(personal identity does not migrate with the organization) |
8. Troubleshooting Quick Reference
| Symptom | Handling |
|---|---|
| Alibaba Cloud deploy 409 | The release repository has a leftover version; you need to manually delete that version directory in the console (the API does not support DELETE) |
| Central reports "Dependencies to SNAPSHOT versions not allowed" | The transitive dependency contains a SNAPSHOT; first release the official version of the dependency or decouple that dependency |
| nexus-staging connecting to oss.sonatype.org 404 | Deprecated; change the release profile to central-publishing-maven-plugin |
After importing the BOM, it reports version is missing |
A leftover jackson entry without a version in dm willshadow the BOM—delete the jackson entries in dm that are purely version managemententirely(you cannot delete only the version line); versionless references in dependencies are supplied with versions by the BOM |
fork VM ${argLine} crashes |
Add -DargLine="" |
| JDK compilation reports "release version not supported" | pom was polluted by another branch,git checkout origin/<branch> -- pom.xml after restoring, re-modify the version |
JDK 8 cannot find Map.of |
JDK 9+ API, change to HashMap manual put |
| Alibaba Cloud mirror is missing the new jackson modules | All 3 modules (databind/core/annotations) must be verified to exist; the annotations version number often has no patch (e.g. 2.22 not 2.22.1), using the BOM avoids manual alignment |
See the complete case at references/troubleshooting.md。
9. Included scripts
| Script | Applicable repositories | Purpose |
|---|---|---|
scripts/cve-scan.sh <分支> [仓库路径] |
General | Scan all dependencies in the pom for OSV CVEs (including jackson dual coordinates to prevent missed reports) |
scripts/release-sdk.sh <feature/X.0.x> <发布日期> <下月日期> [仓库路径] |
SDK (3 branches) | Remove SNAPSHOT → deploy → tag → bump → push; JDK is fixed according to the branch whitelist |
scripts/release-starter.sh <X.Y.x> <发布日期> <下月日期> [仓库路径] |
Starter (10 branches) | One more step: convert dependencies to official versions; JDK automatically infers max(its own version, dependency line) |
Both release scripts include: branch whitelist (reject unknown branches), repository type guard (SDK/starter mutually exclusive, to prevent mixed runs),DRY_RUN=1 dry-run mode,EASY4J_JDK_HOME override.
Release is an irreversible external operation: before executing release.sh or Central deploy,explicit user confirmation must be obtained。