Skip to content

Add Strategy Pattern for module integration#8

Merged
vjr2005 merged 18 commits into
mainfrom
features/module-strategy
Mar 2, 2026
Merged

Add Strategy Pattern for module integration#8
vjr2005 merged 18 commits into
mainfrom
features/module-strategy

Conversation

@vjr2005

@vjr2005 vjr2005 commented Feb 25, 2026

Copy link
Copy Markdown
Owner

Summary

  • Strategy Pattern for modules: ModuleContract protocol with two implementations — FrameworkModule (framework targets) and SPMModule (SPM local packages with auto-generated Package.swift). A global typealias Module in ActiveStrategy.swift selects the active strategy for all modules. Currently set to SPMModule.
  • Strategy-agnostic test command: ModuleAggregation always uses .testPlans(...) with an auto-generated .xctestplan, regardless of the active strategy. CI never needs to change when switching strategies.
  • ModuleKit infrastructure: ModuleFileSystem (auto-detects folder structure), ModuleDependency (dependency specification), ModuleAggregation (unified test plan generation), ExternalPackage (external SPM wrapper).
  • Auto-generation system: PackageSwiftGenerator and TestPlanGenerator for SPM modules, executed at manifest-time during tuist generate.
  • SwiftLint extraction: Moved from ModuleKit to a generic TargetScript+BuildPhases.swift extension — single point of change for all source target build phases.
  • ViewState Equatable: Moved conformances from test extensions to source enums for cross-strategy compatibility.
  • CI improvements: DerivedData cache key includes ActiveStrategy.swift to auto-invalidate on strategy change. Added clear-caches workflow for manual cache cleanup. Test command now uses -testPlan Challenge.

Test plan

  • tuist generate succeeds with SPMModule strategy
  • All module tests pass (xcodebuild test -testPlan Challenge)
  • Test command works both with and without explicit -testPlan flag
  • CI passes after cache invalidation

🤖 Generated with Claude Code

vjr2005 and others added 8 commits February 25, 2026 12:54
Introduce a polymorphic module system that supports switching between
SPM local packages and framework targets via a single typealias.

- Add ModuleContract protocol with FrameworkModule and SPMModule implementations
- Extract ProjectConfig struct replacing scattered globals in Config.swift
- Auto-generate Package.swift files per module (PackageSwiftGenerator)
- Auto-generate .xctestplan from module data (TestPlanGenerator)
- Enable Tuist bundle accessors (disableBundleAccessors: false)
- Enrich ModuleDependency.external with ExternalPackage (URL/version)
- Delete 10 manual Package.swift, manual xctestplan, and Bundle+Module files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow per-module strategy selection (SPM or Framework) instead of a
global typealias. Restructure helpers into ModuleKit/ for reusability,
centralize external package definitions in ExternalPackages.swift, and
derive Package.swift productTypes/dependencies from ExternalPackage
instances. Add SPM→Framework dependency validation and mixed test
aggregation support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…entation

Mixed SPM+Framework modules are not viable with Tuist 4.x, so the global
typealias Module approach is restored to enforce a single strategy at the
type level. Force unwraps in generators are replaced with do/catch for
better error messages. All documentation is updated to reflect the current
Strategy Pattern architecture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move SwiftLint script out of ModuleKit (app-specific, not reusable infra)
into a TargetScript extension with a sourceTargetScripts() function that
both App and FrameworkModule use. Adding a new build phase now requires
changing a single function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Declaring Equatable on imported types in test extensions causes compiler
warnings with the FrameworkModule strategy. Moving the conformance to the
enum declaration uses synthesized Equatable, which is compatible with both
Framework and SPM strategies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Include ActiveStrategy.swift in the DerivedData cache hash so switching
module strategy (Framework ↔ SPM) automatically invalidates stale build
artifacts. Add a manual workflow_dispatch to clear CI caches by scope.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

@github-actions

Copy link
Copy Markdown

Code Coverage

Module Lines Coverage
Challenge 9 / 12 75.0%
ChallengeAppKit 190 / 215 88.4%
ChallengeCharacter 1816 / 1939 93.7%
ChallengeCore 409 / 411 99.5%
ChallengeDesignSystem 1040 / 1048 99.2%
ChallengeEpisode 601 / 615 97.7%
ChallengeHome 514 / 526 97.7%
ChallengeNetworking 168 / 169 99.4%
ChallengeSystem 76 / 79 96.2%
Total 4823 / 5014 96.2%

Minimum required: 80%

📦 Download xcresult

ModuleAggregation now always uses .testPlans(...) with an auto-generated
test plan regardless of the active strategy. This makes the xcodebuild
test command strategy-agnostic — CI never needs to change when switching
between FrameworkModule and SPMModule.

Key fixes:
- Add required configurations section to TestPlanGenerator output
  (xcodebuild silently rejects test plans without it)
- Use Path(stringLiteral:) for dynamic string-to-Path conversion
- Always return .relevant for coverage mode (driven by test plan)
- Update all documentation to reflect SPMModule as current strategy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

@github-actions

Copy link
Copy Markdown

Code Coverage

Module Lines Coverage
Challenge 9 / 12 75.0%
ChallengeAppKit 190 / 215 88.4%
Total 199 / 227 87.7%

Minimum required: 80%

📦 Download xcresult

Replace direct FileSystemMock property reads with actor-mediated
assertions to eliminate potential data races on @unchecked Sendable mock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

The JS template literal broke when the body contained Markdown backticks
(e.g. `TestName`), causing SyntaxError. Pass body via process.env instead,
matching the pattern used by Periphery and coverage comment steps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Unit & Snapshot Test Results

Tests failed.

Test
ImageDiskCacheTests/Skips eviction when directory listing fails

vjr2005 and others added 2 commits February 26, 2026 11:32
…dition

The actor's await suspension point in withCheckedContinuation allowed
complete() to fire before the continuation was stored, causing tests
to hang indefinitely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the static `typealias Module = SPMModule` with a runtime-resolved
`ModuleStrategy` enum (reads `TUIST_MODULE_STRATEGY` env var) and
`ModuleFactory.create(...)` factory method. This allows switching between
SPM and Framework strategies at generation time without editing source.

- Add `ModuleStrategy.swift` with env var resolution (default: spm)
- Add `ModuleFactory` caseless enum as single creation entry point
- Update all module declarations to use `ModuleFactory.create(...)`
- Update `App` to use `any ModuleContract` instead of concrete `Module`
- Fix `TestPlanGenerator` coverage format (`codeCoverage.targets`)
- Fix `FrameworkModule.containerPath` to include xcodeproj name
- Update `generate.sh` with `--strategy` flag and argument parsing
- Update CI workflows to pass module strategy through setup action
- Update documentation (Tuist.md, Scripts.md, tuist skill)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

@github-actions

Copy link
Copy Markdown

UI Test Results

⚠️ 1 test(s) required retry:

Test Attempts Result
CharacterEpisodesUITests/testCharacterEpisodesErrorRetryRefreshCharacterDetailAndBack() 2 Passed

These tests failed initially and were retried via -retry-tests-on-failure.

xcresulttool merge crashes on CI with "unexpected divergence in object
put" (Apple CAS database bug). Keep the merge as primary path for a
single xcresult artifact, but fall back to independent coverage
extraction when it fails:

- Try xcresulttool merge first, output merge_ok flag
- On success: upload merged xcresult, extract coverage from it
- On failure: extract coverage JSON from each xcresult independently,
  merge at file level (max coveredLines per file), upload both xcresults
  as separate artifacts with individual download links

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

@github-actions

Copy link
Copy Markdown

Code Coverage

Module Lines Coverage
Challenge 9 / 12 75.0%
ChallengeAppKit 190 / 215 88.4%
Total 199 / 227 87.7%

Minimum required: 80%

📦 Download xcresult

vjr2005 and others added 4 commits February 27, 2026 12:33
Replace untyped [String: Any] dictionaries and JSONSerialization with
Encodable structs (TestPlan, TestPlanConfiguration, TestPlanCoverageTarget,
etc.) and JSONEncoder with prettyPrinted + sortedKeys. Configuration ID
is now auto-generated via UUID.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Framework strategy provides per-module schemes, code coverage in
workspace, and individual module compilation — capabilities not
available with SPM local packages due to Xcode limitations.

Also fixes the app target containerPath in TestPlanGenerator which
was empty ("container:") for SPM, causing a "missing" target in the
test plan coverage configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Previous references were captured with the simulator set to Spanish,
causing localized text mismatches on CI (English-only).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vjr2005 vjr2005 closed this Mar 2, 2026
@vjr2005 vjr2005 reopened this Mar 2, 2026
@github-actions

github-actions Bot commented Mar 2, 2026

Copy link
Copy Markdown

UI Test Results

Tests failed.

Test
System Failures/ChallengeUITests-Runner (13519) encountered an error

@github-actions

github-actions Bot commented Mar 2, 2026

Copy link
Copy Markdown

Periphery — Dead code detection

✅ No unused code detected.

@github-actions

github-actions Bot commented Mar 2, 2026

Copy link
Copy Markdown

Code Coverage

Module Lines Coverage
Challenge 9 / 12 75.0%
ChallengeAppKit 190 / 215 88.4%
ChallengeCharacter 1816 / 1939 93.7%
ChallengeCore 409 / 411 99.5%
ChallengeDesignSystem 1040 / 1048 99.2%
ChallengeEpisode 601 / 615 97.7%
ChallengeHome 514 / 526 97.7%
ChallengeNetworking 168 / 169 99.4%
ChallengeSystem 76 / 79 96.2%
Total 4823 / 5014 96.2%

Minimum required: 80%

📦 Download xcresult

@vjr2005 vjr2005 merged commit e7eb9d1 into main Mar 2, 2026
4 of 9 checks passed
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.

1 participant