Cost
Scoped triggers and cancellation matter more on expensive macOS runners.
A Swift workflow shaped for XCTest and TestFlight delivery.
# Created by https://pipegrade.io# Template: iOS + TestFlight + XCTest# Edit at https://pipegrade.io/github-actions/ios-testflight-xctest name: iOS CIon: # Limit expensive macOS minutes to iOS source and package changes. push: branches: - main paths: - ios/** - Package.swift - Package.resolved pull_request: paths: - ios/** - Package.swift - Package.resolved permissions: # XCTest and archive steps only need read access to repository contents. contents: read concurrency: # Cancel stale simulator/archive work when a newer commit supersedes it. group: ios-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: test: # macOS minutes are expensive; keep simulator/archive jobs bounded. timeout-minutes: 45 runs-on: macos-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # SwiftPM/Xcode caches are runner-specific; add actions/cache only after measuring hit rates. - run: xcodebuild test -scheme App -destination 'platform=iOS Simulator,name=iPhone 16' - run: xcodebuild archive -scheme App -archivePath build/App.xcarchive # No artifacts are uploaded by default. If you preserve archives, set retention-days. - name: Upload to TestFlight if: github.ref == 'refs/heads/main' env: APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }} run: xcrun altool --upload-app --type ios --file build/App.ipa - name: Notify on failure if: failure() run: echo "Send a failure notification to your team channel."Why it grades A
Scoped triggers and cancellation matter more on expensive macOS runners.
Actions are pinned to full commit SHAs and workflow permissions are read-only by default.
Build and test work is kept in one deterministic job unless the stack has a real dependency boundary.
Every job has timeout-minutes, a concurrency group, and clear deploy gating on main.
Already have a workflow?
Customize
Rules addressed
unpinned-actionmissing-top-level-permissionsmissing-path-filtersmissing-pr-cancel-concurrencymissing-timeout-minutesmissing-concurrency-groupmissing-failure-notificationRelated templates
Updates
These templates use reviewed action SHAs. Get notified when the template library expands.