Originally published on 2009-06-20; fully revised and updated.
This guide shows how to manage a resettable, multi-part build number in Apache Ant. It is useful for teams maintaining Ant-based CI/release pipelines and needing predictable version increments for major, minor, hotfix, revision, and optional continuous builds.
Source context: original DevOpsSchool post.

How this build numbering pattern works
The script keeps version state in a build.number file and updates it based on -Dincrement=.... By default, the build number is four parts (major.minor.hotfix.revision). For continuous builds, it can include a fifth part (continuous).
| increment value | What changes | Output format | Example command |
|---|---|---|---|
| (not set) | Revision increments by 1 | major.minor.hotfix.revision | ant |
| major | major +1, minor=1, hotfix=0, revision=1 | major.minor.hotfix.revision | ant -Dincrement=major |
| minor | minor +1, hotfix=0, revision=1 | major.minor.hotfix.revision | ant -Dincrement=minor |
| hotfix | hotfix +1, revision=1 | major.minor.hotfix.revision | ant -Dincrement=hotfix |
| continuous | continuous +1 | major.minor.hotfix.revision.continuous | ant -Dincrement=continuous |
| contReset | continuous=1 | major.minor.hotfix.revision.continuous | ant -Dincrement=contReset |
| rebuild | No counter change in the provided switch case | major.minor.hotfix.revision | ant -Dincrement=rebuild |
Create the build.number state file
Initialize the property file once in your project root:
major.number=1
minor.number=0
hotfix.number=0
revision.number=0
continuous.number=0
Add task definitions and targets in build.xml
The original snippet relies on Ant-Contrib tasks (such as var, switch, math, if) plus an unset task from Antelope.
Task definitions
<taskdef resource="net/sf/antcontrib/antlib.xml"/>
<taskdef name="unset" classname="ise.antelope.tasks.Unset"/>Code language: HTML, XML (xml)
Target 1: initialize counters from build.number
<target name="initBuildNum" description="Get current build number properties">
<property file="build.number"/>
<var name="next.major" value="${major.number}"/>
<var name="next.minor" value="${minor.number}"/>
<var name="next.hotfix" value="${hotfix.number}"/>
<var name="next.revision" value="${revision.number}"/>
<var name="next.continuous" value="${continuous.number}"/>
</target>Code language: HTML, XML (xml)
Target 2: apply increment logic
<target name="getBuildNum">
<switch value="${increment}">
<case value="rebuild"/>
<case value="major">
<math result="next.major" operand1="${next.major}" operation="+" operand2="1" datatype="int"/>
<var name="next.minor" value="1"/>
<var name="next.hotfix" value="0"/>
<var name="next.revision" value="1"/>
</case>
<case value="minor">
<math result="next.minor" operand1="${next.minor}" operation="+" operand2="1" datatype="int"/>
<var name="next.hotfix" value="0"/>
<var name="next.revision" value="1"/>
</case>
<case value="hotfix">
<math result="next.hotfix" operand1="${next.hotfix}" operation="+" operand2="1" datatype="int"/>
<var name="next.revision" value="1"/>
</case>
<case value="continuous">
<math result="next.continuous" operand1="${next.continuous}" operation="+" operand2="1" datatype="int"/>
</case>
<case value="contReset">
<var name="next.continuous" value="1"/>
</case>
<default>
<math result="next.revision" operand1="${next.revision}" operation="+" operand2="1" datatype="int"/>
</default>
</switch>
</target>Code language: HTML, XML (xml)
Target 3: persist and expose full.buildnumber
<target name="setBuildNumber" depends="initBuildNum,getBuildNum">
<propertyfile file="build.number">
<entry key="major.number" value="${next.major}"/>
<entry key="minor.number" value="${next.minor}"/>
<entry key="hotfix.number" value="${next.hotfix}"/>
<entry key="revision.number" value="${next.revision}"/>
<entry key="continuous.number" value="${next.continuous}"/>
</propertyfile>
<unset name="major.number"/>
<unset name="minor.number"/>
<unset name="hotfix.number"/>
<unset name="revision.number"/>
<unset name="continuous.number"/>
<property file="build.number"/>
<if>
<or>
<equals arg1="${increment}" arg2="continuous"/>
<equals arg1="${increment}" arg2="contReset"/>
</or>
<then>
<property name="full.buildnumber" value="${major.number}.${minor.number}.${hotfix.number}.${revision.number}.${continuous.number}"/>
</then>
<else>
<property name="full.buildnumber" value="${major.number}.${minor.number}.${hotfix.number}.${revision.number}"/>
</else>
</if>
<echo>${full.buildnumber}</echo>
</target>Code language: HTML, XML (xml)
Make your main build target depend on setBuildNumber so the version is calculated before packaging.
<target name="build" depends="setBuildNumber,compile,test,package"/>Code language: HTML, XML (xml)
Command examples
ant
ant -Dincrement=major
ant -Dincrement=minor
ant -Dincrement=hotfix
ant -Dincrement=continuous
ant -Dincrement=contReset
Build number update workflow
flowchart TD
A[Run ant with optional increment] --> B[Load build.number]
B --> C[Evaluate increment mode]
C --> D[Update counters in memory]
D --> E[Write counters to build.number]
E --> F[Reload properties]
F --> G[Set full.buildnumber]
G --> H[Echo version for downstream targets]
Common pitfalls and best practices
- Keep
build.numberunder controlled write access; concurrent CI jobs can overwrite each other. - Ensure required task libraries are on Ant’s classpath before running this script.
- Document when to use
major,minor, andhotfixto avoid accidental version jumps. - Be explicit in CI: pass
-Dincrement=...rather than relying on defaults. - If you need immutable build metadata, publish
${full.buildnumber}as an artifact variable after each run.
I’m Rajesh Kumar, a DevOps, SRE, DevSecOps, Cloud, and Platform Engineering expert passionate about sharing practical knowledge, real-world experiences, and industry best practices. I have worked at Cotocus and regularly write about technology, travel, investing, health, product reviews, and digital marketing through my various platforms.
I publish technical articles at DevOps School, travel stories at Holiday Landmark, stock market insights at Stocks Mantra, health and fitness guidance at My Medic Plus, product reviews at TrueReviewNow, and SEO and digital marketing strategies at Wizbrand.
Find Trusted Cardiac Hospitals
Compare heart hospitals by city and services — all in one place.
Explore Hospitals
When managing build numbers in automation pipelines, one important aspect is maintaining traceability rather than only focusing on incrementing or resetting values. In real CI/CD environments, build numbers are often linked with artifacts, deployment records, release notes, and monitoring systems, so resetting them without a proper versioning strategy can make debugging and auditing difficult. Teams should consider keeping build history, using immutable artifact identifiers, and ensuring that parallel builds do not generate conflicting versions. As projects grow, build numbering becomes part of release governance, and a small script change can impact deployment tracking and production support processes.