Use a Swift command-line tool in a GitHub workflow

Β·

3 min read

Use a Swift command-line tool in a GitHub workflow

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

  1. install Mint, a package manager that installs and runs Swift command line tool packages
  2. 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.

Did you find this article valuable?

Support Marco Eidinger by becoming a sponsor. Any amount is appreciated!