Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d8c8275
Add Strategy Pattern for module integration with auto-generation
vjr2005 Feb 25, 2026
c53dc69
Move generators to dedicated Generators directory
vjr2005 Feb 25, 2026
bed8ff4
Extract ExternalPackage to its own file
vjr2005 Feb 25, 2026
1b403dc
Enable mixed module strategies and centralize external packages
vjr2005 Feb 25, 2026
cf2532b
Restore ActiveStrategy typealias, fix force unwraps, and update docum…
vjr2005 Feb 25, 2026
7b2cbf4
Extract SwiftLint from ModuleKit into generic build phases extension
vjr2005 Feb 25, 2026
2d5a6d2
Move ViewState Equatable conformance to source targets
vjr2005 Feb 25, 2026
0e2939d
Add ActiveStrategy to DerivedData cache key and clear-caches workflow
vjr2005 Feb 25, 2026
50ddb74
Switch active strategy to SPMModule and unify test plan generation
vjr2005 Feb 25, 2026
58463c8
Fix flaky ImageDiskCacheTests by avoiding cross-thread mock access
vjr2005 Feb 25, 2026
b44feab
Fix test-report PR comment failing on backtick-containing output
vjr2005 Feb 26, 2026
ef937af
Fix flaky DSAsyncImage snapshot tests by handling LoadSignal race con…
vjr2005 Feb 26, 2026
3682033
Replace Module typealias with Strategy + Factory Method pattern
vjr2005 Feb 27, 2026
cdf6b52
Add JSON fallback for xcresulttool merge crash in Quality Gate
vjr2005 Feb 27, 2026
cbcf1aa
Refactor TestPlanGenerator with type-safe Encodable model
vjr2005 Feb 27, 2026
3c86f3d
Remove last comma in arrays
vjr2005 Mar 2, 2026
900932f
Switch default module strategy from SPM to Framework
vjr2005 Mar 2, 2026
18061fc
Regenerate snapshot references in English locale
vjr2005 Mar 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions .claude/skills/tuist/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,39 @@ ReadMcpResourceTool(

## Project Options

The root `Project.swift` defines only the app target and UI tests target. All modules are SPM local packages referenced via `packages:`. Auto-generated schemes handle per-package testing.
The root `Project.swift` delegates to `mainApp.project` (defined in `ModuleKit/App.swift`). The project structure adapts based on the active module strategy set in `ModuleFactory.swift`.

```swift
// Project.swift (root — app + UI tests + module packages)
let project = Project(
name: appName,
// Project.swift
let project = mainApp.project

// App.swift — generates the project with all module targets and schemes
Project(
name: name,
options: .options(
automaticSchemesOptions: .enabled(codeCoverageEnabled: true),
automaticSchemesOptions: .disabled, // Manual scheme generation
developmentRegion: "en",
disableBundleAccessors: true, // No TuistBundle+*.swift
disableSynthesizedResourceAccessors: true // No TuistAssets+*.swift
disableBundleAccessors: false,
disableSynthesizedResourceAccessors: true // No TuistAssets+*.swift
),
packages: packages, // Aggregated from modules (empty for FrameworkModule)
settings: .settings(
base: baseSettings.merging([...]) { _, new in new },
configurations: BuildConfiguration.all
),
packages: Modules.packageReferences,
targets: [appTarget, uiTestsTarget],
schemes: AppScheme.allSchemes() + [AppScheme.uiTestsScheme(), AppScheme.moduleTestsScheme()]
targets: [appTarget, uiTestsTarget] + moduleTargets, // App + UI tests + module framework targets
schemes: allSchemes + [uiTestsScheme] + moduleSchemes // Environment schemes + tests + per-module
)
```

```swift
// Workspace.swift — autogenerated workspace schemes with relevant coverage
// Workspace.swift — coverage mode adapts to active strategy
let workspace = Workspace(
name: appName,
name: mainApp.name,
projects: ["."],
generationOptions: .options(
autogeneratedWorkspaceSchemes: .enabled(
codeCoverageMode: .relevant,
codeCoverageMode: mainApp.coverageMode, // .targets() for Framework, .relevant for SPM
testingOptions: []
)
)
Expand All @@ -82,7 +89,7 @@ let workspace = Workspace(

### Module Test Scheme

The `Challenge (Dev)` scheme includes a `.xctestplan` file that aggregates all module test targets. Tests run via `xcodebuild test` (not `tuist test`) because Tuist can't resolve SPM package test targets when using `.testPlans()`.
The `Challenge (Dev)` scheme includes a `.xctestplan` file that aggregates all module test targets. `ModuleAggregation` always uses `.testPlans(...)` with an auto-generated test plan, regardless of the active strategy. This means the test command is always the same:

```bash
xcodebuild test \
Expand All @@ -92,17 +99,14 @@ xcodebuild test \
-destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=latest'
```

Tests run via `xcodebuild test` (not `tuist test`) because Tuist can't resolve SPM package test targets when using `.testPlans()`.

### Why disable synthesizers?

| Option | Effect | Reason |
|--------|--------|--------|
| `disableBundleAccessors` | No `TuistBundle+*.swift` | SPM generates `Bundle.module` for targets with resources |
| `disableSynthesizedResourceAccessors` | No `TuistAssets+*.swift` | Not using generated asset accessors |

### Bundle.module

SPM automatically generates `Bundle.module` for targets that declare resources in their `Package.swift`. No manual `Bundle+Module.swift` is needed for SPM local packages.

---

## Localization Configuration
Expand Down Expand Up @@ -182,7 +186,7 @@ Derived/
└── {AppName}UITests-Info.plist
```

**Contents:** Only `Info.plist` files for the app and UI tests targets. Module Info.plists are managed by SPM.
**Contents:** Only `Info.plist` files for the app and UI tests targets. Module Info.plists are managed by their respective strategy (Tuist for Framework, SPM for SPM packages).

**Git:** The `Derived/` folder is in `.gitignore`.

Expand Down
10 changes: 7 additions & 3 deletions .github/actions/setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ inputs:
derived-data-prefix:
description: Cache key prefix for DerivedData (e.g., unit, ui)
required: true
module-strategy:
description: Module integration strategy (spm or framework)
required: false
default: framework

runs:
using: composite
Expand Down Expand Up @@ -38,9 +42,9 @@ runs:
uses: actions/cache@v4
with:
path: derived_data
key: derived-data-${{ inputs.derived-data-prefix }}-${{ runner.os }}-${{ hashFiles('**/Project.swift', 'Tuist/Package.resolved') }}-${{ github.sha }}
key: derived-data-${{ inputs.derived-data-prefix }}-${{ runner.os }}-${{ hashFiles('**/Project.swift', 'Tuist/Package.resolved') }}-${{ inputs.module-strategy }}-${{ github.sha }}
restore-keys: |
derived-data-${{ inputs.derived-data-prefix }}-${{ runner.os }}-${{ hashFiles('**/Project.swift', 'Tuist/Package.resolved') }}-
derived-data-${{ inputs.derived-data-prefix }}-${{ runner.os }}-${{ hashFiles('**/Project.swift', 'Tuist/Package.resolved') }}-${{ inputs.module-strategy }}-
derived-data-${{ inputs.derived-data-prefix }}-${{ runner.os }}-

- name: Install SPM dependencies
Expand All @@ -49,7 +53,7 @@ runs:

- name: Generate Xcode project
shell: bash
run: mise x -- tuist generate
run: TUIST_MODULE_STRATEGY=${{ inputs.module-strategy }} mise x -- tuist generate

# Simulator Readiness Strategy
#
Expand Down
5 changes: 4 additions & 1 deletion .github/actions/test-report/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,15 @@ runs:

- name: Comment PR with test summary
if: github.event_name == 'pull_request' && steps.test-summary.outputs.has_info == 'true'
continue-on-error: true
uses: actions/github-script@v7
env:
COMMENT_BODY: ${{ steps.test-summary.outputs.body }}
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `${{ steps.test-summary.outputs.body }}`
body: process.env.COMMENT_BODY
});
63 changes: 63 additions & 0 deletions .github/workflows/clear-caches.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Clear Caches

on:
workflow_dispatch:
inputs:
scope:
description: "Which caches to clear"
required: true
type: choice
options:
- all
- derived-data
- tuist
- mise

jobs:
clear:
name: Clear caches
runs-on: ubuntu-latest
steps:
- name: Clear caches
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
SCOPE: ${{ inputs.scope }}
run: |
echo "Fetching cache list..."
CACHES=$(gh cache list --json id,key --limit 100)

filter_and_delete() {
local prefix="$1"
local ids
ids=$(echo "$CACHES" | jq -r --arg p "$prefix" '.[] | select(.key | startswith($p)) | .id')
local count
count=$(echo "$ids" | grep -c . || true)
if [ "$count" -eq 0 ]; then
echo "No caches found with prefix '$prefix'"
return
fi
echo "Deleting $count caches with prefix '$prefix'..."
echo "$ids" | while read -r id; do
gh cache delete "$id" && echo " Deleted $id" || echo " Failed to delete $id"
done
}

case "$SCOPE" in
all)
filter_and_delete "derived-data-"
filter_and_delete "tuist-"
filter_and_delete "mise-"
;;
derived-data)
filter_and_delete "derived-data-"
;;
tuist)
filter_and_delete "tuist-"
;;
mise)
filter_and_delete "mise-"
;;
esac

echo "Done."
Loading
Loading