You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Production-ready parent POM for Java 25 + Maven 3.9+ + Docker CI/CD. Provides opinionated, SOTA-2026 defaults for build, quality, security, packaging, and release. Inherit it to get a fully wired Java project in 10 lines of XML.
For application (non-library) modules, the app.image property selects the packaging mode. Setting it automatically activates the corresponding profile via Maven property activation.
app.image
Profile
Build Output
Docker Target
Run Command
libs
libs
Thin JAR + target/libs/*.jar
finalLibs
java -cp app.jar:libs/* ${app.main.class}
jlink
jlink
Custom JPMS runtime (target/jlink/)
finalJlink
./jlink/bin/myapp
native
native
Single native executable
finalNative
./native-image/myapp
(empty)
—
Standard JAR (library)
—
—
Required properties
<properties>
<app.image>libs</app.image> <!-- libs | jlink | native -->
<app.main.class>com.example.MyApp</app.main.class> <!-- fully qualified main class -->
</properties>
Optional
<properties>
<!-- JPMS module name (defaults to ${project.groupId}.${project.artifactId}) -->
<app.module.name>com.example.myapp</app.module.name>
</properties>
Notes
shadedjar is NOT part of app.image. Activate explicitly: mvn package -Pshadedjar
Static native binaries (alpine/musl): override in your module's POM:
jlink limitation: jigsaw-maven-plugin 1.1.3 link goal only supports launcher, module, output, modulePath, ignoreSigningInformation. The mainClass, multiRelease, jlinkOptions, ignoreMissingDeps elements are present for forward compatibility but currently no-ops.
<dependency-check.version>13.0.0</dependency-check.version>
<!-- failBuildOnCVSS=9: only critical vulns fail the build -->
The parent POM itself has packaging: pom (no direct dependencies), so OWASP runs against the canary integration test project (src/it/canary/) which has real dependencies (Jackson, JUnit).
SBOM
CycloneDX generates both JSON and XML SBOMs on every build:
target/bom.json
target/bom.xml
Release Process
Full standard: see docs/RELEASING.md — trunk-based, tag-driven, two-tier publishing (reusable across all ebpro Java repos).
Releases are tag-driven and handled entirely by CI (release.yml). No maven-release-plugin, no manual mvn release:prepare.
Tier 1 — GitHub Packages (internal): automatic on every release tag
Tier 2 — Maven Central (public): opt-in via workflow_dispatch + publish-central: true
Quick release (GitHub Packages)
git checkout develop && git pull
git tag -a v0.1.20 -m "Release 0.1.20"
git push origin v0.1.20 # CI builds, signs, deploys, then bumps to 0.1.21-SNAPSHOT
Full release (GitHub Packages + Maven Central)
Trigger Actions → Release → Run workflow with version: 0.1.20 and publish-central: true.
Prerequisites
Secret
Purpose
SIGN_KEY / SIGN_KEY_PASS
GPG private key (ed25519) + passphrase
GITHUBTOKEN
PAT with packages:write + contents:write
CENTRAL_PORTAL_USERNAME / CENTRAL_PORTAL_TOKEN
Central Portal credentials (Tier 2 only)
Tag format: v{major}.{minor}.{patch} — e.g., v0.1.20.
CI/CD
Workflows
Workflow
Trigger
Purpose
ci-java.yml
Push/PR to develop
Build, test, SonarQube
security.yml
Push/PR to develop
CodeQL, SBOM, OWASP canary, Dep Review
release.yml
Tag v* / workflow_dispatch
Two-tier publish: GitHub Packages (always) + Central (opt-in)