Swift ci portability
Skill chsistrying/swift-ship-skills/skills/swift-ci-portability
Agent Skills for shipping Swift/macOS apps: .icns icons, .app/DMG packaging, CI portability traps, OSS readiness audit, release flow. Claude Code plugin marketplace.
npx -y skills add chsistrying/swift-ship-skills --skill swift-ci-portabilityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 17 days oldThe repository was created 17 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Sets up GitHub Actions CI for Swift Package Manager projects (minimal macOS build+test workflow, Xcode/macOS version matrix, .build caching, runner image selection) and diagnoses/prevents environment-dependent Swift bugs that pass locally but fail on CI (JSONDecoder ISO8601 fractional seconds, NumberFormatter locale grouping, Date/timezone assumptions, locale-dependent string sorting, hardcoded paths, filesystem case-sensitivity, Bundle.module resource paths, Decimal/Double formatting). Use when asked for "swift CI", "GitHub Actions swift test", when "tests pass locally but fail on CI", or when debugging "locale" or "Foundation differences" between macOS/Xcode versions. Trigger with "/swift-ci-portability".
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.9 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
Swift CI & Portability
Overview
Two jobs, one skill: get a SwiftPM project onto GitHub Actions CI quickly, and stop (or diagnose) the specific class of bug where tests pass on a developer's Mac but fail on CI - or pass on one CI runner and fail on another.
Prerequisites
- A Swift Package Manager project (
Package.swift) hosted on GitHub with Actions enabled. swifttoolchain locally for reproducing CI failures.
When to use this
- Setting up CI for a SwiftPM package/app for the first time.
- A test suite is green locally but red on GitHub Actions (or red on one matrix row and green on another).
- You're about to write code that touches dates, locales, number formatting, file paths, or bundled resources, and want to avoid known traps up front rather than debug them later.
Instructions
Setting up GitHub Actions CI:
-
Copy the template workflow into the repo:
mkdir -p .github/workflows cp assets/ci.yml.template .github/workflows/ci.yml -
Open
.github/workflows/ci.ymland adjust to the project:- Runner image / Xcode version - the
testjob pins one macOS/Xcode combination as the fast required check. Pick versions that match what your team actually develops against. Check currently available images and their preinstalled Xcode versions at https://github.com/actions/runner-images (search the macOS READMEs) before picking a runner label likemacos-14ormacos-15. - Matrix rows (
test-matrixjob) - list the oldest and newest macOS/Xcode combination you need to support. This is what catches the Foundation-version-dependent bugs cataloged inreferences/portability-traps.md- they show up as one matrix row failing while others pass. - Cache key - the template keys the
.build/ SwiftPM cache onhashFiles('**/Package.resolved'), so the cache invalidates exactly when dependencies change. Leave this as-is unless you have a reason to scope it differently (e.g. a monorepo with multiple packages). - Test invocation -
swift test --parallelis the default; add--enable-code-coverage,--filter <TestName>, or--sanitize=threadas needed. - Delete the
test-matrixjob if a single-version check is enough for now (you can always reinstate it later - see references doc for why you might want it sooner rather than later). - Uncomment the Linux job only if the package needs to build under
server-side Swift; note the comment in the template that Linux uses
a different Foundation implementation (
swift-corelibs-foundation), so expect divergence beyond what's cataloged here.
- Runner image / Xcode version - the
-
Commit and push. Confirm the workflow runs on the next PR/push to
main. -
Make the fast
testjob a required status check in the repo's branch protection settings, and lettest-matrixrun as an informational (non-blocking) check if you kept it - this gives fast feedback on every push while still surfacing cross-version drift.
Picking runner images / Xcode versions
- GitHub's
macos-14/macos-15labels map to specific Xcode default versions that change over time; pin the Xcode version explicitly withxcode-select(as the template does) rather than relying on whatever the runner's default happens to be on a given day - GitHub updates runner images regularly and an unpinned default Xcode can silently change under you. - If you need an Xcode version not preinstalled on the runner image,
use the
maxim-lobanov/setup-xcodeaction to install it, or fall back to a self-hosted runner. - Prefer testing against the oldest Xcode/macOS you claim to support and the newest stable one - that pair is what surfaces the Foundation version drift covered under Troubleshooting below.
Output
A committed .github/workflows/ci.yml with a fast required test job and an
optional informational version-matrix job, plus (when debugging) a root-cause
diagnosis mapped to a specific trap in references/portability-traps.md with
a fix at the narrowest layer.
Troubleshooting
Diagnosing "passes locally, fails on CI":
This class of bug is almost always one of:
- A library behavior difference across Foundation versions (older vs. newer macOS/Xcode runner) - the timestamp/number-formatting traps below.
- An implicit environment dependency - timezone, locale, working directory, home directory - that differs between the developer's machine and the CI runner.
- A build/resource-resolution difference - stale local build
artifacts masking a
Bundle.moduleor path bug that a clean CI checkout exposes.
Read references/portability-traps.md for the full catalog
(symptom -> cause -> fix), including two traps verified in a real
project:
- JSONDecoder
.iso8601silently rejects fractional-second timestamps ("2026-07-10T10:00:00.000Z") on older Foundation, dropping records with no visible error on affected runners. - NumberFormatter grouping separators differ:
en_US_POSIXhas grouping size 0 (no thousands separators) by definition, so tests that assert"9,876"while using a POSIX locale are inherently version-fragile.
Also cataloged: Date()/timezone-dependent assertions, locale-dependent
string sort order, hardcoded /Users/... paths, filesystem
case-sensitivity assumptions, Bundle.module resource resolution in
tests, and Decimal/Double string-description differences.
Examples
Don't guess - gather facts, then reproduce locally:
# What does CI actually see? (the template workflow already prints this)
swift --version
xcodebuild -version
defaults read -g AppleLocale # locale
date +%Z # timezone
sw_vers # OS version
# Reproduce a stripped/CI-like environment locally instead of guessing:
env -i HOME="$HOME" PATH=/usr/bin:/bin swift test # strip inherited env
LANG=C.UTF-8 LC_ALL=en_US_POSIX swift test # force a locale
TZ=UTC swift test # force a timezone
If the failure only reproduces on one CI matrix row (e.g. only on
macos-13, or only on the newest Xcode), that immediately tells you
whether you're chasing an "older Foundation" or "newer Foundation" trap -
narrow the catalog search in references/portability-traps.md
accordingly.
Fix at the narrowest layer: inject explicit locale/timezone/clock
dependencies and use custom decoding/formatting strategies (see the
catalog for concrete code) rather than papering over failures with
#if os(...) guards or skipping tests on specific runners - that just
defers the bug to whichever environment you didn't test.
Resources
assets/ci.yml.template- copy-paste GitHub Actions workflow (minimal job + version matrix job, with caching and environment-printing steps built in).references/portability-traps.md- the trap catalog and full debugging methodology; read this when a specific bug is suspected rather than trying to memorize it up front.
What ships with it: 2 files
18.3 KB alongside SKILL.md
assets/
- ci.yml.template5.1 KB
references/
- portability-traps.md13.3 KB