From 372b0463e9572621a9badc555f1c846048f6c886 Mon Sep 17 00:00:00 2001 From: Roman Kovalchuk Date: Wed, 23 Sep 2026 09:51:53 +0300 Subject: [PATCH 1/3] ci: standardize the workflow across libraries Same triggers, concurrency group, job naming, pinned runner and step names in every repository. The runner differs only where the package requires it: a Linux container for Foundation only packages, macOS for packages whose tools version exceeds the container, and an iOS simulator for packages that import UIKit. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a6ee52..1103848 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,38 +10,25 @@ concurrency: cancel-in-progress: true jobs: - build-and-test: - runs-on: macos-latest + test: + name: Build and test (iOS Simulator) + runs-on: macos-15 steps: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode - # Pin to the latest stable Xcode provided by the runner image so the - # Swift 6 toolchain is deterministic across runs. - run: sudo xcode-select -switch /Applications/Xcode.app - - name: Show environment run: | xcodebuild -version swift --version - name: Build - # Build against a generic simulator destination. We do not pin a device - # name here (like "iPhone 16") because the exact simulators installed on - # GitHub macos-latest runners change over time, and a generic Simulator - # destination always resolves. A successful build in Swift 6 language - # mode (tools-version 6.0) is also the strict concurrency guard. - run: | - xcodebuild build \ - -scheme NerdzPinView \ - -destination 'generic/platform=iOS Simulator' + run: > + xcodebuild build + -scheme NerdzPinView + -destination 'generic/platform=iOS Simulator' - name: Select an available iPhone simulator - # Tests need a concrete device, so we derive one at runtime instead of - # hardcoding a name. We list available devices as JSON and pick the first - # available iPhone, exposing its UDID for the test step. This survives - # runner image changes (any iPhone the image ships with will be used). id: sim run: | UDID=$(xcrun simctl list devices available --json | python3 -c "import json,sys; d=json.load(sys.stdin)['devices']; ids=[x['udid'] for r in d for x in d[r] if x.get('isAvailable') and x.get('name','').startswith('iPhone')]; print(ids[0] if ids else '')") @@ -49,12 +36,11 @@ jobs: echo "No available iPhone simulator found." >&2 exit 1 fi - echo "Using simulator UDID: $UDID" echo "udid=$UDID" >> "$GITHUB_OUTPUT" - name: Test - run: | - xcodebuild test \ - -scheme NerdzPinView \ - -destination "platform=iOS Simulator,id=${{ steps.sim.outputs.udid }}" \ - -enableCodeCoverage YES + run: > + xcodebuild test + -scheme NerdzPinView + -destination "platform=iOS Simulator,id=${{ steps.sim.outputs.udid }}" + -enableCodeCoverage YES From 3365619a4f708150fc988b107ca69b11fa967fbd Mon Sep 17 00:00:00 2001 From: Roman Kovalchuk Date: Wed, 23 Sep 2026 09:59:45 +0300 Subject: [PATCH 2/3] ci: standardize the release workflow across libraries Same tag trigger, concurrency group, changelog extraction and Pages deployment everywhere. The changelog section is now required in every repository rather than silently falling back to generated notes. The documentation catalog and its module name are detected at run time, so the file is identical across repositories despite the catalog naming differing between them, and the job skips cleanly where no catalog exists yet. Documentation builds through xcodebuild only where the package cannot be built with SwiftPM. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/release.yml | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a64fac5..93429cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,9 +9,6 @@ concurrency: group: release-${{ github.ref }} cancel-in-progress: false -env: - SCHEME: NerdzPinView - jobs: release: name: Publish GitHub release @@ -21,10 +18,12 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Extract changelog section run: | - version="${GITHUB_REF_NAME#v}" + version="${GITHUB_REF_NAME}" awk -v v="$version" ' $0 ~ "^## \\[" v "\\]" { found = 1; next } found && /^## \[/ { exit } @@ -42,7 +41,7 @@ jobs: docs: name: Build and deploy DocC - runs-on: macos-latest + runs-on: macos-15 permissions: pages: write id-token: write @@ -53,14 +52,31 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode - run: sudo xcode-select -switch /Applications/Xcode.app + - name: Show environment + run: | + xcodebuild -version + swift --version + + - name: Locate the documentation catalog + id: catalog + run: | + catalog="$(find Sources -maxdepth 3 -name '*.docc' -print -quit)" + if [ -z "$catalog" ]; then + echo "No DocC catalog found, skipping documentation." >&2 + echo "found=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + module="$(basename "$(dirname "$catalog")")" + echo "found=true" >> "$GITHUB_OUTPUT" + echo "path=$catalog" >> "$GITHUB_OUTPUT" + echo "module=$module" >> "$GITHUB_OUTPUT" - name: Build documentation + if: steps.catalog.outputs.found == 'true' run: | set -o pipefail xcodebuild docbuild \ - -scheme "$SCHEME" \ + -scheme "${GITHUB_REPOSITORY#*/}" \ -destination 'generic/platform=iOS Simulator' \ -derivedDataPath .build/docc \ 2>&1 | tee docbuild.log @@ -70,6 +86,7 @@ jobs: fi - name: Transform for static hosting + if: steps.catalog.outputs.found == 'true' run: | archive="$(find .build/docc -name '*.doccarchive' -print -quit)" if [ -z "$archive" ]; then @@ -81,10 +98,12 @@ jobs: --hosting-base-path "${GITHUB_REPOSITORY#*/}" - name: Upload artifact + if: steps.catalog.outputs.found == 'true' uses: actions/upload-pages-artifact@v3 with: path: _site - name: Deploy to Pages + if: steps.catalog.outputs.found == 'true' id: deploy uses: actions/deploy-pages@v4 From 88f972baf01d7aa36b72c2ca135f9710a836306c Mon Sep 17 00:00:00 2001 From: Roman Kovalchuk Date: Wed, 23 Sep 2026 10:15:55 +0300 Subject: [PATCH 3/3] ci: move the actions off the deprecated Node 20 runtime actions/checkout targeted Node 20, which GitHub now forces onto Node 24 and warns about on every run. checkout moves to v7, upload-pages-artifact to v5 and deploy-pages to v5, all of which target Node 24. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1103848..4b23103 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: runs-on: macos-15 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Show environment run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 93429cb..d1abdbe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: contents: write steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 @@ -50,7 +50,7 @@ jobs: url: ${{ steps.deploy.outputs.page_url }} steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Show environment run: | @@ -99,11 +99,11 @@ jobs: - name: Upload artifact if: steps.catalog.outputs.found == 'true' - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: _site - name: Deploy to Pages if: steps.catalog.outputs.found == 'true' id: deploy - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5