From ba9a5c50e79057d723dc8a8ef4e6c9e626db8e44 Mon Sep 17 00:00:00 2001 From: leogdion Date: Thu, 2 Apr 2026 12:11:43 -0400 Subject: [PATCH 01/18] Optimize CI with dynamic matrix, concurrency groups, and cache cleanup (#128) - Add concurrency groups to cancel in-progress runs on same branch - Add paths-ignore to skip CI on doc-only changes - Add pull_request trigger for PRs to main - Add configure job with dynamic matrix (minimal on feature branches, full on main/PRs) - Gate Windows builds to full matrix only - Split macOS into core (always) and full (main/PRs only) jobs - Update lint/docs conditions to run when optional jobs are skipped - Update all GitHub Actions to latest versions (checkout v6, cache v5, codecov v6, etc.) - Add cache cleanup workflows for deleted branches and closed PRs - Update CodeQL workflow action versions Closes #128, closes #126, closes #127 Co-Authored-By: Claude Opus 4.6 (1M context) --- .github/workflows/SyntaxKit.yml | 168 ++++++++++++++++-------- .github/workflows/cleanup-caches.yml | 38 ++++++ .github/workflows/cleanup-pr-caches.yml | 39 ++++++ .github/workflows/codeql.yml | 82 ++++++++++++ 4 files changed, 274 insertions(+), 53 deletions(-) create mode 100644 .github/workflows/cleanup-caches.yml create mode 100644 .github/workflows/cleanup-pr-caches.yml create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 5ca13c2..8dfa2f2 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -3,41 +3,71 @@ on: push: branches-ignore: - '*WIP' + paths-ignore: + - '**.md' + - 'Docs/**' + - 'LICENSE' + - '.github/ISSUE_TEMPLATE/**' + pull_request: + branches: [main] + paths-ignore: + - '**.md' + - 'Docs/**' + - 'LICENSE' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: - PACKAGE_NAME: SyntaxKit + PACKAGE_NAME: SyntaxKit jobs: + configure: + name: Configure Build Matrix + runs-on: ubuntu-latest + if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + outputs: + full-matrix: ${{ steps.set-matrix.outputs.full-matrix }} + ubuntu-matrix: ${{ steps.set-matrix.outputs.ubuntu-matrix }} + steps: + - name: Determine build matrix + id: set-matrix + run: | + if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.event_name }}" == "pull_request" ]]; then + echo "full-matrix=true" >> "$GITHUB_OUTPUT" + echo 'ubuntu-matrix={"os":["noble","jammy"],"swift":[{"version":"6.0"},{"version":"6.1"},{"version":"6.1","nightly":true},{"version":"6.2","nightly":true}]}' >> "$GITHUB_OUTPUT" + else + echo "full-matrix=false" >> "$GITHUB_OUTPUT" + echo 'ubuntu-matrix={"os":["noble"],"swift":[{"version":"6.1"}]}' >> "$GITHUB_OUTPUT" + fi + build-ubuntu: name: Build on Ubuntu + needs: [configure] runs-on: ubuntu-latest container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: - matrix: - os: [noble, jammy] - swift: - - version: "6.0" - - version: "6.1" - - version: "6.1" - nightly: true - - version: "6.2" - nightly: true + matrix: ${{ fromJSON(needs.configure.outputs.ubuntu-matrix) }} steps: - - uses: actions/checkout@v4 - - uses: brightdigit/swift-build@v1.3.1 - - uses: sersoft-gmbh/swift-coverage-action@v4 + - uses: actions/checkout@v6 + - uses: brightdigit/swift-build@v1.5.2 + - uses: sersoft-gmbh/swift-coverage-action@v5 id: coverage-files - with: + with: fail-on-empty-output: true - name: Upload coverage to Codecov - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: true - flags: swift-${{ matrix.swift-version }},ubuntu - verbose: true - token: ${{ secrets.CODECOV_TOKEN }} - files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} + flags: swift-${{ matrix.swift.version }},ubuntu + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} + files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} + build-windows: name: Build on Windows + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' runs-on: ${{ matrix.runs-on }} strategy: fail-fast: false @@ -49,51 +79,83 @@ jobs: - swift-version: swift-6.1-release swift-build: 6.1-RELEASE steps: - - uses: actions/checkout@v4 - - uses: brightdigit/swift-build@v1.3.1 + - uses: actions/checkout@v6 + - uses: brightdigit/swift-build@v1.5.2 with: windows-swift-version: ${{ matrix.swift-version }} windows-swift-build: ${{ matrix.swift-build }} - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: fail_ci_if_error: true flags: swift-${{ matrix.swift-version }},windows - verbose: true + verbose: true token: ${{ secrets.CODECOV_TOKEN }} os: windows swift_project: SyntaxKit - # files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} + build-macos: name: Build on macOS env: PACKAGE_NAME: SyntaxKit runs-on: ${{ matrix.runs-on }} - if: "!contains(github.event.head_commit.message, 'ci skip')" + if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} strategy: fail-fast: false matrix: include: - # SPM Build Matrix + # SPM Build - runs-on: macos-15 xcode: "/Applications/Xcode_26.0.app" - - runs-on: macos-15 - xcode: "/Applications/Xcode_16.4.app" - - # macOS Build Matrix + # macOS Build - type: macos runs-on: macos-15 xcode: "/Applications/Xcode_26.0.app" - - type: macos - runs-on: macos-15 + steps: + - uses: actions/checkout@v6 + + - name: Build and Test + uses: brightdigit/swift-build@v1.5.2 + with: + scheme: ${{ env.PACKAGE_NAME }}-Package + type: ${{ matrix.type }} + xcode: ${{ matrix.xcode }} + deviceName: ${{ matrix.deviceName }} + osVersion: ${{ matrix.osVersion }} + download-platform: ${{ matrix.download-platform }} + + # Common Coverage Steps + - name: Process Coverage + uses: sersoft-gmbh/swift-coverage-action@v5 + + - name: Upload Coverage + uses: codecov/codecov-action@v6 + with: + token: ${{ secrets.CODECOV_TOKEN }} + flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }} + + build-macos-full: + name: Build on macOS (Full) + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' + env: + PACKAGE_NAME: SyntaxKit + runs-on: ${{ matrix.runs-on }} + strategy: + fail-fast: false + matrix: + include: + # SPM Build (older Xcode) + - runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" + # macOS Build (older Xcode) - type: macos runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" - + xcode: "/Applications/Xcode_16.4.app" + # iOS Build Matrix - type: ios runs-on: macos-15 @@ -101,7 +163,7 @@ jobs: deviceName: "iPhone 17 Pro" osVersion: "26.0" download-platform: true - + # watchOS Build Matrix - type: watchos runs-on: macos-15 @@ -118,19 +180,19 @@ jobs: osVersion: "26.0" download-platform: true - # visionOS Build Matrix + # visionOS Build Matrix - type: visionos runs-on: macos-15 xcode: "/Applications/Xcode_26.0.app" deviceName: "Apple Vision Pro" osVersion: "26.0" download-platform: true - + steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Build and Test - uses: brightdigit/swift-build@v1.3.1 + uses: brightdigit/swift-build@v1.5.2 with: scheme: ${{ env.PACKAGE_NAME }}-Package type: ${{ matrix.type }} @@ -138,39 +200,39 @@ jobs: deviceName: ${{ matrix.deviceName }} osVersion: ${{ matrix.osVersion }} download-platform: ${{ matrix.download-platform }} - + # Common Coverage Steps - name: Process Coverage - uses: sersoft-gmbh/swift-coverage-action@v4 - + uses: sersoft-gmbh/swift-coverage-action@v5 + - name: Upload Coverage - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }} lint: name: Linting - if: "!contains(github.event.head_commit.message, 'ci skip')" + if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} runs-on: ubuntu-latest - needs: [build-ubuntu, build-macos, build-windows] + needs: [build-ubuntu, build-macos, build-windows, build-macos-full] env: MINT_PATH: .mint/lib MINT_LINK_PATH: .mint/bin steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Cache mint id: cache-mint - uses: actions/cache@v4 + uses: actions/cache@v5 env: cache-name: cache with: path: | .mint - Mint + Mint key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }} restore-keys: | - ${{ runner.os }}-mint- + ${{ runner.os }}-mint- - name: Install mint if: steps.cache-mint.outputs.cache-hit == '' run: | @@ -182,10 +244,10 @@ jobs: docs: name: Documentation Validation - if: "!contains(github.event.head_commit.message, 'ci skip')" - needs: [build-ubuntu, build-macos, build-windows] + if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} + needs: [build-ubuntu, build-macos, build-windows, build-macos-full] runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Validate Documentation run: ./Scripts/validate-docs.sh diff --git a/.github/workflows/cleanup-caches.yml b/.github/workflows/cleanup-caches.yml new file mode 100644 index 0000000..80a0655 --- /dev/null +++ b/.github/workflows/cleanup-caches.yml @@ -0,0 +1,38 @@ +name: Cleanup Branch Caches +on: + delete: + +jobs: + cleanup: + name: Cleanup Caches for Deleted Branch + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - uses: actions/checkout@v6 + - name: Delete branch caches + uses: actions/github-script@v8 + with: + script: | + const ref = `refs/heads/${context.payload.ref}`; + console.log(`Cleaning up caches for branch: ${ref}`); + + const caches = await github.paginate( + github.rest.actions.getActionsCacheList, + { + owner: context.repo.owner, + repo: context.repo.repo, + ref: ref, + } + ); + + for (const cache of caches) { + console.log(`Deleting cache: ${cache.key} (${cache.id})`); + await github.rest.actions.deleteActionsCacheById({ + owner: context.repo.owner, + repo: context.repo.repo, + cache_id: cache.id, + }); + } + + console.log(`Deleted ${caches.length} cache(s) for ${ref}`); diff --git a/.github/workflows/cleanup-pr-caches.yml b/.github/workflows/cleanup-pr-caches.yml new file mode 100644 index 0000000..a180205 --- /dev/null +++ b/.github/workflows/cleanup-pr-caches.yml @@ -0,0 +1,39 @@ +name: Cleanup PR Caches +on: + pull_request: + types: [closed] + +jobs: + cleanup: + name: Cleanup Caches for PR + runs-on: ubuntu-latest + permissions: + actions: write + steps: + - uses: actions/checkout@v6 + - name: Delete PR branch caches + uses: actions/github-script@v8 + with: + script: | + const ref = `refs/pull/${context.payload.pull_request.number}/merge`; + console.log(`Cleaning up caches for PR: ${ref}`); + + const caches = await github.paginate( + github.rest.actions.getActionsCacheList, + { + owner: context.repo.owner, + repo: context.repo.repo, + ref: ref, + } + ); + + for (const cache of caches) { + console.log(`Deleting cache: ${cache.key} (${cache.id})`); + await github.rest.actions.deleteActionsCacheById({ + owner: context.repo.owner, + repo: context.repo.repo, + cache_id: cache.id, + }); + } + + console.log(`Deleted ${caches.length} cache(s) for ${ref}`); diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..e4375fd --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,82 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches-ignore: + - '*WIP' + pull_request: + # The branches below must be a subset of the branches above + branches: [ "main" ] + schedule: + - cron: '20 11 * * 3' + +jobs: + analyze: + name: Analyze + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners + # Consider using larger runners for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-15') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'swift' ] + # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] + # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Setup Xcode + run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer + + - name: Verify Swift Version + run: | + swift --version + swift package --version + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - run: | + echo "Run, Build Application using script" + swift build + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" From a21698e3481dc48806ad42a3264bd5d649a916e8 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 12:55:00 -0400 Subject: [PATCH 02/18] Address PR review comments: update CI matrix and CodeQL workflow - Ubuntu matrix: add Swift 6.2 and 6.3, remove nightly versions; use 6.3 as single-version for feature branches - build-macos: keep SPM-only; move macOS Build to build-macos-full - build-macos-full: add macOS Build with Xcode 26.4; update iOS/watchOS/tvOS/visionOS to Xcode 26.4 / osVersion 26.4 - Add build-android and build-wasm jobs (full-matrix only) - Update lint and docs needs to include new jobs - codeql.yml: update runner to macos-26, Xcode to 26.4 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 56 +++++++++++++++++++++++---------- .github/workflows/codeql.yml | 4 +-- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 8dfa2f2..7ae31d4 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -35,10 +35,10 @@ jobs: run: | if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.event_name }}" == "pull_request" ]]; then echo "full-matrix=true" >> "$GITHUB_OUTPUT" - echo 'ubuntu-matrix={"os":["noble","jammy"],"swift":[{"version":"6.0"},{"version":"6.1"},{"version":"6.1","nightly":true},{"version":"6.2","nightly":true}]}' >> "$GITHUB_OUTPUT" + echo 'ubuntu-matrix={"os":["noble","jammy"],"swift":[{"version":"6.0"},{"version":"6.1"},{"version":"6.2"},{"version":"6.3"}]}' >> "$GITHUB_OUTPUT" else echo "full-matrix=false" >> "$GITHUB_OUTPUT" - echo 'ubuntu-matrix={"os":["noble"],"swift":[{"version":"6.1"}]}' >> "$GITHUB_OUTPUT" + echo 'ubuntu-matrix={"os":["noble"],"swift":[{"version":"6.3"}]}' >> "$GITHUB_OUTPUT" fi build-ubuntu: @@ -108,11 +108,6 @@ jobs: - runs-on: macos-15 xcode: "/Applications/Xcode_26.0.app" - # macOS Build - - type: macos - runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" - steps: - uses: actions/checkout@v6 @@ -156,36 +151,41 @@ jobs: runs-on: macos-15 xcode: "/Applications/Xcode_16.4.app" + # macOS Build + - type: macos + runs-on: macos-15 + xcode: "/Applications/Xcode_26.4.app" + # iOS Build Matrix - type: ios runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" + xcode: "/Applications/Xcode_26.4.app" deviceName: "iPhone 17 Pro" - osVersion: "26.0" + osVersion: "26.4" download-platform: true # watchOS Build Matrix - type: watchos runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" + xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Watch Ultra 3 (49mm)" - osVersion: "26.0" + osVersion: "26.4" download-platform: true # tvOS Build Matrix - type: tvos runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" + xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple TV" - osVersion: "26.0" + osVersion: "26.4" download-platform: true # visionOS Build Matrix - type: visionos runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" + xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Vision Pro" - osVersion: "26.0" + osVersion: "26.4" download-platform: true steps: @@ -211,11 +211,33 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} flags: ${{ matrix.type && format('{0}{1}', matrix.type, matrix.osVersion) || 'spm' }} + build-android: + name: Build for Android + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: brightdigit/swift-build@v1.5.2 + with: + type: android + + build-wasm: + name: Build for WASM + needs: [configure] + if: needs.configure.outputs.full-matrix == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: brightdigit/swift-build@v1.5.2 + with: + type: wasm + lint: name: Linting if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} runs-on: ubuntu-latest - needs: [build-ubuntu, build-macos, build-windows, build-macos-full] + needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android, build-wasm] env: MINT_PATH: .mint/lib MINT_LINK_PATH: .mint/bin @@ -245,7 +267,7 @@ jobs: docs: name: Documentation Validation if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} - needs: [build-ubuntu, build-macos, build-windows, build-macos-full] + needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android, build-wasm] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e4375fd..e0ed2f5 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -29,7 +29,7 @@ jobs: # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-15') || 'ubuntu-latest' }} + runs-on: ${{ (matrix.language == 'swift' && 'macos-26') || 'ubuntu-latest' }} timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: actions: read @@ -50,7 +50,7 @@ jobs: uses: actions/checkout@v6 - name: Setup Xcode - run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer + run: sudo xcode-select -s /Applications/Xcode_26.4.app/Contents/Developer - name: Verify Swift Version run: | From 9efa95043ea22fb587f674219ee4e9a4d6ae8c36 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 13:10:15 -0400 Subject: [PATCH 03/18] Restructure ubuntu matrix to include WASM types; fix Android matrix - configure outputs separate ubuntu-os/swift/type arrays (matching MonthBar pattern) - build-ubuntu composes matrix from all three outputs; wasm/wasm-embedded types use swift:6.3-noble container; type and wasmtime-version: 41.0.3 passed to swift-build - Remove separate build-wasm job - Android: add swift 6.3 alongside 6.2, add free-disk-space step, android-swift-version/android-api-level/android-run-tests parameters, coverage upload Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 84 +++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 7ae31d4..ce930ce 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -28,29 +28,41 @@ jobs: if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} outputs: full-matrix: ${{ steps.set-matrix.outputs.full-matrix }} - ubuntu-matrix: ${{ steps.set-matrix.outputs.ubuntu-matrix }} + ubuntu-os: ${{ steps.set-matrix.outputs.ubuntu-os }} + ubuntu-swift: ${{ steps.set-matrix.outputs.ubuntu-swift }} + ubuntu-type: ${{ steps.set-matrix.outputs.ubuntu-type }} steps: - name: Determine build matrix id: set-matrix run: | if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.event_name }}" == "pull_request" ]]; then echo "full-matrix=true" >> "$GITHUB_OUTPUT" - echo 'ubuntu-matrix={"os":["noble","jammy"],"swift":[{"version":"6.0"},{"version":"6.1"},{"version":"6.2"},{"version":"6.3"}]}' >> "$GITHUB_OUTPUT" + echo 'ubuntu-os=["noble","jammy"]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-swift=[{"version":"6.0"},{"version":"6.1"},{"version":"6.2"},{"version":"6.3"}]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-type=["","wasm","wasm-embedded"]' >> "$GITHUB_OUTPUT" else echo "full-matrix=false" >> "$GITHUB_OUTPUT" - echo 'ubuntu-matrix={"os":["noble"],"swift":[{"version":"6.3"}]}' >> "$GITHUB_OUTPUT" + echo 'ubuntu-os=["noble"]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-swift=[{"version":"6.3"}]' >> "$GITHUB_OUTPUT" + echo 'ubuntu-type=[""]' >> "$GITHUB_OUTPUT" fi build-ubuntu: name: Build on Ubuntu needs: [configure] runs-on: ubuntu-latest - container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} + container: ${{ (matrix.type == 'wasm' || matrix.type == 'wasm-embedded') && 'swift:6.3-noble' || matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} strategy: - matrix: ${{ fromJSON(needs.configure.outputs.ubuntu-matrix) }} + matrix: + os: ${{ fromJSON(needs.configure.outputs.ubuntu-os) }} + swift: ${{ fromJSON(needs.configure.outputs.ubuntu-swift) }} + type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }} steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 + with: + type: ${{ matrix.type }} + wasmtime-version: 41.0.3 - uses: sersoft-gmbh/swift-coverage-action@v5 id: coverage-files with: @@ -74,8 +86,10 @@ jobs: matrix: runs-on: [windows-2022, windows-2025] include: - - swift-version: swift-6.2-branch - swift-build: 6.2-DEVELOPMENT-SNAPSHOT-2025-09-06-a + - swift-version: swift-6.3-release + swift-build: 6.3-RELEASE + - swift-version: swift-6.2-release + swift-build: 6.2-RELEASE - swift-version: swift-6.1-release swift-build: 6.1-RELEASE steps: @@ -105,8 +119,8 @@ jobs: matrix: include: # SPM Build - - runs-on: macos-15 - xcode: "/Applications/Xcode_26.0.app" + - runs-on: macos-26 + xcode: "/Applications/Xcode_26.4.app" steps: - uses: actions/checkout@v6 @@ -153,12 +167,12 @@ jobs: # macOS Build - type: macos - runs-on: macos-15 + runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" # iOS Build Matrix - type: ios - runs-on: macos-15 + runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "iPhone 17 Pro" osVersion: "26.4" @@ -166,7 +180,7 @@ jobs: # watchOS Build Matrix - type: watchos - runs-on: macos-15 + runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Watch Ultra 3 (49mm)" osVersion: "26.4" @@ -174,7 +188,7 @@ jobs: # tvOS Build Matrix - type: tvos - runs-on: macos-15 + runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple TV" osVersion: "26.4" @@ -182,7 +196,7 @@ jobs: # visionOS Build Matrix - type: visionos - runs-on: macos-15 + runs-on: macos-26 xcode: "/Applications/Xcode_26.4.app" deviceName: "Apple Vision Pro" osVersion: "26.4" @@ -216,28 +230,46 @@ jobs: needs: [configure] if: needs.configure.outputs.full-matrix == 'true' runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + swift: + - version: "6.2" + - version: "6.3" + android-api-level: [33, 34] steps: - uses: actions/checkout@v6 + - name: Free disk space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + tool-cache: false + android: false + dotnet: true + haskell: true + large-packages: true + docker-images: true + swap-storage: true - uses: brightdigit/swift-build@v1.5.2 + id: build with: type: android - - build-wasm: - name: Build for WASM - needs: [configure] - if: needs.configure.outputs.full-matrix == 'true' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: brightdigit/swift-build@v1.5.2 + android-swift-version: ${{ matrix.swift.version }} + android-api-level: ${{ matrix.android-api-level }} + android-run-tests: true + - name: Upload coverage to Codecov + if: steps.build.outputs.contains-code-coverage == 'true' + uses: codecov/codecov-action@v6 with: - type: wasm + fail_ci_if_error: true + flags: android-api${{ matrix.android-api-level }}-swift${{ matrix.swift.version }} + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} lint: name: Linting if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} runs-on: ubuntu-latest - needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android, build-wasm] + needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android] env: MINT_PATH: .mint/lib MINT_LINK_PATH: .mint/bin @@ -267,7 +299,7 @@ jobs: docs: name: Documentation Validation if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} - needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android, build-wasm] + needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android] runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 From 9f4f4870996635dd8acaf4551f2a384483b02bbb Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 13:23:56 -0400 Subject: [PATCH 04/18] Fix full matrix not running on PRs - configure: guard ci-skip check with event_name check so it always runs on pull_request events (github.event.head_commit is null on PRs) - build-macos: add needs: [configure], gate on configure success so ci-skip propagates via dependency chain instead of re-checking head_commit.message Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index ce930ce..8e9e1d6 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -25,7 +25,7 @@ jobs: configure: name: Configure Build Matrix runs-on: ubuntu-latest - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + if: ${{ github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip') }} outputs: full-matrix: ${{ steps.set-matrix.outputs.full-matrix }} ubuntu-os: ${{ steps.set-matrix.outputs.ubuntu-os }} @@ -110,10 +110,11 @@ jobs: build-macos: name: Build on macOS + needs: [configure] env: PACKAGE_NAME: SyntaxKit runs-on: ${{ matrix.runs-on }} - if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} + if: ${{ !cancelled() && needs.configure.result == 'success' }} strategy: fail-fast: false matrix: From 68455ac58a221588ee076fdd24dcaa3a46f89795 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 13:25:08 -0400 Subject: [PATCH 05/18] Install curl in Swift 6.3 Ubuntu container before coverage upload The swift:6.3 Docker image does not include curl, which is required by the Codecov uploader. Applies to all 6.3 builds including wasm types. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 8e9e1d6..75fe4b4 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -63,6 +63,11 @@ jobs: with: type: ${{ matrix.type }} wasmtime-version: 41.0.3 + - name: Install curl + if: matrix.swift.version == '6.3' + run: | + apt-get update -q + apt-get install -y curl - uses: sersoft-gmbh/swift-coverage-action@v5 id: coverage-files with: From 9ef6549c74d99d5846e67901b60a43b69686b3a5 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 13:31:18 -0400 Subject: [PATCH 06/18] Fix PR trigger for version branches and prevent duplicate push/PR runs - Add version branch pattern to pull_request trigger so PRs targeting v0.0.4 (and similar) fire the full matrix CI - Use github.head_ref || github.ref_name for concurrency group so push and pull_request events for the same branch share a group; PR run (arrives second) cancels the concurrent push run Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 75fe4b4..360795a 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -9,14 +9,16 @@ on: - 'LICENSE' - '.github/ISSUE_TEMPLATE/**' pull_request: - branches: [main] + branches: + - main + - 'v[0-9]*.[0-9]*.[0-9]*' paths-ignore: - '**.md' - 'Docs/**' - 'LICENSE' concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }} cancel-in-progress: true env: From c90daf87040d94f2dffc23929a8b6384ec86f678 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 13:49:11 -0400 Subject: [PATCH 07/18] Fix test file path resolution for WASM/Android/Linux CI Settings.projectRoot now uses a 3-strategy fallback matching the SyndiKit pattern: working directory first (reliable for WASM/Android/SPM), then #filePath-relative (macOS/Linux CI), then walking up parent directories. Add android-copy-files to copy Documentation.docc to the Android emulator's working directory so the path resolution finds the files via Strategy 1. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 1 + Tests/SyntaxDocTests/Settings.swift | 30 +++++++++++++++++++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 360795a..ffe1a84 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -264,6 +264,7 @@ jobs: android-swift-version: ${{ matrix.swift.version }} android-api-level: ${{ matrix.android-api-level }} android-run-tests: true + android-copy-files: Sources/SyntaxKit/Documentation.docc - name: Upload coverage to Codecov if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 diff --git a/Tests/SyntaxDocTests/Settings.swift b/Tests/SyntaxDocTests/Settings.swift index ab5bda9..205fe6a 100644 --- a/Tests/SyntaxDocTests/Settings.swift +++ b/Tests/SyntaxDocTests/Settings.swift @@ -8,14 +8,36 @@ import Foundation internal enum Settings { - /// Project root directory calculated from the current file location + /// Project root directory calculated with a 3-strategy fallback for cross-platform support internal static let projectRoot: URL = { - let currentFileURL = URL(fileURLWithPath: #filePath) - return - currentFileURL + // Strategy 1: Working directory (most reliable for SPM/WASM/Android) + let workingDir = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) + if FileManager.default.fileExists(atPath: workingDir.appendingPathComponent("Sources").path) { + return workingDir + } + + // Strategy 2: Source-relative via #filePath (macOS/Linux CI) + let sourceRelative = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() // Tests/SyntaxDocTests .deletingLastPathComponent() // Tests .deletingLastPathComponent() // Project root + if FileManager.default.fileExists( + atPath: sourceRelative.appendingPathComponent("Sources").path) + { + return sourceRelative + } + + // Strategy 3: Walk up from working directory (nested execution contexts) + var search = workingDir + for _ in 0..<4 { + if FileManager.default.fileExists(atPath: search.appendingPathComponent("Sources").path) { + return search + } + search = search.deletingLastPathComponent() + } + + // Fallback — will produce a clear error if Sources/ is still not found + return sourceRelative }() /// Document paths to search for documentation files From b1414b69f42b6823be47942c22c7469cbf1b4158 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 14:44:02 -0400 Subject: [PATCH 08/18] Limit doc test paths on WASM to tutorial files only Examples/ (2.1MB) and full Documentation.docc (with images) exceed WASM memory constraints. On WASI, use only the two lightweight tutorial .md files (~36KB total). Matches SyndiKit's #if os(WASI) subset pattern. Co-Authored-By: Claude Sonnet 4.6 --- Tests/SyntaxDocTests/Settings.swift | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Tests/SyntaxDocTests/Settings.swift b/Tests/SyntaxDocTests/Settings.swift index 205fe6a..864212c 100644 --- a/Tests/SyntaxDocTests/Settings.swift +++ b/Tests/SyntaxDocTests/Settings.swift @@ -41,11 +41,22 @@ internal enum Settings { }() /// Document paths to search for documentation files - internal static let docPaths = [ - "Sources/SyntaxKit/Documentation.docc", - "README.md", - "Examples", - ] + /// On WASM, limited to lightweight tutorial files only (no images, no Examples) + /// due to WASM memory constraints (~144KB practical limit) + internal static let docPaths: [String] = { + #if os(WASI) + return [ + "Sources/SyntaxKit/Documentation.docc/Tutorials/Quick-Start-Guide.md", + "Sources/SyntaxKit/Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md", + ] + #else + return [ + "Sources/SyntaxKit/Documentation.docc", + "README.md", + "Examples", + ] + #endif + }() /// Resolves a relative file path to absolute path internal static func resolveFilePath(_ filePath: String) throws -> URL { From 9c8c1037acad2f4644fb0730b823bd08c4331a18 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 14:55:52 -0400 Subject: [PATCH 09/18] Fix Android/WASM doc path resolution and coverage conditions - Add Strategy 1b to Settings.projectRoot: detect Documentation.docc/ in working dir (android-copy-files copies last component only) - Add #if os(Android) branch to docPaths with prefix-less paths matching the flat copy layout on Android devices - Add id: build to swift-build steps and gate coverage upload/processing on steps.build.outputs.contains-code-coverage == 'true' across all jobs Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 11 +++++++++++ Tests/SyntaxDocTests/Settings.swift | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index ffe1a84..1bbc6c0 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -62,6 +62,7 @@ jobs: steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 + id: build with: type: ${{ matrix.type }} wasmtime-version: 41.0.3 @@ -72,9 +73,11 @@ jobs: apt-get install -y curl - uses: sersoft-gmbh/swift-coverage-action@v5 id: coverage-files + if: steps.build.outputs.contains-code-coverage == 'true' with: fail-on-empty-output: true - name: Upload coverage to Codecov + if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: fail_ci_if_error: true @@ -102,10 +105,12 @@ jobs: steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 + id: build with: windows-swift-version: ${{ matrix.swift-version }} windows-swift-build: ${{ matrix.swift-build }} - name: Upload coverage to Codecov + if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: fail_ci_if_error: true @@ -134,6 +139,7 @@ jobs: - uses: actions/checkout@v6 - name: Build and Test + id: build uses: brightdigit/swift-build@v1.5.2 with: scheme: ${{ env.PACKAGE_NAME }}-Package @@ -145,9 +151,11 @@ jobs: # Common Coverage Steps - name: Process Coverage + if: steps.build.outputs.contains-code-coverage == 'true' uses: sersoft-gmbh/swift-coverage-action@v5 - name: Upload Coverage + if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} @@ -214,6 +222,7 @@ jobs: - uses: actions/checkout@v6 - name: Build and Test + id: build uses: brightdigit/swift-build@v1.5.2 with: scheme: ${{ env.PACKAGE_NAME }}-Package @@ -225,9 +234,11 @@ jobs: # Common Coverage Steps - name: Process Coverage + if: steps.build.outputs.contains-code-coverage == 'true' uses: sersoft-gmbh/swift-coverage-action@v5 - name: Upload Coverage + if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Tests/SyntaxDocTests/Settings.swift b/Tests/SyntaxDocTests/Settings.swift index 864212c..b9825e4 100644 --- a/Tests/SyntaxDocTests/Settings.swift +++ b/Tests/SyntaxDocTests/Settings.swift @@ -10,12 +10,20 @@ import Foundation internal enum Settings { /// Project root directory calculated with a 3-strategy fallback for cross-platform support internal static let projectRoot: URL = { - // Strategy 1: Working directory (most reliable for SPM/WASM/Android) let workingDir = URL(fileURLWithPath: FileManager.default.currentDirectoryPath) + + // Strategy 1a: Sources/ in working directory (SPM/WASM/Linux) if FileManager.default.fileExists(atPath: workingDir.appendingPathComponent("Sources").path) { return workingDir } + // Strategy 1b: Documentation.docc copied as last component (Android flat copy via android-copy-files) + if FileManager.default.fileExists( + atPath: workingDir.appendingPathComponent("Documentation.docc").path) + { + return workingDir + } + // Strategy 2: Source-relative via #filePath (macOS/Linux CI) let sourceRelative = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() // Tests/SyntaxDocTests @@ -44,7 +52,13 @@ internal enum Settings { /// On WASM, limited to lightweight tutorial files only (no images, no Examples) /// due to WASM memory constraints (~144KB practical limit) internal static let docPaths: [String] = { - #if os(WASI) + #if os(Android) + // android-copy-files copies Documentation.docc/ as last component to working dir + return [ + "Documentation.docc/Tutorials/Quick-Start-Guide.md", + "Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md", + ] + #elseif os(WASI) return [ "Sources/SyntaxKit/Documentation.docc/Tutorials/Quick-Start-Guide.md", "Sources/SyntaxKit/Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md", From 426285a0e2cee4781d30f6659c7bcb682fb939d3 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 15:05:56 -0400 Subject: [PATCH 10/18] Install curl only when code coverage is present on Ubuntu Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 1bbc6c0..a010891 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -67,7 +67,7 @@ jobs: type: ${{ matrix.type }} wasmtime-version: 41.0.3 - name: Install curl - if: matrix.swift.version == '6.3' + if: steps.build.outputs.contains-code-coverage == 'true' run: | apt-get update -q apt-get install -y curl From a77fb0f45d2a705c59272e97ff0deb984de1cc1c Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 15:19:06 -0400 Subject: [PATCH 11/18] Update swift-docc-plugin to 1.4.6 to fix Windows CI 1.4.6 adds .gitattributes for symlinks on Windows, fixing build failures where symlinked plugin source files were checked out as plain text, causing 'cannot find Lock in scope' and related errors. Co-Authored-By: Claude Sonnet 4.6 --- Package.resolved | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index e355d45..24cfefa 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,13 +1,13 @@ { - "originHash" : "3a9631966aed0489b1d0415c39c345f4083602bf6a2ddfdc7954cd286093542f", + "originHash" : "482d43aff5bb5c075d237e0ea17c12ee2c043b2642e459260752aa1848a20593", "pins" : [ { "identity" : "swift-docc-plugin", "kind" : "remoteSourceControl", "location" : "https://github.com/swiftlang/swift-docc-plugin", "state" : { - "revision" : "3e4f133a77e644a5812911a0513aeb7288b07d06", - "version" : "1.4.5" + "revision" : "e977f65879f82b375a044c8837597f690c067da6", + "version" : "1.4.6" } }, { From 9f5276f0866aca753f234aa363d7c70445cb4372 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 15:13:26 -0400 Subject: [PATCH 12/18] Fix Android doc test path resolution On Android, Documentation.docc is copied flat to the working directory without the Sources/SyntaxKit/ prefix. Centralize the prefix logic in resolveFilePath so test methods use short paths and Settings adds the prefix for non-Android platforms. Co-Authored-By: Claude Sonnet 4.6 --- Tests/SyntaxDocTests/DocumentationExampleTests.swift | 4 ++-- Tests/SyntaxDocTests/Settings.swift | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Tests/SyntaxDocTests/DocumentationExampleTests.swift b/Tests/SyntaxDocTests/DocumentationExampleTests.swift index 602462d..d2222f7 100644 --- a/Tests/SyntaxDocTests/DocumentationExampleTests.swift +++ b/Tests/SyntaxDocTests/DocumentationExampleTests.swift @@ -42,7 +42,7 @@ internal struct DocumentationExampleTests { @Test("Quick Start Guide examples work correctly") internal func validateQuickStartGuideExamples() throws { let quickStartFile = try Settings.resolveFilePath( - "Sources/SyntaxKit/Documentation.docc/Tutorials/Quick-Start-Guide.md" + "Documentation.docc/Tutorials/Quick-Start-Guide.md" ) let results = try testHarness.validateFile(at: quickStartFile) @@ -57,7 +57,7 @@ internal struct DocumentationExampleTests { @Test("Creating Macros tutorial examples work correctly") internal func validateMacroTutorialExamples() throws { let macroTutorialFile = try Settings.resolveFilePath( - "Sources/SyntaxKit/Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md" + "Documentation.docc/Tutorials/Creating-Macros-with-SyntaxKit.md" ) let results = try testHarness.validateFile(at: macroTutorialFile) diff --git a/Tests/SyntaxDocTests/Settings.swift b/Tests/SyntaxDocTests/Settings.swift index b9825e4..5719749 100644 --- a/Tests/SyntaxDocTests/Settings.swift +++ b/Tests/SyntaxDocTests/Settings.swift @@ -81,7 +81,12 @@ internal enum Settings { return .init(fileURLWithPath: filePath) } } else { - return Self.projectRoot.appendingPathComponent(filePath) + #if os(Android) + let resolvedPath = filePath + #else + let resolvedPath = "Sources/SyntaxKit/" + filePath + #endif + return Self.projectRoot.appendingPathComponent(resolvedPath) } } } From 51cf82bf87d4703d10f1c016f20a6092db2de5ae Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 16:04:13 -0400 Subject: [PATCH 13/18] Fix CI workflow issues from PR review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix Windows matrix to use proper cross-product (runs-on × swift) instead of mismatched include entries - Fix lint/docs if conditions to guard against null head_commit on PR events - Fix cache cleanup to skip tag deletions (only process branch deletes) - Add cancellation guard to build-macos-full - Remove hardcoded swift:6.3-noble container for WASM builds; use matrix version - Add paths-ignore to CodeQL to skip doc-only commits Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 28 ++++++++++++++-------------- .github/workflows/cleanup-caches.yml | 5 +++++ .github/workflows/codeql.yml | 10 ++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index a010891..a8495ac 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -53,7 +53,7 @@ jobs: name: Build on Ubuntu needs: [configure] runs-on: ubuntu-latest - container: ${{ (matrix.type == 'wasm' || matrix.type == 'wasm-embedded') && 'swift:6.3-noble' || matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} + container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} strategy: matrix: os: ${{ fromJSON(needs.configure.outputs.ubuntu-os) }} @@ -95,26 +95,26 @@ jobs: fail-fast: false matrix: runs-on: [windows-2022, windows-2025] - include: - - swift-version: swift-6.3-release - swift-build: 6.3-RELEASE - - swift-version: swift-6.2-release - swift-build: 6.2-RELEASE - - swift-version: swift-6.1-release - swift-build: 6.1-RELEASE + swift: + - version: swift-6.3-release + build: 6.3-RELEASE + - version: swift-6.2-release + build: 6.2-RELEASE + - version: swift-6.1-release + build: 6.1-RELEASE steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 id: build with: - windows-swift-version: ${{ matrix.swift-version }} - windows-swift-build: ${{ matrix.swift-build }} + windows-swift-version: ${{ matrix.swift.version }} + windows-swift-build: ${{ matrix.swift.build }} - name: Upload coverage to Codecov if: steps.build.outputs.contains-code-coverage == 'true' uses: codecov/codecov-action@v6 with: fail_ci_if_error: true - flags: swift-${{ matrix.swift-version }},windows + flags: swift-${{ matrix.swift.version }},windows verbose: true token: ${{ secrets.CODECOV_TOKEN }} os: windows @@ -164,7 +164,7 @@ jobs: build-macos-full: name: Build on macOS (Full) needs: [configure] - if: needs.configure.outputs.full-matrix == 'true' + if: ${{ !cancelled() && needs.configure.result == 'success' && needs.configure.outputs.full-matrix == 'true' }} env: PACKAGE_NAME: SyntaxKit runs-on: ${{ matrix.runs-on }} @@ -287,7 +287,7 @@ jobs: lint: name: Linting - if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} + if: ${{ !cancelled() && !failure() && (github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip')) }} runs-on: ubuntu-latest needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android] env: @@ -318,7 +318,7 @@ jobs: docs: name: Documentation Validation - if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }} + if: ${{ !cancelled() && !failure() && (github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip')) }} needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android] runs-on: ubuntu-latest steps: diff --git a/.github/workflows/cleanup-caches.yml b/.github/workflows/cleanup-caches.yml index 80a0655..d754dca 100644 --- a/.github/workflows/cleanup-caches.yml +++ b/.github/workflows/cleanup-caches.yml @@ -14,6 +14,11 @@ jobs: uses: actions/github-script@v8 with: script: | + if (context.payload.ref_type !== 'branch') { + console.log('Not a branch deletion, skipping.'); + return; + } + const ref = `refs/heads/${context.payload.ref}`; console.log(`Cleaning up caches for branch: ${ref}`); diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e0ed2f5..dbbb351 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -15,9 +15,19 @@ on: push: branches-ignore: - '*WIP' + paths-ignore: + - '**.md' + - 'Docs/**' + - 'LICENSE' + - '.github/ISSUE_TEMPLATE/**' pull_request: # The branches below must be a subset of the branches above branches: [ "main" ] + paths-ignore: + - '**.md' + - 'Docs/**' + - 'LICENSE' + - '.github/ISSUE_TEMPLATE/**' schedule: - cron: '20 11 * * 3' From 4ecbcde8ff52e9284ee47a661034cb8ff578199d Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 16:20:04 -0400 Subject: [PATCH 14/18] Exclude wasm/wasm-embedded builds for Swift 6.1 and older on Ubuntu Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index a8495ac..850af85 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -59,6 +59,15 @@ jobs: os: ${{ fromJSON(needs.configure.outputs.ubuntu-os) }} swift: ${{ fromJSON(needs.configure.outputs.ubuntu-swift) }} type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }} + exclude: + - swift: {version: "6.0"} + type: wasm + - swift: {version: "6.0"} + type: wasm-embedded + - swift: {version: "6.1"} + type: wasm + - swift: {version: "6.1"} + type: wasm-embedded steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 From b9aaf2442a914b88ffb29d7d0f34fe62fb8a8bd9 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 16:52:01 -0400 Subject: [PATCH 15/18] Drop Swift 6.1 from Windows and exclude wasm for Swift <=6.1 on Ubuntu swift-docc-plugin 1.4.6 is incompatible with Swift 6.1 on Windows (missing Lock, SnippetExtractor, ParsedSymbolGraphArguments types). Windows matrix now covers 6.2 and 6.3 only. WASM/wasm-embedded tooling does not support Swift 6.0 or 6.1 on Ubuntu, so those combinations are excluded from the build matrix. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 850af85..924d7b3 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -109,8 +109,6 @@ jobs: build: 6.3-RELEASE - version: swift-6.2-release build: 6.2-RELEASE - - version: swift-6.1-release - build: 6.1-RELEASE steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 From 5db53e6aa01983974f4c33e6aa59c27e4c59b8b3 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 17:14:29 -0400 Subject: [PATCH 16/18] Enable git symlinks on Windows before swift-build runs swift-docc-plugin 1.4.6 added .gitattributes to mark symlink files, but git on Windows also needs core.symlinks=true to create real symlinks rather than plain-text stubs. Without it, SPM checks out the plugin's shared source files as text, causing 'cannot find Lock in scope' and related errors. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 924d7b3..f2b27bc 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -111,6 +111,9 @@ jobs: build: 6.2-RELEASE steps: - uses: actions/checkout@v6 + - name: Enable git symlinks + shell: pwsh + run: git config --global core.symlinks true - uses: brightdigit/swift-build@v1.5.2 id: build with: From 8f6ea6d82ce2d19b37c5cd0b1c653f8093eead94 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 17:34:33 -0400 Subject: [PATCH 17/18] Exclude wasm/wasm-embedded builds for Swift 6.2 on Ubuntu Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index f2b27bc..1bbd7f7 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -68,6 +68,10 @@ jobs: type: wasm - swift: {version: "6.1"} type: wasm-embedded + - swift: {version: "6.2"} + type: wasm + - swift: {version: "6.2"} + type: wasm-embedded steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 From f442e81a7d96685c4079a1c62fc3f33d8679e769 Mon Sep 17 00:00:00 2001 From: Leo Dion Date: Thu, 2 Apr 2026 18:03:27 -0400 Subject: [PATCH 18/18] Restore Swift 6.1 Windows, remove wasm excludes, disable fail-fast on Ubuntu Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/SyntaxKit.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/SyntaxKit.yml b/.github/workflows/SyntaxKit.yml index 1bbd7f7..7ddeb09 100644 --- a/.github/workflows/SyntaxKit.yml +++ b/.github/workflows/SyntaxKit.yml @@ -55,23 +55,11 @@ jobs: runs-on: ubuntu-latest container: ${{ matrix.swift.nightly && format('swiftlang/swift:nightly-{0}-{1}', matrix.swift.version, matrix.os) || format('swift:{0}-{1}', matrix.swift.version, matrix.os) }} strategy: + fail-fast: false matrix: os: ${{ fromJSON(needs.configure.outputs.ubuntu-os) }} swift: ${{ fromJSON(needs.configure.outputs.ubuntu-swift) }} type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }} - exclude: - - swift: {version: "6.0"} - type: wasm - - swift: {version: "6.0"} - type: wasm-embedded - - swift: {version: "6.1"} - type: wasm - - swift: {version: "6.1"} - type: wasm-embedded - - swift: {version: "6.2"} - type: wasm - - swift: {version: "6.2"} - type: wasm-embedded steps: - uses: actions/checkout@v6 - uses: brightdigit/swift-build@v1.5.2 @@ -113,6 +101,8 @@ jobs: build: 6.3-RELEASE - version: swift-6.2-release build: 6.2-RELEASE + - version: swift-6.1-release + build: 6.1-RELEASE steps: - uses: actions/checkout@v6 - name: Enable git symlinks