Maven Release Job in GitLab CI/CD: Automate Versioning by release:prepare & release:perform
Automating the software release lifecycle is a critical milestone in DevOps maturity. In this chapter, we integrate the Maven Release Plugin directly into our GitLab CI/CD pipeline. We eliminate manual intervention for version management, remove snapshot suffixes automatically, handle Git authentication safely using project-scoped access tokens, and ensure that our production artifacts target our production database (prod profile) with absolute precision.
1. Why Do We Need Automated Releases? (Status Quo vs. Automation)
In a traditional development lifecycle, releasing software involves several manual, error-prone steps: developers must manually check out the release branch, modify the pom.xml to strip out -SNAPSHOT tags, execute release preparation commands locally, push tags manually, and update the version for the next development iteration.
The Problems with Manual Releases
- Human Error: Developers might forget to change profiles, misconfigure release tags, or push incorrect versions.
- Inconsistent History: Local machine releases lack standardization and audit trails compared to CI/CD pipelines.
- Slow Feedback Loops: Manual version bumping wastes valuable development time and delays deployments.
Real-Life Analogy: The Printing Press vs. Hand-Copying Scribes
Real-Life Analogy: Imagine publishing a book. Hand-copying every single copy (Manual Release) leads to typos, missing pages, and exhausted scribes. Setting up an automated printing press (GitLab CI/CD Pipeline with Maven Release Plugin) requires upfront calibration, but once you press the "Start" button, every book rolls off the assembly line identical, correctly versioned, and bound for the right bookstore shelf.
2. Core Prerequisites in pom.xml & GitLab Security
Before writing a single line of CI/CD configuration, our Maven project (pom.xml) and GitLab repository must be properly configured to allow external tools to commit and push changes back to the repository.
Step A: Configuring SCM Tag in pom.xml
The Maven Release Plugin requires a defined Source Code Management (SCM) block so it knows where to push version updates and release tags.
<scm>
<connection>scm:git:https://gitlab.com/your-username/your-repo.git</connection>
<developerConnection>scm:git:https://gitlab.com/your-username/your-repo.git</developerConnection>
<tag>HEAD</tag>
</scm>
Note: The
developerConnectionURL must match the HTTPS clone URL of your current repository, which you can easily copy directly from your GitLab repository page (Clone with HTTPS).
Step B: Generating a Project Access Token in GitLab
Because the pipeline will execute git commit and git push commands under the hood, it needs authenticated write access to your repository.
- Navigate to your GitLab User Profile -> Edit Profile -> Access Tokens.
- Create a New Token with a short expiration window (e.g., 1 week).
- Assign the following two mandatory scopes:
read_repositorywrite_repository
- Copy the generated token immediately and store it securely (e.g., in GitLab CI/CD Environment Variables).
3. Dynamic Version Extraction & -SNAPSHOT Stripping
A standard Maven release requires specifying a target release version (e.g., 1.0.0) and a next development version (e.g., 1.0.1-SNAPSHOT). In automated CI/CD pipelines, hardcoding these values defeats the purpose. We must dynamically extract the version from our pom.xml.
The Version Extraction Shell Command
We can parse the project version directly from pom.xml and strip out the -SNAPSHOT suffix using shell utilities:
# Extracts version from pom.xml and removes '-SNAPSHOT'
RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | sed 's/-SNAPSHOT//')
echo "Calculated Release Version: $RELEASE_VERSION"
Flow Walkthrough: Version Transformation
4. Writing the GitLab CI/CD Release Job
Now we bring everything together inside .gitlab-ci.yml. We configure Git credentials locally within the job runner, configure repository-specific Git settings, and invoke the Maven Release Plugin phases (clean, release:prepare, release:perform).
Complete .gitlab-ci.yml Release Job Configuration
maven-release-job:
stage: deploy
image: maven:3.8.5-openjdk-17
script:
# 1. Configure local Git user identity for automated commits
- git config user.name "GitLab CI Bot"
- git config user.email "ci-bot@gitlab.com"
# 2. Ensure we are on a tracked local branch and up-to-date
- git checkout -B $CI_COMMIT_REF_NAME
- git pull origin $CI_COMMIT_REF_NAME
# 3. Dynamically compute the release version and tag name
- export RELEASE_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout | sed 's/-SNAPSHOT//')
- export RELEASE_TAG_NAME="v$RELEASE_VERSION"
- echo "Releasing version $RELEASE_VERSION with tag $RELEASE_TAG_NAME"
# 4. Inject credentials into the remote URL for automated push
- git remote set-url origin https://<YOUR_GITLAB_USERNAME>:${GITLAB_ACCESS_TOKEN}@gitlab.com/<YOUR_GROUP>/<YOUR_REPO>.git
# 5. Execute Maven Release Plugin (Clean -> Prepare -> Perform) with Production Profile
- mvn release:clean release:prepare -DreleaseVersion=$RELEASE_VERSION -DdevelopmentVersion=$RELEASE_VERSION.next -Dtag=$RELEASE_TAG_NAME -Darguments="-Pprod"
- mvn release:perform -Darguments="-Pprod"
# 6. Clean up local git remote credentials
- git remote set-url origin https://gitlab.com/<YOUR_GROUP>/<YOUR_REPO>.git
rules:
- if: '$CI_COMMIT_MESSAGE =~ /^\[maven-release-plugin\]/'
when: never
- when: manual
5. Handling Production Profiles and Database Connectivity
Releases are intended for production environments. Therefore, when building the artifact during the release phase, we must activate the production profile (-Pprod) so that database connection strings and environment configurations match our production infrastructure.
Database Verification Walkthrough
To verify that our release artifact successfully binds to the production database:
- Open PostgreSQL (via PGAdmin) and inspect the
blogtable in the production schema (initially empty). - Execute the generated release JAR file on port
1990:BASHjava -jar target/blog-0.0.1.jar - Send a
POSTrequest via Postman to persist a new blog entry. - Refresh the PGAdmin database view to confirm data persistence:
6. Avoiding Infinite Pipeline Loops (Workflow Rules)
The Problem: Push Recursion
When the Maven Release Plugin runs release:prepare, it performs two separate automated git pushes back to your repository:
- Push 1: Commits version changes in
pom.xmland creates the Git Tag (v0.0.1). - Push 2: Bumps the version to the next development snapshot (e.g.,
0.0.2-SNAPSHOT) and pushes again.
If left unchecked, each of these automated pushes will trigger a brand new GitLab CI/CD pipeline, resulting in an infinite feedback loop!
The Solution: Commit Message Guardrails
We prevent this infinite loop by adding a workflow rule that instructs GitLab to skip pipeline execution whenever the commit message originates from the release plugin:
workflow:
rules:
- if: '$CI_COMMIT_MESSAGE =~ /^\[maven-release-plugin\]/'
when: never
- when: always
7. Real-World Debugging & Industrial Best Practices
When implementing automated release pipelines in enterprise environments, engineers frequently encounter specific failure modes. The table below outlines these common pitfalls and their robust solutions.
Summary and Key Takeaways
- Automated Versioning: The Maven Release Plugin eliminates manual version bumps by programmatically deriving release versions from
pom.xml. - SCM Configuration: A properly structured
<scm>block withdeveloperConnectionis mandatory for Maven to push tags and version commits. - GitLab Access Tokens: Use project-scoped personal access tokens with
read_repositoryandwrite_repositorypermissions for secure authentication. - Snapshot Stripping: Use
sedcombined withmvn help:evaluateto cleanly strip the-SNAPSHOTsuffix for production releases. - Production Profiles: Always pass
-Darguments="-Pprod"during release preparation and performance to bind artifacts to production configurations. - Infinite Loop Prevention: Utilize commit message regular expression rules (
^\[maven-release-plugin\]) to prevent recursive pipeline triggers. - Manual Gatekeeping: Production releases and deployments must always be protected with manual execution gates (
when: manual) to maintain operational control.