> The commit that changed the version in source is the one to be tagged
I've never understood this practice of not immediately bumping the version in source after a release. We update the version in source to the next logical version (usually a patch bump with an alpha0 pre-release tag) immediately after tagging and publishing a release. This way you just need to look at the version in source to understand where you're at in a release process, and only a single commit has the full release version in source, which is also tagged as such. This doesn't seem to be a common pattern, so shat are the downsides of this approach? Am I missing something?
This is what I was used to at a prior day job (another one just didn't keep a version in source). When I took over cargo-release [0], this was also the default though not anymore.
Benefits of keeping the last version
- Easier to detect when a change occurred since the last release
- (Rust specific) It makes it harder to patch a registry dependency with a git dependency because the versions will never align [1]. This is why the default changed in cargo-release.
- The "next version" is just speculation. Will the next release bump the pre-release version, patch, minor, or major?
A downside to keeping either last version or next version is if someone builds from master, the bug report could be confusing unless you include the git hash (and whether the repo was dirty).
I've seen some advocate for not keeping a version in source at all [2]. This article advocates against it but doesn't give the reason. I guess one is it requires you to have all tags locally which is a silent failure mode when you don't and disallows shallow clones.
OP here. I agree with this as well, you are not missing anything. I was meaning to write another post about a similar approach.
To illustrate to readers, let's say we are developing over a tag `v1.2-pre` in `main`, and now the code has stabilized. The idea is to release a tagged `v1.2`, then in the `main` branch immediately release a tagged `v1.3-pre` that follows it.
We obtain the following:
1) `main` branch commits look like `v1.3-pre-<number>-g<hash>` in `git-describe`.
2) A maintenance branch may have been forked from the commit that was tagged with `v1.2`, so for the commits in that `release/1.2` branch you automatically get `v1.2-<number>-g<hash>` as output of `git describe`.
One possible downside (of version bumping immediately after release) is that if you use the major/minor/patch scheme, you can never be sure that you’re actually bumping the correct number.
The upcoming release may be a major, a minor, or a patch release, and it might take you a while until you know enough about completed work items so you can make up your mind which one it’s going to be.
Not sure why that is a downside? Once you realize you need to update minor or whatever then update it, done. As a bonus you have great visibility into why and when the minor needed to update.
One complexity with that that comes to mind is how do you then track whether you've already bumped minor/major since last release and so don't need to again? Maybe it's rare enough and quick enough to manually check that that's not a problem, depends on the project I suppose.
> One complexity with that that comes to mind is how do you then track whether you've already bumped minor/major since last release and so don't need to again?
I wonder if it really matters that release version numbers only increment by one. If not, just bump anyway when appropriate change is made - no need to check.
In practice I think the problems would be
a) having to be very disciplined about this on every commit, rather than having a reminder to consider it as part of a release process, and
b) ordering version numbers from different branches when merging to mainline
True, that'd be a decent way to approach it. Though at that point perhaps you may as well actually release all the versions too.
Or you could bump every time as you describe, but on every major/minor bump make sure the parent commit is released first (which would be >=1 commits since the last one and have at least a patch bump). And I suppose you'd never need to bump patch if that was just a lone thing that happened post-release in prep for the next.
As opposed to having to track later when you bump the version whether you need a patch/minor/major bump?
Seems like any way you do it you have to keep track. IMO version numbers are slightly easier to keep track of than breaking changes. Changelogs are not always structured.
If you do it at the time of bumping, you can just look at the then-current version number to decide what new version a major, minor or patch release will result in. If you do it the moment you introduce e.g. a breaking change, you'll have to check whether this is the first breaking change since the last release (i.e. keep the set version number as-is), or if the one that it's currently set to is what it should be.
We do this by looking at the patch version. For example, current version in source is 2.2.1-alpha0. This means the last bump was a patch version from 2.2.0 to 2.2.1, so if you want a minor bump, then you need to bump to 2.3.0-alpha0. Now that the patch version is 0, it's that someone has already bumped the minor version since the last release, so no need to do so again. This would break down if someone bumps to 2.3.1-alpha0 unnecessarily but otherwise it's immediately obvious looking at the current version in source whether someone has already bumped the minor version.
Yes, but what I am saying it is way easier to go back and look at what the most recent release was (most SCMs have a page for all the tags, and there is git tag), but it is harder to go back and figure out if you had breaking changes (would need to use conventional commits or similar, then parse the gitlog or structured changelog).
You'll have to figure out if you had breaking changes regardless of when you change the version number, no? My strategy there is to add to the 'unreleased' section of the changelog as they are introduced.
GP's point (I think I agree now, I was only saying it was a potential issue) being that that's a lot easier to do at the time - you know you've just made a breaking change (or you should do; as much/more than you ever will) so that's the easiest time to bump the version appropriately.
An alternative model I suppose would immediately have major bump, minor bump, and patch bump branches; then you just commit to the appropriate one, and I suppose keep major rebased on minor rebased on patch. (And master = major I suppose.)
Right, but what if that was the second breaking change you made? To figure out whether that's the case, i.e. whether you need to bump the major version or not, you'll have to check out the last released version.
My repos have a `make_release {bump | tag}` script for this purpose. The repos have both a develop and a main branch. The develop branch always has the next version with a `-dev` suffix. e.g. `4.6.1-dev`.
Then, running `make_release tag`:
1. Sets the version to `4.6.1`, commits it, tags it.
2. Sets the version to `4.6.2-dev`, commits it.
I then merge the 4.6.1 tag to main and push main, develop, and the 4.6.1 tag to the repo.
If development calls for a minor or major release, that's what the `bump` option is for. It prompts for which part of the version string should be incremented and creates a commit doing just that.
Setting the version usually involves touching a couple of files. e.g. a source code constant and a podspec or some other metadata file. The script really helps preventing any mistakes.
The downside is that you need a commit which serves no purpose other than to increment the number. The only content which is different is is the version number; you're effectively tagging the same software with an incremented version. Why tag the same thing with two different versions? You're only inviting merge conflicts with changes like this.
Unreleased software doesn't require a version number at all.
What you can do is incorporate some build identifier into local unreleased builds. It could be from the git sha, plus some indication of whether it was a dirty state or exactly that git sha.
The command does it:
git describe --tags --dirty
It will produce a string consisting of the most recent tag, the number of commits since that tag, a portion of the hash and the word "dirty", all separated from each other by a dash. If there are no uncommited changes, then -dirty is omitted, and if there are no commits since the tag (we are at the tagged commit), then the count of commits and hash are omitted.
You can incorporate this sort of identifying string at build time; nothing is checked in. If you build the tagged release in a clean repo, the string will just be the tag. Only if you make new commits and/or have uncommited changes do you get a different string without having to commit anything.
I thought about doing that, but I realized that I have no way of knowing how pervasive my next set of changes will be, whether it will warrant a patch or a minor version bump. The major ones are much easier to predict; I don't go into them without prior planning. But sometimes, what I think are going to be bug fixes turn out to be API changes. So is 2.19.3 going to go to 2.19.4 or 2.20.0 next week? No clue.
Bump it in source to 2.19.4 immediately, and then to 2.20.0 as soon as there is code that requires a minor version bump, then to 3.0.0 as soon as a major version bump is required. Then your version in source is always semver compatible vs by definition being off until you cut the next release as soon as you introduce a breaking change.
Why have the version in the source code at all? A build/packaging process should query git at build/packaging time to get the version (using git describe) and produce versioned artifacts (which can be source code or binaries).
Sometimes people don't use git to get the source. The "Download ZIP" function on Github for example includes no version number at all as far as I can tell, only the branch name in the filename.
For GitLab there there exist a workaround by using .gitattributes to create a VERSION file with "$Format:%(describe:tags)$" which will get expanded to the git describe string on archive creation, so the version number even survives in a .zip. GitHub however ignores .gitattributes and so far I haven't seen another way to get a version number into the .zip other than just including it in the source.
I'm fine with this sacrifice -- if someone downloads HEAD, they are not running a "version", so it would be misleading to have the source think it's at 1.0 or 1.1 if really it's at a commit between those two versions. I have my build scripts call the version "develop" or similar if this is the case.
I’m not sure what you mean by “bumping the version in source after a release”. If it’s after a release then you released it with the old version? Or do you mean prep the repo for the next version by incrementing it optimistically, in which case it probably depends on your branding model. But I don’t understand the “immediately” part of your question.
Where I work we don’t increment until we know what branch/version we are working on, and determine the semantic version, and then we still use tagging because we tag the version + build id which is computed at build time
My understanding is that initially they have version 1.4.2 and release 1.4.2. Then in the next commit, immediately after release, they bump the version to 1.4.3.alpha0 or something of that nature.
I personally think that's messy because it requires two changes to the version instead of one and there's no way of knowing that the next version will be 1.4.3. If a developer makes a breaking change, will they update the version in the code to 2.0.0.alpha0 or do they fix the version when they release?
I suppose if you have nightly releases then it could make sense but then I think using the date or the commit hash as the version number would make more sense.
> We update the version in source to the next logical version
That’s some impressive prognostication! I recently released a semver major for a minor corner case bug fix. I couldn’t have predicted this would be version 4 after over a year maintaining version 3, but it was a significant breaking change to fix a minor bug.
I've never understood this practice of not immediately bumping the version in source after a release. We update the version in source to the next logical version (usually a patch bump with an alpha0 pre-release tag) immediately after tagging and publishing a release. This way you just need to look at the version in source to understand where you're at in a release process, and only a single commit has the full release version in source, which is also tagged as such. This doesn't seem to be a common pattern, so shat are the downsides of this approach? Am I missing something?