Setup Swift GitHub Workflows

This documentation was generated with the assistance of AI. Please report any inaccuracies.

Scaffold (or update) the build.yml, release.yml, sync.yml (gitflow only), security.yml, and .github/dependabot.yml GitHub Actions workflows for a Swift repository produced by iru-setup-swift-library (a SwiftPM package) or iru-setup-apple-app (an XcodeGen/Tuist-generated Xcode app).

Purpose

iru-setup-swift-github-workflows wires up the house CI/CD pipeline for the Swift/Apple stack: SwiftLint
swift format lint --strict, a build/test step whose shape depends on flavor (library: swift test --enable-code-coverage --parallel with a .build cache keyed on Package.resolved, plus an xcrun llvm-cov-to-SonarQube-generic-XML coverage conversion and an optional ubuntu-latest Linux matrix leg; app, following iru-setup-apple-app’s scaffold contract: a Linux `lint job (SwiftLint release binary + swift format in a swift container), a macOS core job (Packages/Core swift test --enable-code-coverage + llvm-cov with an 80% line bar, plus DocC), and an apps matrix with one leg per platform scheme (<AppName>-iOS/-watchOS/ -visionOS/-macOS) that runs xcodegen generate/tuist generate, xcodebuild build-for-testing against generic/platform=<P> Simulator, then test-without-building on the newest simulator runtime resolved from xcrun simctl — visionOS tests reported unverified when the runner image has no visionOS runtime — and uploads each .xcresult), an optional SonarQube/SonarCloud scan (Linux for the app flavor, fed Core’s lcov), a DocC + Antora documentation build published to GitHub Pages from the stable branch (or, with pages: no, built and uploaded as a docs-site workflow artifact without publishing), a release workflow (library: verifies the release tag is valid semver and the manifest still parses, plus an optional release-please job; app: plain xcodebuild archive + -exportArchive with destination: upload to App Store Connect via the API key for iOS (watch app embedded), visionOS and a standalone watchOS app, and Developer ID export + notarization + stapling for macOS — the Fastfile’s match + beta/release lane drives the iOS upload only when fastlane/ exists and signing: fastlane-match, so no job requires Fastlane), a gitflow-only sync.yml that merges a released branch back into the integration branch and bumps the development version (reusing iru-swift-bump-version’s exact rewrite targets via a vendored `sync_versions.py), and a security.yml/dependabot.yml block (dependency-review, CodeQL, OSV-Scanner, gitleaks). Unlike its Java/TypeScript/Android siblings, no single real Swift repository exists to genericize these templates from — they are assembled instead from this catalog’s own already-verified iru-setup-swift-library/ iru-setup-apple-app/iru-swift-coverage/iru-swift-bump-version commands, plus this skill’s own verification pass. It announces whether it is creating the pipeline from scratch or updating an existing one. It creates every workflow that doesn’t exist, and for each one that already exists it warns and asks Update (merge using the templates as reference, keeping foreign steps and jobs) / Keep as is (listing every mismatch with this run’s choices) / Replace (regenerate; customizations lost) / Stop, or applies on-existing from args. Because nearly every job runs on macOS runners (about 10x the cost of Linux minutes, billed against included minutes on private repositories), it resolves visibility and asks sonar, ci-tests and pages with those cost facts; ci-tests: unit skips the app’s XCUITest bundles (-skip-testing:<AppName>UITests-<Platform>, or a library integration-test target) in CI. For the app flavor it recommends runner: xcode-27 + xcode-version: 27.1 (the Xcode the scaffold was verified with), since only that preview image offers it. Neither macos-26 (arm64) nor xcode-27 ships SwiftLint preinstalled (only macos-26-intel does), so the library flavor keeps a brew install swiftlint guard and the app flavor lints on Linux. Every generator-dependent step — the apps legs in build.yml, and in release.yml the version check (project.yml vs Project.swift MARKETING_VERSION) and each archive job’s project generation — is written for exactly the detected generator (xcodegen generate or tuist generate --no-open).

Purpose group

Plugin

Shipped by the iru-swift plugin of the ai-catalog marketplace (install with /plugin install iru-swift@ai-catalog; see Plugins).

Invocation

/iru-setup-swift-github-workflows

Inputs

Argument Required Description Default

args (key: value lines)

No

Pre-resolved values, one per line, for flavor (library/app), platforms, runner (macos-26/xcode-27), xcode-version, integration-branch, stable-branch, branching (gitflow/main-only), generator (xcodegen/tuist, app only), signing (fastlane-match/api-key/none, app only), release-please (library only), plus this catalog’s shared workflow-args vocabulary (open-source, publish, distribution, sonar + coordinates, mode, and the four security-* opt-outs), plus visibility (public/private/internal, driving the Actions-minutes, SonarCloud and Pages guidance), ci-tests (unit or full), pages (yes/no) and on-existing (ask/keep/update/replace). When an orchestrating skill (such as iru-setup-swift-repository) supplies these, the corresponding fact-finding step in Step 1’s survey is skipped for that value.

None — if args is absent or doesn’t match this key: value format, every parameter is instead resolved by surveying Package.swift/project.yml/Project.swift/fastlane/Fastfile/sonar-project.properties and the repository, asking the user directly (via AskUserQuestion for bounded choices) for anything that can’t be inferred — flavor defaults to auto-detected from the repository shape, runner defaults to macos-26, branching defaults to gitflow. Stand-alone, visibility comes from gh repo view --json visibility (else asked); sonar is recommended cloud only for a public open-source repository (otherwise none, with the paid plan caveat); ci-tests defaults to full for public and unit recommended for private/internal; pages defaults to yes for public without asking, and for private/internal is asked with no recommended. ci is never asked — invoking this skill means CI is wanted. on-existing defaults to ask. mode is informational only and no longer short-circuits the per-file question.

Outputs

  • UI-contract capture upload (see Building user interfaces), inside the existing test legs with no extra job or minutes: a package with SwiftUI/UIKit/AppKit views uploads .ui-screenshots/ as a 7-day ui-screenshots artifact; each app leg passes TEST_RUNNER_UI_SCREENSHOTS_DIR, copies the captures attached to the .xcresult into .ui-screenshots/ with xcrun xcresulttool export attachments, and uploads them as ui-screenshots-<scheme> (if-no-files-found: ignore). When .ui-quality.yml sets accessibility: none, no accessibility report (a11y-report.txt), upload or accessibility-only step is emitted; the UI test run and screenshot upload are unaffected.

  • .github/workflows/build.yml, release.yml (app flavor: only when distribution isn’t none), and (gitflow only) sync.yml — created fresh, or updated, replaced or kept as is per file when it already existed (the user’s Update / Keep as is / Replace choice, or on-existing). build.yml honours ci-tests (UI-test bundles or integration targets skipped when unit) and pages (a docs-site artifact upload instead of the Pages deploy job when no).

  • .github/workflows/security.yml and .github/dependabot.yml — always written (or updated), independent of flavor/branching.

  • .github/scripts/sync_versions.py, always written together with sync.yml — never one without the other, and never for main-only.

  • .github/scripts/lcov_to_sonar_generic.py — whenever sonar isn’t none, for both flavors (the app flavor converts Packages/Core’s lcov, not an `.xcresult).

  • A report of the mode announced (from scratch or update), the action taken per file (created / updated / replaced / kept as is, with each mismatch listed for a kept file), how visibility, ci-tests and pages were applied, which apps matrix legs and release jobs (app-store, fastlane-ios, macos-developer-id) were written and whether the visionOS leg runs its tests on the chosen runner, open gaps (missing docs/antora.yml, a Packages/Core with no swift-docc-plugin dependency, an APPLE_TEAM_ID secret the user still needs to add, version wording that didn’t match sync_versions.py’s regexes), the required-secrets table below, and warnings about what could not be exercised locally (the Linux `lint container, signed archives, uploads, notarization, the Fastlane lane, visionOS test runs).

File matrix

flavor × branching Files written

library / gitflow

build.yml, release.yml, sync.yml, sync_versions.py, lcov_to_sonar_generic.py (only if sonar isn’t none), security.yml, .github/dependabot.yml

library / main-only

Same, minus sync.yml/sync_versions.py

app / gitflow

build.yml, release.yml (only if distribution isn’t none), sync.yml, sync_versions.py, lcov_to_sonar_generic.py (only if sonar isn’t none), security.yml, .github/dependabot.yml

app / main-only

Same, minus sync.yml/sync_versions.py

Required secrets

Secret Purpose Only needed when

SONAR_TOKEN

Auth token for the SonarQube/SonarCloud scan

sonar is cloud or self-hosted

MATCH_GIT_URL / MATCH_PASSWORD

`fastlane match’s certs-repo URL and its encryption passphrase

flavor: app, signing: fastlane-match

MATCH_GIT_BASIC_AUTHORIZATION

base64 of <git-user>:<personal-access-token> — how match clones a private certs repository over HTTPS on the runner

flavor: app, signing: fastlane-match

APP_STORE_CONNECT_API_KEY_ID / _ISSUER_ID / _KEY_BASE64

App Store Connect API key — -allowProvisioningUpdates + -exportArchive upload (Admin or App Manager role), notarytool, and Fastlane’s API-key JSON on the Fastlane path

flavor: app, distribution isn’t none — both signing modes, since match certificates never authenticate an upload or a notarization

APPLE_TEAM_ID

Apple Developer Team ID — DEVELOPMENT_TEAM for the archives and the export plists' teamID, filled in at run time

flavor: app, distribution isn’t none

DEVELOPER_ID_CERTIFICATE_P12_BASE64 / DEVELOPER_ID_CERTIFICATE_PASSWORD

Exported Developer ID Application certificate for the macOS developer-id export

flavor: app, macos selected, distribution isn’t none

GITHUB_TOKEN

sync.yml’s `gh calls, `security.yml’s CodeQL/gitleaks jobs

Automatic — no setup needed

sync.yml also needs the repository setting under Settings → Actions → General → "Workflow permissions" set to allow GitHub Actions to create pull requests. The Pages deploy job needs Settings → Pages → "Build and deployment" → Source set to GitHub Actions — only when pages: yes; on a private or internal repository that also needs a GitHub Pro, Team or Enterprise Cloud plan, and the published site is public unless Enterprise Cloud private access control is used.

The generated files' bytes come from template files under templates/ (and, where a template needs variant-specific notes, reference/) in .claude/skills/iru-setup-swift-github-workflows/; the skill reads only the files for the variant it resolved, not the whole set, which keeps its SKILL.md small.

Execution flow

flowchart TD A["Start /iru-setup-swift-github-workflows"] --> B["Step 0: Resolve inputs\n(parse args; stand-alone resolve\nvisibility, sonar, ci-tests, pages)"] B --> C["Step 1: Survey repo\n(flavor, platforms, generator,\nsigning, sonar, branches,\ntest targets, existing workflows)"] C --> D{"Do any workflow\nfiles already exist?"} D -- none exist --> D1["Announce: creating the\npipeline from scratch"] D -- any exists --> D2["Announce: updating an\nexisting pipeline; list files"] D2 --> E{"on-existing set to\nkeep, update or replace?"} E -- yes --> E1["Apply it to every\nexisting file"] E -- no --> E2{"AskUserQuestion per file:\nUpdate / Keep as is /\nReplace / Stop"} E2 -- Stop --> Z["Report which files exist;\nno changes made"] E2 -- other --> E3[Record per-file decision] D1 --> F E1 --> F E3 --> F["Step 2: Look up action/tool\nversions at run time"] F --> G{"flavor?"} G -- library --> H["Step 4: Render build.yml\n(SwiftLint, cached swift test,\nci-tests skips, lcov coverage,\noptional Linux leg,\nDocC + Antora, Pages or artifact)"] G -- app --> I["Step 5: Render build.yml\n(Linux lint, macOS core job with\n80% line bar + DocC, apps matrix\nper platform scheme: generate,\nbuild-for-testing, test-without-\nbuilding with ci-tests skips;\nvisionOS unverified if no runtime;\nLinux sonar, Antora, Pages or artifact)"] H --> J["Step 6: Render release.yml\n(verify semver tag + manifest,\noptional release-please)"] I --> K["Step 7: Render release.yml if distribution set\n(version check, xcodebuild archive +\nexport upload for iOS/visionOS/watchOS,\noptional fastlane-ios lane,\nDeveloper ID + notarize macOS)"] J --> L{"branching == gitflow?"} K --> L L -- yes --> M["Step 8: Render sync.yml\n+ sync_versions.py"] L -- no --> N["Step 9: Render security.yml\n+ dependabot.yml"] M --> N N --> O["Steps 12-13: Write files per decision\nand flavor x branching matrix;\nverify (YAML parse, actionlint,\nuses: refs, real build/test run)"] O --> P["Step 14: Report mode, per-file action,\nrequired secrets, open gaps,\nwarnings"]

Dependencies

Invokes

None — iru-setup-swift-github-workflows is a leaf skill; it does not call any other skill in this catalog.

Invoked by

  • Setup Swift Repository — Step 8 runs iru-setup-swift-github-workflows (via the iru-isolated-skill-executor agent), passing the pipeline parameters it already resolved through args as key: value lines so it isn’t asked — or made to re-survey the repository — for facts the orchestrator already has.

iru-gate-runner — Step 13’s verification pass (YAML validation and, for the app flavor, actionlint + shellcheck of every rendered file, resolving every uses: ref, and real shell-step runs against copied iru-setup-swift-library/iru-setup-apple-app scaffolds) runs through this agent where output could be long.

Source

SKILL.md on GitHub — the file this page was generated from.