ctms

Build numbers

The conventional approach is to use a monotonically increasing integer. Every time I make a build, this integer gets bumped and baked into the artifact. Then, to fix it in place, I'll commit it and (optionally) add a tag. If sometimes later I need to trace the artifact back, I find the commit that contains that integer. All good and straightforward.

But this integer is state. And it is state that I need to maintain, make sure it's always incremented when I make a build, commit it, and so on.

The question is: can I get the benefit of traceability without maintaining this state?

Commit hash

Well, one option is to use the commit hash. This gives me back a unique string of 7 (or more) characters that points to my latest commit:

$ git rev-parse --short HEAD
$ 12790c3

Yes, it's a string and it might not always be suitable (sometimes it really must be an integer), but for my specific case it was just right: the artifact I'm shipping is a sensible-named .zip file that contains all the resources:

$ENV-$NAME-$VERSION.zip

And now, with the git hash added:

alpha-foobar-1.2.0-12790c3.zip

No extra state to maintain, no separate counter. Perfect.

Commit count

But let's say it really has to be an integer... In this case, one other option is to use the commit count. Provided I'm always building the release artifact from the same branch and git history is not rewritten, this will always give me back a monotonically increasing integer:

$ git rev-list --count HEAD
$ 42

A bit more fragile, but good-enough in certain cases. And, most important, it's again something I can get for free.

To get back the commit hash for a certain build number:

$ git log --reverse --format='%H' | sed -n '42p'
$ 12790c3

where 42 was the build number.

Auto-increment?

Fastlane's latest_testflight_build_number provides an interesting escape hatch: I can fetch the latest build number from TestFlight, increment it before starting the build and then, once the build is done (and uploaded), commit the build number. TestFlight keeps my state for free.

latest_build = latest_testflight_build_number(
  app_identifier: "some.random.app",
  platform: "ios",
  api_key: "some app store connect api key"
)

increment_build_number(build_number: latest_build + 1)