Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,32 @@ xcodebuild -project Palace.xcodeproj -scheme Palace \
xcodebuild -project Palace.xcodeproj -scheme Palace \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' test

# Run a single test class
# Run a single test class — SPOT CHECK ONLY, never "validation" (see rule below)
xcodebuild -project Palace.xcodeproj -scheme Palace \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro' \
-only-testing:PalaceTests/MyTestClass test
```

**Local validation MUST run the same full suite CI runs — never a `-only-testing`
subset.** CI executes the whole `Palace` scheme across ALL test targets
(`PalaceTests` + `TenPrintCoverTests`) with `-test-iterations 3
-retry-tests-on-failure` via `scripts/xcode-test-optimized.sh` (~7k executions).
Before claiming a change is verified / green:
- Run `scripts/xcode-test-optimized.sh` (CI parity) **or** `scripts/verify-pr.sh
--quick` (full-scheme single pass). A `-only-testing:<Class>` run is a scoped
spot-check for fast iteration/mutation/debugging — it is NEVER "the suite" and
must never be reported as a full or green pass.
- Confirm the run ended `** TEST SUCCEEDED **` with **no** `exceeded execution
time allowance` or `Restarting after … test timeout` lines. A timeout/restart
is a FAILURE even if the final assertion tally reads "0 failures."
- Read the top-level `Test Suite 'All tests'/'Selected tests'` rollup for the
count; never sum per-suite `Executed N` lines (they double/triple-count).

Incident (PP-4542, 2026-06-09): a `-only-testing:PalaceTests` run was reported as
"full local suite 2359 / 0 failures, PRs verifiably correct." It was one bundle
(CI runs 7121) AND had actually hung + `** TEST FAILED **`. A subset run, itself
failed, cited as whole-suite green. Don't repeat it.

- Xcode 26, iOS 16.0+ deployment target (CI release path: `macos-26` + `xcode-version: '26'`)
- Two targets: `Palace` (full DRM) and `Palace-noDRM` (open-source)
- DRM builds run natively on Apple Silicon — Rosetta is no longer required
Expand Down
8 changes: 0 additions & 8 deletions Palace.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,6 @@
5CDD263A7FFB392F96FA4F03 /* LegacySAMLAuthAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F432B33DEBD1715E2687C7 /* LegacySAMLAuthAdapter.swift */; };
5CEBAAD3ED3AF6889F45F38C /* UserAccountPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3332DED915E45913181C82D /* UserAccountPublisherTests.swift */; };
5CEE9677D8514088B0155974 /* UserRetryTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978A163EF2834D9087A9CDEB /* UserRetryTrackerTests.swift */; };
5D0130219AE7A8767A2B5C3B /* TriageBotCore in Frameworks */ = {isa = PBXBuildFile; productRef = B6BB8DA6C2692FC1F126876A /* TriageBotCore */; };
5D05015D55C6DE54BB6DB0DA /* TPPSignInBusinessLogicExtendedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC9FC06EF6CCEED742C0D44 /* TPPSignInBusinessLogicExtendedTests.swift */; };
5D1B142A22CC179F0006C964 /* TPPAlertUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1B142922CC179F0006C964 /* TPPAlertUtils.swift */; };
5D3A28CC22D3DA850042B3BD /* UserProfileDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D3A28CB22D3DA850042B3BD /* UserProfileDocument.swift */; };
Expand Down Expand Up @@ -3301,7 +3300,6 @@
0725F3E330D15C718C170C94 /* PalaceCatalog in Frameworks */,
811E00F5F755273F726941CC /* PalaceAuth in Frameworks */,
89382FCA6DC82E0D3906130A /* PalaceReadingPosition in Frameworks */,
5D0130219AE7A8767A2B5C3B /* TriageBotCore in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -6317,7 +6315,6 @@
7081635582F472450D1A1B85 /* PalaceCatalog */,
3D8B27DA10A114B76EFB41A1 /* PalaceAuth */,
5F61B627DDE72CE091C181D5 /* PalaceReadingPosition */,
B6BB8DA6C2692FC1F126876A /* TriageBotCore */,
);
productName = SimplyETests;
productReference = 2D2B47721D08F807007F7764 /* PalaceTests.xctest */;
Expand Down Expand Up @@ -9434,11 +9431,6 @@
package = EA43715A76E6B1D14299A3A1 /* XCLocalSwiftPackageReference "PalaceReadingPosition" */;
productName = PalaceReadingPosition;
};
B6BB8DA6C2692FC1F126876A /* TriageBotCore */ = {
isa = XCSwiftPackageProductDependency;
package = DAAF04A6D259B174CFB2811E /* XCLocalSwiftPackageReference "PalaceTriageBot" */;
productName = TriageBotCore;
};
B7890FD5F70ADDBD798D7FC7 /* PalaceLogging */ = {
isa = XCSwiftPackageProductDependency;
package = AD4024ABF2AB1F4247E45BD3 /* XCLocalSwiftPackageReference "PalaceLogging" */;
Expand Down
28 changes: 27 additions & 1 deletion scripts/pre-push-test-gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,21 @@ else
DEST_ARGS=(-destination 'platform=iOS Simulator,name=iPhone 16 Pro')
fi

# When the push originates from a LINKED git worktree, the shared default
# DerivedData was resolved against a different checkout root, so xcodebuild's
# SPM graph points at a stale source-package path and package resolution dies
# with "the package manifest at '/Package.swift' doesn't exist" — a false gate
# failure (the worktree itself builds fine with an isolated DerivedData). Scope
# a per-checkout DerivedData + SourcePackages dir for worktree pushes only; the
# main checkout keeps its warm default cache so the gate stays fast there.
declare -a DERIVED_ARGS=()
if [[ "$(git rev-parse --git-common-dir 2>/dev/null)" != "$(git rev-parse --git-dir 2>/dev/null)" ]]; then
_repo_slug="$(printf '%s' "$REPO_DIR" | shasum 2>/dev/null | cut -c1-12)"
_dd_dir="${TMPDIR:-/tmp}/prepush-dd-${_repo_slug:-wt}"
DERIVED_ARGS=(-derivedDataPath "$_dd_dir" -clonedSourcePackagesDirPath "$_dd_dir/SourcePackages")
echo "[pre-push-test-gate] linked worktree detected — isolating DerivedData at $_dd_dir" >&2
fi

declare -a ONLY_TESTING_ARGS=()
for cls in "${CLASSES[@]}"; do
ONLY_TESTING_ARGS+=("-only-testing:PalaceTests/$cls")
Expand All @@ -189,10 +204,21 @@ run_with_timeout() {
}

# Quiet xcodebuild — we only care about pass/fail at the gate.
if run_with_timeout "$TIMEOUT_SECS" xcodebuild \
# Clear the git hook environment before xcodebuild. git invokes pre-push
# hooks with GIT_DIR/GIT_WORK_TREE/GIT_INDEX_FILE exported; xcodebuild's SPM
# resolution shells out to git internally, and an inherited GIT_WORK_TREE
# makes the local-package path resolve to "/" — failing with "the package
# manifest at '/Package.swift' doesn't exist". This is THE reason a push from
# a linked worktree (or even the main checkout, under some git versions) hits
# that error while the same xcodebuild run from a plain shell succeeds.
# Unsetting these lets xcodebuild resolve packages against the project root.
if run_with_timeout "$TIMEOUT_SECS" \
env -u GIT_DIR -u GIT_WORK_TREE -u GIT_INDEX_FILE -u GIT_PREFIX -u GIT_EXEC_PATH \
xcodebuild \
-project Palace.xcodeproj \
-scheme Palace \
"${DEST_ARGS[@]}" \
"${DERIVED_ARGS[@]}" \
"${ONLY_TESTING_ARGS[@]}" \
test \
-quiet >/tmp/pre-push-test-gate.log 2>&1; then
Expand Down
3 changes: 3 additions & 0 deletions scripts/xcode-test-optimized.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ if [ "${BUILD_CONTEXT:-}" == "ci" ]; then
-enableCodeCoverage YES \
-retry-tests-on-failure \
-test-iterations 3 \
-test-timeouts-enabled YES \
-default-test-execution-time-allowance 120 \
-maximum-test-execution-time-allowance 300 \
-parallel-testing-enabled NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGNING_ALLOWED=NO \
Expand Down
Loading