From abc6533dd947632e9447cce82d77c876e5af42a4 Mon Sep 17 00:00:00 2001 From: David Hunt Date: Wed, 2 Sep 2026 19:31:08 +0200 Subject: [PATCH] fix(fosmvvm-doctor): R4's testing-product remedy names both halves; ci: the scaffolder job fans out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FOSTesting-in-a-non-test-target finding now says to link the testing product directly on each test target that uses it, and why the one-doorway rule does not apply there — removing the link alone breaks the test bundles. The serial scaffolder CI job (fast suite, walking skeletons, four generated-app UI-test runs; seventy minutes) becomes three jobs, the last a four-leg matrix over app shape × destination. Each leg builds the CLI and scaffolds its own app, so nothing waits on anything. --- .github/workflows/ci.yml | 121 ++++++++++++------ CHANGELOG.md | 14 ++ .../Doctor/ProjectRule+Linkage.swift | 2 +- 3 files changed, 96 insertions(+), 41 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27c0f33..88c7ad0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -163,23 +163,26 @@ jobs: # swift test/build hang on macOS runners (see run_tests matrix note), and # the bootstrap targets are mac-host-gated so ubuntu never sees them. # - # Layers: + # Layers, one job each (fanned out 2026-09-02: serial, they ran seventy + # minutes — fast suite, skeletons, then four generated-app runs back to + # back; none feeds another, so the wall clock is now the longest leg): # 1. Fast suite (config/tokens/emitter/consistency tests; skeletons # self-skip without the env gate). # 2. Walking skeletons: emit each shape, run its verification doors. # TEST_RUNNER_ prefix forwards the gate to the test process. # 3. Generation matrix: generated UI tests on real destinations — the - # automated BIG TEST. The least-proven leg (design §7): signing and - # simulator behavior on hosted runners may need iteration. - bootstrap_generation_matrix: - name: Scaffolder fast suite + skeletons + generated-app tests + # automated BIG TEST, one leg per app shape × destination. The + # least-proven leg (design §7): signing and simulator behavior on + # hosted runners may need iteration. + bootstrap_fast_suite: + name: Scaffolder fast suite runs-on: macos-latest steps: - uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: latest-stable - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install xcodegen run: brew install xcodegen @@ -204,6 +207,22 @@ jobs: -scheme FOSUtilities-Package -destination "platform=macOS,arch=arm64,name=My Mac" \ -only-testing:FOSMVVMBootstrapTests test + bootstrap_skeletons: + name: Scaffolder walking skeletons + runs-on: macos-latest + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - uses: actions/checkout@v4 + + - name: Install xcodegen + run: brew install xcodegen + + - name: Skip macro fingerprint validation + run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES + - name: Walking skeletons env: TEST_RUNNER_FOSMVVM_BOOTSTRAP_SKELETONS: "1" @@ -215,29 +234,62 @@ jobs: -scheme FOSUtilities-Package -destination "platform=macOS,arch=arm64,name=My Mac" \ -only-testing:FOSMVVMBootstrapTests/IntegrationTests test - - name: Generate app shapes for the matrix + # One leg per (app shape × destination). Each leg builds the CLI and + # scaffolds its own app: a few minutes of build per leg buys running the + # four legs side by side instead of one after another. + bootstrap_generated_app_tests: + name: Generated UI tests (${{ matrix.app }}, ${{ matrix.destination }}) + runs-on: macos-latest + strategy: + fail-fast: false + matrix: + app: [LocalDemo, ServerDemo] + destination: [iOS Simulator, macOS] + include: + - app: LocalDemo + shape: localOnly + bundle_id_root: com.example.localdemo + - app: ServerDemo + shape: clientServer + bundle_id_root: com.example.serverdemo + - destination: iOS Simulator + xcode_destination: "platform=iOS Simulator,name=iPhone 17" + signing: "CODE_SIGNING_ALLOWED=NO" + - destination: macOS + xcode_destination: "platform=macOS,arch=arm64,name=My Mac" + signing: "CODE_SIGN_IDENTITY=- DEVELOPMENT_TEAM= CODE_SIGN_STYLE=Manual" + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - uses: actions/checkout@v4 + + - name: Install xcodegen + run: brew install xcodegen + + - name: Skip macro fingerprint validation + run: defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES + + - name: Build the scaffolder + run: | + set -o pipefail + xcrun xcodebuild -IDEClonedSourcePackagesDirPathOverride="$PWD/.dependencies" \ + -skipMacroValidation -skipPackagePluginValidation \ + -derivedDataPath "$PWD/.derivedData" \ + -scheme fosmvvm-bootstrap -destination "platform=macOS,arch=arm64,name=My Mac" build + + - name: Generate ${{ matrix.app }} run: | set -o pipefail BIN="$PWD/.derivedData/Build/Products/Debug/fosmvvm-bootstrap" - if [ ! -x "$BIN" ]; then - xcrun xcodebuild -IDEClonedSourcePackagesDirPathOverride="$PWD/.dependencies" \ - -skipMacroValidation -skipPackagePluginValidation \ - -derivedDataPath "$PWD/.derivedData" \ - -scheme fosmvvm-bootstrap -destination "platform=macOS,arch=arm64,name=My Mac" build - fi mkdir -p "$RUNNER_TEMP/matrix" - cat > "$RUNNER_TEMP/local.json" <<'CONFIG' - { "projectName": "LocalDemo", "shape": "localOnly", - "platforms": { "macOS": "14.0", "iOS": "17.0" }, - "bundleIdRoot": "com.example.localdemo", "teamId": "ABCDE12345" } - CONFIG - cat > "$RUNNER_TEMP/server.json" <<'CONFIG' - { "projectName": "ServerDemo", "shape": "clientServer", + cat > "$RUNNER_TEMP/app.json" <