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
gittag 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
Theversionstring has a-dirtysuffix 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 ormakewill 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 |