Publishing to Maven Central ​
This document explains how to publish The Pipeline Framework to Maven Central and how to manage the project's versioning and release process properly.
TL;DR: Guarded Release Process ​
The release process uses the Maven Release Plugin for the root reactor, then GitHub Actions publishes framework artifacts from the release tag. Keep Maven's push step disabled so the generated release and next-development commits can be inspected before they reach main.
Prepare locally without pushing:
bash./mvnw release:prepare \ -DpushChanges=false \ -DreleaseVersion=26.5.2 \ -DdevelopmentVersion=26.6.1-SNAPSHOT \ -Dtag=v26.5.2 \ -Darguments="-DskipTests"Synchronize release-coupled standalone POMs: confirm alternate topology and standalone reference POMs moved to the next snapshot, including
examples/csv-payments/pom.pipeline-runtime.xml,examples/csv-payments/pom.monolith.xml,examples/checkout/pom.xml, andai-sdk/pom.xml.Run the release validation gate: at minimum run version-drift checks, framework verification, CSV topology checks, and docs build before pushing.
Push only after validation: push the prepared commits to
main, then push the immutablevX.Y.Ztag to trigger publishing.Verify on Maven Central: check artifacts at https://central.sonatype.com/.
Publish the bridge separately when needed:
tpf-mcp-bridgeuses npm semver and tag formattpf-mcp-bridge-vX.Y.Z; it is coordinated with TPF releases but not version-number synchronized.
The GitHub Actions workflow automatically:
- Runs the workflows selected by the pushed paths on
main - Signs all artifacts with GPG
- Deploys to Sonatype Central
- Creates a GitHub release with notes
Table of Contents ​
- Overview
- Version Management
- Maven Central Publishing Setup
- settings.xml Configuration
- GitHub Actions Workflow
- Safe Release Process
- Coordinated tpf-mcp-bridge Release
- Troubleshooting
Overview ​
The Pipeline Framework is published to Maven Central to make it available to developers who want to use it in their projects. This document outlines the process, configuration, and best practices for publishing releases.
Version Management ​
The framework release version is defined in the root POM (pom.xml) as the root reactor's <version>. Most root-reactor modules inherit that version through parent links, but several compatibility and reference surfaces are intentionally outside that reactor or use alternate top-level POMs.
Keep these categories aligned during every release:
- Root reactor: root, framework, main examples, and plugins listed in the root
pom.xml. - Alternate topology POMs: build-entry POMs such as
examples/csv-payments/pom.pipeline-runtime.xmlandexamples/csv-payments/pom.monolith.xml. - Standalone compatibility surfaces: POMs such as
ai-sdk/pom.xmland reference examples outside the root reactor.
Version Property Definition ​
In the root POM (pom.xml):
<version>26.4.5-SNAPSHOT</version>Root-reactor children should inherit this version through the parent relationship and omit their own project <version> where possible. Alternate top-level POMs and standalone builds may need an explicit parent version or dependency property, so they must be checked separately.
For the v26.5.2 release, use release version 26.5.2, tag v26.5.2, and next development version 26.6.1-SNAPSHOT.
Using Maven Versions Plugin ​
To update root-reactor versions consistently, use the Maven Versions Plugin:
# Update the version across all modules
./mvnw versions:set -DnewVersion=1.0.0
# Verify the changes before committing
./mvnw versions:commit
# Or rollback if needed
./mvnw versions:revertThis ensures that modules in the selected Maven reactor are updated consistently. It does not automatically update alternate POM entrypoints or standalone surfaces that are not part of that reactor.
Documentation Snapshot (Hybrid Versioning) ​
For released docs versions, snapshot the docs into docs/versions/:
cd docs
npm run snapshot -- --version v0.9.3Then update docs/versions.md to mark the latest version and confirm the version selector list.
Maven Central Publishing Setup ​
The Maven Central publishing configuration is located in the framework's parent POM (framework/pom.xml). It is handled by the GitHub Actions workflow, but this is a description of how such setup could be done on your local workstation.
Required Plugins ​
The following plugins are configured in the framework POM for Maven Central compliance:
- Source Plugin: Generates sources JAR
- Javadoc Plugin: Generates documentation JAR
- GPG Plugin: Signs artifacts
- Central Publishing Plugin: Deploys to Sonatype Central
For the complete configuration, see the central-publishing profile in framework/pom.xml.
Local settings.xml Configuration ​
To authenticate with Sonatype Central and provide GPG credentials, you could configure your ~/.m2/settings.xml file:
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
<server>
<id>central</id>
<username>your-sonatype-username</username>
<password>your-encrypted-sonatype-password</password>
</server>
</servers>
</settings>GPG Configuration ​
The publishing workflow handles GPG signing on GitHub Actions, but for reference, this is how you could configure it on your local machine:
<profiles>
<profile>
<id>central-publishing</id>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.passphrase>your-gpg-passphrase</gpg.passphrase>
<gpg.keyname>your-gpg-key-id</gpg.keyname>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>central-publishing</activeProfile>
</activeProfiles>Encrypting Passwords ​
To encrypt your Sonatype password on your local settings.xml:
Create the master password:
bash./mvnw --encrypt-master-passwordThis creates
~/.m2/settings-security.xmlEncrypt your Sonatype password:
bash./mvnw --encrypt-passwordUpdate settings.xml with the encrypted password (prefixed with
{and suffixed with}).
GitHub Actions Workflow ​
The publishing process is automated using GitHub Actions.
Required GitHub Secrets ​
These secrets must exist in the GitHub repository:
CENTRAL_USERNAME- Your Sonatype usernameCENTRAL_PASSWORD- Your Sonatype passwordGPG_PRIVATE_KEY- Your GPG private key exported withgpg --export-secret-keys --armor <your-key-id>GPG_PASSPHRASE- The passphrase for your GPG key
Safe Release Process ​
Standard Release Workflow ​
Use the Maven Release Plugin as the versioning tool for the root reactor, but keep publication gated by explicit inspection and CI validation.
Start from a clean release branch:
bashgit status --short git fetch origin --tagsConfirm the branch contains the intended docs snapshot and no unrelated local changes.
Prepare the release locally without pushing:
bash./mvnw release:prepare \ -DpushChanges=false \ -DreleaseVersion=X.Y.Z \ -DdevelopmentVersion=NEXT_VERSION-SNAPSHOT \ -Dtag=vX.Y.Z \ -Darguments="-DskipTests"The root POM also configures
<pushChanges>false</pushChanges>for the release plugin. Keep the command-line flag anyway so the behavior is explicit in shell history and release notes.The plugin creates two local commits and a local tag:
[maven-release-plugin] prepare release vX.Y.Z[maven-release-plugin] prepare for next development iterationvX.Y.Z
Inspect the release plugin output before any push:
bashgit log --oneline --decorate -n 5 git diff HEAD~2..HEAD -- pom.xml framework/pom.xml examples ai-sdk pluginsThe release commit should contain the release version. The next-development commit should contain the next
-SNAPSHOTversion.Synchronize release-coupled POMs outside the root reactor: The release plugin only updates POMs in the Maven reactor it runs. After
release:prepare, check and fix alternate topology and standalone POMs so they match the next development version onmain.Required checks:
bashrg -n "X\\.Y\\.Z-SNAPSHOT" --glob "pom*.xml" --glob "!docs/versions/**" rg -n "X\\.Y\\.Z" examples ai-sdk --glob "pom*.xml"Replace
X.Y.Zwith the just-released version. There should be no remaining references to the old snapshot in active POMs after the next-development commit.At minimum, check:
examples/csv-payments/pom.pipeline-runtime.xmlexamples/csv-payments/pom.monolith.xmlexamples/csv-payments/pipeline-runtime-svc/pom.xmlexamples/csv-payments/monolith-svc/pom.xmlexamples/checkout/pom.xmland its child modulesai-sdk/pom.xml
Run the release validation gate before pushing:
bash./mvnw -f framework/pom.xml verify ./examples/csv-payments/build-pipeline-runtime.sh -pl orchestrator-svc -Dcsv.runtime.layout=pipeline-runtime -Dtest=PipelineRuntimeTopologyTest -Dit.test=CsvPaymentsPipelineRuntimeEndToEndIT verify ./examples/csv-payments/build-monolith.sh -DskipTests ./mvnw -f ai-sdk/pom.xml test npm --prefix docs run buildIf time forces a smaller gate, record exactly which commands were skipped and do not claim full release validation.
Push the validated commits:
bashgit push origin mainWatch the
mainworkflows. If a workflow fails because of version drift, fixmainbefore pushing the tag.Publish the release:
bashgit push origin vX.Y.ZThis triggers the GitHub Actions workflow that runs
mvn deployto publish framework artifacts to Maven Central.
Note: The mvn release:perform step is not used in this setup since deployment is handled by GitHub Actions when a tag is pushed.
Coordinated tpf-mcp-bridge release ​
The tpf-mcp-bridge repository is released independently from the Maven framework artifacts. It contains the MCP bridge and the vendored template generator snapshot, so it should be published after a framework release when schema or generator-facing behavior changes.
Do not reuse the Maven framework version for this package. Keep normal npm semver in package.json and publish with the bridge tag format:
git tag tpf-mcp-bridge-vX.Y.Z
git push origin tpf-mcp-bridge-vX.Y.ZPushing that tag triggers the bridge repository's .github/workflows/publish.yml workflow. The workflow verifies that the tag name matches the root npm package.json version, runs the bridge and vendored generator tests, runs npm pack --dry-run, then publishes @pipelineframework/tpf-mcp-bridge to npm.
The bridge publish workflow uses npm trusted publishing rather than a long-lived NPM_TOKEN. The npm package must have a trusted publisher configured for:
- package:
@pipelineframework/tpf-mcp-bridge - repository:
The-Pipeline-Framework/tpf-mcp-bridge - workflow file:
.github/workflows/publish.yml
The workflow must retain permissions: id-token: write and use an npm version that supports trusted publishing. The current bridge workflow uses Node 24 with actions/setup-node@v6 and checks for npm >= 11.5.1, matching npm's trusted publishing requirements. If npm publish fails with a misleading 404 Not Found for an existing package, treat that as an npm authentication/trusted-publisher configuration problem before assuming the package is missing.
Before tagging the bridge:
Sync
template-generator-node/src/pipeline-template-schema.jsonfrom the matching released framework deployment artifact.Update generator templates that embed framework parent or dependency versions so generated projects target the released TPF version.
Bump the root npm package version and keep
package-lock.jsonaligned.Run
npm test,npm --prefix template-generator-node test, andnpm pack --dry-run.For generator changes that affect generated Java, generated POMs, runtime layout, REST await outputs, mapper contracts, or framework version alignment, run the generated-scaffold compile smoke against the matching framework tag. Generate inside a framework tag worktree under
examples/<smoke-name>so the scaffold root POM's<relativePath>../../pom.xml</relativePath>resolves topipeline-framework-root; then run:bash./mvnw -f examples/<smoke-name>/pom.xml -DskipTests compileNote the compatible TPF release in the bridge release notes, for example
compatible with TPF v26.5.2.
After the workflow completes:
npm view @pipelineframework/tpf-mcp-bridge version --registry=https://registry.npmjs.orgConfirm the returned version matches the bridge package version. If a publish fails after the tag exists, keep the tag immutable in normal release practice: fix the workflow or package metadata, then publish a new patch version and tag.
Important Publishing Details (Central Validation) ​
Publishing scope ​
The publish workflow deploys only the framework artifacts (parent, runtime, deployment) and skips examples:
- Maven runs from the repo root with
-pl framework,framework/runtime,framework/deployment -am. - The root POM is included in the reactor but is not deployed (
maven.deploy.skip=truein the root, overridden to false inframework/pom.xml).
Note: Publishing the framework-parent artifact is expected. It is the BOM/parent POM that consumers import for dependency management, so it will appear in Maven Central autocomplete results.
Publishing only framework artifacts does not mean example and SDK versions can drift. The E2E CI lanes build compatibility surfaces from the same checkout and expect their parent versions and tpf.version properties to match the root development version. A stale alternate POM can fail before tests start with Non-resolvable parent POM because its relativePath points to the checked-out root POM at a different version.
Main-branch E2E impact ​
The CSV E2E workflows run from alternate topology POMs. During the v26.4.4 release, release:prepare updated the root reactor to 26.4.5-SNAPSHOT, but left examples/csv-payments/pom.pipeline-runtime.xml, examples/csv-payments/pom.monolith.xml, and ai-sdk/pom.xml at 26.4.4-SNAPSHOT. Because the release plugin pushed by default, main received the drift before it was reviewed. The next main E2E runs then failed at Maven model resolution rather than test execution.
Treat this as a release-blocking condition:
rg -n "OLD_VERSION-SNAPSHOT" --glob "pom*.xml" --glob "!docs/versions/**"The command should return no active POMs after the next-development commit is prepared.
Flattening at publish time (property-gated) ​
Central validation requires resolved dependency versions. We avoid profiles for core behavior and enable flattening only during publish using a property:
- Default:
-Dtpf.flatten.skip=true(skip flatten during normal builds) - Publish:
-Dtpf.flatten.skip=false(flatten only for release)
This keeps local builds clean while producing Central-compliant POMs.
Tag immutability ​
Release tags are treated as immutable. If a publish fails after a tag is created, you generally cannot reuse the same tag:
- Create a new patch tag (e.g.,
v26.2.1,v26.2.2) for retries. - If repo rules allow, tags can be created from the GitHub UI as part of a release draft.
When to Use the Versions Plugin Approach ​
Use this manual approach only when you need fine-grained control or the Release Plugin is not available:
- Manual Version Update:
Update the version using the Maven Versions Plugin:
bash./mvnw versions:set -DnewVersion=1.0.0 ./mvnw versions:commitTest the build with
./mvnw clean install -P central-publishingCreate a Git tag (e.g.,
v1.0.0)Push the tag to trigger the GitHub Actions release workflow
Comparison:
- Release Plugin: Handles version updates across the selected Maven reactor, creates local release commits, and creates the local tag. It must run with push disabled so version drift can be fixed before
mainmoves. - Versions Plugin: Offers more manual control but requires multiple manual steps and careful coordination
Alternative: Manual Release Workflow ​
For more control, you can create a workflow dispatch that requires manual triggering:
name: Manual Publish to Maven Central
on:
workflow_dispatch:
inputs:
release_version:
description: 'Release version (e.g., 1.0.0)'
required: true
type: string
dry_run:
description: 'Dry run? (true/false)'
required: false
default: true
type: booleanThis approach allows manual control over when releases happen.
Troubleshooting ​
GPG Signing Errors:
- Verify your GPG key is properly configured
- Check that the GPG key ID matches what's in your keystore
- Ensure the GPG agent is running or passphrase is provided
Sonatype Authentication Errors:
- Verify your credentials in settings.xml
- Ensure you're using encrypted passwords in public repositories
- Check that your Central account has permissions for the group ID
Sonatype Central Publishing Errors:
- Review Sonatype Central logs in the GitHub Actions workflow
- Ensure all required artifacts (JARs, sources, javadoc, signatures) are present
- Check that artifacts meet Maven Central requirements
Main E2E Fails Before Tests Start:
- Check for stale parent versions in alternate POMs:
rg -n "OLD_VERSION-SNAPSHOT" --glob "pom*.xml" --glob "!docs/versions/**" - Check standalone TPF dependency properties such as
ai-sdk/pom.xml'stpf.version - Confirm the local root POM version matches the parent version in
examples/csv-payments/pom.pipeline-runtime.xmlandexamples/csv-payments/pom.monolith.xml
Testing the Setup ​
Before pushing a tag that triggers the release workflow:
mvn clean verify- test the build locally (runs unit tests)mvn clean verify -DskipITs- test build without integration testsmvn clean verify -P central-publishing- test with the central publishing profile but without deployment- Use a test Sonatype repository for verification
Important Notes ​
- Only the framework artifacts are published to Maven Central.
- Examples and SDK surfaces are not Central artifacts, but they are release-coupled because CI and users build them against the checked-out framework version.
- The root POM orchestrates the main build while the framework POM handles publishing.
- Prefer parent inheritance inside the root reactor. Check alternate top-level POMs and standalone POM properties separately during release preparation.
- Always verify release artifacts on Maven Central after a successful deployment.