The golden-path XCUITest suite, adapted to current rulings where the card body had gone stale: trash flows follow the materialized-trash grammar (no Put Back, restore is drag or cut/paste out), git flows are pro-m1 scope and skipped, and fixtures extend m11's in-container --ui-test-fixture-board mechanism (the sandbox forbids the card's --open-board path handoff) with exact-match variant flags: standard (the three-lane audit board), large (8 lanes x 40 cards for masonry/reflow), malformed (BoardWriter-built board with one card's index.md overwritten to unterminated YAML, opened through the ORDINARY path so the failure is the loader's own). EndToEndFlowTests: create card/lane, inline rename, coordinate drag across lanes, cut/paste, undo/redo of a move, delete-to-trash / show-trash / restore-by-cut-paste / Empty Trash confirm - all asserting on lane accessibility labels. FailFastLaunchTests: welcome appears, no board window ever, a welcome row carries the loader's sentence naming the file; byte-fidelity of the malformed board pinned unconditionally in KanbanTests plus an identically-refused relaunch. Performance: launch metric plus explicit wall-clock gates (30s launch / 5s Show Trash on 320 cards) since XCTest baselines don't travel. Powerbox panels (template save panel, Duplicate fallback, Open) are documented as manual in EndToEndVerification.md, not faked. The smoke test now launches on the standard fixture (it launched bare before, opening the developer's real boards); README's everyday test command scopes to -only-testing:KanbanTests. Suite compiles on both schemes (build-for-testing verified); flows await a real display + automation permission to execute - run instructions in KanbanUITests/EndToEndVerification.md. +8 unit tests; 1669 green both schemes. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
260 lines
10 KiB
YAML
260 lines
10 KiB
YAML
name: Kanban
|
|
options:
|
|
bundleIdPrefix: dev.rzen.indie
|
|
deploymentTarget:
|
|
macOS: "26.0"
|
|
xcodeVersion: "26.0"
|
|
defaultConfig: Debug
|
|
|
|
packages:
|
|
Yams:
|
|
url: https://github.com/jpsim/Yams.git
|
|
from: 6.0.0
|
|
# apple/swift-markdown — the card window's Preview parser (05-card-window.md ▸ Preview).
|
|
# Pinned to the next *minor* rather than the next major because the package is pre-1.0: a
|
|
# `from:` range here would silently accept 0.9's breaking changes, which is not what `from:`
|
|
# means for Yams at 6.x.
|
|
swift-markdown:
|
|
url: https://github.com/apple/swift-markdown.git
|
|
minorVersion: 0.8.0
|
|
# The About window (both editions): app icon, version/build from the stamped Info.plist, and
|
|
# the bundled LICENSE/CHANGELOG documents. Pre-1.0 like swift-markdown, same pinning logic.
|
|
IndieAbout:
|
|
url: https://git.rzen.dev/rzen/indie-about.git
|
|
minorVersion: 0.2.2
|
|
|
|
settings:
|
|
base:
|
|
SWIFT_VERSION: "6.0"
|
|
MACOSX_DEPLOYMENT_TARGET: "26.0"
|
|
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
|
|
# The bundled template store (09-templates.md ▸ Definition format): real board folders,
|
|
# copied into `Contents/Resources/Templates/` verbatim as a folder reference — the same
|
|
# arrangement `Fixtures/` uses below, and for the same reason. A template is a board, so it
|
|
# must reach the bundle as directories on disk, not as flattened resource files.
|
|
- path: Templates
|
|
type: folder
|
|
buildPhase: resources
|
|
# The About window's documents (LICENSE, CHANGELOG) — bundled so IndieAbout can render
|
|
# them in-app. Apple-inline-Markdown subset only: no `#` headings, no hard-wrapped
|
|
# paragraphs (every newline renders literally).
|
|
- path: LICENSE.md
|
|
type: file
|
|
buildPhase: resources
|
|
- path: CHANGELOG.md
|
|
type: file
|
|
buildPhase: resources
|
|
dependencies: &appDependencies
|
|
- package: Yams
|
|
- package: swift-markdown
|
|
product: Markdown
|
|
- package: IndieAbout
|
|
postBuildScripts: &updateBuildInfo
|
|
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
|
name: Update Build Info
|
|
shell: /bin/sh
|
|
basedOnDependencyAnalysis: false
|
|
inputFiles:
|
|
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
|
|
- $(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
|
|
CODE_SIGN_ENTITLEMENTS: Kanban/Kanban.entitlements
|
|
GENERATE_INFOPLIST_FILE: false
|
|
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/**
|
|
# Base's About-box configuration carries the one-line Pro signpost (12-editions.md ▸
|
|
# Quiet signposts); Pro compiles its own `KanbanPro/Edition/EditionAbout.swift` twin
|
|
# without it — the edition split's file-level doctrine, applied to a string.
|
|
- App/EditionAbout.swift
|
|
# Pro's own source root — reserved now, filled by pro-m1. See KanbanPro/Edition/ProEdition.swift.
|
|
- path: KanbanPro
|
|
excludes:
|
|
- Info.plist
|
|
- KanbanPro.entitlements
|
|
# The same bundled template store — the inventory is the app's, not an edition's.
|
|
- path: Templates
|
|
type: folder
|
|
buildPhase: resources
|
|
# The About documents, same files as base — one license, one changelog, two bundles.
|
|
- path: LICENSE.md
|
|
type: file
|
|
buildPhase: resources
|
|
- path: CHANGELOG.md
|
|
type: file
|
|
buildPhase: resources
|
|
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: &unitTestSources
|
|
- KanbanTests
|
|
- path: Fixtures
|
|
type: folder
|
|
buildPhase: resources
|
|
dependencies:
|
|
- target: Kanban
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanTests
|
|
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: these tests launch the real app and drive its menu bar, which is the
|
|
# slowest, most environment-dependent part of the suite, and a second copy would double it to
|
|
# re-assert something the Pro unit bundle already proves (it launches the Pro app as its test host
|
|
# on every run). The accessibility audits (10-accessibility.md ▸ Verification) are edition-blind —
|
|
# every surface they walk is built from the shared tree.
|
|
|
|
KanbanUITests:
|
|
type: bundle.ui-testing
|
|
platform: macOS
|
|
sources:
|
|
# The verification documents live with the tests they belong to (10-accessibility.md
|
|
# ▸ Verification: "A manual VoiceOver smoke script lives with the test plan"), so they are
|
|
# listed in the project — visible where the suite is — but with no build phase: they are
|
|
# documentation, not resources the bundle should carry. `EndToEndVerification.md` is the same
|
|
# arrangement for the golden-flow, fail-fast and performance suites: the run command, the
|
|
# prerequisites, and the flows that stayed manual.
|
|
- path: KanbanUITests
|
|
excludes:
|
|
- "**/*.md"
|
|
- path: KanbanUITests/AccessibilityVerification.md
|
|
buildPhase: none
|
|
- path: KanbanUITests/EndToEndVerification.md
|
|
buildPhase: none
|
|
dependencies:
|
|
- target: Kanban
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanUITests
|
|
GENERATE_INFOPLIST_FILE: true
|
|
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:
|
|
Kanban: all
|
|
run:
|
|
config: Debug
|
|
test:
|
|
config: Debug
|
|
gatherCoverageData: false
|
|
targets:
|
|
- KanbanTests
|
|
- KanbanUITests
|
|
profile:
|
|
config: Release
|
|
analyze:
|
|
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
|