In software engineering, software versioning is the practice of assigning unique version numbers or names to different releases of a software application. Each version number represents a specific set of changes or updates to the software.
Software versioning is important because it ensures applications are reliable, consistent and well-maintained over time. It allows developers to:
- Keep track of changes: Assigning version numbers to different releases allows developers to easily track changes to the software over time.
- Communicate effectively: Versioning provides a simple and clear way to communicate which version of the software is being used. This is especially important in collaborative environments where multiple developers are working on the same codebase.
- Manage releases of software updates and new features
- Troubleshoot issues: Access a historical record of changes to the software, which can be useful for tracking bugs and issues, identifying the cause of errors, and verifying compliance with regulations and standards.
In this blog, we will learn how to automatically create versions using Buildkite pipeline and GitVersion.
Software versioning using GitVersion and Buildkite
In simple layman language, software versioning involves tagging your code at a particular point in time. It is as simple as tagging your git commit.
A tag in the git world is just an alias for a commit SHA. If it was easier to refer and read a commit SHA, then it could be used for versioning. However, since it is a string of random characters, there is a need to create human readable tags.
Commit SHA — ecfb1a78d7ce3fe5c0080fa305132edd4e84e6d3
Git tag → v1.0.0+ecfb1a7
Different Versioning Schemes
In the scope of versioning an application, there are a couple of different schemes that can be chosen. The choice depends on what the chosen continuous integration software can support.
The most simple versioning scheme could be versioning your system as v1, v2, and so on. But this might not provide any meaningful information to the end user.
Common versioning schemes that teams choose are:
- Build numbers: Using an incremental number that is defined by the run of an automatic build pipeline
- Date and time: Using the timestamp of a build as a unique timestamp to define a version
- Semantic version (SemVer): Creating defined major.minor.patch+<additional_metadata> schema versions
Semantic versioning (SemVer) offers a solution that allows version numbers to be more descriptive. A semantic version number follows the structure MAJOR.MINOR.PATCH.
The different sections are numbers that we increment as:
- MAJOR – When we introduce incompatible/API breaking changes
- MINOR – When we add functionality in a backwards compatible manner
- PATCH – When we make backwards compatible bug fixes
The main disadvantage with the first two schemes is that they aren’t descriptive. When comparing multiple versions that follow an incremented version, it’s hard for a user to understand if non-breaking changes have been introduced in a new version.
The semantic version strategy is the industry standard to version applications.
How to achieve automated versioning using GitVersion
GitVersion is a Command Line Interface (CLI) used to generate these version numbers. GitVersion works well with existing Git branching strategies like Mainline, GitFlow or GitHub Flow.
Although using a standardised branching strategy is recommended, GitVersion’s flexible configuration allows the tool to be set up according to desired needs.
Versioning Strategy
- Follow the continuous delivery approach
- Assumes main branch is protected and can only be committed via a PR process
- Every PR merge commit to main will increment the minor version
- Any hotfix branch will increment the patch version
- Any commit message containing the text ‘major:’ will increment the major version.
- Create initial tag 1.0.0 manually, otherwise gitversion default version will start from 0.1.0
This strategy will ensure every branch has a unique version and can be published independently.
User Workflow
GitVersion Configuration
Ensure you have a git repository cloned locally, or can use repo.
Checkout a branch using git checkout -b feature/add-versioning
- Create GitVersion.yml in the root of your project
mode: ContinuousDelivery |
- Create script files to be executed in Buildkite
scripts/calculate-version.ps1
scripts/push-git-tag.ps1
# file scripts/calculate-version.ps1 |
# file: scripts/push-git-tag.ps1 |
Create module
files
scripts/modules/git.psm1
scripts/modules/version.psm1
scripts/modules/buildkite.psm1
# file : scripts/modules/git.psm1 |
Version.psm1
# file: scripts/modules/version.psm1 |
# file: scripts/modules/buildkite.psm1 |
This is how the folder structure would look:
Lastly, let’s create a Buildkite pipeline to create git tags on every commit to the main branch.
Add a new file .buildkite/pipeline.yml
steps: |
Execute command `pwsh scripts/calculate-version.ps1` locally to return below output.
Here is your first calculated version — 0.1.0.ci-1-feature-add-versioning.aa41245
Conclusion
Now, you have a way to automate the versioning process as part of your pipeline, and GitVersion will take care of creating the next git tag for your application.
Key points:
- You can keep track of every transition in the software development phase.
- The first version starts at 0.1.0 and not at 0.0.1, as no bug fixes have taken place. Rather, we start with a set of features as the first draft of the project.
- Versioning can do the job of explaining to the developers about what type of changes have taken place and the possible updates in the software.
- It helps to keep things clean and meaningful.
- It helps other people who might be using your project as a dependency.