Skip to content

refactor(Scripts): migrate risky developer tooling to Swift - #234

Open
kyleve wants to merge 15 commits into
mainfrom
codex/swift-tooling-migration
Open

refactor(Scripts): migrate risky developer tooling to Swift#234
kyleve wants to merge 15 commits into
mainfrom
codex/swift-tooling-migration

Conversation

@kyleve

@kyleve kyleve commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Posted by an AI agent on kve's behalf.

Summary

  • Add an independent macOS Tools package with a thin stuff executable and a directly tested StuffToolCore library.
  • Keep the public ./xcstrings, ./simulator, ./test, ./profile, ./flaky, ./icons, ./Where/install, and ./Ledger/install paths as compatibility shims while moving their substantive orchestration to Swift.
  • Share typed process, Tuist graph, xcresult, simulator, device-selection, filesystem transaction, clock, and terminal infrastructure across commands without merging their distinct policies.
  • Keep healthy cross-platform Python and Ruby implementations, extract the last embedded Python from shell, and add direct fixture coverage for TLA manifests, attribution, agent synchronization, region generation, and backup upgrades.
  • Pin ShellCheck 0.11.0, run Swift and retained-language tool tests in CI, update attribution and repository guidance, and close the completed Scripts backlog item.
  • Merge the current main CI topology: iOS and the complete serial snapshot suite run on CircleCI M4 Pro workers, and the retired snapshot-shard map, balancer, report generation, and CLI flags are removed from both legacy and Swift tooling.

Why

Roughly 2,300 lines of shell and embedded Python controlled the highest-risk Xcode, result-bundle, simulator, and deletion workflows with little direct coverage. A bounded SwiftPM migration gives those macOS-only workflows typed inputs and hermetic orchestration tests without forcing language purity onto structured, Linux-capable tools that were already healthy.

Changes

Swift tooling package

  • Swift Argument Parser preserves the supported flags, defaults, public help paths, and per-shim usage exit statuses, including Ledger's distinct status 2.
  • Swift Subprocess sits behind CommandRunning; tests cover streaming volume, stream ordering, non-UTF-8 bytes, signals, cancellation, handler failure, environment, working directory, stdin, and non-zero status.
  • Foundation-backed filesystem, JSON, clock, terminal, lock, and rollback seams keep command behavior testable without Xcode, devices, or /Applications.

Xcode and simulator orchestration

  • Simulator ownership, locking, boot resolution, stale-lock recovery, and exact-target deletion are shared by test, profile, and flaky.
  • test derives affected bundles from Tuist JSON plus swift package dump-package, runs snapshot scope as the complete unfiltered StuffSnapshotTests scheme, preserves progress reporting, and treats a successful zero-test process as failure.
  • Snapshot runs fail before generation when Git LFS is unavailable or references are still pointers, forward SNAPSHOT_SETTLE_TIMEOUT_MULTIPLIER, and keep cached test/image progress counters consistently labeled.
  • profile and flaky retain their separate semantics over shared xcresult and process infrastructure; profile keeps capture-phase timings but no longer writes shard-balancer input.

Mutating workflows

  • Icon updates calculate and validate every output before a rollback-safe multi-file replacement.
  • Where installation uses typed devicectl decoding and exact physical-device selection, surfaces signing-team lookup failures, and treats confirmation EOF as cancellation before installation.
  • Ledger installation validates the destination and staged app, terminates only the exact installed executable with bounded escalation, and restores the previous app on replacement failure.
  • Scoped --dry-run modes perform no mutation for icons, simulator deletion, and both installers.

Retained-language hardening

  • tla-check is now bootstrap/discovery shell around importable, Java-free-tested Tools/tla_check.py behavior.
  • Attribution and region generators plus sync-agents are require-safe and tested against temporary repositories; the sync test also guards one-pass .gitignore mirroring.
  • The RegionKit generator reproduces all 54 committed outputs byte-for-byte.

Design decisions

  • The Tools package is macOS-only and independent from the application package, so developer commands do not resolve the app graph and no compiled binaries are committed.
  • Large #!/usr/bin/swift scripts and SwiftPM command plugins remain rejected: the former preserve cold-start and testability costs, while the latter do not fit DerivedData, simulator, device, network, and /Applications access.
  • Shell remains for bootstrapping and environments below the Swift boundary; existing structured Ruby/Python tools remain in place when portability and direct tests are more valuable than a rewrite.
  • Swift Subprocess is isolated behind a repository protocol because its pre-1.0 API can change without spreading through command code.

Review focus

  • Process cancellation and stream-draining behavior in CommandRunner.
  • Tuist/package closure, full snapshot-scheme planning, and zero-test policy in RepositoryGraph, TestRunPlan, and TestService.
  • Snapshot Git LFS preflight, progress-counter selection, and test-runner environment forwarding.
  • Simulator ownership/deletion invariants and exact installed-process matching.
  • Rollback behavior for icons and Ledger, plus the guarantee that every dry run is non-mutating.
  • Public CLI compatibility and the intentional boundary between migrated Swift and retained cross-platform tools.

Compatibility

Public root command paths, help follow-ups, usage exit policies, supported defaults, and non-sharding flags remain stable; --dry-run is additive. The retired --snapshot-shard and --timing-report options disappear together with main's shard map and balancer. Linux setup remains independent of the macOS Tools package for its supported formatting, shell lint, and agent-sync workflows.

Backlog reconciliation

The bounded Swift tooling migration moved from the open P2 list to Completed issues after all migration phases, embedded-Python extraction, and retained-tool regression coverage passed.

Testing

  • swift test --package-path Tools — 118 tests in 42 suites passed after merging main.
  • python3 -m unittest discover -s Tools/Tests -p 'test_*.py' — 6 retained Python tests passed after shard-tool removal.
  • Ruby retained-tool loader — 8 tests / 49 assertions passed.
  • ruby Where/Tools/Tests/upgrade_backup_test.rb — 7 tests / 17 assertions passed.
  • ./test StuffCoreTests — backup regression plus 1 selected iOS test passed through the public shim after merging main.
  • ./test --help, invalid ./test usage (status 1), and invalid ./Ledger/install usage (status 2) matched their public contracts.
  • ./profile --skip-clean --no-snapshots — live profile run passed.
  • ./flaky --suite-runs 1 --iterations 2 --no-update — 1,964 tests completed with no suspects.
  • ./icons --list, add dry run, and remove dry run passed.
  • ./Where/install --dry-run --cloudkit --device "Probe Phone" passed without signing or device access.
  • ./Ledger/install --dry-run --no-open passed without build, process, filesystem, or launch work.
  • ruby Where/RegionKit/Tools/generate-regions.rb reproduced all committed resources without a diff.
  • git lfs ls-files --json reported zero unresolved snapshot PNG references.
  • ./swiftformat --lint
  • ./shellcheck
  • ./xcstrings --lint — 7 catalogs matched Xcode serialization.
  • ./attribution --check — 13 credits up to date.
  • ./sync-agents

kyleve added 15 commits August 9, 2026 22:50
Add the independent macOS Tools package with typed process infrastructure and a tested xcstrings port. Pin ArgumentParser, Subprocess, and ShellCheck; wire package and shell tests into CI; and credit the new development dependencies.
Replace the 437-line shell implementation with the Stuff tools command. Decode simctl JSON into typed models, preserve the legacy registry, serialize checkout creation with bounded stale-lock recovery, and restrict deletion to exact owned targets. Add dry runs for delete and recreate plus hermetic coverage for ambiguity, interruption, pruning, and ownership.
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