Versioning code

Hello GitLab Community,

I have a question in regards to versioning code. Just looking for ideas on how other teams do this. Right now, it is a very manual process for my team to update the version number in our codebase after a release. Currently we run a script to update the version number in the code, commit it, and push. Wanted to how other folks automate this process. Do you use ChatOps, like a slack bot or something similar to automate this? Any ideas/comments/thoughts are greatly appreciated!

Thank you,

1 Like

I am also quite interested in knowing this. I’m becoming a huge fanatic of CI, but manual versioning is still a bummer - though I confess that I haven’t really looked into the options…

I use git tags. Tag each release with your version number, e.g. ‘v1.0.0’, you can then have a script in your build process that updates the version number in the code based on the current tag, which you can get from the CI_COMMIT_TAG variable ({CI_COMMIT_TAG#v} if you want to exclude the leading ‘v’).

You can also limit your deployment jobs to tags that match the version pattern, e.g.:

    - if: '$CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9-\.]+)?$/'

And protect ‘v*’ tags as well to limit only allowing certain users to create releases and/or protect CI variables reserved for the deployment job.

1 Like


Thank you for your response. I see what you are saying in regards to tags. When the script runs to update the version in the code, how does the code get committed? I was under the impression it is not a good idea to have your CI/CD pipeline push commits to your repository as it can be difficult to undo changes, and it does not reflect the developer’s intent. Having the CI/CD pipeline push commits to the repository would violate rule 6 here on the 11 Rules of GitLab Flow:

How does the team update the version number in the codebase, automate it, yet not have the CI/CD pipeline update the version number in the code? Is this done via a Slack bot (“ChatOps”)?

Thank again for taking time to respond. I very much appreciate your thoughts and opinions.

I don’t commit the version back into the repo, just edit whatever files are necessary for the build/deploy process in place then run the build/deploy. The end result is captured as a build artifact or deployed to a server. You can always recreate the same build by re-running the CI job.

Here’s an example project using the technique, in this case a python module that gets deployed to PyPI:

If you have a need to commit it (which I don’t recommend), you can commit to a different branch, potentially one per release.

(FWIW, I have no affiliation with, that’s just the name portion of the email I used to sign up on and unfortunately the forum took that as my username and won’t let me change it.)

The strategy I’m most fond of uses these principles:

  • The Major.Minor.Patch of semver is committed as source and is updated in standard MR fashion
  • All tags are 100% automatic & done by CI
  • Semver Extraversion is incremented automatically per build. The assigned semver is not commited in the repository, it is strictly a tag
  • Git tags are retrospective and are always correct. If you’re looking at a released build, you know exactly the tag it came from because the artifact was created before the tag was added.
  • Artifacts receive additional metadata (such as a build number / CI_BUILD_IID) allowing them to be directly addressed even in the event that there’s a rebuild of a tag
  • Candidate artifacts are published to locations that include the branch as an identifier
  • Only certain blessed branches are permitted to release. This is enforced in CI.

Using these principles all the natural development flows work naturally. Forking and branching carry the semver naturally. There’s an idea of a development and release stream that can change over time and can be associated naturally with branches. The act of release is 100% automatic, no action required other than to merge the MR and optionally press the manual approve button in the CI release stage.

1 Like


I took at your code. I see what you are doing. I am currently doing the same thing. I have a variable for the version and an arbitrary placeholder value. When I push a tag, I set the version to the tag just like you are doing in your codebase. It seems as though release branches are the way to go if I want commit to the version back into the repository.

Thank you again for your help and insights. It is nice how to know how other teams operate in this “DevOps” world we live in today.

Hello @hstenzel,

Thank you for your response. So when you version code, you have everything done via the CI build process. How does the CI pipeline know when to create a tag? Is this triggered by a MR?

Hey @cbille0,

Yes, everything is done in the CI pipeline.

Not every MR necessarily gets an independent release or a tag. However, every build on a release branch does have the potential receive a tag.

Here is the basic flow:

  1. Build (including unit tests, etc)
  2. Integrate (runs appropriate integration tests, including upgrade/downgrade)
  3. Approve (release branches only, may be manual. This is where the tag is computed and assigned)
  4. Release (release branches only, puts the build artifacts in the release locations)
  5. Tag (release branches only, git tag)
  6. Deploy (release branches only, if appropriate)

Hope this helps


1 Like

We use tags with pyscaffold for our python packages. Works automatically whenever we do release.

It certainly does help. Thank you for your response!


1 Like