From da03b3ad0ac9febd498c4535ce83a732ab32cc75 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Thu, 18 Jun 2026 22:38:17 +1000 Subject: [PATCH 1/9] Add test script --- .github/workflows/pr_build_test.yml | 43 +++++++++++++++++++++++++++-- AGENTS.md | 11 ++++++++ scripts/test | 19 +++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100755 scripts/test diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index f932049..ad745bd 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -10,7 +10,11 @@ on: - "BrewUITests/**" - "Brew.xcodeproj/**" - "Brew.entitlements" + - "Sources/**" + - "Tests/**" + - "Package.swift" - "Package.resolved" + - "Tools/BrewUILint/**" - ".github/workflows/pr_build_test.yml" pull_request: types: [opened, reopened, synchronize] @@ -20,7 +24,11 @@ on: - "BrewUITests/**" - "Brew.xcodeproj/**" - "Brew.entitlements" + - "Sources/**" + - "Tests/**" + - "Package.swift" - "Package.resolved" + - "Tools/BrewUILint/**" - ".github/workflows/pr_build_test.yml" permissions: @@ -34,7 +42,7 @@ jobs: build-and-test: name: Build + unit tests runs-on: macos-26 - timeout-minutes: 20 + timeout-minutes: 30 steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 @@ -42,7 +50,7 @@ jobs: - name: Resolve package dependencies run: xcodebuild -resolvePackageDependencies -project Brew.xcodeproj - - name: Build and test + - name: Build and test (Xcode app target) run: | set -o pipefail xcodebuild test \ @@ -57,10 +65,39 @@ jobs: CODE_SIGNING_ALLOWED=NO \ | tee xcodebuild.log + - name: Cache BrewKit .build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: .build + key: ${{ runner.os }}-brewkit-${{ hashFiles('Package.swift', 'Package.resolved') }} + restore-keys: | + ${{ runner.os }}-brewkit- + + - name: Run BrewKit package tests + run: | + set -o pipefail + xcrun swift test --package-path . | tee swift-test-brewkit.log + + - name: Cache BrewUILint .build + uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + with: + path: Tools/BrewUILint/.build + key: ${{ runner.os }}-brewuilint-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} + restore-keys: | + ${{ runner.os }}-brewuilint- + + - name: Run BrewUILint package tests + run: | + set -o pipefail + xcrun swift test --package-path Tools/BrewUILint | tee swift-test-brewuilint.log + - name: Upload logs on failure if: failure() || cancelled() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: pr-build-test-logs - path: xcodebuild.log + path: | + xcodebuild.log + swift-test-brewkit.log + swift-test-brewuilint.log retention-days: 7 diff --git a/AGENTS.md b/AGENTS.md index c4b064e..d7f12e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -89,6 +89,17 @@ When you change Swift sources or anything that affects Swift formatting or linti The pre-commit hook formats and lints **staged** Swift files (SwiftFormat/SwiftLint) and additionally runs **BrewUILint over the whole tree** on every commit; these manual commands validate the whole tree like CI and catch drift in unstaged paths. +### Tests (local parity with CI) + +`scripts/test` runs `swift test` for both packages — `BrewKit` (root `Package.swift`) and `BrewUILint` (`Tools/BrewUILint/Package.swift`) — matching what `.github/workflows/pr_build_test.yml` runs in CI. + +**Agents must run `scripts/test` and confirm it exits 0 at both of these points:** + +1. **Before any `git commit` you make.** Tests are intentionally **not** enforced by the pre-commit git hook (too slow to run on every staged-file commit during interactive work), so the responsibility moves to the agent. If `scripts/test` fails, fix it before committing — do not commit with failures, and do not skip the run. +2. **Before reporting a code-changing turn complete to the user,** when that turn modified Swift sources under `Sources/`, `Tests/`, `Brew/`, or `Tools/BrewUILint/`, *or* changed `Package.swift` / `Package.resolved` / `.github/workflows/pr_build_test.yml`. For turns that only touch docs, YAML unrelated to tests, or other non-Swift files, the run is optional. + +If a test failure surfaces a real regression that's out of scope for the current turn, surface it to the user rather than silently skipping it — never paper over a red test with `.disabled` or `--filter` exclusions without flagging. + --- ## What Lives Where diff --git a/scripts/test b/scripts/test new file mode 100755 index 0000000..11bb2d4 --- /dev/null +++ b/scripts/test @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# +# test — run all Swift Package tests (BrewKit + BrewUILint). +# +# Mirrors what CI runs in .github/workflows/pr_build_test.yml, so green +# locally means green on CI. + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT_DIR" + +echo "==> BrewKit package tests" +xcrun swift test --package-path . + +echo "==> BrewUILint package tests" +xcrun swift test --package-path Tools/BrewUILint + +echo "==> All package tests passed." From 290bf8ce78d5826d2245be77b6bfa66541f6c062 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Thu, 18 Jun 2026 22:57:22 +1000 Subject: [PATCH 2/9] Revert timeout to 20 mins --- .github/workflows/pr_build_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index ad745bd..24db00f 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -42,7 +42,7 @@ jobs: build-and-test: name: Build + unit tests runs-on: macos-26 - timeout-minutes: 30 + timeout-minutes: 20 steps: - name: Checkout uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 From 224b7a1875e58baf15c03c529a9ee23ff2a5f938 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Thu, 18 Jun 2026 23:11:26 +1000 Subject: [PATCH 3/9] Run CI on test script changes Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/pr_build_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index 24db00f..bef4145 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -15,6 +15,7 @@ on: - "Package.swift" - "Package.resolved" - "Tools/BrewUILint/**" + - "scripts/test" - ".github/workflows/pr_build_test.yml" pull_request: types: [opened, reopened, synchronize] From 02a98c443727e2a718986edda620e2caabace287 Mon Sep 17 00:00:00 2001 From: Graeme Arthur <2030310+graeme@users.noreply.github.com> Date: Fri, 19 Jun 2026 18:55:30 +1000 Subject: [PATCH 4/9] Also run tests when test script changes Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/pr_build_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index bef4145..7a4ee19 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -30,6 +30,7 @@ on: - "Package.swift" - "Package.resolved" - "Tools/BrewUILint/**" + - "scripts/test" - ".github/workflows/pr_build_test.yml" permissions: From a5e794932124561787b9460a594a77a637ebf886 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Fri, 19 Jun 2026 19:01:24 +1000 Subject: [PATCH 5/9] CI: key Swift .build caches on toolchain fingerprint The .build caches keyed only on package manifests with bare restore-keys prefixes, so a runner-image Xcode/Swift bump could restore artifacts built by a different compiler. Add a swift --version fingerprint to each cache key and restore-keys prefix so the cache busts on a real toolchain change while staying warm across ordinary source edits. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/pr_build_test.yml | 12 ++++++++---- .github/workflows/swift_quality.yml | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index 7a4ee19..e1c8583 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -67,13 +67,17 @@ jobs: CODE_SIGNING_ALLOWED=NO \ | tee xcodebuild.log + - name: Capture Swift toolchain fingerprint + id: swift-toolchain + run: echo "fingerprint=$(swift --version | shasum -a 256 | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Cache BrewKit .build uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: .build - key: ${{ runner.os }}-brewkit-${{ hashFiles('Package.swift', 'Package.resolved') }} + key: ${{ runner.os }}-brewkit-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Package.swift', 'Package.resolved') }} restore-keys: | - ${{ runner.os }}-brewkit- + ${{ runner.os }}-brewkit-${{ steps.swift-toolchain.outputs.fingerprint }}- - name: Run BrewKit package tests run: | @@ -84,9 +88,9 @@ jobs: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: Tools/BrewUILint/.build - key: ${{ runner.os }}-brewuilint-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} + key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} restore-keys: | - ${{ runner.os }}-brewuilint- + ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}- - name: Run BrewUILint package tests run: | diff --git a/.github/workflows/swift_quality.yml b/.github/workflows/swift_quality.yml index 2aec7e5..d524b69 100644 --- a/.github/workflows/swift_quality.yml +++ b/.github/workflows/swift_quality.yml @@ -64,13 +64,17 @@ jobs: - name: Run SwiftLint (strict) run: mint run swiftlint lint --strict + - name: Capture Swift toolchain fingerprint + id: swift-toolchain + run: echo "fingerprint=$(swift --version | shasum -a 256 | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + - name: Cache BrewUILint build uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: Tools/BrewUILint/.build - key: ${{ runner.os }}-brewuilint-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} + key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} restore-keys: | - ${{ runner.os }}-brewuilint- + ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}- - name: Build BrewUILint run: swift build --package-path Tools/BrewUILint -c release --enable-experimental-prebuilts From baa74c99dde2251bbb2278f0f2ac7fd8acb602fe Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Fri, 19 Jun 2026 20:00:46 +1000 Subject: [PATCH 6/9] Hash Tests/** in the BrewUILint cache key --- .github/workflows/pr_build_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index e1c8583..ae1a21b 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -88,7 +88,7 @@ jobs: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: Tools/BrewUILint/.build - key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Plugins/**') }} + key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Tests/**', 'Tools/BrewUILint/Plugins/**') }} restore-keys: | ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}- From b7dd5fb350193880f874a981ddd782263aa7a5e9 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Sun, 21 Jun 2026 13:17:14 +1000 Subject: [PATCH 7/9] Update SwiftSyntax --- .../xcshareddata/swiftpm/Package.resolved | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..c4be5b5 --- /dev/null +++ b/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "ccb2f4bca1ba0c5e0aa6e69e84f2f9812ceb3af8d262378abf4603b128cbdfa9", + "pins" : [ + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/swiftlang/swift-syntax.git", + "state" : { + "revision" : "4799286537280063c85a32f09884cfbca301b1a1", + "version" : "602.0.0" + } + } + ], + "version" : 3 +} From 0b181223711f06e5beaed440a39eadf5a58df232 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Sun, 21 Jun 2026 21:05:35 +1000 Subject: [PATCH 8/9] Revert "Update SwiftSyntax" This reverts commit b7dd5fb350193880f874a981ddd782263aa7a5e9. --- .../xcshareddata/swiftpm/Package.resolved | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index c4be5b5..0000000 --- a/Homebrew.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,15 +0,0 @@ -{ - "originHash" : "ccb2f4bca1ba0c5e0aa6e69e84f2f9812ceb3af8d262378abf4603b128cbdfa9", - "pins" : [ - { - "identity" : "swift-syntax", - "kind" : "remoteSourceControl", - "location" : "https://github.com/swiftlang/swift-syntax.git", - "state" : { - "revision" : "4799286537280063c85a32f09884cfbca301b1a1", - "version" : "602.0.0" - } - } - ], - "version" : 3 -} From 947e8b5c4dde8a1955cbcc5f5a373fd410e0df74 Mon Sep 17 00:00:00 2001 From: Graeme Arthur Date: Mon, 22 Jun 2026 22:20:25 +1000 Subject: [PATCH 9/9] Fix pr_build_test.yml --- .github/workflows/pr_build_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr_build_test.yml b/.github/workflows/pr_build_test.yml index ae1a21b..13fdec3 100644 --- a/.github/workflows/pr_build_test.yml +++ b/.github/workflows/pr_build_test.yml @@ -88,7 +88,7 @@ jobs: uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: Tools/BrewUILint/.build - key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Tests/**', 'Tools/BrewUILint/Plugins/**') }} + key: ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-${{ hashFiles('Tools/BrewUILint/Package.swift', 'Tools/BrewUILint/Package.resolved', 'Tools/BrewUILint/Sources/**', 'Tools/BrewUILint/Tests/**', 'Tools/BrewUILint/Plugins/**') }} restore-keys: | ${{ runner.os }}-brewuilint-${{ steps.swift-toolchain.outputs.fingerprint }}-