Skip to content

Optimize CI workflows with dynamic matrix and cache cleanup#129

Merged
leogdion merged 21 commits intov0.0.4from
128-workflow-updates
Apr 3, 2026
Merged

Optimize CI workflows with dynamic matrix and cache cleanup#129
leogdion merged 21 commits intov0.0.4from
128-workflow-updates

Conversation

@leogdion
Copy link
Copy Markdown
Member

@leogdion leogdion commented Apr 2, 2026

Summary

  • Add concurrency groups to cancel in-progress CI runs on the same branch
  • Add paths-ignore and pull_request trigger to skip CI on doc-only changes
  • Add configure job with dynamic matrix — minimal builds on feature branches (Ubuntu noble + Swift 6.1, core macOS only), full matrix on main/PRs-to-main
  • Gate Windows and extended macOS builds (iOS, watchOS, tvOS, visionOS, older Xcode) to full matrix only
  • Update lint/docs job conditions so they run when optional jobs are skipped
  • Update all GitHub Actions to latest versions (checkout v6, cache v5, codecov v6, swift-coverage v5, swift-build v1.5.2, codeql-action v4, github-script v8)
  • Add cache cleanup workflows for deleted branches and closed PRs
  • Update CodeQL workflow action versions

Closes #128, closes #126, closes #127

Test plan

  • Push to feature branch → verify only configure, minimal Ubuntu (1 job), core macOS (2 jobs), lint, docs run; Windows and macOS-full are skipped
  • Open PR to main → verify full matrix fires (all Ubuntu, Windows, all macOS entries)
  • Push again quickly to same branch → verify concurrency cancels the previous run
  • Push a doc-only change → verify CI is skipped via paths-ignore
  • Verify lint and docs jobs still pass when optional jobs are skipped

🤖 Generated with Claude Code


Perform an AI-assisted review on CodePeer.com

#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) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b053b470-ca60-4f28-b66c-52490bd8a6ea

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 128-workflow-updates

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.04%. Comparing base (18c1e25) to head (6a9f9f5).
⚠️ Report is 1 commits behind head on v0.0.4.

Additional details and impacted files
@@            Coverage Diff             @@
##           v0.0.4     #129      +/-   ##
==========================================
- Coverage   78.16%   78.04%   -0.12%     
==========================================
  Files         126      126              
  Lines        5651     4555    -1096     
==========================================
- Hits         4417     3555     -862     
+ Misses       1234     1000     -234     
Flag Coverage Δ
spm 77.84% <ø> (+0.28%) ⬆️
swift- ?
swift-6.0 78.00% <ø> (?)
swift-6.1 77.89% <ø> (?)
swift-6.2 77.87% <ø> (?)
swift-6.3 77.84% <ø> (?)
ubuntu 78.04% <ø> (-0.09%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

CI Workflow Review

Overall this is a well-structured PR. The dynamic matrix, concurrency groups, and cache cleanup are all solid improvements. A few issues worth addressing.

Bugs / Correctness Issues

1. build-ubuntu missing fail-fast: false

All other matrix jobs (build-macos, build-macos-full, build-windows) explicitly set fail-fast: false. build-ubuntu does not, so a single Ubuntu build failure cancels the entire matrix. This is likely unintentional and makes CI noisier on flaky runs.

2. build-macos bypasses configure but sibling jobs don't

build-macos has no needs: [configure] and uses its own if: check. build-ubuntu and build-windows both depend on configure. If configure fails, build-ubuntu is skipped but build-macos still runs — and lint/docs will see asymmetric skip/failure state from their two upstream paths.

If the intent is "core macOS always runs regardless", that's a valid design — just worth a comment in the YAML to make it look intentional rather than an oversight.

3. ci skip check doesn't work on PR events

In configure and build-macos:

if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}

For pull_request events, github.event.head_commit is null — property access returns empty string, so [ci skip] in commit messages silently fails to suppress CI on PRs. This is consistent with the original behavior, but since this PR adds a pull_request trigger it's now newly relevant.

Minor Issues

4. Unnecessary checkout in cache cleanup workflows

Both cleanup-caches.yml and cleanup-pr-caches.yml call actions/checkout but never use any repository files — the github-script step only calls the REST API. This adds ~10-15s per cleanup run and requires implicit contents: read permission. Removing the checkout step would be cleaner.

5. codeql.yml hardcodes Xcode 16.4

run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer

If the macos-15 runner image drops Xcode 16.4, scheduled CodeQL scans will start failing silently. Consider removing the step to use the runner default, or updating it to match the primary build-macos job (Xcode_26.0).

6. codeql.yml has no paths-ignore

Unlike SyntaxKit.yml, CodeQL fires on every push. A doc-only change will trigger a security scan but no build CI. Adding a matching paths-ignore is optional but makes the two workflows consistent.

Positive Highlights

  • Concurrency groups with cancel-in-progress: true are exactly right.
  • The configure dynamic matrix pattern cleanly reduces CI minutes on feature branches without sacrificing full coverage on main/PRs.
  • Updating all action versions in one PR avoids future drift.
  • Cleanup workflows correctly use paginate for repos with many caches.
  • lint and docs using !cancelled() && !failure() instead of the old hard-fail needs is the correct fix for optional upstream jobs.

Comment on lines 112 to 114
- type: macos
runs-on: macos-15
xcode: "/Applications/Xcode_26.0.app"
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to macOS-full

uses: actions/checkout@v6

- name: Setup Xcode
run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update to Xcode 26.4 and macOS 26

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

- 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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid improvement to the CI infrastructure. The dynamic matrix approach, concurrency groups, and cache cleanup are all well-designed. A few items worth addressing:

BUGS / CORRECTNESS ISSUES

  1. Unnecessary checkout in cache cleanup workflows

Both cleanup-caches.yml and cleanup-pr-caches.yml include actions/checkout@v6, but the scripts only use GitHub API calls via github-script and do not read any repo files. The checkout is wasted I/O and slightly expands the attack surface on delete/closed events.

  1. build-ubuntu still has nightly-conditional container expression

The container line still references matrix.swift.nightly, but the new dynamic matrix never sets a nightly field. This always evaluates the fallback branch — dead code. Simplify to format('swift:{0}-{1}', matrix.swift.version, matrix.os) to avoid confusion.

  1. codeql.yml missing paths-ignore

SyntaxKit.yml skips doc-only changes via paths-ignore, but codeql.yml has no such filter. A commit touching only .md files or Docs/ will skip the main build but still trigger full CodeQL analysis on macOS runners (expensive).

DESIGN / CONSISTENCY

  1. build-macos if syntax inconsistency

build-macos still uses old-style string syntax for the ci-skip check while all other jobs use expression syntax dollar-brace notation. Minor but inconsistent.

  1. lint and docs depend on jobs always skipped on feature branches

On feature branches, four of the six declared needs are skipped. The !failure() condition correctly prevents blocking, but lint and docs still wait until GitHub resolves all six dependency slots. A dedicated gate job or trimming the needs list would let these run earlier on feature branches.

  1. build-ubuntu missing fail-fast: false

All other matrix jobs (build-windows, build-macos-full) set fail-fast: false. build-ubuntu omits this, so a single matrix job failure cancels the rest of the Ubuntu matrix. Worth making consistent.

MINOR / INFORMATIONAL

  1. Misleading autobuild comment in codeql.yml

The boilerplate comment "Run, Build Application using script" is left on the step that simply runs swift build. Should be updated or removed.

  1. configure ci-skip check unreliable for PR events

github.event.head_commit.message is not reliably populated on pull_request events. Minor in practice but worth a comment in the workflow.

WHAT IS WELL DONE

  • Dynamic matrix cleanly reduces feature branch Ubuntu builds from 8 to 1 — meaningful CI speedup.
  • Concurrency groups are set up correctly, cancelling in-flight runs on the same ref without affecting other branches.
  • Codecov flag bug fixed: the original used matrix.swift-version (wrong notation) and this PR fixes it to matrix.swift.version.
  • Cache cleanup workflows correctly use paginate for repos with many caches.
  • CodeQL addition is a good security baseline with a reasonable 120-minute Swift timeout.
  • The PR description test plan is thorough — running those scenarios manually before merging is recommended given the complexity of the conditional job logic.

- 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 <noreply@anthropic.com>
@leogdion leogdion marked this pull request as ready for review April 2, 2026 17:12
leogdion and others added 2 commits April 2, 2026 13:23
- 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid improvement. The dynamic matrix, concurrency groups, and cache cleanup are all good additions. A few issues worth addressing:

BUGS / POTENTIAL FAILURES

  1. WASM builds will fail the coverage step (SyntaxKit.yml around line 66)

build-ubuntu runs sersoft-gmbh/swift-coverage-action@v5 with fail-on-empty-output: true for all matrix entries, including wasm and wasm-embedded types. WASM builds do not produce LLVM coverage data in the standard way, so these jobs will always fail at the coverage step. The coverage and Codecov upload steps should be conditional on matrix.type being empty.

  1. Missing fail-fast: false on build-ubuntu (SyntaxKit.yml around line 55)

build-ubuntu is the only multi-entry matrix job without fail-fast: false. On a full matrix run this is 2 OS x 4 Swift versions x 3 types = 24 combinations. A single wasm failure (see point 1 above) would cancel all 23 other builds. All sibling jobs (build-windows, build-macos-full, build-android) already have fail-fast: false set.

INCONSISTENCIES

  1. lint and docs jobs: null-safety for head_commit.message on PRs

The configure job correctly guards the null case by first checking whether event_name equals pull_request. The lint and docs jobs lack this guard. On pull_request events, github.event.head_commit is null. The contains(null, 'ci skip') expression evaluates to false in GHA so it does not cause failures, but it is inconsistent with the configure job pattern and could mislead future maintainers.

MINOR ISSUES

  1. Unnecessary actions/checkout in cleanup workflows

Both cleanup-caches.yml and cleanup-pr-caches.yml include an actions/checkout@v6 step, but the jobs only make GitHub API calls via github-script and never read repository files. These checkout steps can be removed.

  1. CodeQL runs on doc-only pushes and all non-WIP branches

codeql.yml has no paths-ignore, so it triggers on every non-WIP push including documentation-only changes. Consider adding the same paths-ignore as SyntaxKit.yml. CodeQL also currently runs on every non-WIP branch rather than just main and PRs, which may be broader than intended given the dynamic matrix optimization goals.

SUMMARY OF ACTIONABLE ITEMS

  • Fix coverage step for wasm/wasm-embedded types (these will always fail with current config)
  • Add fail-fast: false to build-ubuntu
  • Remove unnecessary checkout steps from cleanup workflows
  • Optionally: add paths-ignore to codeql.yml and/or restrict its push trigger to main

Generated with Claude Code

@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026


CI Workflow Review

Overall this is a solid improvement — the dynamic matrix, concurrency groups, and cache cleanup workflows all address real pain points. A few issues worth addressing before merge:


Bugs

1. lint / docs jobs: head_commit.message is null on pull_request events

github.event.head_commit is null on pull_request events — the configure job already handles this correctly with its event_name == 'pull_request' guard, but lint and docs still have the raw head_commit.message check. On a PR this evaluates to !contains(null, 'ci skip') which is true (so it won't silently skip CI), but it's fragile and inconsistent. Consider mirroring the configure guard:

if: !cancelled() && !failure() && (github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip'))

2. build-windows matrix: include entries without matching base keys create orphaned jobs

Because swift-version doesn't appear in the base matrix dimensions (only runs-on does), none of the include entries match any existing combination — GitHub Actions adds them as standalone jobs with no runs-on value. The base 2 jobs (windows-2022, windows-2025) also get no swift-version. This was a pre-existing bug but the PR adds a third entry, making it worse. The likely intent is a 2D matrix (runs-on x swift-version), or each include entry should also specify runs-on.


Performance / Correctness

3. WASM container hardcoded to swift:6.3-noble across all Swift versions

In the full matrix, ubuntu-type is ["","wasm","wasm-embedded"] combined with all 4 Swift versions (6.0–6.3). WASM builds for Swift 6.0, 6.1, and 6.2 will all run inside the swift:6.3-noble container — likely producing failures or misleading results. Either restrict WASM to only Swift 6.3 in configure, or parameterize the container image per Swift version.


Minor

4. codeql.yml missing paths-ignore on pull_request

The main workflow skips doc-only PRs via paths-ignore, but codeql.yml doesn't have this on the pull_request trigger. A docs-only PR will still trigger a ~2h CodeQL scan on macos-26.

5. build-macos flags reference undefined matrix.type

matrix.type isn't in the build-macos matrix (only the single SPM entry), so the flags expression always evaluates to 'spm'. Works correctly but is confusing — safe to simplify to: flags: spm


Positive notes

  • Concurrency groups are correctly scoped per workflow+ref; cancel-in-progress prevents wasted runs on rapid pushes.
  • The configure job's event_name == 'pull_request' guard correctly handles the null head_commit issue for build jobs.
  • Cache cleanup workflows are well-structured; using paginate to handle multi-page cache lists is the right approach.
  • Installing curl before Codecov upload in Swift 6.3 containers is a good catch.
  • Coverage flag fix (matrix.swift.version vs the old broken matrix.swift-version) is a nice cleanup.

- 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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid improvement — the concurrency groups, dynamic matrix, cache cleanup workflows, and CodeQL integration are all well-designed. Here are a few items worth addressing before merging:


🐛 Potential Bugs

1. Windows matrix include creates broken combinations

In build-windows, the three include entries only define swift-version/swift-build without a matching runs-on key. Since these keys aren't in the matrix, GitHub Actions creates new standalone combinations for each include rather than extending the runs-on combinations. The result is 5 jobs: 2 that have runs-on but no Swift version, and 3 that have a Swift version but no runs-on.

This pattern was present before the PR (2 includes + 2 runs-on), and this change adds a third include. Recommend restructuring to explicit combos:

matrix:
  include:
    - runs-on: windows-2022
      swift-version: swift-6.3-release
      swift-build: 6.3-RELEASE
    - runs-on: windows-2025
      swift-version: swift-6.3-release
      swift-build: 6.3-RELEASE
    # ... etc

2. WASM builds will fail coverage step

In build-ubuntu, the swift-coverage-action has fail-on-empty-output: true. For wasm and wasm-embedded build types, code coverage data is unlikely to be produced, which will cause the job to fail at this step. Consider either:

  • Conditionally skipping the coverage steps for WASM types: if: matrix.type == ''
  • Or setting fail-on-empty-output: false for WASM builds

⚠️ Minor Issues

3. Coverage flags missing build type for Ubuntu WASM builds

The Codecov upload in build-ubuntu uses flags: swift-${{ matrix.swift.version }},ubuntu regardless of matrix.type. If WASM coverage ever does get generated, it would be incorrectly attributed as a regular Ubuntu build. A flag like swift-${{ matrix.swift.version }},${{ matrix.type || 'ubuntu' }} would be more accurate.

4. Inconsistent fail_ci_if_error for coverage uploads

build-ubuntu and build-windows both set fail_ci_if_error: true on Codecov uploads, but build-macos and build-macos-full omit this. A silent Codecov failure on macOS won't block the job. Recommend adding it for consistency:

- name: Upload Coverage
  uses: codecov/codecov-action@v6
  with:
    token: ${{ secrets.CODECOV_TOKEN }}
    fail_ci_if_error: true   # add this
    flags: ...

5. Unnecessary checkout in cleanup workflows

Both cleanup-caches.yml and cleanup-pr-caches.yml have a uses: actions/checkout@v6 step, but it's not needed — the github-script action uses the GitHub API directly and doesn't touch the filesystem. Removing it saves a few seconds per cleanup run.

6. lint/docs ci-skip check on PR events

if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }}

On PR events github.event.head_commit is null. GitHub Actions expression language silently coerces null property access to '', so contains('', 'ci skip') is false and the condition evaluates to true — meaning it effectively never skips on PRs. This is arguably the desired behavior (you probably want lint/docs to always run on PRs), but it's worth being explicit like the configure job is:

if: ${{ !cancelled() && !failure() && (github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip')) }}

✅ Looks Good

  • Concurrency groups with github.head_ref || github.ref_name correctly prevents duplicate push+PR runs for the same branch
  • configure job's ci-skip guard properly checks event_name first, avoiding the null-access issue on PRs
  • build-macos gating on needs.configure.result == 'success' with !cancelled() is the right pattern for propagating the ci-skip through the dependency chain
  • curl install placement (after swift-build, before coverage steps) is correct per the intent
  • CodeQL workflow is well-structured with appropriate timeout and runner selection for Swift
  • Cache cleanup workflows handle the two separate scopes (branches vs. PRs) properly

🤖 Generated with Claude Code

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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

CI Workflow Review

Overall this is a well-structured overhaul with solid improvements. A few issues worth addressing before merge.

Bugs / Logic Issues

1. lint and docs jobs run even when ci skip is in commit message

When a push has ci skip in the commit message, configure is skipped (correctly). Because build-ubuntu, build-macos, etc. depend on it, they are also skipped. However, GitHub Actions treats a skipped dependency as neither failed nor cancelled, so the lint/docs if condition using !failure() and !cancelled() still evaluates to true (skipped does not equal failed). The trailing !contains(..., 'ci skip') guard catches this for push events, but consider also adding needs: [configure] and gating on needs.configure.result == 'success' in lint/docs for consistency.

2. WASM container hardcoded regardless of matrix OS/Swift version

The WASM container is hardcoded to swift:6.3-noble regardless of the matrix os or swift.version values. On a full-matrix run, a jammy + 6.0 + wasm combination silently builds against 6.3-noble instead. Consider either deriving the container image from matrix values, or restricting WASM matrix entries to a single OS+Swift combination.

Minor Issues

3. build-macos coverage step is missing fail-on-empty-output

build-ubuntu uses fail-on-empty-output: true on sersoft-gmbh/swift-coverage-action@v5, but build-macos omits this flag. Missing coverage data will silently succeed rather than alerting you.

4. Windows matrix include items are standalone rows, not a cross-product

The Windows matrix combines runs-on: [windows-2022, windows-2025] with include entries that only set swift-version/swift-build. In GitHub Actions, these include entries are appended as standalone jobs rather than cross-multiplied with runs-on. Each runs-on entry ends up with no Swift version set. This appears to be a latent bug inherited from before this PR and should likely be a fully explicit include-only list.

5. Magic constant 4 in Settings.swift Strategy 3

The loop for _ in 0..<4 is unexplained. A brief comment such as // max 4 levels up would prevent future readers from wondering if it is significant.

6. android-copy-files path is fragile

android-copy-files: Sources/SyntaxKit/Documentation.docc -- if this path is ever moved or renamed, the CI step will fail without an obvious error. A comment noting why this path matters would help future maintainers.

Positives

  • Concurrency groups correctly use github.head_ref || github.ref_name so push and PR events for the same branch share a group and the PR run cancels the in-flight push run.
  • The dynamic configure matrix cleanly separates fast feature-branch builds from the full matrix on main/PRs.
  • Cache cleanup workflows are well-implemented and correctly scoped to refs/heads/ and refs/pull/ refs.
  • The 3-strategy fallback in Settings.swift is a solid improvement over the single #filePath-based approach; the per-strategy comments are helpful.
  • CodeQL on macos-26 with Xcode 26.4 is appropriate for a Swift-only repo.

Overall the concurrency, dynamic matrix, and cache-cleanup pieces are solid. Issue 2 (hardcoded WASM container) is the most impactful to address before merge.

leogdion and others added 2 commits April 2, 2026 14:44
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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

This is a well-structured PR that meaningfully improves CI efficiency. Here is my review:


Strengths

  • Concurrency groups are correctly configured; the github.head_ref || github.ref_name fallback ensures push and PR events for the same branch share a group, preventing duplicate runs.
  • Dynamic matrix via configure job cleanly separates minimal/full builds rather than using complex per-job conditions.
  • Coverage gates (steps.build.outputs.contains-code-coverage == 'true') prevent spurious failures when coverage is not produced (e.g., WASM).
  • Cache cleanup workflows for deleted branches and closed PRs are a good housekeeping addition.
  • Settings.swift fallback chain is a practical solution for WASM/Android path resolution differences.

Issues

1. lint and docs ci-skip check is fragile on PR events (medium)

On pull_request events, github.event.head_commit is null, so contains(null, 'ci skip') evaluates to false and the jobs run — which is correct by accident, not by design. The configure job already handles this correctly with an explicit github.event_name == 'pull_request' guard. The same pattern should be applied to lint and docs for clarity and correctness.

2. WASM type hard-codes Swift 6.3 container regardless of matrix.swift.version (medium)

The container expression pins WASM/WASM-embedded to swift:6.3-noble unconditionally. When the full matrix runs, ubuntu-swift includes 6.0, 6.1, 6.2, and 6.3. A job with Swift 6.1 and type: wasm will silently run in the 6.3 container, making those matrix combinations meaningless. Consider either restricting WASM types to Swift 6.3 only in the configure job output, or using matrix.swift.version in the container expression for WASM builds.

3. build-macos-full SPM entry — confirm intentional (low)

build-macos (core) now only has a single macos-26/Xcode_26.4 SPM entry. SPM on macos-15 is only validated in the full matrix via build-macos-full. Worth confirming this is intentional and not a gap in the always-on matrix.

4. codeql.yml autobuild step has leftover template text (low)

The autobuild step echoes "Run, Build Application using script" which is verbatim from the CodeQL starter template and adds noise to CI logs. The echo can be removed, leaving just swift build.

5. Windows matrix does not create a cross-product (pre-existing, informational)

GitHub Actions include entries without matching matrix keys add standalone jobs rather than combining with existing matrix values. The current Windows config produces 2 jobs from runs-on plus 3 from include, rather than 6 jobs (2 runners x 3 Swift versions). This is pre-existing behavior, not introduced here, but worth a follow-up issue if full cross-product testing is desired.


Settings.swift Notes

The 3-strategy fallback is clean and the platform-specific docPaths is a good improvement over the previous constant. One minor note: Strategy 1b (checking for Documentation.docc in working dir) and Strategy 1a (checking for Sources/) both return workingDir, but they correspond to different docPaths layouts (prefix-less paths for Android vs Sources/SyntaxKit/... for others). The logic is correct because each platform triggers a different strategy, but this implicit coupling is worth a brief comment to help future maintainers understand why the strategy selection and docPaths content must stay in sync.


Summary

The PR achieves its goals well. The two medium-priority issues (fragile ci-skip check on PR events and WASM container version mismatch) are worth addressing before merge. The rest are minor polish items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid improvement — the concurrency groups, dynamic matrix, and cache cleanup all add real value. A few things worth addressing before merging.


Issues

1. WASM matrix generates redundant jobs (medium)

In build-ubuntu, the full matrix is os=["noble","jammy"] × swift=[6.0,6.1,6.2,6.3] × type=["","wasm","wasm-embedded"] = 24 jobs. However, the WASM container is hardcoded:

container: ${{ (matrix.type == 'wasm' || matrix.type == 'wasm-embedded') && 'swift:6.3-noble' || ... }}

So all 8 wasm jobs and all 8 wasm-embedded jobs use the same swift:6.3-noble container — 14 out of 16 WASM jobs are pure duplicates. Consider filtering the configure output so WASM types only pair with noble/6.3:

# In configure step, for full matrix:
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=[""]' >> "$GITHUB_OUTPUT"
# And add a separate wasm-specific output, or use matrix exclusions

Alternatively, use exclude: in the build-ubuntu matrix to drop type != "" && (os != noble || swift.version != "6.3").

2. Unnecessary checkout in cache cleanup workflows (minor)

Both cleanup-caches.yml and cleanup-pr-caches.yml have:

- uses: actions/checkout@v6

The github-script steps that follow only make API calls — no filesystem access needed. Removing the checkout step saves ~5-10s per cleanup run.

3. CodeQL autobuild step has misleading comment (minor)

In codeql.yml:

- run: |
    echo "Run, Build Application using script"
    swift build

The echo suggests a script will run but it's just swift build. Drop the echo or replace with a named step.

4. wasmtime-version passed to all Ubuntu builds (nit)

- uses: brightdigit/swift-build@v1.5.2
  with:
    type: ${{ matrix.type }}
    wasmtime-version: 41.0.3

wasmtime-version is passed even for regular (type="") builds. If swift-build silently ignores unknown inputs this is harmless, but it would be cleaner to gate it on the type:

wasmtime-version: ${{ (matrix.type == 'wasm' || matrix.type == 'wasm-embedded') && '41.0.3' || '' }}

Observations

lint/docs dependency on optional jobs — The needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android] combined with if: ${{ !cancelled() && !failure() }} correctly handles skipped optional jobs on feature branches. No action needed, just worth confirming this was tested.

Settings.swift Strategy 1b — Detecting Documentation.docc in the working directory for Android is clever. One edge case: if tests are ever run from within a directory that coincidentally contains a Documentation.docc folder for other reasons, Strategy 1b would short-circuit before Strategy 2. The check is probably fine for CI environments.

configure ci-skip check — The guard github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip') is correct. Note that lint/docs still have their own head_commit.message checks which evaluate to true on PRs (null doesn't contain the string), so the behavior is consistent even if slightly inconsistent in approach.


Summary

The two actionable items are the WASM matrix redundancy (#1, worth fixing for cost savings) and the unnecessary checkouts in cache cleanup (#2, easy win). Everything else is working correctly.

@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

Code Review: Optimize CI workflows with dynamic matrix and cache cleanup

Summary: Significant CI improvement. The dynamic matrix, action version updates, and cache cleanup workflows are all solid additions. A few issues worth addressing before merge.

Findings

1. cleanup-caches.yml fires on tag deletions too

The delete event triggers for both branch and tag deletions. The script builds a refs/heads/ ref unconditionally, which would produce an incorrect ref for tag deletions (e.g., refs/heads/v1.0.0 instead of refs/tags/v1.0.0). Add a guard:

if (context.payload.ref_type !== 'branch') {
  console.log('Not a branch deletion, skipping.');
  return;
}

2. WASM builds always use Swift 6.3 container regardless of matrix version

In build-ubuntu, the container is hardcoded to swift:6.3-noble for wasm/wasm-embedded types:

container: ${{ (matrix.type == 'wasm' || matrix.type == 'wasm-embedded') && 'swift:6.3-noble' || ... }}

This means WASM builds silently ignore matrix.swift.version (6.0, 6.1, 6.2 on full matrix). If multi-version WASM testing is desired, the container selection needs to be dynamic. If Swift 6.3 is intentionally the only supported WASM version, a comment or matrix exclusion would clarify the intent.

3. lint and docs block on build-android

On full-matrix runs, lint and docs now wait for Android builds, which can be slow. On feature branches (non-full matrix), build-android is skipped — and since skipped jobs don't set failure(), the !cancelled() && !failure() condition correctly unblocks lint/docs. This works, but it may be worth calling out in comments that this is intentional.

4. wasmtime-version passed to non-WASM builds

wasmtime-version: 41.0.3 is always passed to swift-build, even for standard Ubuntu builds. This is likely a no-op if the action ignores it, but could be made conditional on matrix.type to be more explicit.

5. Original build-ubuntu had duplicate Swift 6.1 entries (now fixed)

The original matrix had version: "6.1" twice (once with nightly: true). This PR correctly removes those entries. Good catch.

Minor / Style

  • Trailing whitespace cleanup throughout the YAML is appreciated.
  • Concurrency group using github.head_ref || github.ref_name is correct and handles both PR and push events.
  • actions/cache@v5, codecov/codecov-action@v6, swift-coverage-action@v5 updates are all good.

Overall: The structure is a clear improvement. The tag-deletion guard in cleanup-caches.yml is the most actionable fix before merge. The WASM matrix question is worth a quick clarification of intent.

@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

Code Review: Optimize CI workflows

Well-structured improvement. Dynamic matrix, concurrency cancellation, and cache cleanup are all good additions. A few things worth verifying:

Windows matrix - include items without runs-on

The include objects in the Windows matrix have no runs-on key, so per GitHub Actions docs they create new matrix combinations rather than extending existing ones. The new combinations have no runner (matrix.runs-on is undefined), which likely causes them to silently fail or skip. This is pre-existing behavior but worth auditing — if the intent is every swift version on every runner, the include items should specify runs-on or the matrix should be restructured as a full cross-product.

build-macos slim job references undefined matrix keys

The slim macOS matrix only defines runs-on and xcode, but the build step still passes matrix.type, matrix.deviceName, matrix.osVersion, and matrix.download-platform to swift-build. These will be empty strings. If the action handles empty strings gracefully this is fine, but removing unused with: keys would reduce confusion.

lint job head_commit.message on PRs

github.event.head_commit is null on pull_request events, so contains(null, 'ci skip') is false and lint correctly runs. This works, but matching the configure job's pattern of github.event_name == 'pull_request' || !contains(...) would make the intent clearer.

Positives:

  • Concurrency cancellation is a real improvement for fast-moving branches
  • Dynamic matrix is a sensible cost/coverage trade-off
  • Cache cleanup on PR close/branch delete is good hygiene
  • Fixing flags: swift-${{ matrix.swift-version }} to swift-${{ matrix.swift.version }} corrects a bug that was sending undefined flags to Codecov
  • Updating all action versions in one PR avoids drift

leogdion and others added 2 commits April 2, 2026 15:31
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Good overall direction — the dynamic matrix, concurrency groups, and cache cleanup are all solid improvements. A few issues worth addressing before merge:


Bugs

1. Windows matrix is likely misconfigured (.github/workflows/SyntaxKit.yml)

The build-windows matrix uses include entries that don't contain runs-on, which in GitHub Actions creates standalone jobs rather than expanding existing combinations. The result is 2 jobs from the runs-on array (without swift-version) plus 3 standalone jobs from the include entries (without runs-on), rather than the intended 2×3 matrix. To get all 6 combinations, add runs-on to each include entry or restructure as a 2D matrix:

strategy:
  fail-fast: false
  matrix:
    runs-on: [windows-2022, windows-2025]
    include:
      - runs-on: windows-2022
        swift-version: swift-6.3-release
        swift-build: 6.3-RELEASE
      # ... etc

Or use a proper swift matrix dimension alongside runs-on.

2. lint and docs if conditions will fail on PRs (.github/workflows/SyntaxKit.yml, lines ~297, ~321)

Both jobs still use:

if: ${{ !cancelled() && !failure() && !contains(github.event.head_commit.message, 'ci skip') }}

github.event.head_commit is null on pull_request events — this is the exact same bug that was fixed for the configure job (github.event_name == 'pull_request' || ...). On PRs, the contains(null, 'ci skip') expression may error or behave unexpectedly. Apply the same guard used in configure:

if: ${{ !cancelled() && !failure() && (github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip')) }}

3. Cache cleanup processes tag deletions as branches (.github/workflows/cleanup-caches.yml)

The delete event fires for both branch and tag deletions. The script unconditionally prefixes refs/heads/ to context.payload.ref, which will look for a branch cache when a tag is deleted. Add a guard:

if (context.payload.ref_type !== 'branch') {
  console.log('Not a branch deletion, skipping.');
  return;
}

Minor Issues

4. build-macos-full missing cancellation guard (.github/workflows/SyntaxKit.yml)

build-macos uses if: ${{ !cancelled() && needs.configure.result == 'success' }} but build-macos-full only uses if: needs.configure.outputs.full-matrix == 'true'. If configure is cancelled, build-macos-full will still attempt to run and fail. Use the same pattern:

if: ${{ !cancelled() && needs.configure.result == 'success' && needs.configure.outputs.full-matrix == 'true' }}

5. WASM builds use a hardcoded Swift version regardless of matrix (.github/workflows/SyntaxKit.yml)

When matrix.type == 'wasm' or 'wasm-embedded', the container is always swift:6.3-noble regardless of what Swift version is in matrix.swift. This means the ubuntu-swift matrix expansion has no effect for WASM types. If this is intentional (WASM only supported on 6.3+), consider excluding earlier Swift versions from WASM type combinations to avoid confusing ghost matrix entries.

6. CodeQL lacks paths-ignore (.github/workflows/codeql.yml)

The main CI workflow skips doc-only changes via paths-ignore, but CodeQL does not. Consider adding the same paths-ignore list to avoid running security scans on documentation commits.


Positive Notes

  • Concurrency group using github.head_ref || github.ref_name correctly handles both push and PR events sharing a group — good fix.
  • Gating build-windows, build-macos-full, and build-android behind full-matrix is a clean separation.
  • contains-code-coverage output gate for curl install and coverage upload is a nice conditional improvement.
  • swift-docc-plugin 1.4.6 upgrade for Windows symlink fix is the right call.
  • Settings.swift 3-strategy fallback for cross-platform path resolution is well-structured.

🤖 Generated with Claude Code

- 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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: CI/CD Workflow Optimization (#129)

This is a well-structured PR with meaningful improvements to CI efficiency. Below is a detailed review.


Summary

The dynamic matrix approach is excellent — feature branches now run ~4 jobs vs. 10+ on main, a 60-75% reduction in compute. The concurrency controls and cache cleanup workflows are solid additions. However, there are a few issues that should be addressed before merging.


Critical Issues

1. Undefined matrix variables in build-macos job

In SyntaxKit.yml (lines 146-150), the build-macos job references matrix.type, matrix.deviceName, matrix.osVersion, and matrix.download-platform, but the defined matrix only includes runs-on and xcode. These will resolve to empty strings, which will likely cause the swift-build action to behave unexpectedly or fail silently.

Either define these variables in the matrix or remove the references if they're unused.

2. Android path assumptions in Settings.swift

Settings.swift line 85 returns the filePath as-is for Android, but android-copy-files copies Sources/SyntaxKit/Documentation.docc to what appears to be the working directory root. If the test expects Sources/SyntaxKit/Documentation.docc/... but the file is placed at Documentation.docc/..., tests will fail on Android. Please verify the actual placement.


High Severity

3. build-android coverage flag inconsistency

The flag android-api${{ matrix.android-api-level }}-swift${{ matrix.swift.version }} uses a different naming convention than other platforms (e.g., swift-6.3,ubuntu). This can cause fragmented coverage reports in Codecov. Consider standardizing the flag naming format across all platforms.

4. Hardcoded Xcode version in codeql.yml

codeql.yml line 63 hardcodes Xcode 26.4. If this version isn't available on the runner, the workflow will fail with an unhelpful error. Consider adding a version existence check or a fallback.


Medium Severity

5. No timeout on build-android

The Android build job has no timeout-minutes set. A hung Android emulator or build could block runner resources indefinitely. Recommend adding timeout-minutes: 60.

6. Cache key scoping

The Mint cache uses hashFiles('**/Mintfile') without branch scoping. Feature branches and main share cache keys, which could result in stale or mismatched tool versions across branches. Consider adding ${{ github.ref_name }}- as a prefix.


Positive Notes

  • Concurrency config (github.head_ref || github.ref_name) is well-formed and prevents stale run waste.
  • Conditional coverage steps gated on contains-code-coverage == 'true' are clean and correct.
  • Cache cleanup workflows (cleanup-caches.yml, cleanup-pr-caches.yml) are minimal and correct — good use of github.paginate.
  • Settings.swift fallback chain (4 strategies for project root discovery) is well-commented and resilient.
  • Action version consistency — all actions updated to current versions in a single pass is good hygiene.
  • Package.resolved changes are low-risk patch bumps only.

Test Coverage Observations

  • Documentation example tests cover the cross-platform path resolution well.
  • The CI matrix logic itself has no automated validation — the configure job output (JSON matrix) isn't verified before downstream jobs consume it. A quick validation step (e.g., echo + jq parse) would catch formatting errors early.
  • The !cancelled() && !failure() conditions on lint/docs aren't tested for edge cases where optional jobs are skipped vs. failed.

Suggested Priority Before Merge

Priority Issue
Must fix Undefined matrix variables in build-macos
Must fix Verify Android file placement matches Settings.swift expectations
Should fix Add timeout-minutes to build-android
Should fix Standardize Codecov flag naming
Nice to have Cache key branch scoping
Nice to have Matrix output validation step in configure job

Overall this is a solid CI improvement. The dynamic matrix pattern is the right call for a project of this scale. Addressing the build-macos matrix variable issue and the Android path assumption before merging would make this clean.

leogdion and others added 2 commits April 2, 2026 16:20
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid CI improvement. The dynamic matrix, concurrency cancellation, and cache cleanup additions are all good engineering. Here are my observations:


Bugs / Correctness Issues

1. build-macos vs build-windows gating inconsistency

build-macos always runs when configure succeeds:

if: ${{ !cancelled() && needs.configure.result == 'success' }}

But build-windows is gated on full-matrix:

if: needs.configure.outputs.full-matrix == 'true'

If the intent is for build-macos (core, single entry) to run on all branches, this is fine — but worth a comment clarifying the intent since Windows is gated and macOS-full is gated, but the core macOS job is not.

2. Potential double-prefix for WASI docPaths

In Settings.swift, resolveFilePath prepends "Sources/SyntaxKit/" on non-Android platforms:

let resolvedPath = "Sources/SyntaxKit/" + filePath

But the WASI docPaths already includes the full prefix:

"Sources/SyntaxKit/Documentation.docc/Tutorials/Quick-Start-Guide.md"

If these paths are ever passed through resolveFilePath, they'll resolve to Sources/SyntaxKit/Sources/SyntaxKit/.... The two explicit resolveFilePath calls in DocumentationExampleTests.swift are correctly updated to stripped paths — but any code iterating over docPaths and passing them through resolveFilePath would double-prefix on WASI. Worth auditing the harness code that consumes docPaths.

3. configure job ci skip check may silently pass on PRs

if: ${{ github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip') }}

On pull_request events, github.event.head_commit is null. The !contains(null, 'ci skip') evaluates to true, so the OR makes this always run on PRs — which is the desired behavior. But the condition is a bit misleading. Consider simplifying to:

if: ${{ github.event_name == 'pull_request' || !contains(github.event.head_commit.message, 'ci skip') }}

...or adding a comment explaining why this works correctly even when head_commit is null.


Improvements & Observations

4. Coverage flag bug fix — good catch

Old: flags: swift-${{ matrix.swift-version }},ubuntu (using non-existent swift-version key)
New: flags: swift-${{ matrix.swift.version }},ubuntu

This was a silent bug where coverage flags were being uploaded as swift- (empty string suffix). Fixed correctly.

5. Hardcoded android-copy-files path

android-copy-files: Sources/SyntaxKit/Documentation.docc

This hardcodes the source path. If the package structure changes this path would silently break the Android test's file access. A comment here noting the coupling with Settings.swift's Strategy 1b would help future maintainers.

6. Nightly Swift builds removed from Ubuntu matrix

The old matrix included nightly builds (nightly: true for Swift 6.1 and 6.2). These are gone in the new config. If catching Swift nightly regressions early is a goal, consider whether a scheduled nightly run against the latest nightly should be added back, separate from the per-push matrix.

7. build-android in lint/docs dependencies

Both lint and docs now depend on build-android. Since build-android is full-matrix-only, these jobs will show as having a skipped dependency on feature branches — the !cancelled() && !failure() condition handles this correctly. This is fine.

8. actions/checkout@v6 — verify availability

The PR bumps to checkout@v6 across all workflows. Confirm this version tag exists in the actions/checkout registry before merging to avoid broken workflows.


Settings.swift Architecture

The 3-strategy fallback in projectRoot is pragmatic for cross-platform support, but complex. A few thoughts:

  • Strategy 1a and 1b are checked before Strategy 2 (source-relative). On environments where #filePath is well-defined (macOS, Linux), Strategy 2 is more reliable since it doesn't depend on the current working directory at runtime. Consider reordering: try source-relative first (Strategy 2), then fall through to working-directory heuristics.
  • The walk-up loop in Strategy 3 is a reasonable last resort — just make sure the error message on final fallback is clear about which strategies were tried.

Summary

Category Status
Bug: coverage flag fix ✅ Fixed
Bug: WASI double-prefix risk ⚠️ Needs verification
Inconsistency: macOS/Windows gating ⚠️ Clarify intent
Cache cleanup workflows ✅ Well implemented
Concurrency cancellation ✅ Correct
CodeQL integration ✅ Appropriate
Action version updates ✅ Good hygiene
Android build support ✅ Good addition

The PR is in good shape. The WASI double-prefix concern is the most actionable item to verify before merge.

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 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: Optimize CI workflows with dynamic matrix and cache cleanup

Overall this is a solid, well-structured CI overhaul. The dynamic matrix strategy, concurrency groups, and cache cleanup workflows are all good patterns. A few issues worth addressing before merge:


ISSUES

1. build-android missing coverage file processing step

build-ubuntu uses sersoft-gmbh/swift-coverage-action to locate coverage files and passes them explicitly to Codecov. build-android skips this step and uploads without a files: parameter, relying on Codecov auto-discovery. This may silently produce empty or missing coverage reports for Android.

2. paths-ignore inconsistency between triggers

The push trigger ignores .github/ISSUE_TEMPLATE/** but the pull_request trigger does not. Minor, but worth aligning by adding .github/ISSUE_TEMPLATE/** to pull_request paths-ignore as well.

3. build-windows missing !cancelled() guard

build-macos and build-macos-full both check !cancelled(), but build-windows does not. If configure is cancelled mid-run, build-windows could error rather than being skipped cleanly. Suggest adding !cancelled() && for consistency.


PRE-EXISTING ISSUE (not introduced here, but worth noting)

The build-macos and build-macos-full jobs run sersoft-gmbh/swift-coverage-action without an id:, so the output files list is never referenced in the Codecov upload step. macOS coverage is therefore auto-discovered. This predates this PR but could be cleaned up in a follow-up.


POSITIVES

  • Dynamic matrix cleanly separates feature-branch (fast/cheap) vs. main/PR (full) builds
  • Concurrency group using head_ref || ref_name correctly deduplicates push+PR runs for the same branch
  • Coverage guards (contains-code-coverage == true) prevent wasted steps on builds without coverage
  • Settings.swift 3-strategy fallback for project root is robust and well-commented
  • Cache cleanup workflows are a great addition - often overlooked
  • paths-ignore on both triggers avoids burning CI minutes on doc-only commits
  • Windows matrix fixed to a proper cross-product (was mismatched include entries before)
  • Dropping Swift 6.1 from Windows due to swift-docc-plugin 1.4.6 incompatibility is properly documented in the commit message

Generated with Claude Code (https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 2, 2026

PR Review: CI Workflow Optimization

Overall this is a solid, well-thought-out PR. The dynamic matrix, concurrency cancellation, and cache cleanup are all good additions. A few issues worth addressing:


Bugs / Correctness Issues

1. cleanup-caches.yml and cleanup-pr-caches.yml — unnecessary checkout

Both cleanup workflows include uses: actions/checkout@v6 but the GitHub Script steps don't use the repository contents. This adds ~5–10s and a network call for no benefit. The checkout can be removed from both.

2. codeql.yml — leftover template noise

- run: |
    echo "Run, Build Application using script"
    swift build

The echo is a copy-paste artifact from the CodeQL template. It's harmless but should be removed.

3. lint / docsneeds list includes jobs gated to full-matrix

needs: [build-ubuntu, build-macos, build-windows, build-macos-full, build-android]
if: ${{ !cancelled() && !failure() && ... }}

On feature branches, build-windows, build-macos-full, and build-android are all skipped. The !failure() condition correctly allows lint/docs to proceed past skipped jobs — this is fine as-is, but worth a comment to make the intent explicit for future maintainers.


Minor / Nits

4. build-macos matrix — orphaned matrix.type reference

type: ${{ matrix.type }}

The build-macos matrix has only one entry with no type key, so matrix.type evaluates to ''. This works but is easy to misread. A comment or explicit type: "" in the matrix entry would clarify intent.

5. Settings.swift Strategy 1b — fragile heuristic

// Strategy 1b: Documentation.docc copied as last component (Android flat copy)
if FileManager.default.fileExists(atPath: workingDir.appendingPathComponent("Documentation.docc").path) {
  return workingDir
}

This will silently match any directory that happens to contain Documentation.docc/ (e.g. if tests run from a project root on macOS). Strategy 1a (Sources/ check) should catch that case first, but if the order ever changes this could produce a wrong root. A comment tying this explicitly to the Android android-copy-files step would help.

6. Settings.swift#if os(Android) in resolveFilePath

The #if os(Android) conditional compilation guard is correct Swift but unusual — worth a brief comment confirming this is a recognized Swift platform condition (it is, since Swift 5.9), for the benefit of anyone who hasn't seen it before.

7. Action version pinning

All actions are pinned to major version tags (e.g. @v6) rather than commit SHAs. For a library with published users, pinning to commit SHAs is best practice for supply chain security. This is a common trade-off and acceptable here, but worth noting.


What's Good

  • The dynamic matrix design (minimal on feature branches, full on main/PRs) is clean and the configure job pattern is idiomatic.
  • The github.event_name == 'pull_request' guard in configure correctly prevents a null-deref on head_commit.message for PR events.
  • The concurrency group ${{ github.head_ref || github.ref_name }} correctly handles both push and pull_request events sharing a group.
  • Coverage upload now correctly gated on steps.build.outputs.contains-code-coverage == 'true' — no more spurious failures on WASM/Android where coverage isn't collected.
  • Cache cleanup workflows properly guard against tag deletions (ref_type !== 'branch').
  • Windows symlink fix (core.symlinks=true) is a real edge case handled correctly.

🤖 Generated with Claude Code

leogdion and others added 4 commits April 2, 2026 18:17
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove unnecessary checkout steps from cleanup workflows
- Remove template echo from codeql.yml build step
- Add comments to lint/docs jobs explaining !failure() intent
- Clarify empty matrix.type in build-macos
- Improve Settings.swift comments for Strategy 1b and os(Android)
- Update all swift-build references from branch ref to v1.5.3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@leogdion leogdion merged commit 2862d16 into v0.0.4 Apr 3, 2026
40 of 43 checks passed
@leogdion leogdion deleted the 128-workflow-updates branch April 3, 2026 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants