Files
Abue Ammar 7caa286ed0 Turn on the hardened runtime, and teach the updater the identity it will switch to (#620)
* Turn on the hardened runtime, and teach the updater the identity it will switch to

Notarization needs two things Tinycast doesn't have: the hardened runtime, and
an Apple Developer ID signature. This does the first and prepares for the second.

The updater compares signatures before it installs, byte-for-byte against the
leaf the running app carries. A Developer ID leaf is a different certificate, so
switching identities outright would make every installed copy reject every future
update. `BundleSignature` therefore learns the Developer ID requirement now, while
releases are still signed with `Tinycast Self-Signed` — the code that trusts the
new identity has to reach people before the first build carrying it does.

The requirement pins the team, not the certificate, so a renewal strands nobody.
It omits the `notarized` keyword on purpose: that resolves a ticket through
syspolicyd or the network, and the updater verifies inside a cache directory
Gatekeeper has never assessed, so an offline Mac would refuse a bundle the chain
already proves is ours.

Hardened runtime needs two entitlements. JavaScriptCore compiles every extension
command, and without `allow-jit` it falls back to the interpreter. Without
`automation.apple-events` every Apple event is refused with -1743 and no prompt,
which silently kills Get Info, the Finder selection extensions read, and the
System Events-driven system actions. Nothing else is required: the only dlopen is
Apple's own IOBluetooth, so library validation stays on.

The flag is not part of the designated requirement, so no Accessibility grant is
lost here. `project.yml` keeps signing with `Tinycast Self-Signed`, so nothing
changes for a contributor building locally.

`Scripts/verify-signature.sh` asserts what notarization will check — the runtime
flag on the app and on the embedded helper, an intact nested seal, and no
get-task-allow. Both release jobs run it before packaging, because a nested binary
missing the runtime flag is the most common notarization rejection there is.

* Keep the hardened runtime out of Debug, where library validation refuses the debug dylib

Hardened runtime turns on library validation, and a Debug build links
`Tinycast Dev.debug.dylib`. The self-signed identity carries no Team ID, so the
loader sees a team mismatch and aborts at launch — every local Debug build died
with a DYLD "Library missing" termination.

Notarization only ever sees Release, so the flag belongs in that config alone.
2026-09-12 22:04:09 +06:00

84 lines
3.0 KiB
YAML

name: Tinycast
options:
bundleIdPrefix: com.tinycast
deploymentTarget:
macOS: "26.0"
createIntermediateGroups: true
groupSortPosition: top
settings:
base:
MARKETING_VERSION: "0.1.0"
CURRENT_PROJECT_VERSION: "1"
SWIFT_VERSION: "6.0"
DEVELOPMENT_TEAM: ""
CODE_SIGN_STYLE: Manual
# Stable self-signed identity so macOS keeps the Accessibility grant across rebuilds.
# Create it once — see docs/signing.md.
CODE_SIGN_IDENTITY: "Tinycast Self-Signed"
SWIFT_STRICT_CONCURRENCY: complete
configs:
Release:
# Release-only: library validation refuses Debug's `.debug.dylib`, whose team never matches.
ENABLE_HARDENED_RUNTIME: YES
# Strip the shipped binary (dSYM is still generated first) and drop unused code.
DEPLOYMENT_POSTPROCESSING: YES
STRIP_INSTALLED_PRODUCT: YES
STRIP_SWIFT_SYMBOLS: YES
DEAD_CODE_STRIPPING: YES
targets:
Tinycast:
type: application
platform: macOS
sources:
- path: Tinycast
# The helper's own sources: compiling them in would link Vision and PDFKit into the app,
# which is the whole thing running recognition out of process avoids.
excludes:
- Features/Clipboard/Service/ClipboardTextExtractor.swift
- Features/Clipboard/Service/ClipboardTextHelper.swift
# The MIT mark's licence asks the notice to travel with the binary, not just the repo.
- path: NOTICE.md
buildPhase: resources
dependencies:
- target: ClipboardTextHelper
link: false
embed: true
codeSign: true
copy:
destination: wrapper
subpath: Contents/Helpers
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: com.tinycast.app
INFOPLIST_FILE: Tinycast/Info.plist
CODE_SIGN_ENTITLEMENTS: Tinycast/Tinycast.entitlements
ASSETCATALOG_COMPILER_APPICON_NAME: tinycast
COMBINE_HIDPI_IMAGES: YES
LD_RUNPATH_SEARCH_PATHS:
- "$(inherited)"
- "@executable_path/../Frameworks"
configs:
# Debug is its own channel, like beta/stable in release.yml: a distinct bundle id means a
# locally-run build gets its own prefs, caches, TCC grants and login item instead of
# inheriting — and corrupting — the installed app's state.
Debug:
PRODUCT_NAME: Tinycast Dev
PRODUCT_BUNDLE_IDENTIFIER: com.tinycast.app.dev
ClipboardTextHelper:
type: tool
platform: macOS
sources:
- Tinycast/Features/Clipboard/Service/ClipboardTextExtractor.swift
- Tinycast/Features/Clipboard/Service/ClipboardTextHelper.swift
settings:
base:
# release.yml passes PRODUCT_NAME on the xcodebuild line, which hits every target: without
# these two pinned, a channel build renames the executable `ClipboardTextWorker` looks for
# and gives the module a name with a space in it.
EXECUTABLE_NAME: ClipboardTextHelper
PRODUCT_MODULE_NAME: ClipboardTextHelper
SKIP_INSTALL: YES