Building and testing Swift packages on Windows using GitHub Actions

RevenueCat logo
Relax, you can roll back your mobile release

No one is immune from shipping critical bugs to production, but Runway helps you limit the amount of havoc that can cause.

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:

name: Build for Windows

      - main

    name: Build Windows Executable
    # 1
    runs-on: windows-latest
      # 2
      - uses: compnerd/gha-setup-swift@main
          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
          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.