Auto-generate a version name for a Swift PM project
Previously, we discussed how to use git describe
to generate an informative version name of a project. How do we use it in a Swift Package Manager project to automate the version name generation?
Problem: Multiple sources of truth for version info
It’s a common practice to ship binaries of a program with the version info and provide a way to check the version through its interface.
Let’s say we are building a command line program in Swift using Swift Package Manager:
├── Package.resolved |
In the main source file MyCommandLineTool.swift
, the version
is defined as a string constant "1.2.3"
.
import ArgumentParser |
Thanks to Swift Argument Parser, making the version available to the command line is very easy. We can check the version number by passing in the --version
flag:
$ swift build |
Creating a git
tag for each release is also a common practice. In this example, we can tag this release with 1.2.3
:
$ git tag -a 1.2.3 |
This works but is not ideal, because there are two places holding the same version that may get out of sync:
- A string constant in the source code
- A
git
tag of the project repository
How can we consolidate the version into one place? We know it’s possible to generate a pretty informative version name using git describe
as discussed in the previous post, so why not write an easy script to automate this version generate process?
Solution
Auto-generate GitInfo.swift
Let’s create a script Scripts/gen-git-info.sh
:
|
Running this script will generate a new file Sources/MyCommandLineTool/GitInfo.swift
:
/// Auto-Generated Git Info. |
ℹ️ NOTE
Theversion
string has a-dirty
suffix because we have uncommitted changes.
Finally, we can update the main MyCommandLineTool.swift
file to read the new auto-generated version number:
import ArgumentParser |
Now, let’s check the result:
$ swift build |
🙌 auto-generated version string is printed out!
Ignore the GitInfo.swift
file
Because the GitInfo.swift
file is auto-generated based on the info from the git
repo, committing this file into the repo will affect the versing string generated from git describe
. It’s better to make sure we don’t commit the file by adding it to .gitignore
:
# Other ignored files ... |
Use Makefile
to manage build tasks
Everything works so far, but we have to remember to run the gen-git-info.sh
script manually every time before building. How can we automate this process?
Turns out the good old make
command can help us. We can create a Makefile
for the project and manage the build process:
gen-git-info: |
⚠️
Makefiles must be indented using TABs and not spaces ormake
will fail.
Here, we make the gen-git-info
script as the dependency of the build
and release
, so that the script is guaranteed to run before the swift build
command.
Put everything together
Now our project looks like this:
├── Makefile |
To build a debug build, we can run make build
:
$ make build |
To build a release build and install it to the ~/bin
directory we can run make install
:
$ make install |