-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathproject.yml
More file actions
439 lines (431 loc) · 20 KB
/
Copy pathproject.yml
File metadata and controls
439 lines (431 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
name: final final
options:
bundleIdPrefix: com.kerim
deploymentTarget:
macOS: "15.0"
xcodeVersion: "15.0"
generateEmptyDirectories: true
# DebugTest exists so the Test action builds the app under a SEPARATE bundle
# identity (see the app target's DebugTest config). macOS keys UserDefaults,
# NSWindow frame/full-screen autosave, and process identity to the bundle ID,
# so a test-hosted app instance under the real ID could inherit the user's
# saved full-screen state (live incident 2026-08-05: a test run launched the
# app full-screen over the user's work) and collide with a running production
# copy. A distinct test identity removes that whole class.
configs:
Debug: debug
DebugTest: debug
Release: release
schemes:
final final:
build:
targets:
final final: all
QuickLook Extension: all
final finalTests: [test]
final finalUITests: [test]
# Mirrors the committed .xcscheme's BuildAction/@runPostActionsOnFailure="NO".
runPostActionsOnFailure: false
run:
config: Debug
executable: final final
test:
config: DebugTest
# Forwards TEST_RUNNER_FF_E2E_SHOT_DIR (a build-setting override
# `vmtest run` passes on the xcodebuild command line — see
# scripts/vmtest/guest/run.sh) into the XCUITest RUNNER process's own
# environment as FF_E2E_SHOT_DIR. This is NOT automatic: xcodebuild
# forwarding a TEST_RUNNER_-prefixed build setting only reaches the
# runner if the scheme's Test Action explicitly references it here —
# found live (Verification 5) that the "e2e-shots" scratch screenshot
# landed in E2EShotDir's fallback (NSTemporaryDirectory()) instead of
# the intended container path, because this entry didn't exist yet and
# the runner process never actually saw FF_E2E_SHOT_DIR at all.
environmentVariables:
FF_E2E_SHOT_DIR: $(TEST_RUNNER_FF_E2E_SHOT_DIR)
targets:
# parallelizable: false is pinned explicitly (xcodegen's own default) —
# a dropped/flipped value here previously caused flaky isHittable UI
# test failures. scripts/verify-scheme.sh guards the generated output.
- name: final finalTests
parallelizable: false
- name: final finalUITests
parallelizable: false
packages:
GRDB:
url: https://github.com/groue/GRDB.swift
majorVersion: 7.0.0
Sparkle:
url: https://github.com/sparkle-project/Sparkle
majorVersion: 2.0.0
aggregateTargets:
# Captures the current git branch/commit before the app target builds.
# Deliberately exempt from ENABLE_USER_SCRIPT_SANDBOXING: it runs `git`
# directly (arbitrary reads outside the source tree, e.g. .git/logs), which
# a sandboxed script phase would have denied. Kept as its own tiny target
# (rather than a script directly on the app target) so only this one
# narrow phase needs the exemption — everything else on the app target
# stays sandboxed.
GitStamp:
settings:
base:
ENABLE_USER_SCRIPT_SANDBOXING: NO
buildScripts:
- name: "Capture git branch and commit"
# No declared inputs — REQUIRED with a declared output, or Xcode
# runs this phase exactly once (at the first-ever build) and then
# treats the output as up to date forever, freezing branch/commit at
# whatever HEAD was during that first build.
basedOnDependencyAnalysis: false
outputFiles:
- $(BUILT_PRODUCTS_DIR)/GitStamp/git-info.env
script: |
OUT_DIR="${BUILT_PRODUCTS_DIR}/GitStamp"
mkdir -p "$OUT_DIR"
BRANCH=$(git -C "${SRCROOT}" rev-parse --abbrev-ref HEAD 2>/dev/null) || BRANCH=""
COMMIT=$(git -C "${SRCROOT}" rev-parse --short HEAD 2>/dev/null) || COMMIT=""
if [ -z "$BRANCH" ] || [ -z "$COMMIT" ]; then
echo "warning: git rev-parse failed in the GitStamp target (the git command itself failed, or the repository is unreadable — NOT detached HEAD, which rev-parse --abbrev-ref reports as the literal string 'HEAD'). Stamping 'git-unavailable' for branch/commit. This target is exempt from script sandboxing, so a sandbox denial is NOT the expected cause — investigate the repo state." >&2
BRANCH="${BRANCH:-git-unavailable}"
COMMIT="${COMMIT:-git-unavailable}"
fi
printf 'BRANCH=%s\nCOMMIT=%s\n' "$BRANCH" "$COMMIT" > "$OUT_DIR/git-info.env"
- name: "Clean stale QuickLook DerivedData"
# Re-homed here (off the app target) when script sandboxing went on:
# this script does its own `rm -rf`/directory scanning outside
# SRCROOT and DERIVED_DATA, which can't be expressed as declared
# build-script inputs/outputs. GitStamp is already exempt from
# ENABLE_USER_SCRIPT_SANDBOXING and already runs on every build, so
# it's a natural home — no sandboxing concerns, no doc disclosure
# needed about losing the automatic cleanup.
basedOnDependencyAnalysis: false
script: |
# See scripts/clean-stale-deriveddata.sh for what this does and why.
# (Extracted to a script file because the concurrency-safe version is
# too long to stay legible as an inline YAML script fragment.)
"${SRCROOT}/scripts/clean-stale-deriveddata.sh"
targets:
final final:
type: application
platform: macOS
sources:
- path: final final
excludes:
- "**/.DS_Store"
- "Resources/Export"
- "Resources/editor"
- "Resources/TinyTeX"
- "Resources/getting-started.ff"
- path: final final/Resources/Export
type: folder
buildPhase: resources
- path: final final/Resources/editor
type: folder
buildPhase: resources
- path: final final/Resources/TinyTeX
type: folder
buildPhase: resources
- path: final final/Resources/getting-started.ff
type: folder
buildPhase: resources
preBuildScripts:
- name: "Build web editors if missing"
basedOnDependencyAnalysis: false
script: |
EDITOR_DIR="${SRCROOT}/final final/Resources/editor"
if [ ! -f "$EDITOR_DIR/milkdown/milkdown.html" ] || [ ! -f "$EDITOR_DIR/codemirror/codemirror.html" ]; then
echo "error: Web editors are not built. Run: cd web && pnpm build — then build again. (This phase can no longer build them itself: script sandboxing is on and pnpm's writes cannot be declared as build outputs.)" >&2
exit 1
fi
- name: "Guard: no SQLite sidecars in bundled getting-started.ff"
basedOnDependencyAnalysis: false
# Declared purely to grant the sandboxed script read access to list
# this directory (a bare `[ -f ... ]` stat is allowed without this,
# but the `ls` directory-listing this guard needs is not) — found
# empirically: basedOnDependencyAnalysis: false means this input
# doesn't gate whether the phase runs, only what it may read.
inputFiles:
- $(SRCROOT)/final final/Resources/getting-started.ff
script: |
# A leftover -wal/-shm pair next to the bundled guide gets copied into
# the app and silently rolls the document back to whatever state the
# WAL froze at (this shipped a stale guide in v0.2.101–v0.2.104).
# Fail fast. The fix is a clean open/checkpoint/close, which removes
# both sidecars by itself — no `rm` (denied by .claude/settings.json),
# and not the Apple sqlite3 CLI (persistent-WAL leaves them behind):
# cd "final final/Resources/getting-started.ff"
# python3 -c "import sqlite3; c=sqlite3.connect('content.sqlite'); c.execute('PRAGMA wal_checkpoint(TRUNCATE)'); c.close()"
#
# Matches full listing lines, not substrings: "content.sqlite" is a
# literal prefix of "content.sqlite-wal"/"content.sqlite-shm", so a
# naive substring/glob check would also match the main db file itself.
GS_DIR="${SRCROOT}/final final/Resources/getting-started.ff"
LISTING=$(/bin/ls "$GS_DIR" 2>&1) || {
echo "error: SQLite sidecar guard could not list $GS_DIR ($LISTING). Under script sandboxing this means the guard cannot run — failing closed rather than shipping an unchecked bundle." >&2
exit 1
}
if ! printf '%s\n' "$LISTING" | grep -qx 'content.sqlite'; then
echo "error: SQLite sidecar guard read a listing of $GS_DIR with no 'content.sqlite' entry — the read was blocked, or the directory is wrong. Refusing to pass." >&2
exit 1
fi
if printf '%s\n' "$LISTING" | grep -qxE 'content\.sqlite-(wal|shm)'; then
echo "error: Stale SQLite sidecar would be bundled into the app in $GS_DIR — it reverts the Getting Started guide to old content. Fix: cd 'final final/Resources/getting-started.ff' && python3 -c \"import sqlite3; c=sqlite3.connect('content.sqlite'); c.execute('PRAGMA wal_checkpoint(TRUNCATE)'); c.close()\" — then rebuild. Do not use rm; if the sidecars persist, the app still has the database open." >&2
exit 1
fi
postBuildScripts:
- name: "Stamp Git Info into GitInfo.plist"
# Declared inputs/outputs, but this does NOT skip when HEAD hasn't
# moved: its input is GitStamp's git-info.env, which GitStamp
# rewrites (new mtime) on every single build regardless of content
# (see GitStamp's own basedOnDependencyAnalysis: false above) — so
# this phase's declared input always looks "changed" and it reruns
# every build too, same net effect as basedOnDependencyAnalysis:
# false without needing that flag here.
# The GitStamp aggregate target (see aggregateTargets below) captures
# branch/commit before this target builds — it runs git directly and
# is exempt from script sandboxing, so this phase only reads its
# output env file and writes a plist. Output is a SEPARATE plist (not
# Info.plist) — declaring the generated Info.plist as a script output
# would conflict with the build system's own ProcessInfoPlist step,
# and an undeclared write to it is denied under
# ENABLE_USER_SCRIPT_SANDBOXING.
inputFiles:
- $(BUILT_PRODUCTS_DIR)/GitStamp/git-info.env
outputFiles:
- $(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/Resources/GitInfo.plist
- $(DERIVED_FILE_DIR)/git-stamp.txt
script: |
PLIST="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}/Resources/GitInfo.plist"
ENV_FILE="${BUILT_PRODUCTS_DIR}/GitStamp/git-info.env"
# Parsed with sed, NOT sourced (`.`/source). Git branch names may
# contain parens, backticks, and $(...) — nearly everything except
# whitespace and a few reserved chars is legal. Sourcing a file
# with `BRANCH=feat(x)` breaks silently (empty BRANCH/COMMIT,
# bypassing both the git-unavailable and no-git-stamp sentinels —
# the badge just goes blank with no signal); a branch containing a
# backtick or $(...) would actually EXECUTE during the build.
if [ -f "$ENV_FILE" ]; then
BRANCH=$(sed -n 's/^BRANCH=//p' "$ENV_FILE")
COMMIT=$(sed -n 's/^COMMIT=//p' "$ENV_FILE")
else
# BUILT_PRODUCTS_DIR mismatch between GitStamp and this target,
# or GitStamp didn't run — fail soft with the build-wiring
# sentinel rather than aborting on a raw "No such file" error
# that gives no hint GitStamp is the cause.
BRANCH="no-git-stamp"
COMMIT="no-git-stamp"
fi
# Worktree folder name — only set when building from a superdev/autodev
# worktree, empty for the main checkout. DevBuildBadge.swift reads it via
# GitInfo.devLabel to label the running app, falling back to BRANCH.
case "${SRCROOT}" in
*/.claude/worktrees/*) WORKTREE=$(basename "${SRCROOT}") ;;
*) WORKTREE="" ;;
esac
mkdir -p "$(dirname "$PLIST")"
if [ ! -f "$PLIST" ]; then
/usr/libexec/PlistBuddy -c "Save" "$PLIST" >/dev/null
fi
/usr/libexec/PlistBuddy \
-c "Clear dict" \
-c "Add :GitBranch string $BRANCH" \
-c "Add :GitCommit string $COMMIT" \
"$PLIST"
if [ -n "$WORKTREE" ]; then
/usr/libexec/PlistBuddy -c "Add :DevWorktree string $WORKTREE" "$PLIST"
fi
mkdir -p "${DERIVED_FILE_DIR}"
echo "${BRANCH} ${COMMIT} ${WORKTREE}" > "${DERIVED_FILE_DIR}/git-stamp.txt"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final
PRODUCT_NAME: FINAL|FINAL
PRODUCT_MODULE_NAME: final_final
MARKETING_VERSION: "1"
CURRENT_PROJECT_VERSION: "0.2.132"
INFOPLIST_GENERATION_MODE: GeneratedFile
GENERATE_INFOPLIST_FILE: YES
SWIFT_VERSION: "5.9"
MACOSX_DEPLOYMENT_TARGET: "15.0"
ENABLE_HARDENED_RUNTIME: YES
CODE_SIGN_ENTITLEMENTS: final final/final final.entitlements
# ENABLE_USER_SCRIPT_SANDBOXING restricts what build-phase scripts may
# read/write (see the preBuildScripts/postBuildScripts above for what
# that required). FUSE_BUILD_SCRIPT_PHASES lets independent script
# phases run in parallel with compilation instead of serializing the
# whole build around them — not required for sandboxing itself, but
# requested alongside it in the original ask ("script sandboxing +
# parallel script phases").
ENABLE_USER_SCRIPT_SANDBOXING: YES
FUSE_BUILD_SCRIPT_PHASES: YES
configs:
Debug:
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual
# Lock in incremental compilation + no optimization for debug builds
# (otherwise inherited implicitly; explicit so a default change can't
# silently switch debug builds to whole-module recompiles).
SWIFT_COMPILATION_MODE: singlefile
SWIFT_OPTIMIZATION_LEVEL: "-Onone"
# Surface slow-to-type-check functions/expressions (>200ms) as warnings.
OTHER_SWIFT_FLAGS: "-Xfrontend -warn-long-function-bodies=200 -Xfrontend -warn-long-expression-type-checking=200"
DebugTest:
# Same build as Debug, but a SEPARATE app identity for the Test
# action — see the configs comment at the top of this file.
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final.testhost
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual
SWIFT_COMPILATION_MODE: singlefile
SWIFT_OPTIMIZATION_LEVEL: "-Onone"
OTHER_SWIFT_FLAGS: "-Xfrontend -warn-long-function-bodies=200 -Xfrontend -warn-long-expression-type-checking=200"
Release:
CODE_SIGN_IDENTITY: "Developer ID Application"
CODE_SIGN_STYLE: Manual
DEVELOPMENT_TEAM: HBHZWD24V7
dependencies:
- package: GRDB
- package: Sparkle
- target: QuickLook Extension
embed: true
link: false
- target: GitStamp
# An aggregate target has no product, so embed/link are likely
# inert here — explicit anyway rather than letting the
# pbxproj-determinism guard flag this as unreviewed churn.
embed: false
link: false
info:
path: final final/Info.plist
properties:
CFBundleShortVersionString: $(MARKETING_VERSION)
CFBundleVersion: $(CURRENT_PROJECT_VERSION)
CFBundleDisplayName: FINAL|FINAL
CFBundleName: FINAL|FINAL
LSMinimumSystemVersion: "15.0"
NSPrincipalClass: NSApplication
NSHighResolutionCapable: true
SUFeedURL: https://finalfinalapp.cc/appcast.xml
SUEnableAutomaticChecks: true
SUPublicEDKey: gPX00ZVXHKsqmHM6W3Yspp4uTZnQg2yUCH8jkpKnBc4=
CFBundleURLTypes:
- CFBundleURLName: com.kerim.final-final
CFBundleURLSchemes:
- finalfinal
CFBundleDocumentTypes:
- CFBundleTypeName: FINAL|FINAL Document
CFBundleTypeRole: Editor
LSHandlerRank: Owner
LSItemContentTypes:
- com.kerim.final-final.document
UTExportedTypeDeclarations:
- UTTypeIdentifier: com.kerim.final-final.document
UTTypeDescription: FINAL|FINAL Document
UTTypeConformsTo:
- com.apple.package
- public.composite-content
UTTypeTagSpecification:
public.filename-extension:
- ff
- UTTypeIdentifier: com.kerim.final-final.section
UTTypeDescription: Section Transfer
UTTypeConformsTo:
- public.data
UTTypeTagSpecification:
public.mime-type:
- application/x-final-final-section
- UTTypeIdentifier: org.textbundle.package
UTTypeDescription: TextBundle
UTTypeConformsTo:
- com.apple.package
UTTypeTagSpecification:
public.filename-extension:
- textbundle
QuickLook Extension:
type: app-extension
platform: macOS
sources:
- path: QuickLook Extension
excludes:
- "**/.DS_Store"
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final.quicklook
PRODUCT_NAME: QuickLook Extension
PRODUCT_MODULE_NAME: QuickLook_Extension
SWIFT_VERSION: "5.9"
MACOSX_DEPLOYMENT_TARGET: "15.0"
MARKETING_VERSION: "1"
CURRENT_PROJECT_VERSION: "0.2.132"
CODE_SIGN_IDENTITY: "Developer ID Application"
CODE_SIGN_STYLE: Manual
DEVELOPMENT_TEAM: HBHZWD24V7
ENABLE_HARDENED_RUNTIME: YES
CODE_SIGN_ENTITLEMENTS: QuickLook Extension/QuickLook Extension.entitlements
SKIP_INSTALL: YES
OTHER_LDFLAGS: ["-lsqlite3"]
configs:
Debug:
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual
DebugTest:
# Embedded-extension IDs must be prefixed by the host app's ID, and
# the Test action's host app is …final-final.testhost.
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final.testhost.quicklook
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual
Release:
CODE_SIGN_IDENTITY: "Developer ID Application"
CODE_SIGN_STYLE: Manual
DEVELOPMENT_TEAM: HBHZWD24V7
info:
path: QuickLook Extension/Info.plist
properties:
CFBundleDisplayName: FINAL|FINAL Quick Look
CFBundleShortVersionString: $(MARKETING_VERSION)
CFBundleVersion: $(CURRENT_PROJECT_VERSION)
NSExtension:
NSExtensionPointIdentifier: com.apple.quicklook.preview
NSExtensionPrincipalClass: QuickLook_Extension.PreviewViewController
NSExtensionAttributes:
QLSupportedContentTypes:
- com.kerim.final-final.document
QLSupportsSecureCoding: true
final finalTests:
type: bundle.unit-test
platform: macOS
sources:
- path: final finalTests
excludes:
- "**/.DS_Store"
- "**/*.sqlite-wal"
- "**/*.sqlite-shm"
- "Fixtures"
dependencies:
- target: final final
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final-tests
SWIFT_VERSION: "5.9"
GENERATE_INFOPLIST_FILE: YES
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/FINAL|FINAL.app/Contents/MacOS/FINAL|FINAL"
BUNDLE_LOADER: "$(TEST_HOST)"
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual
final finalUITests:
type: bundle.ui-testing
platform: macOS
sources:
- path: final finalUITests
- path: final finalTests/Fixtures
type: folder
buildPhase: resources
dependencies:
- target: final final
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.kerim.final-final-uitests
SWIFT_VERSION: "5.9"
GENERATE_INFOPLIST_FILE: YES
TEST_TARGET_NAME: final final
CODE_SIGN_IDENTITY: "-"
CODE_SIGN_STYLE: Manual