project.yml
name: Fader
options:
bundleIdPrefix: dev.pantafive
deploymentTarget:
macOS: "15.0"
createIntermediateGroups: true
packages:
Sparkle:
url: https://github.com/sparkle-project/Sparkle
exactVersion: 2.9.2
settings:
base:
SWIFT_VERSION: "6.0"
SWIFT_STRICT_CONCURRENCY: complete
MARKETING_VERSION: 0.15.0
# Sparkle compares CFBundleVersion, not the marketing string — tying them
# together means the CI's MARKETING_VERSION override feeds both, and a
# static build number can never silently disable updates.
CURRENT_PROJECT_VERSION: $(MARKETING_VERSION)
CODE_SIGN_STYLE: Automatic
ENABLE_HARDENED_RUNTIME: true
# The local-build marker script edits the built Info.plist.
ENABLE_USER_SCRIPT_SANDBOXING: false
DEAD_CODE_STRIPPING: true
configs:
Release:
# Size-tuned: the only hot path is trivial float math in the IO proc,
# -Osize costs it nothing. Strip runs inside xcodebuild before signing;
# release.yml verifies the symbol table is actually gone.
SWIFT_OPTIMIZATION_LEVEL: -Osize
DEPLOYMENT_POSTPROCESSING: true
STRIP_INSTALLED_PRODUCT: true
STRIP_STYLE: all
targets:
Fader:
type: application
platform: macOS
sources: [Fader]
settings:
base:
PRODUCT_BUNDLE_IDENTIFIER: dev.pantafive.fader
INFOPLIST_FILE: Fader/Resources/Info.plist
CODE_SIGN_ENTITLEMENTS: Fader/Resources/Fader.entitlements
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
dependencies:
- package: Sparkle
# Local builds show "local" in the menu header instead of the release
# number they were forked from. Stamps only the display string — the
# numeric CFBundleVersion stays, so Sparkle's comparison is untouched.
# CI (GitHub Actions sets CI=true) is left alone.
postBuildScripts:
- name: Mark local build
# Same path as input and output marks this a mutation of the built
# plist: ordered after Info.plist processing, before code signing.
# Either alone breaks — input-only let the stamp land post-signing
# (invalid seal), output-only let plist processing overwrite it.
inputFiles:
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
outputFiles:
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
# A mutating task must also produce a non-mutable node, or the
# build graph rejects it ("no other virtual output node").
- $(DERIVED_FILE_DIR)/local-build-mark
script: |
if [ "${CI:-}" != "true" ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString local" \
"$TARGET_BUILD_DIR/$INFOPLIST_PATH"
fi
touch "$DERIVED_FILE_DIR/local-build-mark"
entitlements:
path: Fader/Resources/Fader.entitlements
properties:
com.apple.security.app-sandbox: true
com.apple.security.device.audio-input: true
com.apple.security.device.bluetooth: true
com.apple.security.network.client: true
# Sparkle's sandboxed-install path: the app talks to its Installer
# XPC service over these mach names. Literal bundle id, not
# $(PRODUCT_BUNDLE_IDENTIFIER) — release.yml re-signs with raw
# codesign, which does not expand build settings.
com.apple.security.temporary-exception.mach-lookup.global-name:
- dev.pantafive.fader-spks
- dev.pantafive.fader-spki
# Hostless unit tests: pure-logic sources are compiled into the bundle
# directly, so no app process needs to launch (LSUIElement hosts hang
# the test runner).
FaderTests:
type: bundle.unit-test
platform: macOS
sources:
# Hand-listed on purpose (a glob would silently pull HAL-importing
# files in) — extracting a new pure-logic source? Add it here too.
- Tests
- Fader/Audio/VolumeStore.swift
- Fader/Audio/DeviceUsageStore.swift
- Fader/Audio/DevicePriorityStore.swift
- Fader/Audio/JSONDefaults.swift
- Fader/Audio/DeviceSymbol.swift
- Fader/Audio/AudioDevice.swift
- Fader/Audio/DevicePriorityPolicy.swift
- Fader/Audio/MultiOutputPolicy.swift
- Fader/Audio/VolumeTaper.swift
# Property wrappers and AudioDirection; nothing in the tests touches
# a live HAL.
- Fader/Audio/HALProperty.swift
settings:
base:
GENERATE_INFOPLIST_FILE: true
schemes:
Fader:
build:
targets:
Fader: all
test:
targets:
- FaderTests