This blog post is for you if
- You are using GitHub actions and workflows for CI/CD
- You don't know how to use a Swift command-line tool in a GitHub workflow OR
- You wonder if your workflow, executing a Swift command-line tool, can be improved regarding performance or maintainability
My example: I'll want to use Swiftformat, a command-line tool to reformat Swift code in my current GitHub repository.
Simple solution
name: Execute a Swift package command-line tool
on:
push:
branches: [ main ]
jobs:
swiftformat:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- run: |
brew install mint
mint run nicklockwood/SwiftFormat@0.49.1 --verbose .
This workflow will
- install Mint, a package manager that installs and runs Swift command line tool packages
- use Mint to download, build from source and then run the executable of the Swift Package
The simplicity of this workflow has several drawbacks:
- the Swift Package gets built from source for every workflow execution
- I have to change the workflow code when changing the version of the command-line tool to be used, e.g. at some point in the future I want to use version 0.50.0
I will explain how to improve the workflow above step-by-step. You can copy the complete and improved workflow at the end of this blog post.
Step-by-step improvements
First, create a Mintfile
in the root of your repository.
nicklockwood/SwiftFormat@0.49.1
A Mintfile can specify a list of versioned packages. It makes installing and running these packages easy, as the specific repos and versions are centralized.
Then modify your workflow to leverage GitHub action cache to cache Swift Packages installed and built by Mint.
- name: Cache/Restore Mint packages
id: mint-cache
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: ${{ runner.os }}-mint-
Let's separate the installation of Mint for better readability. If Mint is already installed let's upgrade.
- name: Install package manager "Mint" to install and run Swift command line tool packages
run: |
brew upgrade mint || brew install mint || true
Now comes the important part: only install and build the Swift Package if there is no cached version.
- name: Install command line tool (if not yet cached)
if: steps.mint-cache.outputs.cache-hit != 'true'
run: mint bootstrap
With mint bootstrap
the package manager will find the version declared in the Mintfile
and install that version.
As the final step, let's run the command-line tool.
- name: Run command line tool
run: mint run swiftformat --verbose .
Mint will search for the newest tag in the installed packages and run that version when ditching the version.
End result
The whole improved workflow:
name: Execute Swift package command-line tool
on:
push:
branches: [ main ]
jobs:
swiftformat:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: Cache/Restore Mint packages
id: mint-cache
uses: actions/cache@v2
with:
path: ${{ github.workspace }}/mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: ${{ runner.os }}-mint-
- name: Install package manager "Mint" to install and run Swift command line tool packages
run: |
brew upgrade mint || brew install mint || true
- name: Install command line tool (if not yet cached)
if: steps.mint-cache.outputs.cache-hit != 'true'
run: mint bootstrap
- name: Run command line tool
run: mint run swiftformat --verbose .
Step "Install command line tool (if not yet cached)" will only be executed for the very first workflow run. Subsequent workflow runs will skip that step as the previous step "Cache/Restore Mint packages" will restore the cache from the first workflow run.