Repository navigation
[Java] Cease using maven-release-plugin due to its opinionated mutations of git history #2579
Description
Activity
At today's SDK triage, @roji stated it is unlikely that the "incorporate SDK into
copilot-agent-runtime" merge will happen this week. Therefore, the deadline for this work is no earlier than 2026-09-18.Therefore, the deadline for this work is no earlier than 2026-09-18.
That would be my assumption, but let me chat with Dev and see how the team design meeting goes tomorrow before we commit on a date. Either way, I'd treat this as the most urgent Java-side issue at the moment.
Proposed plan: replace
maven-release-pluginwith Maven CI-Friendly VersionsGoal
Make the Java release pipeline a read-only consumer of the commit being released, consistent with the other SDKs. A Java release (success or failure) must create no commits on
main, require no elevated repository token or ruleset bypass, and publish artifacts whose version + source SHA match the other SDK artifacts in the same release.Current state (the problem)
.github/workflows/java-publish-maven.ymlis the only publisher that mutates git history:prepare-releasecommits a docs version bump and pushes tomain, then runsmvn release:prepare, which commits the release version + the next-SNAPSHOTand pushes both plus ajava/vX.Y.Ztag tomain.- This uses
JAVA_RELEASE_TOKEN(push perms,contents: write, repository-ruleset bypass). - A
rollback-releasejob force-reverts those commits/tag on failure.
Versions are hardcoded (
1.0.14-SNAPSHOT) injava/pom.xml,java/sdk/pom.xml,java/copilot-native/pom.xml.flatten-maven-plugin(ossrh mode) is already configured — the prerequisite for CI-friendly versions is in place. Other SDKs (rust/python/dotnet) consume the shared computed version and set it in-workspace only, committing nothing.Design
Adopt the officially-documented
${revision}+flatten-maven-pluginpattern — the standard modern replacement formaven-release-pluginthat stops mutating git history. Release version is injected at build time with-Drevision=<version>; the committed default stays-SNAPSHOTfor local/snapshot builds.Work items
1. POMs ->
${revision}(java/pom.xml,java/sdk/pom.xml,java/copilot-native/pom.xml)- Add
<revision>1.0.14-SNAPSHOT</revision>in the parent POM. - Replace the three hardcoded
<version>/ parent-<version>values with${revision}.${project.version}cross-references (sdk -> copilot-native runtime) stay valid; flatten resolves them at publish. - Remove
maven-release-pluginfrompluginManagement.
2. Rewrite
java-publish-maven.yml- Delete the
preflight,prepare-release, androllback-releasejobs. - Add a
resolve-sourcejob (mirroringjava-publish-snapshot.yml) that pins the triggering SHA; add asourceShainput. - Every classifier build job +
deploy-mavenchecks out that SHA withpersist-credentials: falseand buildsmvn deploy -Prelease -Drevision=<releaseVersion>— one immutable SHA, one version, all classifiers. - Drop the
JAVA_RELEASE_TOKENsecret and changecontents: write->contents: read. Keep Maven Central + GPG secrets (they authorize package publication, not repo mutation). Remove thejava/vX.Y.Z-tag dependency for classifier checkouts. - Documentation version bump leaves this workflow.
scripts/update-documentation-versions.shstays and runs through a normal reviewed PR instead of as a publish side effect.
3.
publish.ymlpublish-java:contents: read; passsourceSha: ${{ github.sha }}+releaseVersion: ${{ needs.version.outputs.version }}; keep consumingmavenPublished.github-release(already gated on publish success): after publication, create thejava/vX.Y.Ztraceability tag pointing atgithub.sha, alongside the existing Go/Rust tag steps. The commonvX.Y.Zrelease is already created only post-publish — unchanged.- Keep
workflow_dispatchon the maven workflow so a failed Maven Central deploy is independently retryable for the same version + SHA without rerunning other languages.
4.
java-publish-snapshot.yml- Already read-only. With
${revision}defaulting to1.0.14-SNAPSHOT, snapshot deploy is functionally unchanged; verifymvn deployresolves${revision}.
5. Site deploy
deploy-sitekeyed off the version/SHA (the tag is now created bygithub-release); sequence it after the tag exists or pass the version directly.
6. Contributor mental-model note (
java/README.md)
CI-friendly versions are a genuine workflow-habit change for Java devs used tomaven-release-plugin: the committed<revision>stays a fixed-SNAPSHOTand the real release version is computed externally (byget-version.js, like the other SDKs). Document that the POM intentionally does not track the next release version, releases inject it via-Drevision=, and there is norelease:prepareceremony (tagging happens inpublish.ymlafter publication).7. Verification
- Local
mvn installmust still resolve${revision}for downstream consumers (flatten produces resolved installed POMs) — verify a consumer resolvescom.github:copilot-sdk-javawith no unresolved${revision}. mvn -Prelease deploy -Drevision=X.Y.Z -DskipTests(against a local/staging repo where possible) produces artifacts + flattened POMs carrying the literalX.Y.Z, no${revision}leakage, parent stripped, correct classifier attachments.
Definition of done
- A successful or failed Java release creates no commits on
main. - No branch-protection bypass or elevated repository token is required.
- Published artifacts carry the shared computed version and are built from the triggering SHA, matching the other SDK artifacts in the same release.
java/README.mddocuments the new versioning model.
- added a commit that references this issue
on Sep 14, 2026 - added a commit that references this issue
on Sep 15, 2026
High level rationale
Java’s Maven release is the only SDK publication process that treats releasing as a source-control mutation: it uses privileged automation to push multiple versioning commits and a tag directly to
mainbefore publication completes. Steve Sanderson questioned why the workflow must push directly tomaininstead of producing a PR, while Stephen Toub similarly objected to the direct push and asked whether it should be submitted as a PR. Their objections center on the branch-protection bypass, inconsistency with the other SDKs, and resulting failure and rollback complexity, favoring a PR-based or otherwise independently retryable CI publication flow.Context
The need for this work was first articulated by @SteveSandersonMS way back around 2026-05-14 in https://github.com/github/copilot-sdk-internal/issues/95 . At the time, we decided to allow the best practice for Java releases as encoded in
maven-release-pluginto determine how we performed git tagging and versioning for the Java SDK. See this comment for the decision record. See also this slack thread.Further interaction with the need for this work happened in Request review of planned action #1873 . In particular, see this comment from @brunoborges: comment link. Also, teams link.
Job-to-be-done
Make the Java release pipeline a read-only consumer of the commit being released, consistent with the other language publishers. A Java release must derive its version from the shared release workflow, build and publish from an immutable source commit, and complete without committing to or otherwise modifying
main.The implementation must:
maven-release-pluginandrelease:preparewith CI-friendly Maven versioning, using the version supplied by the release workflow only within the build workspace. No release or next-development version change may be committed to the repository..github/workflows/java-publish-maven.ymlso it checks out an explicitly resolved source SHA rather than creating a release commit and using a pre-publicationjava/vX.Y.Ztag as its source.SNAPSHOTcommit, and every direct push tomainfrom.github/workflows/java-publish-maven.yml. Documentation version updates must be made through the normal reviewed-PR process rather than as a side effect of publishing.JAVA_RELEASE_TOKENpreflight check, privileged checkout,contents: writerequirement, and repository-ruleset bypass dependency. Maven Central and GPG credentials may remain because they authorize package publication rather than repository mutation.rollback-releasejob and the commit/tag identity outputs that exist solely to undo partialrelease:preparemutations. A failed publication must leave bothmainand repository tags unchanged..github/workflows/publish.yml..github/workflows/publish.ymlsopublish-javafollows the same contract as the other publishers: consume the shared version, build from the triggering SHA, publish the resulting artifacts, and report success without changing source-controlled files.vX.Y.ZGitHub release only after package publication succeeds. If a Java-specificjava/vX.Y.Ztraceability tag remains necessary, create it after successful publication, point it at the original release SHA, and manage it alongside the existing Go and Rust language tags in thegithub-releasejob rather than inside the Maven publication workflow..github/workflows/java-publish-snapshot.ymland the Java POM hierarchy as required by the new CI-friendly version mechanism, while preserving the snapshot workflow’s existing read-only relationship with the repository.The work is complete when a successful or failed Java release creates no commits on
main, requires no branch-protection bypass or elevated repository token, and publishes Maven artifacts whose version and source commit match the other SDK artifacts in the same release.Internal User Story
dd-3061183-cease-maven-release-plugin