Split the project into Lanework and Lanework Pro targets
Two app targets from one source tree — no build flags, no #if in shared code: an edition difference is a file one target compiles and the other does not. Base keeps everything it had (dev.rzen.indie.Kanban, minimal entitlements, AppIcon); KanbanPro compiles the same sources plus the reserved KanbanPro/ root (Git/, Remote/, Auth/ land with pro-m1 — libgit2 deliberately not added yet), adds network-client and its keychain group, and hand-writes its Info.plist with the UTI block verbatim — base exports the type, Pro imports it, one format either app opens. The unit-test sources compile twice, once per host, with Pro's module aliased so 56 test files keep @testable import Kanban unchanged; scheme Kanban stays the muscle-memory command and LaneworkPro joins it. InertGitTests pins the base posture with bytes and mtimes — a full editing session over boards carrying realistic .git trees at root and nested in a card leaves all twelve entries untouched, and moves and copies carry them verbatim. scripts/verify-editions.sh proves the rest: 26 checks over signatures, symbols, entitlements, identity, and the shared UTI, discounting Xcode's test-host exceptions by name rather than silently. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
+108
-3
@@ -25,17 +25,29 @@ settings:
|
||||
DEVELOPMENT_TEAM: ${APPLE_TEAM_ID}
|
||||
ENABLE_USER_SCRIPT_SANDBOXING: NO
|
||||
|
||||
# The edition split (12-editions.md ▸ Targets). Two app targets compiled from one source tree:
|
||||
# base builds `Kanban/` alone, Pro builds `Kanban/` *plus* `KanbanPro/`. There is no build flag
|
||||
# and no `#if` in shared code — "why a real split and not feature flags" (12) is answered by the
|
||||
# file list, so an edition difference is always a file one target compiles and the other does not.
|
||||
# libgit2 arrives on the Pro target with pro-m1; the target, its entitlements and its source root
|
||||
# exist now so that milestone lands without re-splitting anything.
|
||||
#
|
||||
# YAML anchors (`&name` / `*name`) carry the genuinely identical parts across the two apps and the
|
||||
# two unit-test bundles. They are resolved by the YAML parser before XcodeGen sees the file, so
|
||||
# they cost nothing at generate time and cannot drift the way copy-paste does.
|
||||
targets:
|
||||
# MARK: - Lanework (base)
|
||||
|
||||
Kanban:
|
||||
type: application
|
||||
platform: macOS
|
||||
sources:
|
||||
- Kanban
|
||||
dependencies:
|
||||
dependencies: &appDependencies
|
||||
- package: Yams
|
||||
- package: swift-markdown
|
||||
product: Markdown
|
||||
postBuildScripts:
|
||||
postBuildScripts: &updateBuildInfo
|
||||
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
||||
name: Update Build Info
|
||||
shell: /bin/sh
|
||||
@@ -45,6 +57,8 @@ targets:
|
||||
- $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
|
||||
settings:
|
||||
base:
|
||||
# Base keeps the current identifier — it is the app that ships first, so nothing
|
||||
# re-wires (12 ▸ Targets, ruled 2026-07-27).
|
||||
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Kanban
|
||||
MARKETING_VERSION: "2.0"
|
||||
INFOPLIST_FILE: Kanban/Info.plist
|
||||
@@ -53,10 +67,57 @@ targets:
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
||||
|
||||
# MARK: - Lanework Pro
|
||||
|
||||
KanbanPro:
|
||||
type: application
|
||||
platform: macOS
|
||||
sources:
|
||||
# The shared tree, minus the three files that are base's *identity* rather than its code.
|
||||
# (XcodeGen already keeps `Info.plist` and `.entitlements` out of build phases; excluding
|
||||
# them here keeps them out of the Pro target's file list too, so there is exactly one
|
||||
# plist and one entitlements file visible per app.)
|
||||
- path: Kanban
|
||||
excludes:
|
||||
- Info.plist
|
||||
- Kanban.entitlements
|
||||
- Assets.xcassets
|
||||
- Assets.xcassets/**
|
||||
# Pro's own source root — reserved now, filled by pro-m1. See KanbanPro/Edition/ProEdition.swift.
|
||||
- path: KanbanPro
|
||||
excludes:
|
||||
- Info.plist
|
||||
- KanbanPro.entitlements
|
||||
dependencies: *appDependencies
|
||||
postBuildScripts: *updateBuildInfo
|
||||
settings:
|
||||
base:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanPro
|
||||
MARKETING_VERSION: "2.0"
|
||||
INFOPLIST_FILE: KanbanPro/Info.plist
|
||||
CODE_SIGN_ENTITLEMENTS: KanbanPro/KanbanPro.entitlements
|
||||
GENERATE_INFOPLIST_FILE: false
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME: AppIconPro
|
||||
# Spelled out because the whole shared-test-sources arrangement below hangs on it: the two
|
||||
# apps must have *different* Swift module names, since both emit `<module>.swiftmodule`
|
||||
# into the same `Build/Products/<config>/` and equal names would have one app silently
|
||||
# overwrite the other's testable interface.
|
||||
PRODUCT_MODULE_NAME: KanbanPro
|
||||
|
||||
# MARK: - Unit tests
|
||||
#
|
||||
# One suite, run twice — once hosted by each app. Every test in `KanbanTests/` is
|
||||
# edition-agnostic (it exercises the storage format, the live store and the UI model, none of
|
||||
# which differ between editions), so duplicating the *sources* would be duplicating the thing
|
||||
# that is identical; what is worth running twice is the suite against each *binary*, which is
|
||||
# what these two targets do. The Pro bundle compiles the very same files — it is the same
|
||||
# `sources` list, by anchor — against `KanbanPro`.
|
||||
|
||||
KanbanTests:
|
||||
type: bundle.unit-test
|
||||
platform: macOS
|
||||
sources:
|
||||
sources: &unitTestSources
|
||||
- KanbanTests
|
||||
- path: Fixtures
|
||||
type: folder
|
||||
@@ -69,6 +130,30 @@ targets:
|
||||
GENERATE_INFOPLIST_FILE: true
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
|
||||
KanbanProTests:
|
||||
type: bundle.unit-test
|
||||
platform: macOS
|
||||
sources: *unitTestSources
|
||||
dependencies:
|
||||
- target: KanbanPro
|
||||
settings:
|
||||
base:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanProTests
|
||||
GENERATE_INFOPLIST_FILE: true
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
# The shared sources say `@testable import Kanban`, and they should: they are testing the
|
||||
# app, not an edition. `-module-alias` (Swift 5.7+) resolves that import to the Pro
|
||||
# module for this bundle only — the supported way to bind one source name to a different
|
||||
# module, and it keeps the 56 test files edition-blind instead of littering them with
|
||||
# conditional imports.
|
||||
OTHER_SWIFT_FLAGS: -module-alias Kanban=KanbanPro
|
||||
|
||||
# MARK: - UI tests
|
||||
#
|
||||
# Base-only, deliberately: the single test here is `testAppLaunches`, and a second copy would
|
||||
# double the slowest, most environment-dependent part of the suite to re-assert something the
|
||||
# Pro unit bundle already proves (it launches the Pro app as its test host on every run).
|
||||
|
||||
KanbanUITests:
|
||||
type: bundle.ui-testing
|
||||
platform: macOS
|
||||
@@ -83,6 +168,8 @@ targets:
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
|
||||
schemes:
|
||||
# `Kanban` is unchanged, on purpose: `xcodebuild … -scheme Kanban` is the established command
|
||||
# for this repo and base is the app it has always meant.
|
||||
Kanban:
|
||||
build:
|
||||
targets:
|
||||
@@ -101,3 +188,21 @@ schemes:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
|
||||
LaneworkPro:
|
||||
build:
|
||||
targets:
|
||||
KanbanPro: all
|
||||
run:
|
||||
config: Debug
|
||||
test:
|
||||
config: Debug
|
||||
gatherCoverageData: false
|
||||
targets:
|
||||
- KanbanProTests
|
||||
profile:
|
||||
config: Release
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
|
||||
Reference in New Issue
Block a user