Building and testing Swift packages on Windows using GitHub Actions

Sponsored
RevenueCat logo
Releases so easy your work will never pile up

Runway handles the release coordination and busywork so you can focus on building great apps. You do the building, we'll do the shipping.

Swift is a great language for writing cross-platform libraries and command-line applications. While Swift is commonly used on Apple platforms and Linux-based operating systems, it is also possible to run Swift programs on Windows.

Having said this, setting up a Windows development environment for Swift on a non-Windows machine can be a bit of a challenge as cross-compilation is highly complex and I didn’t manage to get Docker working locally either.

Thankfully, there is an easy way of setting up a CI/CD pipeline to build and test your Swift packages on Windows using GitHub Actions thanks to an amazing action by Saleem Abdulrasool, the driving force of Swift on Windows.

Building the workflow

Let’s consider we have a Swift package with a single executable target and product and we want to make it available for Windows users.

To do so, we can create a GitHub Actions workflow that runs on every push to the main branch, builds and tests the package on a Windows machine and uploads the executable as an artefact:

windows.yml
name: Build for Windows

on:
  push:
    branches:
      - main

  build-windows-executable:
    name: Build Windows Executable
    # 1
    runs-on: windows-latest
    steps:
      # 2
      - uses: compnerd/gha-setup-swift@main
        with:
          branch: swift-5.6-release
          tag: 5.6-RELEASE
      # 3
      - uses: actions/checkout@v4
      # 4
      - run: git config --global core.protectNTFS false
      # 5
      - run: swift build -c release --static-swift-stdlib --product ReadingTimeCLI
      # 6
      - run: cp .build\release\ReadingTimeCLI.exe ReadingTimeCLI.exe
      # 7
      - uses: actions/upload-artifact@v3
        with:
          name: ReadingTimeCLI-windows.exe
          path: ReadingTimeCLI.exe

Let’s break down the workflow above step by step:

  1. We specify that the workflow should run on the most up-to-date Windows machine available.
  2. We use the gha-setup-swift action by Saleem Abdulrasool which will make the Swift toolchain available on the machine.
  3. We check out the repository.
  4. We disable NTFS protection as it can cause issues when copying files and cloning the repository.
  5. We build the package using the swift build command. We specify the --static-swift-stdlib flag to statically link the Swift standard library.
  6. We copy the executable from the build folder to the root of the repository. We must remember to use the backslash (\) as the path separator as opposed to what we would normally use on Unix systems (/).
  7. We upload the executable as an artefact using GitHub’s own actions/upload-artifact action so that we can download it later.

You might run into some issues when SPM resolves the package’s dependencies due to the way git works on Windows. For example, git can’t check out files that contain colons : in their paths. If you want to learn how you can get around these issues, check out this article on my blog.