@hasna/recordings 0.2.11 → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +349 -69
- package/bun.lock +3 -0
- package/dist/__tests__/helpers/native-fs-guard.d.ts +2 -0
- package/dist/__tests__/helpers/native-fs-guard.d.ts.map +1 -0
- package/dist/cli/index.js +1728 -320
- package/dist/cli/macos-permissions.d.ts +13 -0
- package/dist/cli/macos-permissions.d.ts.map +1 -0
- package/dist/cli/options.d.ts +12 -0
- package/dist/cli/options.d.ts.map +1 -1
- package/dist/db/pg-migrations.d.ts.map +1 -1
- package/dist/db/recordings.d.ts +2 -1
- package/dist/db/recordings.d.ts.map +1 -1
- package/dist/db/remote-storage.d.ts +5 -1
- package/dist/db/remote-storage.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +628 -147
- package/dist/lib/bun-runtime.d.ts +11 -0
- package/dist/lib/bun-runtime.d.ts.map +1 -0
- package/dist/lib/config.d.ts +8 -1
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/enhancer.d.ts +10 -2
- package/dist/lib/enhancer.d.ts.map +1 -1
- package/dist/lib/install-maintenance.d.ts +24 -0
- package/dist/lib/install-maintenance.d.ts.map +1 -0
- package/dist/lib/machine.d.ts +2 -0
- package/dist/lib/machine.d.ts.map +1 -0
- package/dist/lib/recorder.d.ts +2 -1
- package/dist/lib/recorder.d.ts.map +1 -1
- package/dist/lib/recording-create-identity.d.ts +13 -0
- package/dist/lib/recording-create-identity.d.ts.map +1 -0
- package/dist/lib/release-install-policy.d.ts +31 -0
- package/dist/lib/release-install-policy.d.ts.map +1 -0
- package/dist/lib/transcriber.d.ts.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +977 -194
- package/dist/sdk/index.js +5 -1
- package/dist/sdk/v1.generated.d.ts +4 -0
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/cloud-config.d.ts +9 -0
- package/dist/server/cloud-config.d.ts.map +1 -0
- package/dist/server/cloud-readiness.d.ts +10 -0
- package/dist/server/cloud-readiness.d.ts.map +1 -0
- package/dist/server/cloud.d.ts +9 -18
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +1654 -304
- package/dist/server/migrate-command.d.ts +11 -0
- package/dist/server/migrate-command.d.ts.map +1 -0
- package/dist/server/openapi.d.ts +22 -0
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/repo.d.ts +8 -1
- package/dist/server/repo.d.ts.map +1 -1
- package/dist/server/serve.d.ts +6 -1
- package/dist/server/serve.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +515 -80
- package/dist/store.d.ts +3 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +7 -4
- package/packaging/macos/Empty.entitlements +5 -0
- package/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist +34 -0
- package/packaging/macos/Verifier.entitlements +10 -0
- package/packaging/macos/artifact-verifier.sb +33 -0
- package/packaging/macos/build_release_pkg.sh +872 -0
- package/packaging/macos/managed_bootstrap.sh +623 -0
- package/packaging/macos/pkgutil_fingerprint.awk +37 -0
- package/packaging/macos/release_lifecycle.ts +463 -0
- package/packaging/macos/scripts/postinstall +565 -0
- package/packaging/macos/scripts/preinstall +297 -0
- package/scripts/build_companion_cli.sh +337 -0
- package/scripts/build_native_fs_guard.sh +59 -0
- package/scripts/generate-sdk.ts +19 -1
- package/scripts/install_macos_app.sh +1919 -98
- package/scripts/macos_artifact.ts +5316 -0
- package/scripts/migrate.ts +38 -10
- package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
- package/scripts/native/recordings_fs_guard.c +1167 -0
- package/scripts/native_fs_guard.ts +158 -0
- package/scripts/release-guard.ts +20 -1
- package/scripts/resolve_tailscale_cli.sh +389 -0
- package/scripts/smoke_macos_app.sh +573 -0
- package/src/native/Recordings/App/ContentView.swift +83 -0
- package/src/native/Recordings/App/MenuBarStatusView.swift +102 -0
- package/src/native/Recordings/App/RecordWorkspaceView.swift +468 -0
- package/src/native/Recordings/App/RecordingDetailView.swift +138 -0
- package/src/native/Recordings/App/RecordingsApp.swift +367 -44
- package/src/native/Recordings/App/RecordingsListView.swift +130 -0
- package/src/native/Recordings/App/RecordingsStore.swift +206 -0
- package/src/native/Recordings/App/RuntimeSmoke.swift +158 -0
- package/src/native/Recordings/App/SidebarView.swift +212 -0
- package/src/native/Recordings/App/Theme.swift +87 -0
- package/src/native/Recordings/Package.resolved +5 -5
- package/src/native/Recordings/Package.swift +62 -2
- package/src/native/Recordings/RecordingsLib/AccessibilityPromptGate.swift +104 -0
- package/src/native/Recordings/RecordingsLib/BrandAssets.swift +0 -32
- package/src/native/Recordings/RecordingsLib/ChromeSurface.swift +17 -0
- package/src/native/Recordings/RecordingsLib/Info.plist +7 -3
- package/src/native/Recordings/RecordingsLib/MenuBarPresentation.swift +37 -0
- package/src/native/Recordings/RecordingsLib/NativeAppDiagnostics.swift +32 -2
- package/src/native/Recordings/RecordingsLib/NativeMachineIdentity.swift +24 -0
- package/src/native/Recordings/RecordingsLib/NativePCMRecorder.swift +262 -23
- package/src/native/Recordings/RecordingsLib/OpenAIAPIKeyStore.swift +56 -0
- package/src/native/Recordings/RecordingsLib/PermissionRequestLaunchPlan.swift +56 -0
- package/src/native/Recordings/RecordingsLib/ProjectStore.swift +321 -25
- package/src/native/Recordings/RecordingsLib/RealtimeTranscriptionClient.swift +605 -90
- package/src/native/Recordings/RecordingsLib/Recording.swift +141 -0
- package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +3704 -376
- package/src/native/Recordings/RecordingsLib/RecordingStartControlPresentation.swift +29 -0
- package/src/native/Recordings/RecordingsLib/RecordingsCLI.entitlements +10 -0
- package/src/native/Recordings/RecordingsLib/RecordingsCLI.swift +222 -0
- package/src/native/Recordings/RecordingsLib/SettingsView.swift +45 -11
- package/src/native/Recordings/RecordingsLib/SpeechIntent.swift +331 -0
- package/src/native/Recordings/RecordingsLib/SpeechIntentClassifier.swift +232 -0
- package/src/native/Recordings/RecordingsLib/VoiceShortcuts.swift +17 -7
- package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +1343 -0
- package/src/native/Recordings/RecordingsTests/MenuBarPresentationTests.swift +98 -0
- package/src/native/Recordings/RecordingsTests/NativeAppDiagnosticsTests.swift +20 -0
- package/src/native/Recordings/RecordingsTests/NativePCMRecorderTests.swift +370 -1
- package/src/native/Recordings/RecordingsTests/PasteTargetTests.swift +987 -1
- package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +385 -0
- package/src/native/Recordings/RecordingsTests/RealtimeTranscriptionTests.swift +835 -8
- package/src/native/Recordings/RecordingsTests/RecordingBridgeTests.swift +180 -0
- package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +760 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartControlPresentationTests.swift +42 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartGateTests.swift +264 -0
- package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +129 -0
- package/src/native/Recordings/RecordingsTests/SpeechIntentTests.swift +600 -0
- package/src/native/Recordings/RecordingsTests/TranscriptResolutionTests.swift +62 -1
- package/src/native/Recordings/RecordingsTests/TranscriptionResultIdentityTests.swift +21 -0
- package/src/native/Recordings/Updater/BootstrapPreflight/BootstrapPreflightMain.swift +739 -0
- package/src/native/Recordings/Updater/Broker/ActivationRecoveryPolicy.swift +254 -0
- package/src/native/Recordings/Updater/Broker/ApplicationNamespace.swift +463 -0
- package/src/native/Recordings/Updater/Broker/ApplicationProcessQuiescence.swift +55 -0
- package/src/native/Recordings/Updater/Broker/ArtifactIngest.swift +341 -0
- package/src/native/Recordings/Updater/Broker/AtomicActivation.swift +477 -0
- package/src/native/Recordings/Updater/Broker/BrokerMain.swift +624 -0
- package/src/native/Recordings/Updater/Broker/CanonicalTreeCopy.swift +63 -0
- package/src/native/Recordings/Updater/Broker/CodeValidation.swift +561 -0
- package/src/native/Recordings/Updater/Broker/DarwinACLValidator.swift +66 -0
- package/src/native/Recordings/Updater/Broker/HostOSProductVersion.swift +50 -0
- package/src/native/Recordings/Updater/Broker/InstallJournal.swift +362 -0
- package/src/native/Recordings/Updater/Broker/InstallRecovery.swift +662 -0
- package/src/native/Recordings/Updater/Broker/MonotonicState.swift +456 -0
- package/src/native/Recordings/Updater/Broker/PeerIdentity.swift +274 -0
- package/src/native/Recordings/Updater/Broker/VerifierRunner.swift +81 -0
- package/src/native/Recordings/Updater/BrokerTests/ActivationRecoveryPolicyTests.swift +259 -0
- package/src/native/Recordings/Updater/BrokerTests/CanonicalReleaseOrderTests.swift +21 -0
- package/src/native/Recordings/Updater/Client/ClientMain.swift +119 -0
- package/src/native/Recordings/Updater/Protocol/BoundedProcess.swift +159 -0
- package/src/native/Recordings/Updater/Protocol/CandidateMetadataPolicy.swift +214 -0
- package/src/native/Recordings/Updater/Protocol/HostOSVersionPolicy.swift +55 -0
- package/src/native/Recordings/Updater/Protocol/MonotonicReleasePolicy.swift +152 -0
- package/src/native/Recordings/Updater/Protocol/ReleaseEnvelope.swift +229 -0
- package/src/native/Recordings/Updater/Protocol/UpdateProtocol.swift +119 -0
- package/src/native/Recordings/Updater/ProtocolTests/BoundedProcessRunnerTests.swift +60 -0
- package/src/native/Recordings/Updater/ProtocolTests/CandidateMetadataPolicyTests.swift +168 -0
- package/src/native/Recordings/Updater/ProtocolTests/HostOSVersionPolicyTests.swift +62 -0
- package/src/native/Recordings/Updater/ProtocolTests/MonotonicReleasePolicyTests.swift +172 -0
- package/src/native/Recordings/Updater/ProtocolTests/ReleaseEnvelopeValidationTests.swift +65 -0
- package/src/native/Recordings/Updater/Signer/SignerMain.swift +210 -0
- package/src/native/Recordings/Updater/VerifierLauncher/RecordingsVerifierLauncher.c +725 -0
- package/src/native/Recordings/Updater/VerifierLauncher/include/RecordingsVerifierLauncher.h +33 -0
- package/src/native/Recordings/build.sh +1973 -236
- package/src/native/Recordings/RecordingsLib/MenuBarPopover.swift +0 -380
|
@@ -1,272 +1,2009 @@
|
|
|
1
|
-
#!/
|
|
2
|
-
# Build Recordings.app
|
|
3
|
-
# Usage: ./build.sh [debug|release
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Build, sign, notarize, and finalize a Recordings.app artifact.
|
|
3
|
+
# Usage: ./build.sh [debug|local] | ./build.sh release <initial-bootstrap|app-update>
|
|
4
4
|
|
|
5
5
|
set -euo pipefail
|
|
6
|
+
umask 077
|
|
7
|
+
|
|
8
|
+
HOST_UNAME_EXECUTABLE="/usr/bin/uname"
|
|
9
|
+
SYSTEM_DIRNAME_EXECUTABLE="/usr/bin/dirname"
|
|
10
|
+
SYSTEM_PWD_EXECUTABLE="/bin/pwd"
|
|
11
|
+
SANITIZED_PATH="/usr/bin:/bin:/usr/sbin:/sbin"
|
|
12
|
+
|
|
13
|
+
require_bootstrap_executable() {
|
|
14
|
+
local label="$1"
|
|
15
|
+
local executable="$2"
|
|
16
|
+
case "$executable" in
|
|
17
|
+
/*) ;;
|
|
18
|
+
*)
|
|
19
|
+
echo "$label must be configured as an absolute executable path." >&2
|
|
20
|
+
exit 1
|
|
21
|
+
;;
|
|
22
|
+
esac
|
|
23
|
+
if [ ! -f "$executable" ] || [ ! -x "$executable" ]; then
|
|
24
|
+
echo "$label is missing or is not an executable file: $executable" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
require_bootstrap_executable "HOST_UNAME_EXECUTABLE" "$HOST_UNAME_EXECUTABLE"
|
|
30
|
+
require_bootstrap_executable "SYSTEM_DIRNAME_EXECUTABLE" "$SYSTEM_DIRNAME_EXECUTABLE"
|
|
31
|
+
require_bootstrap_executable "SYSTEM_PWD_EXECUTABLE" "$SYSTEM_PWD_EXECUTABLE"
|
|
32
|
+
HOST_PLATFORM="$("$HOST_UNAME_EXECUTABLE" -s)"
|
|
33
|
+
readonly HOST_PLATFORM
|
|
34
|
+
|
|
35
|
+
select_executable() {
|
|
36
|
+
local system_executable="$1"
|
|
37
|
+
local test_override="${2:-}"
|
|
38
|
+
if [ "$HOST_PLATFORM" = "Darwin" ] || [ -z "$test_override" ]; then
|
|
39
|
+
printf '%s\n' "$system_executable"
|
|
40
|
+
else
|
|
41
|
+
printf '%s\n' "$test_override"
|
|
42
|
+
fi
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
DIRNAME_EXECUTABLE="$(select_executable "$SYSTEM_DIRNAME_EXECUTABLE" "${RECORDINGS_TEST_DIRNAME_EXECUTABLE:-}")"
|
|
46
|
+
PWD_EXECUTABLE="$(select_executable "$SYSTEM_PWD_EXECUTABLE" "${RECORDINGS_TEST_PWD_EXECUTABLE:-}")"
|
|
47
|
+
require_bootstrap_executable "DIRNAME_EXECUTABLE" "$DIRNAME_EXECUTABLE"
|
|
48
|
+
require_bootstrap_executable "PWD_EXECUTABLE" "$PWD_EXECUTABLE"
|
|
6
49
|
|
|
7
50
|
MODE="${1:-release}"
|
|
8
|
-
|
|
51
|
+
RELEASE_SUBTYPE="${2:-}"
|
|
52
|
+
SCRIPT_DIR="$(cd "$("$DIRNAME_EXECUTABLE" "$0")" && "$PWD_EXECUTABLE" -P)"
|
|
53
|
+
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/../../.." && "$PWD_EXECUTABLE" -P)"
|
|
9
54
|
cd "$SCRIPT_DIR"
|
|
10
55
|
|
|
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
|
-
|
|
56
|
+
case "$MODE" in
|
|
57
|
+
debug|local|release) ;;
|
|
58
|
+
*)
|
|
59
|
+
echo "Mode must be debug, local, or release" >&2
|
|
60
|
+
exit 2
|
|
61
|
+
;;
|
|
62
|
+
esac
|
|
63
|
+
if [ "$MODE" = "release" ]; then
|
|
64
|
+
if [ -z "$RELEASE_SUBTYPE" ]; then
|
|
65
|
+
echo "Release builds require an explicit subtype: initial-bootstrap or app-update." >&2
|
|
66
|
+
exit 2
|
|
67
|
+
fi
|
|
68
|
+
case "$RELEASE_SUBTYPE" in
|
|
69
|
+
initial-bootstrap|app-update) ;;
|
|
70
|
+
*)
|
|
71
|
+
echo "Release subtype must be initial-bootstrap or app-update." >&2
|
|
72
|
+
exit 2
|
|
73
|
+
;;
|
|
74
|
+
esac
|
|
75
|
+
if [ "$#" -ne 2 ]; then
|
|
76
|
+
echo "Release builds accept exactly one subtype argument." >&2
|
|
77
|
+
exit 2
|
|
78
|
+
fi
|
|
79
|
+
elif [ "$#" -ne 1 ]; then
|
|
80
|
+
echo "Debug and local builds do not accept a release subtype." >&2
|
|
81
|
+
exit 2
|
|
82
|
+
fi
|
|
83
|
+
readonly RELEASE_SUBTYPE
|
|
84
|
+
RELEASE_ARCHITECTURES=(arm64 x86_64)
|
|
85
|
+
|
|
86
|
+
CODESIGN_IDENTITY="${RECORDINGS_CODESIGN_IDENTITY:-}"
|
|
87
|
+
EXPECTED_TEAM_ID="${RECORDINGS_EXPECTED_TEAM_IDENTIFIER:-}"
|
|
88
|
+
NOTARY_PROFILE="${RECORDINGS_NOTARY_KEYCHAIN_PROFILE:-}"
|
|
89
|
+
INSTALLER_IDENTITY="${RECORDINGS_INSTALLER_CODESIGN_IDENTITY:-}"
|
|
90
|
+
RELEASE_SEQUENCE="${RECORDINGS_RELEASE_SEQUENCE:-}"
|
|
91
|
+
KEY_EPOCH="${RECORDINGS_RELEASE_KEY_EPOCH:-}"
|
|
92
|
+
ENVELOPE_EXPIRES_AT_UTC="${RECORDINGS_RELEASE_ENVELOPE_EXPIRES_AT_UTC:-}"
|
|
93
|
+
ENVELOPE_PRIVATE_KEY="${RECORDINGS_RELEASE_ENVELOPE_PRIVATE_KEY:-}"
|
|
94
|
+
ENVELOPE_PUBLIC_KEY="${RECORDINGS_RELEASE_ENVELOPE_PUBLIC_KEY:-}"
|
|
95
|
+
COMPATIBLE_COHORT_MANIFEST="${RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST:-}"
|
|
96
|
+
LOCAL_APPROVED_TARGET="${RECORDINGS_LOCAL_APPROVED_TARGET:-}"
|
|
97
|
+
LOCAL_APPROVED_TARGET_IDENTITY_KIND="${RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_KIND:-}"
|
|
98
|
+
LOCAL_APPROVED_TARGET_IDENTITY_SHA256="${RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_SHA256:-}"
|
|
99
|
+
PLIST_BUDDY="$(select_executable "/usr/libexec/PlistBuddy" "${RECORDINGS_TEST_PLIST_BUDDY_EXECUTABLE:-${PLIST_BUDDY:-}}")"
|
|
100
|
+
PLUTIL="$(select_executable "/usr/bin/plutil" "${RECORDINGS_TEST_PLUTIL_EXECUTABLE:-${PLUTIL:-}}")"
|
|
101
|
+
BUN_EXECUTABLE="${BUN_EXECUTABLE:-}"
|
|
102
|
+
TAILSCALE_RESOLVER="$PACKAGE_ROOT/scripts/resolve_tailscale_cli.sh"
|
|
103
|
+
BASH_EXECUTABLE="$(select_executable "/bin/bash" "${RECORDINGS_TEST_BASH_EXECUTABLE:-}")"
|
|
104
|
+
GIT_EXECUTABLE="$(select_executable "/usr/bin/git" "${RECORDINGS_TEST_GIT_EXECUTABLE:-}")"
|
|
105
|
+
TAR_EXECUTABLE="$(select_executable "/usr/bin/tar" "${RECORDINGS_TEST_TAR_EXECUTABLE:-}")"
|
|
106
|
+
SWIFT_EXECUTABLE="$(select_executable "/usr/bin/swift" "${RECORDINGS_TEST_SWIFT_EXECUTABLE:-}")"
|
|
107
|
+
CODESIGN_EXECUTABLE="$(select_executable "/usr/bin/codesign" "${RECORDINGS_TEST_CODESIGN_EXECUTABLE:-}")"
|
|
108
|
+
XCRUN_EXECUTABLE="$(select_executable "/usr/bin/xcrun" "${RECORDINGS_TEST_XCRUN_EXECUTABLE:-}")"
|
|
109
|
+
SPCTL_EXECUTABLE="$(select_executable "/usr/sbin/spctl" "${RECORDINGS_TEST_SPCTL_EXECUTABLE:-}")"
|
|
110
|
+
SYSPOLICY_CHECK_EXECUTABLE="$(select_executable "/usr/bin/syspolicy_check" "${RECORDINGS_TEST_SYSPOLICY_CHECK_EXECUTABLE:-}")"
|
|
111
|
+
DITTO_EXECUTABLE="$(select_executable "/usr/bin/ditto" "${RECORDINGS_TEST_DITTO_EXECUTABLE:-}")"
|
|
112
|
+
SHASUM_EXECUTABLE="$(select_executable "/usr/bin/shasum" "${RECORDINGS_TEST_SHASUM_EXECUTABLE:-}")"
|
|
113
|
+
AWK_EXECUTABLE="$(select_executable "/usr/bin/awk" "${RECORDINGS_TEST_AWK_EXECUTABLE:-}")"
|
|
114
|
+
BASENAME_EXECUTABLE="$(select_executable "/usr/bin/basename" "${RECORDINGS_TEST_BASENAME_EXECUTABLE:-}")"
|
|
115
|
+
CAT_EXECUTABLE="$(select_executable "/bin/cat" "${RECORDINGS_TEST_CAT_EXECUTABLE:-}")"
|
|
116
|
+
CHMOD_EXECUTABLE="$(select_executable "/bin/chmod" "${RECORDINGS_TEST_CHMOD_EXECUTABLE:-}")"
|
|
117
|
+
CP_EXECUTABLE="$(select_executable "/bin/cp" "${RECORDINGS_TEST_CP_EXECUTABLE:-}")"
|
|
118
|
+
ENV_EXECUTABLE="$(select_executable "/usr/bin/env" "${RECORDINGS_TEST_ENV_EXECUTABLE:-}")"
|
|
119
|
+
HEAD_EXECUTABLE="$(select_executable "/usr/bin/head" "${RECORDINGS_TEST_HEAD_EXECUTABLE:-}")"
|
|
120
|
+
HOSTNAME_EXECUTABLE="$(select_executable "/bin/hostname" "${RECORDINGS_TEST_HOSTNAME_EXECUTABLE:-}")"
|
|
121
|
+
MKDIR_EXECUTABLE="$(select_executable "/bin/mkdir" "${RECORDINGS_TEST_MKDIR_EXECUTABLE:-}")"
|
|
122
|
+
MKTEMP_EXECUTABLE="$(select_executable "/usr/bin/mktemp" "${RECORDINGS_TEST_MKTEMP_EXECUTABLE:-}")"
|
|
123
|
+
MV_EXECUTABLE="$(select_executable "/bin/mv" "${RECORDINGS_TEST_MV_EXECUTABLE:-}")"
|
|
124
|
+
LN_EXECUTABLE="$(select_executable "/bin/ln" "${RECORDINGS_TEST_LN_EXECUTABLE:-}")"
|
|
125
|
+
RMDIR_EXECUTABLE="$(select_executable "/bin/rmdir" "${RECORDINGS_TEST_RMDIR_EXECUTABLE:-}")"
|
|
126
|
+
RM_EXECUTABLE="$(select_executable "/bin/rm" "${RECORDINGS_TEST_RM_EXECUTABLE:-}")"
|
|
127
|
+
SED_EXECUTABLE="$(select_executable "/usr/bin/sed" "${RECORDINGS_TEST_SED_EXECUTABLE:-}")"
|
|
128
|
+
TR_EXECUTABLE="$(select_executable "/usr/bin/tr" "${RECORDINGS_TEST_TR_EXECUTABLE:-}")"
|
|
129
|
+
GREP_EXECUTABLE="$(select_executable "/usr/bin/grep" "${RECORDINGS_TEST_GREP_EXECUTABLE:-}")"
|
|
130
|
+
FIND_EXECUTABLE="$(select_executable "/usr/bin/find" "${RECORDINGS_TEST_FIND_EXECUTABLE:-}")"
|
|
131
|
+
LS_EXECUTABLE="$(select_executable "/bin/ls" "${RECORDINGS_TEST_LS_EXECUTABLE:-}")"
|
|
132
|
+
ID_EXECUTABLE="$(select_executable "/usr/bin/id" "${RECORDINGS_TEST_ID_EXECUTABLE:-}")"
|
|
133
|
+
LIPO_EXECUTABLE="$(select_executable "/usr/bin/lipo" "${RECORDINGS_TEST_LIPO_EXECUTABLE:-}")"
|
|
134
|
+
STAT_EXECUTABLE="$(select_executable "/usr/bin/stat" "${RECORDINGS_TEST_STAT_EXECUTABLE:-}")"
|
|
135
|
+
TEST_GIT_EXECUTABLE="$(select_executable "" "${RECORDINGS_TEST_GIT_EXECUTABLE:-}")"
|
|
136
|
+
TEST_SWIFT_EXECUTABLE="$(select_executable "" "${RECORDINGS_TEST_SWIFT_EXECUTABLE:-}")"
|
|
137
|
+
|
|
138
|
+
require_executable() {
|
|
139
|
+
local label="$1"
|
|
140
|
+
local executable="$2"
|
|
141
|
+
case "$executable" in
|
|
142
|
+
/*) ;;
|
|
143
|
+
*)
|
|
144
|
+
echo "$label must be configured as an absolute executable path." >&2
|
|
145
|
+
exit 1
|
|
146
|
+
;;
|
|
147
|
+
esac
|
|
148
|
+
if [ ! -f "$executable" ] || [ ! -x "$executable" ]; then
|
|
149
|
+
echo "$label is missing or is not an executable file: $executable" >&2
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
require_bun_executable() {
|
|
155
|
+
local executable="$1"
|
|
156
|
+
if [ -z "$executable" ]; then
|
|
157
|
+
echo "Release builds require BUN_EXECUTABLE as an explicit absolute Bun executable path." >&2
|
|
158
|
+
exit 1
|
|
159
|
+
fi
|
|
160
|
+
require_executable "BUN_EXECUTABLE" "$executable"
|
|
161
|
+
if ! "$ENV_EXECUTABLE" -i \
|
|
162
|
+
HOME="/tmp" \
|
|
163
|
+
PATH="$SANITIZED_PATH" \
|
|
164
|
+
"$executable" -e '
|
|
165
|
+
import { realpathSync, statSync } from "node:fs";
|
|
166
|
+
const expected = process.argv[1];
|
|
167
|
+
const actual = process.execPath;
|
|
168
|
+
if (!expected || realpathSync(actual) !== realpathSync(expected)) process.exit(66);
|
|
169
|
+
if (!statSync(actual).isFile()) process.exit(66);
|
|
170
|
+
if (!/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(Bun.version)) process.exit(66);
|
|
171
|
+
' "$executable"; then
|
|
172
|
+
echo "BUN_EXECUTABLE did not identify the Bun executable that actually ran." >&2
|
|
173
|
+
exit 1
|
|
174
|
+
fi
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
run_source_git() {
|
|
178
|
+
"$ENV_EXECUTABLE" -i \
|
|
179
|
+
HOME="/tmp" \
|
|
180
|
+
PATH="$SANITIZED_PATH" \
|
|
181
|
+
TMPDIR="/tmp" \
|
|
182
|
+
GIT_CONFIG_NOSYSTEM="1" \
|
|
183
|
+
GIT_CONFIG_GLOBAL="/dev/null" \
|
|
184
|
+
GIT_NO_REPLACE_OBJECTS="1" \
|
|
185
|
+
GIT_OPTIONAL_LOCKS="0" \
|
|
186
|
+
"$GIT_EXECUTABLE" \
|
|
187
|
+
-c core.fsmonitor=false \
|
|
188
|
+
-c core.hooksPath=/dev/null \
|
|
189
|
+
-C "$PACKAGE_ROOT" \
|
|
190
|
+
"$@"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
read_source_sha() {
|
|
194
|
+
local source_sha
|
|
195
|
+
if ! source_sha="$(run_source_git rev-parse --verify 'HEAD^{commit}')"; then
|
|
196
|
+
echo "Could not resolve the source git revision." >&2
|
|
197
|
+
return 1
|
|
198
|
+
fi
|
|
199
|
+
if ! [[ "$source_sha" =~ ^[0-9a-f]{40}$ ]]; then
|
|
200
|
+
echo "Resolved git revision is not a full 40-character commit SHA." >&2
|
|
201
|
+
return 1
|
|
202
|
+
fi
|
|
203
|
+
printf '%s\n' "$source_sha"
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
require_clean_source() {
|
|
207
|
+
local status
|
|
208
|
+
status="$(run_source_git status --porcelain=v1 --untracked-files=all)"
|
|
209
|
+
if [ -n "$status" ]; then
|
|
210
|
+
echo "Source worktree must be clean before building." >&2
|
|
211
|
+
return 1
|
|
212
|
+
fi
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
verify_source_unchanged() {
|
|
216
|
+
local current_sha
|
|
217
|
+
current_sha="$(read_source_sha)"
|
|
218
|
+
if [ "$current_sha" != "$SOURCE_SHA" ]; then
|
|
219
|
+
echo "Source revision changed during the build; refusing to emit an artifact." >&2
|
|
220
|
+
return 1
|
|
221
|
+
fi
|
|
222
|
+
require_clean_source
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
require_executable "GIT_EXECUTABLE" "$GIT_EXECUTABLE"
|
|
226
|
+
require_executable "TAR_EXECUTABLE" "$TAR_EXECUTABLE"
|
|
227
|
+
require_executable "SWIFT_EXECUTABLE" "$SWIFT_EXECUTABLE"
|
|
228
|
+
require_executable "CODESIGN_EXECUTABLE" "$CODESIGN_EXECUTABLE"
|
|
229
|
+
require_executable "DITTO_EXECUTABLE" "$DITTO_EXECUTABLE"
|
|
230
|
+
require_executable "BASH_EXECUTABLE" "$BASH_EXECUTABLE"
|
|
231
|
+
require_executable "PLIST_BUDDY" "$PLIST_BUDDY"
|
|
232
|
+
require_executable "PLUTIL" "$PLUTIL"
|
|
233
|
+
require_executable "AWK_EXECUTABLE" "$AWK_EXECUTABLE"
|
|
234
|
+
require_executable "BASENAME_EXECUTABLE" "$BASENAME_EXECUTABLE"
|
|
235
|
+
require_executable "CAT_EXECUTABLE" "$CAT_EXECUTABLE"
|
|
236
|
+
require_executable "CHMOD_EXECUTABLE" "$CHMOD_EXECUTABLE"
|
|
237
|
+
require_executable "CP_EXECUTABLE" "$CP_EXECUTABLE"
|
|
238
|
+
require_executable "ENV_EXECUTABLE" "$ENV_EXECUTABLE"
|
|
239
|
+
require_executable "HEAD_EXECUTABLE" "$HEAD_EXECUTABLE"
|
|
240
|
+
require_executable "HOSTNAME_EXECUTABLE" "$HOSTNAME_EXECUTABLE"
|
|
241
|
+
require_executable "MKDIR_EXECUTABLE" "$MKDIR_EXECUTABLE"
|
|
242
|
+
require_executable "MKTEMP_EXECUTABLE" "$MKTEMP_EXECUTABLE"
|
|
243
|
+
require_executable "MV_EXECUTABLE" "$MV_EXECUTABLE"
|
|
244
|
+
require_executable "LN_EXECUTABLE" "$LN_EXECUTABLE"
|
|
245
|
+
require_executable "RMDIR_EXECUTABLE" "$RMDIR_EXECUTABLE"
|
|
246
|
+
require_executable "RM_EXECUTABLE" "$RM_EXECUTABLE"
|
|
247
|
+
require_executable "SED_EXECUTABLE" "$SED_EXECUTABLE"
|
|
248
|
+
require_executable "TR_EXECUTABLE" "$TR_EXECUTABLE"
|
|
249
|
+
require_executable "GREP_EXECUTABLE" "$GREP_EXECUTABLE"
|
|
250
|
+
require_executable "FIND_EXECUTABLE" "$FIND_EXECUTABLE"
|
|
251
|
+
require_executable "LS_EXECUTABLE" "$LS_EXECUTABLE"
|
|
252
|
+
require_executable "ID_EXECUTABLE" "$ID_EXECUTABLE"
|
|
253
|
+
require_executable "STAT_EXECUTABLE" "$STAT_EXECUTABLE"
|
|
254
|
+
if [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
255
|
+
require_executable "LIPO_EXECUTABLE" "$LIPO_EXECUTABLE"
|
|
256
|
+
fi
|
|
257
|
+
require_bun_executable "$BUN_EXECUTABLE"
|
|
258
|
+
if [ "$MODE" = "release" ]; then
|
|
259
|
+
require_executable "XCRUN_EXECUTABLE" "$XCRUN_EXECUTABLE"
|
|
260
|
+
require_executable "SPCTL_EXECUTABLE" "$SPCTL_EXECUTABLE"
|
|
261
|
+
require_executable "SYSPOLICY_CHECK_EXECUTABLE" "$SYSPOLICY_CHECK_EXECUTABLE"
|
|
262
|
+
require_executable "SHASUM_EXECUTABLE" "$SHASUM_EXECUTABLE"
|
|
263
|
+
fi
|
|
264
|
+
|
|
265
|
+
SOURCE_SHA="$(read_source_sha)"
|
|
266
|
+
readonly SOURCE_SHA
|
|
267
|
+
require_clean_source
|
|
268
|
+
|
|
269
|
+
OPERATOR_HOME="${HOME:-}"
|
|
270
|
+
case "$OPERATOR_HOME" in
|
|
271
|
+
/*) ;;
|
|
272
|
+
*)
|
|
273
|
+
echo "Build requires an absolute HOME path." >&2
|
|
274
|
+
exit 1
|
|
275
|
+
;;
|
|
276
|
+
esac
|
|
277
|
+
if [ ! -d "$OPERATOR_HOME" ]; then
|
|
278
|
+
echo "Build HOME is not a directory: $OPERATOR_HOME" >&2
|
|
279
|
+
exit 1
|
|
280
|
+
fi
|
|
281
|
+
|
|
282
|
+
BUILD_ROOT="/tmp"
|
|
283
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
284
|
+
BUILD_ROOT="/private/var/recordings-build"
|
|
285
|
+
BUILD_ATTESTATION="/Library/Application Support/Hasna/Recordings/BuildTrust/isolated-builder-v1"
|
|
286
|
+
if [ "$($ID_EXECUTABLE -un)" != "_recordingsbuild" ]; then
|
|
287
|
+
echo "Release builds may only run as the isolated _recordingsbuild identity." >&2
|
|
288
|
+
exit 1
|
|
289
|
+
fi
|
|
290
|
+
if [ ! -f "$BUILD_ATTESTATION" ] || [ -L "$BUILD_ATTESTATION" ] || \
|
|
291
|
+
[ "$($STAT_EXECUTABLE -f '%u' "$BUILD_ATTESTATION")" != "0" ]; then
|
|
292
|
+
echo "Managed isolated-builder attestation is missing or unsafe." >&2
|
|
293
|
+
exit 1
|
|
294
|
+
fi
|
|
295
|
+
case "$($STAT_EXECUTABLE -f '%Lp' "$BUILD_ATTESTATION")" in
|
|
296
|
+
400|440|444) ;;
|
|
297
|
+
*) echo "Managed isolated-builder attestation mode is unsafe." >&2; exit 1 ;;
|
|
298
|
+
esac
|
|
299
|
+
if [ "$("$CAT_EXECUTABLE" "$BUILD_ATTESTATION")" != "recordings-isolated-builder-v1" ]; then
|
|
300
|
+
echo "Managed isolated-builder attestation content is invalid." >&2
|
|
301
|
+
exit 1
|
|
302
|
+
fi
|
|
303
|
+
if [ ! -d "$BUILD_ROOT" ] || [ -L "$BUILD_ROOT" ] || \
|
|
304
|
+
[ "$($STAT_EXECUTABLE -f '%u' "$BUILD_ROOT")" != "$($ID_EXECUTABLE -u)" ] || \
|
|
305
|
+
[ "$($STAT_EXECUTABLE -f '%Lp' "$BUILD_ROOT")" != "700" ]; then
|
|
306
|
+
echo "Managed release build root must be owned by _recordingsbuild with mode 0700." >&2
|
|
307
|
+
exit 1
|
|
308
|
+
fi
|
|
309
|
+
BUILD_ROOT_PARENT="$($DIRNAME_EXECUTABLE "$BUILD_ROOT")"
|
|
310
|
+
if [ "$($STAT_EXECUTABLE -f '%u' "$BUILD_ROOT_PARENT")" != "0" ]; then
|
|
311
|
+
echo "Managed release build parent must be root-owned." >&2
|
|
312
|
+
exit 1
|
|
313
|
+
fi
|
|
314
|
+
case "$($STAT_EXECUTABLE -f '%Lp' "$BUILD_ROOT_PARENT")" in
|
|
315
|
+
*[2367][0-9]|*[0-9][2367]) echo "Managed release build parent is group/other writable." >&2; exit 1 ;;
|
|
316
|
+
esac
|
|
317
|
+
fi
|
|
318
|
+
RELEASE_OUTPUT_ROOT=""
|
|
319
|
+
RELEASE_STAGING_DIR=""
|
|
320
|
+
RELEASE_FINAL_DIR=""
|
|
321
|
+
RELEASE_RESERVATION=""
|
|
322
|
+
RELEASE_RESERVATION_OWNED=0
|
|
323
|
+
RELEASE_DIRECTORY_PUBLISHED=0
|
|
324
|
+
RELEASE_PUBLICATION_IDENTITY_SHA256=""
|
|
325
|
+
PACKAGE_PUBLICATION_IDENTITY_SHA256=""
|
|
326
|
+
ENVELOPE_PUBLIC_KEY_SNAPSHOT=""
|
|
327
|
+
ENVELOPE_PUBLIC_KEY_SHA256=""
|
|
328
|
+
COMPATIBLE_COHORT_SHA256=""
|
|
329
|
+
BUILD_WORK_DIR="$($MKTEMP_EXECUTABLE -d "$BUILD_ROOT/recordings-native-build.XXXXXX")"
|
|
330
|
+
BUILD_HOME="$BUILD_WORK_DIR/home"
|
|
331
|
+
SWIFT_SCRATCH_PATH="$BUILD_WORK_DIR/swift"
|
|
332
|
+
SOURCE_PACKAGE_ROOT="$BUILD_WORK_DIR/source"
|
|
333
|
+
"$MKDIR_EXECUTABLE" -p "$BUILD_HOME" "$SWIFT_SCRATCH_PATH" "$SOURCE_PACKAGE_ROOT"
|
|
334
|
+
cleanup_build_work() {
|
|
335
|
+
local status=$?
|
|
336
|
+
trap - EXIT
|
|
337
|
+
"$RM_EXECUTABLE" -rf "$BUILD_WORK_DIR"
|
|
338
|
+
if [ "$RELEASE_DIRECTORY_PUBLISHED" -eq 0 ] && [ -n "$RELEASE_STAGING_DIR" ]; then
|
|
339
|
+
"$RM_EXECUTABLE" -rf "$RELEASE_STAGING_DIR"
|
|
340
|
+
fi
|
|
341
|
+
if [ "$RELEASE_RESERVATION_OWNED" -eq 1 ] && \
|
|
342
|
+
[ ! -e "$RELEASE_FINAL_DIR" ] && [ ! -L "$RELEASE_FINAL_DIR" ]; then
|
|
343
|
+
"$RM_EXECUTABLE" -rf "$RELEASE_RESERVATION"
|
|
344
|
+
fi
|
|
345
|
+
exit "$status"
|
|
346
|
+
}
|
|
347
|
+
trap cleanup_build_work EXIT
|
|
348
|
+
|
|
349
|
+
if [ "$HOST_PLATFORM" != "Darwin" ] && [ -n "$TEST_GIT_EXECUTABLE" ]; then
|
|
350
|
+
# Explicit non-Darwin fixtures do not contain an object database. Production Darwin
|
|
351
|
+
# builds never take this branch and always compile the exact archived commit below.
|
|
352
|
+
SOURCE_PACKAGE_ROOT="$PACKAGE_ROOT"
|
|
353
|
+
else
|
|
354
|
+
if ! run_source_git archive --format=tar "$SOURCE_SHA" | \
|
|
355
|
+
"$TAR_EXECUTABLE" -x -f - -C "$SOURCE_PACKAGE_ROOT"; then
|
|
356
|
+
echo "Could not materialize the exact source commit for an isolated build." >&2
|
|
357
|
+
exit 1
|
|
358
|
+
fi
|
|
359
|
+
fi
|
|
360
|
+
SOURCE_NATIVE_DIR="$SOURCE_PACKAGE_ROOT/src/native/Recordings"
|
|
361
|
+
RELEASE_LIFECYCLE_HELPER="$SOURCE_PACKAGE_ROOT/packaging/macos/release_lifecycle.ts"
|
|
362
|
+
TAILSCALE_RESOLVER="$SOURCE_PACKAGE_ROOT/scripts/resolve_tailscale_cli.sh"
|
|
363
|
+
APP_ENTITLEMENTS="$SOURCE_NATIVE_DIR/RecordingsLib/Recordings.entitlements"
|
|
364
|
+
HELPER_ENTITLEMENTS="$SOURCE_NATIVE_DIR/RecordingsLib/RecordingsCLI.entitlements"
|
|
365
|
+
if [ "$HOST_PLATFORM" != "Darwin" ] && [ "$SOURCE_PACKAGE_ROOT" = "$PACKAGE_ROOT" ]; then
|
|
366
|
+
# Preserve the historical fixture log contract. Production Darwin builds always
|
|
367
|
+
# sign against the absolute entitlements paths in the archived source snapshot.
|
|
368
|
+
APP_ENTITLEMENTS="RecordingsLib/Recordings.entitlements"
|
|
369
|
+
HELPER_ENTITLEMENTS="RecordingsLib/RecordingsCLI.entitlements"
|
|
370
|
+
fi
|
|
371
|
+
if [ "$SOURCE_PACKAGE_ROOT" != "$PACKAGE_ROOT" ]; then
|
|
372
|
+
for required_snapshot_input in \
|
|
373
|
+
"$SOURCE_NATIVE_DIR/Package.swift" \
|
|
374
|
+
"$SOURCE_NATIVE_DIR/RecordingsLib/Info.plist" \
|
|
375
|
+
"$SOURCE_NATIVE_DIR/RecordingsLib/Recordings.entitlements" \
|
|
376
|
+
"$SOURCE_NATIVE_DIR/RecordingsLib/RecordingsCLI.entitlements" \
|
|
377
|
+
"$SOURCE_PACKAGE_ROOT/scripts/build_companion_cli.sh" \
|
|
378
|
+
"$SOURCE_PACKAGE_ROOT/scripts/smoke_macos_app.sh" \
|
|
379
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" \
|
|
380
|
+
"$SOURCE_PACKAGE_ROOT/scripts/native_fs_guard.ts" \
|
|
381
|
+
"$SOURCE_PACKAGE_ROOT/scripts/build_native_fs_guard.sh" \
|
|
382
|
+
"$SOURCE_PACKAGE_ROOT/scripts/native/recordings_fs_guard.c" \
|
|
383
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/build_release_pkg.sh" \
|
|
384
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/release_lifecycle.ts" \
|
|
385
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/Verifier.entitlements" \
|
|
386
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/Empty.entitlements" \
|
|
387
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/artifact-verifier.sb" \
|
|
388
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/Library/LaunchDaemons/com.hasna.recordings.updater.plist" \
|
|
389
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/scripts/preinstall" \
|
|
390
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/scripts/postinstall" \
|
|
391
|
+
"$SOURCE_PACKAGE_ROOT/package.json" \
|
|
392
|
+
"$SOURCE_PACKAGE_ROOT/bun.lock" \
|
|
393
|
+
"$SOURCE_PACKAGE_ROOT/bunfig.toml"; do
|
|
394
|
+
if [ ! -f "$required_snapshot_input" ]; then
|
|
395
|
+
echo "Archived source input is missing: $required_snapshot_input" >&2
|
|
396
|
+
exit 1
|
|
95
397
|
fi
|
|
96
|
-
|
|
398
|
+
done
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
generate_and_verify_native_fs_guard() {
|
|
402
|
+
[ "$HOST_PLATFORM" = "Darwin" ] || return 0
|
|
403
|
+
local dependency_root="$BUILD_WORK_DIR/native-guard-dependencies"
|
|
404
|
+
local pack_root="$BUILD_WORK_DIR/native-guard-pack"
|
|
405
|
+
local addon="$SOURCE_PACKAGE_ROOT/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node"
|
|
406
|
+
"$MKDIR_EXECUTABLE" -p "$dependency_root" "$pack_root"
|
|
407
|
+
"$CP_EXECUTABLE" "$SOURCE_PACKAGE_ROOT/package.json" "$SOURCE_PACKAGE_ROOT/bun.lock" \
|
|
408
|
+
"$SOURCE_PACKAGE_ROOT/bunfig.toml" "$dependency_root/"
|
|
409
|
+
run_bun install \
|
|
410
|
+
--cwd "$dependency_root" \
|
|
411
|
+
--frozen-lockfile \
|
|
412
|
+
--ignore-scripts \
|
|
413
|
+
--minimum-release-age=604800 \
|
|
414
|
+
--no-progress
|
|
415
|
+
"$ENV_EXECUTABLE" -i \
|
|
416
|
+
HOME="$BUILD_HOME" \
|
|
417
|
+
PATH="$SANITIZED_PATH" \
|
|
418
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
419
|
+
"$BASH_EXECUTABLE" "$SOURCE_PACKAGE_ROOT/scripts/build_native_fs_guard.sh" \
|
|
420
|
+
"$addon" "$dependency_root/node_modules/node-api-headers/include"
|
|
421
|
+
/usr/bin/lipo -verify_arch arm64 x86_64 "$addon"
|
|
422
|
+
if [ "$MODE" = "release" ]; then
|
|
423
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --options runtime --timestamp "$addon"
|
|
424
|
+
else
|
|
425
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --options runtime "$addon"
|
|
426
|
+
fi
|
|
427
|
+
run_codesign --verify --strict --all-architectures --verbose=2 "$addon"
|
|
428
|
+
run_bun -e '
|
|
429
|
+
import { createRequire } from "node:module";
|
|
430
|
+
const addon = createRequire(import.meta.url)(process.argv[1]);
|
|
431
|
+
const expected = [
|
|
432
|
+
"chmodHandle", "close", "copyRegularNoReplaceAt", "fsyncHandle",
|
|
433
|
+
"linkNoReplaceAt", "mkdirAt", "openDirAt", "openRegularAt", "openTrustedHome",
|
|
434
|
+
"readDir", "readRegularAt", "removeTreeAt", "removeTreeHandleAt",
|
|
435
|
+
"renameHandleNoReplaceAt", "renameNoReplaceAt", "renameReplaceAt",
|
|
436
|
+
"sameBinding", "sha256Handle", "sha256RegularAt", "statAt", "statHandle",
|
|
437
|
+
"unlinkDirAt", "unlinkFileAt", "unlinkFileHandleAt", "writeFileAt",
|
|
438
|
+
];
|
|
439
|
+
const actual = Object.getOwnPropertyNames(addon).sort();
|
|
440
|
+
if (JSON.stringify(actual) !== JSON.stringify(expected)) {
|
|
441
|
+
console.error(`Native filesystem guard exports are incompatible: ${actual.join(", ")}`);
|
|
442
|
+
process.exit(66);
|
|
443
|
+
}
|
|
444
|
+
' "$addon"
|
|
445
|
+
(
|
|
446
|
+
cd "$SOURCE_PACKAGE_ROOT"
|
|
447
|
+
run_bun pm pack --destination "$pack_root" --ignore-scripts >/dev/null
|
|
448
|
+
)
|
|
449
|
+
local package_archives=("$pack_root"/*.tgz)
|
|
450
|
+
if [ "${#package_archives[@]}" -ne 1 ] || [ ! -f "${package_archives[0]}" ]; then
|
|
451
|
+
echo "Native filesystem guard package staging did not emit exactly one tarball." >&2
|
|
452
|
+
return 1
|
|
453
|
+
fi
|
|
454
|
+
local package_listing
|
|
455
|
+
package_listing="$("$TAR_EXECUTABLE" -tzf "${package_archives[0]}")"
|
|
456
|
+
if ! "$GREP_EXECUTABLE" -Fxq \
|
|
457
|
+
"package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node" \
|
|
458
|
+
<<<"$package_listing"; then
|
|
459
|
+
echo "Packed release tarball is missing the generated native filesystem guard." >&2
|
|
460
|
+
return 1
|
|
461
|
+
fi
|
|
462
|
+
if "$GREP_EXECUTABLE" -Eq \
|
|
463
|
+
'(^|/)(\.recordings-fs-guard-build|arm64\.node|x86_64\.node|.*\.(o|obj))($|/)' \
|
|
464
|
+
<<<"$package_listing"; then
|
|
465
|
+
echo "Packed release tarball contains native filesystem guard intermediates." >&2
|
|
466
|
+
return 1
|
|
467
|
+
fi
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
RUN_BUN_TEST_ENVIRONMENT=()
|
|
471
|
+
COMPANION_TEST_ENVIRONMENT=()
|
|
472
|
+
SMOKE_TEST_ENVIRONMENT=()
|
|
473
|
+
XCRUN_TEST_ENVIRONMENT=()
|
|
474
|
+
RELEASE_SENSITIVE_TEST_ENVIRONMENT=()
|
|
475
|
+
if [ "$HOST_PLATFORM" != "Darwin" ]; then
|
|
476
|
+
if [ -n "${MARKER_DIRECTORY:-}" ]; then
|
|
477
|
+
RUN_BUN_TEST_ENVIRONMENT+=("MARKER_DIRECTORY=$MARKER_DIRECTORY")
|
|
478
|
+
SMOKE_TEST_ENVIRONMENT+=("MARKER_DIRECTORY=$MARKER_DIRECTORY")
|
|
479
|
+
fi
|
|
480
|
+
COMPANION_TEST_ENVIRONMENT+=(
|
|
481
|
+
"BREAK_SIGNED_HELPER=${BREAK_SIGNED_HELPER:-0}"
|
|
482
|
+
"MALFORMED_SIGNED_HELPER_OUTPUT=${MALFORMED_SIGNED_HELPER_OUTPUT:-0}"
|
|
483
|
+
)
|
|
484
|
+
XCRUN_TEST_ENVIRONMENT+=(
|
|
485
|
+
"MARKER_DIRECTORY=${MARKER_DIRECTORY:-}"
|
|
486
|
+
"NOTARY_SUBMIT_REJECTED=${NOTARY_SUBMIT_REJECTED:-0}"
|
|
487
|
+
"NOTARY_LOG_ISSUES=${NOTARY_LOG_ISSUES:-0}"
|
|
488
|
+
)
|
|
489
|
+
RELEASE_SENSITIVE_TEST_ENVIRONMENT+=(
|
|
490
|
+
"MARKER_DIRECTORY=${MARKER_DIRECTORY:-}"
|
|
491
|
+
"EXPECTED_HELPER_ENTITLEMENTS=${EXPECTED_HELPER_ENTITLEMENTS:-}"
|
|
492
|
+
"EXTRA_HELPER_ENTITLEMENT=${EXTRA_HELPER_ENTITLEMENT:-0}"
|
|
493
|
+
"SIGNING_AUTHORITY=${SIGNING_AUTHORITY:-}"
|
|
494
|
+
"SIGNING_TEAM=${SIGNING_TEAM:-}"
|
|
495
|
+
"SIGNING_FLAGS=${SIGNING_FLAGS:-}"
|
|
496
|
+
"MISSING_TIMESTAMP=${MISSING_TIMESTAMP:-0}"
|
|
497
|
+
)
|
|
498
|
+
fi
|
|
499
|
+
|
|
500
|
+
run_bun() {
|
|
501
|
+
"$ENV_EXECUTABLE" -i \
|
|
502
|
+
HOME="$BUILD_HOME" \
|
|
503
|
+
PATH="$SANITIZED_PATH" \
|
|
504
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
505
|
+
${RUN_BUN_TEST_ENVIRONMENT[0]+"${RUN_BUN_TEST_ENVIRONMENT[@]}"} \
|
|
506
|
+
"$BUN_EXECUTABLE" "$@"
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
run_xcrun() {
|
|
510
|
+
"$ENV_EXECUTABLE" -i \
|
|
511
|
+
HOME="$OPERATOR_HOME" \
|
|
512
|
+
PATH="$SANITIZED_PATH" \
|
|
513
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
514
|
+
${XCRUN_TEST_ENVIRONMENT[0]+"${XCRUN_TEST_ENVIRONMENT[@]}"} \
|
|
515
|
+
"$XCRUN_EXECUTABLE" "$@"
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
run_release_sensitive_tool() {
|
|
519
|
+
local executable="$1"
|
|
520
|
+
shift
|
|
521
|
+
"$ENV_EXECUTABLE" -i \
|
|
522
|
+
HOME="$OPERATOR_HOME" \
|
|
523
|
+
PATH="$SANITIZED_PATH" \
|
|
524
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
525
|
+
${RELEASE_SENSITIVE_TEST_ENVIRONMENT[0]+"${RELEASE_SENSITIVE_TEST_ENVIRONMENT[@]}"} \
|
|
526
|
+
"$executable" "$@"
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
run_sensitive_tool() {
|
|
530
|
+
if [ "$MODE" = "release" ]; then
|
|
531
|
+
run_release_sensitive_tool "$@"
|
|
532
|
+
else
|
|
533
|
+
"$@"
|
|
97
534
|
fi
|
|
535
|
+
}
|
|
98
536
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
537
|
+
run_codesign() {
|
|
538
|
+
if [ "$MODE" = "release" ]; then
|
|
539
|
+
run_release_sensitive_tool "$CODESIGN_EXECUTABLE" "$@"
|
|
540
|
+
else
|
|
541
|
+
"$CODESIGN_EXECUTABLE" "$@"
|
|
542
|
+
fi
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
run_swift() {
|
|
546
|
+
"$ENV_EXECUTABLE" -i \
|
|
547
|
+
HOME="$BUILD_HOME" \
|
|
548
|
+
PATH="$SANITIZED_PATH" \
|
|
549
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
550
|
+
"$SWIFT_EXECUTABLE" "$@"
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
run_lipo() {
|
|
554
|
+
if [ "$MODE" = "release" ]; then
|
|
555
|
+
run_release_sensitive_tool "$LIPO_EXECUTABLE" "$@"
|
|
556
|
+
else
|
|
557
|
+
"$LIPO_EXECUTABLE" "$@"
|
|
558
|
+
fi
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
verify_exact_binary_architectures() {
|
|
562
|
+
local binary="$1"
|
|
563
|
+
shift
|
|
564
|
+
local actual_architectures
|
|
565
|
+
local actual_architecture
|
|
566
|
+
local expected_architecture
|
|
567
|
+
local actual_count=0
|
|
568
|
+
local match_count
|
|
569
|
+
|
|
570
|
+
if ! actual_architectures="$(run_lipo -archs "$binary")"; then
|
|
571
|
+
echo "Could not read binary architectures: $binary" >&2
|
|
572
|
+
return 1
|
|
573
|
+
fi
|
|
574
|
+
for actual_architecture in $actual_architectures; do
|
|
575
|
+
actual_count=$((actual_count + 1))
|
|
576
|
+
case " $* " in
|
|
577
|
+
*" $actual_architecture "*) ;;
|
|
578
|
+
*)
|
|
579
|
+
echo "Binary contains unsupported architecture $actual_architecture: $binary" >&2
|
|
580
|
+
return 1
|
|
581
|
+
;;
|
|
582
|
+
esac
|
|
583
|
+
done
|
|
584
|
+
if [ "$actual_count" -ne "$#" ]; then
|
|
585
|
+
echo "Binary architecture count does not match release policy: $binary" >&2
|
|
586
|
+
return 1
|
|
587
|
+
fi
|
|
588
|
+
for expected_architecture in "$@"; do
|
|
589
|
+
match_count=0
|
|
590
|
+
for actual_architecture in $actual_architectures; do
|
|
591
|
+
if [ "$actual_architecture" = "$expected_architecture" ]; then
|
|
592
|
+
match_count=$((match_count + 1))
|
|
593
|
+
fi
|
|
594
|
+
done
|
|
595
|
+
if [ "$match_count" -ne 1 ]; then
|
|
596
|
+
echo "Binary must contain exactly one $expected_architecture slice: $binary" >&2
|
|
597
|
+
return 1
|
|
105
598
|
fi
|
|
599
|
+
done
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
merge_release_swift_product() {
|
|
603
|
+
local product="$1"
|
|
604
|
+
local output="$2"
|
|
605
|
+
local arm64_input="$RELEASE_ARM64_PRODUCT_DIR/$product"
|
|
606
|
+
local x86_64_input="$RELEASE_X86_64_PRODUCT_DIR/$product"
|
|
607
|
+
verify_exact_binary_architectures "$arm64_input" arm64
|
|
608
|
+
verify_exact_binary_architectures "$x86_64_input" x86_64
|
|
609
|
+
run_lipo -create "$arm64_input" "$x86_64_input" -output "$output"
|
|
610
|
+
"$CHMOD_EXECUTABLE" 0755 "$output"
|
|
611
|
+
verify_exact_binary_architectures "$output" arm64 x86_64
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
normalize_unsigned_launch_file_mode() {
|
|
615
|
+
local path="$1"
|
|
616
|
+
local label="$2"
|
|
617
|
+
local hardlinked
|
|
618
|
+
local mode_match
|
|
619
|
+
|
|
620
|
+
if [ ! -f "$path" ] || [ -L "$path" ]; then
|
|
621
|
+
echo "$label is missing, linked, or not a regular file: $path" >&2
|
|
622
|
+
return 1
|
|
623
|
+
fi
|
|
624
|
+
if ! hardlinked="$("$FIND_EXECUTABLE" "$path" -xdev -type f -links +1 -print -quit)"; then
|
|
625
|
+
echo "Could not inspect $label link count: $path" >&2
|
|
626
|
+
return 1
|
|
627
|
+
fi
|
|
628
|
+
if [ -n "$hardlinked" ]; then
|
|
629
|
+
echo "$label must not be multiply linked: $path" >&2
|
|
630
|
+
return 1
|
|
631
|
+
fi
|
|
632
|
+
"$CHMOD_EXECUTABLE" 0755 "$path"
|
|
633
|
+
if ! mode_match="$("$FIND_EXECUTABLE" "$path" -xdev -type f -links 1 -perm 0755 -print -quit)"; then
|
|
634
|
+
echo "Could not verify $label mode: $path" >&2
|
|
635
|
+
return 1
|
|
636
|
+
fi
|
|
637
|
+
if [ "$mode_match" != "$path" ]; then
|
|
638
|
+
echo "$label must have exact mode 0755: $path" >&2
|
|
639
|
+
return 1
|
|
640
|
+
fi
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
normalize_unsigned_app_bundle_modes() {
|
|
644
|
+
local app="$1"
|
|
645
|
+
local unexpected
|
|
646
|
+
local hardlinked
|
|
647
|
+
local bad_mode
|
|
648
|
+
local launch_file
|
|
649
|
+
local -a launch_files=(
|
|
650
|
+
"$MACOS/Recordings"
|
|
651
|
+
"$HELPERS/recordings"
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
if [ ! -d "$app" ] || [ -L "$app" ]; then
|
|
655
|
+
echo "App bundle root is missing, linked, or not a directory: $app" >&2
|
|
656
|
+
return 1
|
|
657
|
+
fi
|
|
658
|
+
if [ -e "$UPDATE_CLIENT" ] || [ -L "$UPDATE_CLIENT" ]; then
|
|
659
|
+
launch_files+=("$UPDATE_CLIENT")
|
|
106
660
|
fi
|
|
107
|
-
if ! "$
|
|
108
|
-
echo "
|
|
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
|
-
|
|
661
|
+
if ! unexpected="$("$FIND_EXECUTABLE" "$app" -xdev ! -type d ! -type f -print -quit)"; then
|
|
662
|
+
echo "Could not inspect the app bundle tree structure." >&2
|
|
663
|
+
return 1
|
|
664
|
+
fi
|
|
665
|
+
if [ -n "$unexpected" ]; then
|
|
666
|
+
echo "App bundle tree contains a symbolic link or special file: $unexpected" >&2
|
|
667
|
+
return 1
|
|
668
|
+
fi
|
|
669
|
+
if ! hardlinked="$("$FIND_EXECUTABLE" "$app" -xdev -type f -links +1 -print -quit)"; then
|
|
670
|
+
echo "Could not inspect app bundle regular-file link counts." >&2
|
|
671
|
+
return 1
|
|
672
|
+
fi
|
|
673
|
+
if [ -n "$hardlinked" ]; then
|
|
674
|
+
echo "App bundle tree contains a multiply-linked regular file: $hardlinked" >&2
|
|
675
|
+
return 1
|
|
676
|
+
fi
|
|
677
|
+
if ! "$FIND_EXECUTABLE" "$app" -xdev -type d -exec "$CHMOD_EXECUTABLE" 0755 {} +; then
|
|
678
|
+
echo "Could not normalize app bundle directory modes." >&2
|
|
679
|
+
return 1
|
|
680
|
+
fi
|
|
681
|
+
if ! "$FIND_EXECUTABLE" "$app" -xdev -type f -exec "$CHMOD_EXECUTABLE" 0644 {} +; then
|
|
682
|
+
echo "Could not normalize app bundle data-file modes." >&2
|
|
683
|
+
return 1
|
|
684
|
+
fi
|
|
685
|
+
for launch_file in "${launch_files[@]}"; do
|
|
686
|
+
normalize_unsigned_launch_file_mode "$launch_file" "App bundle launch file"
|
|
687
|
+
done
|
|
688
|
+
if ! bad_mode="$("$FIND_EXECUTABLE" "$app" -xdev -type d ! -perm 0755 -print -quit)"; then
|
|
689
|
+
echo "Could not verify app bundle directory modes." >&2
|
|
690
|
+
return 1
|
|
691
|
+
fi
|
|
692
|
+
if [ -n "$bad_mode" ]; then
|
|
693
|
+
echo "App bundle directory does not have exact mode 0755: $bad_mode" >&2
|
|
694
|
+
return 1
|
|
695
|
+
fi
|
|
696
|
+
if ! bad_mode="$("$FIND_EXECUTABLE" "$app" -xdev -type f \
|
|
697
|
+
! -path "$MACOS/Recordings" \
|
|
698
|
+
! -path "$HELPERS/recordings" \
|
|
699
|
+
! -path "$UPDATE_CLIENT" \
|
|
700
|
+
! -perm 0644 -print -quit)"; then
|
|
701
|
+
echo "Could not verify app bundle data-file modes." >&2
|
|
702
|
+
return 1
|
|
703
|
+
fi
|
|
704
|
+
if [ -n "$bad_mode" ]; then
|
|
705
|
+
echo "App bundle data file does not have exact mode 0644: $bad_mode" >&2
|
|
706
|
+
return 1
|
|
707
|
+
fi
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
normalize_unsigned_app_data_file_mode() {
|
|
711
|
+
local path="$1"
|
|
712
|
+
local label="$2"
|
|
713
|
+
local hardlinked
|
|
714
|
+
local mode_match
|
|
715
|
+
|
|
716
|
+
if [ ! -f "$path" ] || [ -L "$path" ]; then
|
|
717
|
+
echo "$label is missing, linked, or not a regular file: $path" >&2
|
|
718
|
+
return 1
|
|
719
|
+
fi
|
|
720
|
+
if ! hardlinked="$("$FIND_EXECUTABLE" "$path" -xdev -type f -links +1 -print -quit)"; then
|
|
721
|
+
echo "Could not inspect $label link count: $path" >&2
|
|
722
|
+
return 1
|
|
723
|
+
fi
|
|
724
|
+
if [ -n "$hardlinked" ]; then
|
|
725
|
+
echo "$label must not be multiply linked: $path" >&2
|
|
726
|
+
return 1
|
|
727
|
+
fi
|
|
728
|
+
"$CHMOD_EXECUTABLE" 0644 "$path"
|
|
729
|
+
if ! mode_match="$("$FIND_EXECUTABLE" "$path" -xdev -type f -links 1 -perm 0644 -print -quit)"; then
|
|
730
|
+
echo "Could not verify $label mode: $path" >&2
|
|
731
|
+
return 1
|
|
732
|
+
fi
|
|
733
|
+
if [ "$mode_match" != "$path" ]; then
|
|
734
|
+
echo "$label must have exact mode 0644: $path" >&2
|
|
735
|
+
return 1
|
|
736
|
+
fi
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
verify_app_bundle_modes() {
|
|
740
|
+
local app="$1"
|
|
741
|
+
local unexpected
|
|
742
|
+
local hardlinked
|
|
743
|
+
local bad_mode
|
|
744
|
+
local launch_file
|
|
745
|
+
local mode_match
|
|
746
|
+
local main="$app/Contents/MacOS/Recordings"
|
|
747
|
+
local companion="$app/Contents/Helpers/recordings"
|
|
748
|
+
local client="$app/Contents/Helpers/recordings-update-client"
|
|
749
|
+
local -a launch_files=(
|
|
750
|
+
"$main"
|
|
751
|
+
"$companion"
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
if [ ! -d "$app" ] || [ -L "$app" ]; then
|
|
755
|
+
echo "Final unsigned app bundle root is missing, linked, or not a directory: $app" >&2
|
|
756
|
+
return 1
|
|
757
|
+
fi
|
|
758
|
+
if [ -e "$client" ] || [ -L "$client" ]; then
|
|
759
|
+
launch_files+=("$client")
|
|
760
|
+
fi
|
|
761
|
+
if ! unexpected="$("$FIND_EXECUTABLE" "$app" -xdev ! -type d ! -type f -print -quit)"; then
|
|
762
|
+
echo "Could not inspect the final unsigned app bundle tree structure." >&2
|
|
763
|
+
return 1
|
|
764
|
+
fi
|
|
765
|
+
if [ -n "$unexpected" ]; then
|
|
766
|
+
echo "Final unsigned app bundle contains a symbolic link or special file: $unexpected" >&2
|
|
767
|
+
return 1
|
|
768
|
+
fi
|
|
769
|
+
if ! hardlinked="$("$FIND_EXECUTABLE" "$app" -xdev -type f -links +1 -print -quit)"; then
|
|
770
|
+
echo "Could not inspect final unsigned app bundle link counts." >&2
|
|
771
|
+
return 1
|
|
772
|
+
fi
|
|
773
|
+
if [ -n "$hardlinked" ]; then
|
|
774
|
+
echo "Final unsigned app bundle contains a multiply-linked regular file: $hardlinked" >&2
|
|
775
|
+
return 1
|
|
776
|
+
fi
|
|
777
|
+
if ! bad_mode="$("$FIND_EXECUTABLE" "$app" -xdev -type d ! -perm 0755 -print -quit)"; then
|
|
778
|
+
echo "Could not verify final unsigned app bundle directory modes." >&2
|
|
779
|
+
return 1
|
|
780
|
+
fi
|
|
781
|
+
if [ -n "$bad_mode" ]; then
|
|
782
|
+
echo "Final unsigned app bundle directory does not have exact mode 0755: $bad_mode" >&2
|
|
783
|
+
return 1
|
|
784
|
+
fi
|
|
785
|
+
for launch_file in "${launch_files[@]}"; do
|
|
786
|
+
if ! mode_match="$("$FIND_EXECUTABLE" "$launch_file" -xdev -type f -links 1 -perm 0755 -print -quit)"; then
|
|
787
|
+
echo "Could not verify final app launch-file mode: $launch_file" >&2
|
|
788
|
+
return 1
|
|
161
789
|
fi
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
--label "Recordings signing cert ($(signing_host))" >/dev/null 2>&1 \
|
|
166
|
-
|| echo "WARN: could not persist signing certificate to vault ($cert_key)" >&2
|
|
167
|
-
secrets set "$key_key" "$(openssl base64 -A -in "$key")" --type private_key \
|
|
168
|
-
--label "Recordings signing key ($(signing_host))" >/dev/null 2>&1 \
|
|
169
|
-
|| echo "WARN: could not persist signing key to vault ($key_key)" >&2
|
|
170
|
-
fi
|
|
171
|
-
|
|
172
|
-
# Package as PKCS#12 for `security import`. macOS's Security framework cannot
|
|
173
|
-
# read the PBKDF2/AES encoding OpenSSL 3 emits by default, so prefer -legacy
|
|
174
|
-
# (RC2/3DES + SHA1 MAC) and fall back for OpenSSL builds without it. Use a
|
|
175
|
-
# non-empty transport passphrase (the keychain password): an empty p12
|
|
176
|
-
# password trips "MAC verification failed" on macOS import.
|
|
177
|
-
if ! openssl pkcs12 -export -legacy -inkey "$key" -in "$crt" -name "$SIGNING_CN" \
|
|
178
|
-
-out "$p12" -passout pass:"$kc_pw" >/dev/null 2>&1; then
|
|
179
|
-
if ! openssl pkcs12 -export -inkey "$key" -in "$crt" -name "$SIGNING_CN" \
|
|
180
|
-
-out "$p12" -passout pass:"$kc_pw" >/dev/null 2>&1; then
|
|
181
|
-
rm -rf "$work"; echo "ERROR: failed to package signing identity as PKCS#12" >&2; return 1
|
|
790
|
+
if [ "$mode_match" != "$launch_file" ]; then
|
|
791
|
+
echo "Final app launch file must be singly linked with exact mode 0755: $launch_file" >&2
|
|
792
|
+
return 1
|
|
182
793
|
fi
|
|
794
|
+
done
|
|
795
|
+
if ! bad_mode="$("$FIND_EXECUTABLE" "$app" -xdev -type f \
|
|
796
|
+
! -path "$main" \
|
|
797
|
+
! -path "$companion" \
|
|
798
|
+
! -path "$client" \
|
|
799
|
+
! -perm 0644 -print -quit)"; then
|
|
800
|
+
echo "Could not verify final unsigned app bundle data-file modes." >&2
|
|
801
|
+
return 1
|
|
183
802
|
fi
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
803
|
+
if [ -n "$bad_mode" ]; then
|
|
804
|
+
echo "Final unsigned app bundle data file does not have exact mode 0644: $bad_mode" >&2
|
|
805
|
+
return 1
|
|
806
|
+
fi
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
require_path_without_extended_acl() {
|
|
810
|
+
local path="$1"
|
|
811
|
+
local acl_listing
|
|
812
|
+
local acl_status
|
|
813
|
+
|
|
814
|
+
acl_listing="$("$LS_EXECUTABLE" -lade "$path")" || {
|
|
815
|
+
echo "Could not inspect app output extended ACLs on: $path" >&2
|
|
816
|
+
return 1
|
|
817
|
+
}
|
|
818
|
+
if printf '%s\n' "$acl_listing" | "$AWK_EXECUTABLE" '
|
|
819
|
+
NR == 1 && length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
|
|
820
|
+
NR > 1 && $1 ~ /^[0-9]+:$/ { found = 1 }
|
|
821
|
+
END { exit found ? 0 : 1 }
|
|
822
|
+
'; then
|
|
823
|
+
echo "App output contains an unexpected extended ACL: $path" >&2
|
|
824
|
+
return 1
|
|
825
|
+
else
|
|
826
|
+
acl_status=$?
|
|
827
|
+
if [ "$acl_status" -ne 1 ]; then
|
|
828
|
+
echo "Could not evaluate app output extended ACLs on: $path" >&2
|
|
829
|
+
return 1
|
|
830
|
+
fi
|
|
188
831
|
fi
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
require_app_tree_without_extended_acl() {
|
|
835
|
+
local tree="$1"
|
|
836
|
+
local acl_listing
|
|
837
|
+
local acl_status
|
|
838
|
+
|
|
839
|
+
require_path_without_extended_acl "$tree"
|
|
840
|
+
acl_listing="$("$LS_EXECUTABLE" -laeR "$tree")" || {
|
|
841
|
+
echo "Could not recursively inspect app output extended ACLs on: $tree" >&2
|
|
842
|
+
return 1
|
|
843
|
+
}
|
|
844
|
+
if printf '%s\n' "$acl_listing" | "$AWK_EXECUTABLE" '
|
|
845
|
+
length($1) == 11 && substr($1, 11, 1) == "+" { found = 1 }
|
|
846
|
+
$1 ~ /^[0-9]+:$/ { found = 1 }
|
|
847
|
+
END { exit found ? 0 : 1 }
|
|
848
|
+
'; then
|
|
849
|
+
echo "App output tree contains an unexpected extended ACL: $tree" >&2
|
|
850
|
+
return 1
|
|
851
|
+
else
|
|
852
|
+
acl_status=$?
|
|
853
|
+
if [ "$acl_status" -ne 1 ]; then
|
|
854
|
+
echo "Could not evaluate recursive app output extended ACLs on: $tree" >&2
|
|
855
|
+
return 1
|
|
856
|
+
fi
|
|
196
857
|
fi
|
|
197
|
-
|
|
858
|
+
}
|
|
198
859
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
[ -
|
|
860
|
+
require_root_owned_nonwritable_directory() {
|
|
861
|
+
local directory="$1"
|
|
862
|
+
local mode
|
|
863
|
+
[ -d "$directory" ] && [ ! -L "$directory" ] && \
|
|
864
|
+
[ "$("$STAT_EXECUTABLE" -f '%u' "$directory")" = "0" ] || {
|
|
865
|
+
echo "Compatible-cohort ancestry is missing, linked, or not root-owned: $directory" >&2
|
|
866
|
+
return 1
|
|
867
|
+
}
|
|
868
|
+
mode="$("$STAT_EXECUTABLE" -f '%Lp' "$directory")"
|
|
869
|
+
[ $((8#$mode & 8#022)) -eq 0 ] || {
|
|
870
|
+
echo "Compatible-cohort ancestry is group/other writable: $directory" >&2
|
|
871
|
+
return 1
|
|
872
|
+
}
|
|
873
|
+
require_path_without_extended_acl "$directory"
|
|
203
874
|
}
|
|
204
875
|
|
|
205
|
-
|
|
206
|
-
|
|
876
|
+
prepare_compatible_cohort_manifest() {
|
|
877
|
+
local cohort_root="/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts"
|
|
878
|
+
local relative current component leaf
|
|
879
|
+
[ "$(cd "$cohort_root" && "$PWD_EXECUTABLE" -P)" = "$cohort_root" ] || {
|
|
880
|
+
echo "Compatible-cohort directory is not canonical." >&2
|
|
881
|
+
return 1
|
|
882
|
+
}
|
|
883
|
+
require_root_owned_nonwritable_directory "/"
|
|
884
|
+
relative="${cohort_root#/}"
|
|
885
|
+
current=""
|
|
886
|
+
IFS='/' read -r -a cohort_components <<<"$relative"
|
|
887
|
+
for component in "${cohort_components[@]}"; do
|
|
888
|
+
current="$current/$component"
|
|
889
|
+
require_root_owned_nonwritable_directory "$current"
|
|
890
|
+
done
|
|
891
|
+
leaf="$("$BASENAME_EXECUTABLE" "$COMPATIBLE_COHORT_MANIFEST")"
|
|
892
|
+
[ "$COMPATIBLE_COHORT_MANIFEST" = "$cohort_root/$leaf" ] || {
|
|
893
|
+
echo "Compatible-cohort manifest path must be canonical." >&2
|
|
894
|
+
return 1
|
|
895
|
+
}
|
|
896
|
+
[ -f "$COMPATIBLE_COHORT_MANIFEST" ] && [ ! -L "$COMPATIBLE_COHORT_MANIFEST" ] && \
|
|
897
|
+
[ "$("$STAT_EXECUTABLE" -f '%u' "$COMPATIBLE_COHORT_MANIFEST")" = "0" ] && \
|
|
898
|
+
[ "$("$STAT_EXECUTABLE" -f '%Lp' "$COMPATIBLE_COHORT_MANIFEST")" = "444" ] && \
|
|
899
|
+
[ "$("$FIND_EXECUTABLE" "$COMPATIBLE_COHORT_MANIFEST" -xdev -type f -links 1 -perm 0444 -print -quit)" = "$COMPATIBLE_COHORT_MANIFEST" ] || {
|
|
900
|
+
echo "Compatible-cohort manifest must be a singly linked root-owned mode-0444 regular file." >&2
|
|
901
|
+
return 1
|
|
902
|
+
}
|
|
903
|
+
require_path_without_extended_acl "$COMPATIBLE_COHORT_MANIFEST"
|
|
904
|
+
COMPATIBLE_COHORT_SNAPSHOT="$BUILD_WORK_DIR/compatible-cohort.json"
|
|
905
|
+
COMPATIBLE_COHORT_SHA256="$(run_bun \
|
|
906
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" snapshot-regular-file \
|
|
907
|
+
--source "$COMPATIBLE_COHORT_MANIFEST" \
|
|
908
|
+
--destination "$COMPATIBLE_COHORT_SNAPSHOT" \
|
|
909
|
+
--maximum-bytes 65536)"
|
|
910
|
+
[ "$leaf" = "${COMPATIBLE_COHORT_SHA256}.json" ] || {
|
|
911
|
+
echo "Compatible-cohort manifest filename must authenticate its snapshotted contents." >&2
|
|
912
|
+
return 1
|
|
913
|
+
}
|
|
914
|
+
run_bun "$RELEASE_LIFECYCLE_HELPER" validate-compatible-cohort \
|
|
915
|
+
--manifest "$COMPATIBLE_COHORT_SNAPSHOT" \
|
|
916
|
+
--public-key "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
917
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
918
|
+
--key-epoch "$KEY_EPOCH"
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
prepare_envelope_public_key() {
|
|
922
|
+
ENVELOPE_PUBLIC_KEY_SNAPSHOT="$BUILD_WORK_DIR/release-envelope-public.raw"
|
|
923
|
+
ENVELOPE_PUBLIC_KEY_SHA256="$(run_bun \
|
|
924
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" snapshot-regular-file \
|
|
925
|
+
--source "$ENVELOPE_PUBLIC_KEY" \
|
|
926
|
+
--destination "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
927
|
+
--maximum-bytes 32 \
|
|
928
|
+
--expected-bytes 32)"
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
BUILD_CONFIGURATION="release"
|
|
932
|
+
ARTIFACT_POLICY="release"
|
|
933
|
+
APPROVED_TARGET="fleet"
|
|
934
|
+
APPROVED_TARGET_IDENTITY_KIND="none"
|
|
935
|
+
APPROVED_TARGET_IDENTITY_SHA256="none"
|
|
936
|
+
BUILDER_IDENTITY_KIND="none"
|
|
937
|
+
BUILDER_IDENTITY_SHA256="none"
|
|
938
|
+
if [ "$MODE" = "release" ]; then
|
|
939
|
+
if [ -z "$CODESIGN_IDENTITY" ] || [ "$CODESIGN_IDENTITY" = "-" ]; then
|
|
940
|
+
echo "Release builds require RECORDINGS_CODESIGN_IDENTITY for a Developer ID Application identity." >&2
|
|
941
|
+
exit 1
|
|
942
|
+
fi
|
|
943
|
+
if [ -z "$EXPECTED_TEAM_ID" ]; then
|
|
944
|
+
echo "Release builds require RECORDINGS_EXPECTED_TEAM_IDENTIFIER to pin the Developer ID team." >&2
|
|
945
|
+
exit 1
|
|
946
|
+
fi
|
|
947
|
+
if [ -z "$NOTARY_PROFILE" ]; then
|
|
948
|
+
echo "Release builds require RECORDINGS_NOTARY_KEYCHAIN_PROFILE for notarization." >&2
|
|
949
|
+
exit 1
|
|
950
|
+
fi
|
|
951
|
+
if ! [[ "$RELEASE_SEQUENCE" =~ ^[1-9][0-9]*$ ]] || ! [[ "$KEY_EPOCH" =~ ^[1-9][0-9]*$ ]]; then
|
|
952
|
+
echo "Release builds require positive release sequence and key epoch values." >&2
|
|
953
|
+
exit 1
|
|
954
|
+
fi
|
|
955
|
+
if [ -z "$ENVELOPE_EXPIRES_AT_UTC" ]; then
|
|
956
|
+
echo "Release builds require an explicit release-envelope expiry." >&2
|
|
957
|
+
exit 1
|
|
958
|
+
fi
|
|
959
|
+
for envelope_key in "$ENVELOPE_PRIVATE_KEY" "$ENVELOPE_PUBLIC_KEY"; do
|
|
960
|
+
case "$envelope_key" in /*) ;; *) echo "Release envelope key paths must be absolute." >&2; exit 1 ;; esac
|
|
961
|
+
if [ ! -f "$envelope_key" ] || [ -L "$envelope_key" ]; then
|
|
962
|
+
echo "A release envelope key file is missing or unsafe." >&2
|
|
963
|
+
exit 1
|
|
964
|
+
fi
|
|
965
|
+
done
|
|
966
|
+
prepare_envelope_public_key
|
|
967
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
968
|
+
if [[ "$INSTALLER_IDENTITY" != "Developer ID Installer:"* ]]; then
|
|
969
|
+
echo "Initial-bootstrap releases require RECORDINGS_INSTALLER_CODESIGN_IDENTITY for the Developer ID Installer identity." >&2
|
|
970
|
+
exit 1
|
|
971
|
+
fi
|
|
972
|
+
if [ -n "$COMPATIBLE_COHORT_MANIFEST" ]; then
|
|
973
|
+
echo "Initial-bootstrap releases do not consume RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST." >&2
|
|
974
|
+
exit 1
|
|
975
|
+
fi
|
|
976
|
+
else
|
|
977
|
+
if [ -n "$INSTALLER_IDENTITY" ]; then
|
|
978
|
+
echo "App-update releases do not consume RECORDINGS_INSTALLER_CODESIGN_IDENTITY." >&2
|
|
979
|
+
exit 1
|
|
980
|
+
fi
|
|
981
|
+
if [ "$HOST_PLATFORM" != "Darwin" ] && [ -n "$TEST_GIT_EXECUTABLE" ]; then
|
|
982
|
+
[ -f "$COMPATIBLE_COHORT_MANIFEST" ] && [ ! -L "$COMPATIBLE_COHORT_MANIFEST" ] || {
|
|
983
|
+
echo "App-update test fixture requires one regular compatible-cohort manifest." >&2
|
|
984
|
+
exit 1
|
|
985
|
+
}
|
|
986
|
+
else
|
|
987
|
+
case "$COMPATIBLE_COHORT_MANIFEST" in
|
|
988
|
+
"/Library/Application Support/Hasna/Recordings/BuildTrust/compatible-cohorts/"*.json) ;;
|
|
989
|
+
*) echo "App-update releases require one root-preauthorized RECORDINGS_RELEASE_COMPATIBLE_COHORT_MANIFEST." >&2; exit 1 ;;
|
|
990
|
+
esac
|
|
991
|
+
fi
|
|
992
|
+
fi
|
|
993
|
+
elif [ "$MODE" = "local" ]; then
|
|
994
|
+
if [ "$LOCAL_APPROVED_TARGET" != "station06" ]; then
|
|
995
|
+
echo "Local-only builds currently require RECORDINGS_LOCAL_APPROVED_TARGET=station06." >&2
|
|
996
|
+
exit 1
|
|
997
|
+
fi
|
|
998
|
+
if ! [[ "$LOCAL_APPROVED_TARGET_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]]; then
|
|
999
|
+
echo "Local-only builds require an authenticated RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_SHA256." >&2
|
|
1000
|
+
exit 1
|
|
1001
|
+
fi
|
|
1002
|
+
if [ "$LOCAL_APPROVED_TARGET_IDENTITY_KIND" != "tailscale_node_id_sha256" ]; then
|
|
1003
|
+
echo "New local-only builds require RECORDINGS_LOCAL_APPROVED_TARGET_IDENTITY_KIND=tailscale_node_id_sha256." >&2
|
|
1004
|
+
exit 1
|
|
1005
|
+
fi
|
|
1006
|
+
BUILD_HOST="$("$HOSTNAME_EXECUTABLE" -s)"
|
|
1007
|
+
if [ "$BUILD_HOST" = "$LOCAL_APPROVED_TARGET" ]; then
|
|
1008
|
+
echo "Local-only artifacts must be built on a non-target Mac." >&2
|
|
1009
|
+
exit 1
|
|
1010
|
+
fi
|
|
1011
|
+
if [ ! -r "$TAILSCALE_RESOLVER" ]; then
|
|
1012
|
+
echo "Packaged Tailscale CLI resolver is missing." >&2
|
|
1013
|
+
exit 1
|
|
1014
|
+
fi
|
|
1015
|
+
# shellcheck source=/dev/null
|
|
1016
|
+
source "$TAILSCALE_RESOLVER"
|
|
1017
|
+
if ! TAILSCALE_CLI="$(recordings_resolve_trusted_tailscale_app_cli "$BUILD_WORK_DIR")"; then
|
|
1018
|
+
echo "Tailscale is required to authenticate the non-target build Mac." >&2
|
|
1019
|
+
exit 1
|
|
1020
|
+
fi
|
|
1021
|
+
if ! BUILDER_IDENTITY_SHA256="$(recordings_run_trusted_tailscale_status "$TAILSCALE_CLI" "$BUILD_WORK_DIR" | run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" tailscale-node-id-sha256 --expected-hostname "$BUILD_HOST")"; then
|
|
1022
|
+
echo "Could not authenticate the live Tailscale node identity for the build Mac." >&2
|
|
1023
|
+
exit 1
|
|
1024
|
+
fi
|
|
1025
|
+
if ! [[ "$BUILDER_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]]; then
|
|
1026
|
+
echo "Build Mac Tailscale node identity did not produce a valid SHA-256 digest." >&2
|
|
1027
|
+
exit 1
|
|
1028
|
+
fi
|
|
1029
|
+
BUILDER_IDENTITY_KIND="tailscale_node_id_sha256"
|
|
1030
|
+
if [ "$BUILDER_IDENTITY_SHA256" = "$LOCAL_APPROVED_TARGET_IDENTITY_SHA256" ]; then
|
|
1031
|
+
echo "Local-only artifacts must be built on a different authenticated Tailscale node." >&2
|
|
1032
|
+
exit 1
|
|
1033
|
+
fi
|
|
1034
|
+
CODESIGN_IDENTITY="-"
|
|
1035
|
+
EXPECTED_TEAM_ID="ADHOC"
|
|
1036
|
+
ARTIFACT_POLICY="local_only"
|
|
1037
|
+
APPROVED_TARGET="$LOCAL_APPROVED_TARGET"
|
|
1038
|
+
APPROVED_TARGET_IDENTITY_KIND="$LOCAL_APPROVED_TARGET_IDENTITY_KIND"
|
|
1039
|
+
APPROVED_TARGET_IDENTITY_SHA256="$LOCAL_APPROVED_TARGET_IDENTITY_SHA256"
|
|
1040
|
+
echo "WARNING: local-only artifacts are ad-hoc signed, non-notarized, and restricted to ${APPROVED_TARGET}." >&2
|
|
1041
|
+
echo "WARNING: installing can change code identity and require Microphone or Accessibility reauthorization." >&2
|
|
1042
|
+
else
|
|
1043
|
+
BUILD_CONFIGURATION="debug"
|
|
1044
|
+
CODESIGN_IDENTITY="-"
|
|
1045
|
+
EXPECTED_TEAM_ID="ADHOC"
|
|
1046
|
+
ARTIFACT_POLICY="local_only"
|
|
1047
|
+
APPROVED_TARGET="debug-only"
|
|
1048
|
+
echo "WARNING: debug builds are ad-hoc signed and non-distributable; never install or upload this output." >&2
|
|
1049
|
+
fi
|
|
207
1050
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
1051
|
+
COMPATIBLE_COHORT_SNAPSHOT=""
|
|
1052
|
+
if [ "$MODE" = "release" ] && [ "$RELEASE_SUBTYPE" = "app-update" ]; then
|
|
1053
|
+
if [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1054
|
+
prepare_compatible_cohort_manifest
|
|
1055
|
+
elif [ -n "$TEST_GIT_EXECUTABLE" ]; then
|
|
1056
|
+
COMPATIBLE_COHORT_SNAPSHOT="$BUILD_WORK_DIR/compatible-cohort-fixture.json"
|
|
1057
|
+
COMPATIBLE_COHORT_SHA256="$(run_bun \
|
|
1058
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" snapshot-regular-file \
|
|
1059
|
+
--source "$COMPATIBLE_COHORT_MANIFEST" \
|
|
1060
|
+
--destination "$COMPATIBLE_COHORT_SNAPSHOT" \
|
|
1061
|
+
--maximum-bytes 65536)"
|
|
1062
|
+
run_bun "$RELEASE_LIFECYCLE_HELPER" validate-compatible-cohort \
|
|
1063
|
+
--manifest "$COMPATIBLE_COHORT_SNAPSHOT" \
|
|
1064
|
+
--public-key "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
1065
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
1066
|
+
--key-epoch "$KEY_EPOCH"
|
|
1067
|
+
fi
|
|
1068
|
+
fi
|
|
1069
|
+
|
|
1070
|
+
generate_and_verify_native_fs_guard
|
|
1071
|
+
|
|
1072
|
+
echo "Building Recordings.app ($MODE)..."
|
|
1073
|
+
if [ "$MODE" = "release" ]; then
|
|
1074
|
+
OUTPUT_BUILD_DIR="$BUILD_ROOT/release-output"
|
|
1075
|
+
else
|
|
1076
|
+
OUTPUT_BUILD_DIR="$SCRIPT_DIR/.build/$BUILD_CONFIGURATION"
|
|
1077
|
+
fi
|
|
1078
|
+
OUTPUT_APP_DIR="$OUTPUT_BUILD_DIR/Recordings.app"
|
|
1079
|
+
SWIFT_PRODUCT_DIR="$SWIFT_SCRATCH_PATH/$BUILD_CONFIGURATION"
|
|
1080
|
+
APP_DIR="$BUILD_WORK_DIR/Recordings.app"
|
|
211
1081
|
CONTENTS="$APP_DIR/Contents"
|
|
212
1082
|
MACOS="$CONTENTS/MacOS"
|
|
213
1083
|
RESOURCES="$CONTENTS/Resources"
|
|
1084
|
+
HELPERS="$CONTENTS/Helpers"
|
|
1085
|
+
COMPANION_BUILD_SCRIPT="$SOURCE_PACKAGE_ROOT/scripts/build_companion_cli.sh"
|
|
1086
|
+
SMOKE_SCRIPT="$SOURCE_PACKAGE_ROOT/scripts/smoke_macos_app.sh"
|
|
214
1087
|
|
|
215
|
-
|
|
216
|
-
|
|
1088
|
+
if [ "$MODE" = "release" ]; then
|
|
1089
|
+
if [ -e "$OUTPUT_BUILD_DIR" ]; then
|
|
1090
|
+
[ -d "$OUTPUT_BUILD_DIR" ] && [ ! -L "$OUTPUT_BUILD_DIR" ] && \
|
|
1091
|
+
[ "$("$STAT_EXECUTABLE" -f '%u' "$OUTPUT_BUILD_DIR")" = "$("$ID_EXECUTABLE" -u)" ] && \
|
|
1092
|
+
[ "$("$STAT_EXECUTABLE" -f '%Lp' "$OUTPUT_BUILD_DIR")" = "700" ] || {
|
|
1093
|
+
echo "Managed release output must be an isolated builder-owned 0700 directory." >&2
|
|
1094
|
+
exit 1
|
|
1095
|
+
}
|
|
1096
|
+
else
|
|
1097
|
+
"$MKDIR_EXECUTABLE" -m 0700 "$OUTPUT_BUILD_DIR"
|
|
1098
|
+
fi
|
|
1099
|
+
else
|
|
1100
|
+
"$RM_EXECUTABLE" -rf "$OUTPUT_BUILD_DIR"
|
|
1101
|
+
"$MKDIR_EXECUTABLE" -p "$OUTPUT_BUILD_DIR"
|
|
1102
|
+
fi
|
|
1103
|
+
if [ "$MODE" = "release" ]; then
|
|
1104
|
+
run_swift test -c "$BUILD_CONFIGURATION" \
|
|
1105
|
+
--package-path "$SOURCE_NATIVE_DIR" \
|
|
1106
|
+
--scratch-path "$SWIFT_SCRATCH_PATH/tests"
|
|
1107
|
+
fi
|
|
1108
|
+
SWIFT_RESOURCE_PRODUCT_DIR="$SWIFT_PRODUCT_DIR"
|
|
1109
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1110
|
+
RELEASE_ARM64_PRODUCT_DIR=""
|
|
1111
|
+
RELEASE_X86_64_PRODUCT_DIR=""
|
|
1112
|
+
RELEASE_UPDATER_PRODUCTS=(recordings-update-client recordings-envelope-signer)
|
|
1113
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1114
|
+
RELEASE_UPDATER_PRODUCTS+=(recordings-update-broker recordings-bootstrap-preflight)
|
|
1115
|
+
fi
|
|
1116
|
+
for swift_architecture in "${RELEASE_ARCHITECTURES[@]}"; do
|
|
1117
|
+
swift_arch_scratch="$SWIFT_SCRATCH_PATH/$swift_architecture"
|
|
1118
|
+
run_swift build -c "$BUILD_CONFIGURATION" \
|
|
1119
|
+
--arch "$swift_architecture" \
|
|
1120
|
+
--product App \
|
|
1121
|
+
--package-path "$SOURCE_NATIVE_DIR" \
|
|
1122
|
+
--scratch-path "$swift_arch_scratch"
|
|
1123
|
+
for updater_product in "${RELEASE_UPDATER_PRODUCTS[@]}"; do
|
|
1124
|
+
run_swift build -c "$BUILD_CONFIGURATION" \
|
|
1125
|
+
--arch "$swift_architecture" \
|
|
1126
|
+
--product "$updater_product" \
|
|
1127
|
+
--package-path "$SOURCE_NATIVE_DIR" \
|
|
1128
|
+
--scratch-path "$swift_arch_scratch"
|
|
1129
|
+
done
|
|
1130
|
+
swift_arch_product_dir="$(run_swift build -c "$BUILD_CONFIGURATION" \
|
|
1131
|
+
--arch "$swift_architecture" \
|
|
1132
|
+
--package-path "$SOURCE_NATIVE_DIR" \
|
|
1133
|
+
--scratch-path "$swift_arch_scratch" \
|
|
1134
|
+
--show-bin-path)"
|
|
1135
|
+
case "$swift_arch_product_dir" in
|
|
1136
|
+
"$swift_arch_scratch"/*) ;;
|
|
1137
|
+
*)
|
|
1138
|
+
echo "Swift product path escaped the architecture-specific scratch root." >&2
|
|
1139
|
+
exit 1
|
|
1140
|
+
;;
|
|
1141
|
+
esac
|
|
1142
|
+
if [ ! -d "$swift_arch_product_dir" ]; then
|
|
1143
|
+
echo "Swift did not emit an architecture-specific product directory." >&2
|
|
1144
|
+
exit 1
|
|
1145
|
+
fi
|
|
1146
|
+
if [ "$swift_architecture" = "arm64" ]; then
|
|
1147
|
+
RELEASE_ARM64_PRODUCT_DIR="$swift_arch_product_dir"
|
|
1148
|
+
else
|
|
1149
|
+
RELEASE_X86_64_PRODUCT_DIR="$swift_arch_product_dir"
|
|
1150
|
+
fi
|
|
1151
|
+
done
|
|
1152
|
+
SWIFT_PRODUCT_DIR="$BUILD_WORK_DIR/swift-products-universal"
|
|
1153
|
+
"$MKDIR_EXECUTABLE" -p "$SWIFT_PRODUCT_DIR"
|
|
1154
|
+
SWIFT_RESOURCE_PRODUCT_DIR="$RELEASE_ARM64_PRODUCT_DIR"
|
|
1155
|
+
merge_release_swift_product "App" "$SWIFT_PRODUCT_DIR/App"
|
|
1156
|
+
merge_release_swift_product "recordings-update-client" "$SWIFT_PRODUCT_DIR/recordings-update-client"
|
|
1157
|
+
merge_release_swift_product "recordings-envelope-signer" "$SWIFT_PRODUCT_DIR/recordings-envelope-signer"
|
|
1158
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1159
|
+
merge_release_swift_product "recordings-update-broker" "$SWIFT_PRODUCT_DIR/recordings-update-broker"
|
|
1160
|
+
merge_release_swift_product "recordings-bootstrap-preflight" "$SWIFT_PRODUCT_DIR/recordings-bootstrap-preflight"
|
|
1161
|
+
fi
|
|
1162
|
+
else
|
|
1163
|
+
run_swift build -c "$BUILD_CONFIGURATION" \
|
|
1164
|
+
--product App \
|
|
1165
|
+
--package-path "$SOURCE_NATIVE_DIR" \
|
|
1166
|
+
--scratch-path "$SWIFT_SCRATCH_PATH"
|
|
1167
|
+
fi
|
|
1168
|
+
if [ "$HOST_PLATFORM" != "Darwin" ] && [ -n "$TEST_SWIFT_EXECUTABLE" ]; then
|
|
1169
|
+
SWIFT_PRODUCT_DIR="$OUTPUT_BUILD_DIR"
|
|
1170
|
+
SWIFT_RESOURCE_PRODUCT_DIR="$OUTPUT_BUILD_DIR"
|
|
1171
|
+
fi
|
|
1172
|
+
|
|
1173
|
+
"$RM_EXECUTABLE" -rf "$APP_DIR"
|
|
1174
|
+
"$MKDIR_EXECUTABLE" -p "$MACOS" "$RESOURCES" "$HELPERS"
|
|
1175
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/App" "$MACOS/Recordings"
|
|
1176
|
+
UPDATE_BROKER="$BUILD_WORK_DIR/com.hasna.recordings.updater"
|
|
1177
|
+
UPDATE_CLIENT="$HELPERS/recordings-update-client"
|
|
1178
|
+
ENVELOPE_SIGNER="$BUILD_WORK_DIR/recordings-envelope-signer"
|
|
1179
|
+
BOOTSTRAP_PREFLIGHT_VERIFIER="$BUILD_WORK_DIR/recordings-bootstrap-preflight"
|
|
1180
|
+
ARTIFACT_VERIFIER="$BUILD_WORK_DIR/com.hasna.recordings.artifact-verifier"
|
|
1181
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1182
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-update-client" "$UPDATE_CLIENT"
|
|
1183
|
+
"$CHMOD_EXECUTABLE" 0755 "$UPDATE_CLIENT"
|
|
1184
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-envelope-signer" "$ENVELOPE_SIGNER"
|
|
1185
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1186
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-update-broker" "$UPDATE_BROKER"
|
|
1187
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-bootstrap-preflight" "$BOOTSTRAP_PREFLIGHT_VERIFIER"
|
|
1188
|
+
"$CHMOD_EXECUTABLE" 0755 "$BOOTSTRAP_PREFLIGHT_VERIFIER"
|
|
1189
|
+
VERIFIER_ARM64="$BUILD_WORK_DIR/recordings-artifact-verifier.arm64"
|
|
1190
|
+
VERIFIER_X86_64="$BUILD_WORK_DIR/recordings-artifact-verifier.x86_64"
|
|
1191
|
+
for verifier_target in arm64 x64; do
|
|
1192
|
+
verifier_output="$VERIFIER_ARM64"
|
|
1193
|
+
[ "$verifier_target" = "arm64" ] || verifier_output="$VERIFIER_X86_64"
|
|
1194
|
+
run_bun build \
|
|
1195
|
+
--compile \
|
|
1196
|
+
--target="bun-darwin-${verifier_target}" \
|
|
1197
|
+
--reject-unresolved \
|
|
1198
|
+
--no-compile-autoload-dotenv \
|
|
1199
|
+
--no-compile-autoload-bunfig \
|
|
1200
|
+
--no-compile-autoload-tsconfig \
|
|
1201
|
+
--no-compile-autoload-package-json \
|
|
1202
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" \
|
|
1203
|
+
--outfile "$verifier_output"
|
|
1204
|
+
done
|
|
1205
|
+
run_lipo -create "$VERIFIER_ARM64" "$VERIFIER_X86_64" -output "$ARTIFACT_VERIFIER"
|
|
1206
|
+
"$CHMOD_EXECUTABLE" 0755 "$ARTIFACT_VERIFIER"
|
|
1207
|
+
verify_exact_binary_architectures "$ARTIFACT_VERIFIER" arm64 x86_64
|
|
1208
|
+
"$RM_EXECUTABLE" -f "$VERIFIER_ARM64" "$VERIFIER_X86_64"
|
|
1209
|
+
fi
|
|
1210
|
+
elif [ "$MODE" = "release" ] && [ -n "$TEST_SWIFT_EXECUTABLE" ]; then
|
|
1211
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-update-client" "$UPDATE_CLIENT"
|
|
1212
|
+
"$CHMOD_EXECUTABLE" 0755 "$UPDATE_CLIENT"
|
|
1213
|
+
"$CP_EXECUTABLE" "$SWIFT_PRODUCT_DIR/recordings-envelope-signer" "$ENVELOPE_SIGNER"
|
|
1214
|
+
"$CHMOD_EXECUTABLE" 0755 "$ENVELOPE_SIGNER"
|
|
1215
|
+
fi
|
|
1216
|
+
COMPANION_BUILD_KIND="native"
|
|
1217
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1218
|
+
COMPANION_BUILD_KIND="universal"
|
|
1219
|
+
fi
|
|
1220
|
+
"$ENV_EXECUTABLE" -i \
|
|
1221
|
+
HOME="$BUILD_HOME" \
|
|
1222
|
+
PATH="$SANITIZED_PATH" \
|
|
1223
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
1224
|
+
${COMPANION_TEST_ENVIRONMENT[0]+"${COMPANION_TEST_ENVIRONMENT[@]}"} \
|
|
1225
|
+
"$BASH_EXECUTABLE" "$COMPANION_BUILD_SCRIPT" "$HELPERS/recordings" "$BUN_EXECUTABLE" "$COMPANION_BUILD_KIND"
|
|
1226
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1227
|
+
verify_exact_binary_architectures "$MACOS/Recordings" arm64 x86_64
|
|
1228
|
+
verify_exact_binary_architectures "$UPDATE_CLIENT" arm64 x86_64
|
|
1229
|
+
verify_exact_binary_architectures "$ENVELOPE_SIGNER" arm64 x86_64
|
|
1230
|
+
verify_exact_binary_architectures "$HELPERS/recordings" arm64 x86_64
|
|
1231
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1232
|
+
verify_exact_binary_architectures "$UPDATE_BROKER" arm64 x86_64
|
|
1233
|
+
verify_exact_binary_architectures "$ARTIFACT_VERIFIER" arm64 x86_64
|
|
1234
|
+
verify_exact_binary_architectures "$BOOTSTRAP_PREFLIGHT_VERIFIER" arm64 x86_64
|
|
1235
|
+
fi
|
|
1236
|
+
fi
|
|
1237
|
+
"$CP_EXECUTABLE" "$SOURCE_NATIVE_DIR/RecordingsLib/Info.plist" "$CONTENTS/Info.plist"
|
|
1238
|
+
VERSION="$("$PLIST_BUDDY" -c 'Print :CFBundleShortVersionString' "$CONTENTS/Info.plist")"
|
|
1239
|
+
|
|
1240
|
+
compute_release_publication_identity() {
|
|
1241
|
+
local -a identity_arguments=(
|
|
1242
|
+
--component "release_kind=$RELEASE_SUBTYPE"
|
|
1243
|
+
--component "source_sha=$SOURCE_SHA"
|
|
1244
|
+
--component "version=$VERSION"
|
|
1245
|
+
--component "codesign_identity=$CODESIGN_IDENTITY"
|
|
1246
|
+
--component "team_id=$EXPECTED_TEAM_ID"
|
|
1247
|
+
--component "notary_profile=$NOTARY_PROFILE"
|
|
1248
|
+
--component "release_sequence=$RELEASE_SEQUENCE"
|
|
1249
|
+
--component "key_epoch=$KEY_EPOCH"
|
|
1250
|
+
--component "expires_at_utc=$ENVELOPE_EXPIRES_AT_UTC"
|
|
1251
|
+
)
|
|
1252
|
+
identity_arguments+=(--component "envelope_public_key_sha256=$ENVELOPE_PUBLIC_KEY_SHA256")
|
|
1253
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1254
|
+
identity_arguments+=(--component "installer_identity=$INSTALLER_IDENTITY")
|
|
1255
|
+
else
|
|
1256
|
+
identity_arguments+=(--component "compatible_cohort_sha256=$COMPATIBLE_COHORT_SHA256")
|
|
1257
|
+
fi
|
|
1258
|
+
RELEASE_PUBLICATION_IDENTITY_SHA256="$(run_bun \
|
|
1259
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" release-publication-identity \
|
|
1260
|
+
"${identity_arguments[@]}")"
|
|
1261
|
+
[[ "$RELEASE_PUBLICATION_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]] || {
|
|
1262
|
+
echo "Release publication identity is invalid." >&2
|
|
1263
|
+
return 1
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
217
1266
|
|
|
218
|
-
|
|
219
|
-
|
|
1267
|
+
reserve_release_output() {
|
|
1268
|
+
local release_set_basename reserved_output
|
|
1269
|
+
release_set_basename="Recordings-${VERSION}-macos-${RELEASE_SUBTYPE}"
|
|
1270
|
+
compute_release_publication_identity
|
|
1271
|
+
RELEASE_OUTPUT_ROOT="$OUTPUT_BUILD_DIR"
|
|
1272
|
+
RELEASE_FINAL_DIR="$RELEASE_OUTPUT_ROOT/${release_set_basename}.release"
|
|
1273
|
+
RELEASE_RESERVATION="$RELEASE_OUTPUT_ROOT/.${release_set_basename}.reservation"
|
|
1274
|
+
RELEASE_OUTPUT_ALIASES=(
|
|
1275
|
+
"${release_set_basename}.zip"
|
|
1276
|
+
"${release_set_basename}.manifest.json"
|
|
1277
|
+
"${release_set_basename}.notary-submit.json"
|
|
1278
|
+
"${release_set_basename}.notary-log.json"
|
|
1279
|
+
)
|
|
1280
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1281
|
+
RELEASE_OUTPUT_ALIASES+=(
|
|
1282
|
+
"${release_set_basename}-updater.pkg"
|
|
1283
|
+
"${release_set_basename}-updater.notary-submit.json"
|
|
1284
|
+
"${release_set_basename}-updater.notary-log.json"
|
|
1285
|
+
"${release_set_basename}-updater.pkg.sha256"
|
|
1286
|
+
"${release_set_basename}-updater.bootstrap-envelope.json"
|
|
1287
|
+
"${release_set_basename}-updater.compatible-cohort.json"
|
|
1288
|
+
"${release_set_basename}-updater.compatible-cohort.json.sha256"
|
|
1289
|
+
)
|
|
1290
|
+
else
|
|
1291
|
+
RELEASE_OUTPUT_ALIASES+=("${release_set_basename}.update-envelope.json")
|
|
1292
|
+
fi
|
|
1293
|
+
if [ -e "$RELEASE_FINAL_DIR" ] || [ -L "$RELEASE_FINAL_DIR" ]; then
|
|
1294
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" complete-release-publication \
|
|
1295
|
+
--destination "$RELEASE_FINAL_DIR" \
|
|
1296
|
+
--reservation "$RELEASE_RESERVATION" \
|
|
1297
|
+
--output-root "$RELEASE_OUTPUT_ROOT" \
|
|
1298
|
+
--publication-identity-sha256 "$RELEASE_PUBLICATION_IDENTITY_SHA256"
|
|
1299
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" assert-release-publication-complete \
|
|
1300
|
+
--destination "$RELEASE_FINAL_DIR" \
|
|
1301
|
+
--output-root "$RELEASE_OUTPUT_ROOT" \
|
|
1302
|
+
--publication-identity-sha256 "$RELEASE_PUBLICATION_IDENTITY_SHA256"
|
|
1303
|
+
echo "Recovered authenticated same-version release publication: $RELEASE_FINAL_DIR"
|
|
1304
|
+
exit 0
|
|
1305
|
+
fi
|
|
1306
|
+
[ ! -e "$RELEASE_FINAL_DIR" ] && [ ! -L "$RELEASE_FINAL_DIR" ] || {
|
|
1307
|
+
echo "Release output already exists; same-version publication is immutable: $RELEASE_FINAL_DIR" >&2
|
|
1308
|
+
exit 1
|
|
1309
|
+
}
|
|
1310
|
+
for reserved_output in "${RELEASE_OUTPUT_ALIASES[@]}"; do
|
|
1311
|
+
[ ! -e "$RELEASE_OUTPUT_ROOT/$reserved_output" ] && \
|
|
1312
|
+
[ ! -L "$RELEASE_OUTPUT_ROOT/$reserved_output" ] || {
|
|
1313
|
+
echo "Release compatibility output already exists and cannot be replaced: $reserved_output" >&2
|
|
1314
|
+
exit 1
|
|
1315
|
+
}
|
|
1316
|
+
done
|
|
1317
|
+
if ! "$MKDIR_EXECUTABLE" -m 0700 "$RELEASE_RESERVATION"; then
|
|
1318
|
+
echo "Release output is already reserved by another or interrupted builder." >&2
|
|
1319
|
+
exit 1
|
|
1320
|
+
fi
|
|
1321
|
+
RELEASE_RESERVATION_OWNED=1
|
|
1322
|
+
[ ! -e "$RELEASE_FINAL_DIR" ] && [ ! -L "$RELEASE_FINAL_DIR" ] || {
|
|
1323
|
+
echo "Release output appeared while acquiring its reservation." >&2
|
|
1324
|
+
exit 1
|
|
1325
|
+
}
|
|
1326
|
+
for reserved_output in "${RELEASE_OUTPUT_ALIASES[@]}"; do
|
|
1327
|
+
[ ! -e "$RELEASE_OUTPUT_ROOT/$reserved_output" ] && \
|
|
1328
|
+
[ ! -L "$RELEASE_OUTPUT_ROOT/$reserved_output" ] || {
|
|
1329
|
+
echo "Release compatibility output appeared while acquiring its reservation." >&2
|
|
1330
|
+
exit 1
|
|
1331
|
+
}
|
|
1332
|
+
done
|
|
1333
|
+
RELEASE_STAGING_DIR="$($MKTEMP_EXECUTABLE -d "$RELEASE_OUTPUT_ROOT/.${release_set_basename}.staging.XXXXXX")"
|
|
1334
|
+
"$CHMOD_EXECUTABLE" 0700 "$RELEASE_STAGING_DIR"
|
|
1335
|
+
OUTPUT_BUILD_DIR="$RELEASE_STAGING_DIR"
|
|
1336
|
+
OUTPUT_APP_DIR="$OUTPUT_BUILD_DIR/Recordings.app"
|
|
1337
|
+
}
|
|
220
1338
|
|
|
221
|
-
|
|
222
|
-
|
|
1339
|
+
if [ "$MODE" = "release" ]; then
|
|
1340
|
+
reserve_release_output
|
|
1341
|
+
fi
|
|
223
1342
|
|
|
224
|
-
|
|
225
|
-
for bundle in "$BUILD_DIR"/*.resources "$BUILD_DIR"/*.bundle .build/*/"$MODE"/*.resources .build/*/"$MODE"/*.bundle; do
|
|
1343
|
+
for bundle in "$SWIFT_RESOURCE_PRODUCT_DIR"/*.resources "$SWIFT_RESOURCE_PRODUCT_DIR"/*.bundle "$SWIFT_SCRATCH_PATH"/*/"$BUILD_CONFIGURATION"/*.resources "$SWIFT_SCRATCH_PATH"/*/"$BUILD_CONFIGURATION"/*.bundle; do
|
|
226
1344
|
[ -e "$bundle" ] || continue
|
|
227
|
-
|
|
228
|
-
|
|
1345
|
+
"$RM_EXECUTABLE" -rf "$RESOURCES/$("$BASENAME_EXECUTABLE" "$bundle")"
|
|
1346
|
+
run_sensitive_tool "$DITTO_EXECUTABLE" "$bundle" "$RESOURCES/$("$BASENAME_EXECUTABLE" "$bundle")"
|
|
229
1347
|
done
|
|
230
1348
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
exit 1
|
|
1349
|
+
APP_SIGN_ARGUMENTS=(--force --sign "$CODESIGN_IDENTITY")
|
|
1350
|
+
HELPER_SIGN_ARGUMENTS=(--force --sign "$CODESIGN_IDENTITY" --options runtime)
|
|
1351
|
+
if [ "$MODE" != "debug" ]; then
|
|
1352
|
+
APP_SIGN_ARGUMENTS+=(--options runtime)
|
|
1353
|
+
fi
|
|
1354
|
+
if [ "$MODE" = "release" ]; then
|
|
1355
|
+
APP_SIGN_ARGUMENTS+=(--timestamp)
|
|
1356
|
+
HELPER_SIGN_ARGUMENTS+=(--timestamp)
|
|
240
1357
|
fi
|
|
1358
|
+
normalize_unsigned_app_bundle_modes "$APP_DIR"
|
|
1359
|
+
for standalone_launch_file in \
|
|
1360
|
+
"$UPDATE_BROKER" \
|
|
1361
|
+
"$ARTIFACT_VERIFIER" \
|
|
1362
|
+
"$ENVELOPE_SIGNER" \
|
|
1363
|
+
"$BOOTSTRAP_PREFLIGHT_VERIFIER"; do
|
|
1364
|
+
[ -e "$standalone_launch_file" ] || [ -L "$standalone_launch_file" ] || continue
|
|
1365
|
+
normalize_unsigned_launch_file_mode "$standalone_launch_file" "Release launch file"
|
|
1366
|
+
done
|
|
241
1367
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
# Precedence:
|
|
247
|
-
# 1. RECORDINGS_CODESIGN_IDENTITY — explicit identity (Developer ID, station
|
|
248
|
-
# signing cert, or "-" for an intentional ad-hoc build). An explicit
|
|
249
|
-
# identity that fails to sign fails the build.
|
|
250
|
-
# 2. The per-machine "Hasna Recordings Signing" self-signed certificate in the
|
|
251
|
-
# DEDICATED signing keychain, created once and reused, so the default build
|
|
252
|
-
# is stably signed non-interactively (works over SSH) with no out-of-band
|
|
253
|
-
# configuration. We never fall back to ad-hoc by default.
|
|
254
|
-
if [ -n "${RECORDINGS_CODESIGN_IDENTITY:-}" ]; then
|
|
255
|
-
"$CODESIGN" --force --sign "$RECORDINGS_CODESIGN_IDENTITY" --entitlements RecordingsLib/Recordings.entitlements "$APP_DIR"
|
|
256
|
-
elif ensure_local_signing_identity; then
|
|
257
|
-
"$CODESIGN" --force --keychain "$SIGNING_KEYCHAIN" --sign "$SIGNING_IDENTITY" --entitlements RecordingsLib/Recordings.entitlements "$APP_DIR"
|
|
1368
|
+
if [ "$MODE" = "release" ]; then
|
|
1369
|
+
run_codesign "${HELPER_SIGN_ARGUMENTS[@]}" \
|
|
1370
|
+
--entitlements "$HELPER_ENTITLEMENTS" \
|
|
1371
|
+
"$HELPERS/recordings"
|
|
258
1372
|
else
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
1373
|
+
"$CODESIGN_EXECUTABLE" "${HELPER_SIGN_ARGUMENTS[@]}" \
|
|
1374
|
+
--entitlements "$HELPER_ENTITLEMENTS" \
|
|
1375
|
+
"$HELPERS/recordings"
|
|
1376
|
+
fi
|
|
1377
|
+
|
|
1378
|
+
if [ "$MODE" = "release" ] && [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1379
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --identifier com.hasna.recordings.update-client \
|
|
1380
|
+
--options runtime --timestamp --entitlements "$SOURCE_PACKAGE_ROOT/packaging/macos/Empty.entitlements" \
|
|
1381
|
+
"$UPDATE_CLIENT"
|
|
1382
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --identifier com.hasna.recordings.envelope-signer \
|
|
1383
|
+
--options runtime --timestamp --entitlements "$SOURCE_PACKAGE_ROOT/packaging/macos/Empty.entitlements" \
|
|
1384
|
+
"$ENVELOPE_SIGNER"
|
|
1385
|
+
RELEASE_SIGNED_UPDATER_CODE=("$UPDATE_CLIENT" "$ENVELOPE_SIGNER")
|
|
1386
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1387
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --identifier com.hasna.recordings.updater \
|
|
1388
|
+
--options runtime --timestamp --entitlements "$SOURCE_PACKAGE_ROOT/packaging/macos/Empty.entitlements" \
|
|
1389
|
+
"$UPDATE_BROKER"
|
|
1390
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --identifier com.hasna.recordings.artifact-verifier \
|
|
1391
|
+
--options runtime --timestamp \
|
|
1392
|
+
--entitlements "$SOURCE_PACKAGE_ROOT/packaging/macos/Verifier.entitlements" \
|
|
1393
|
+
"$ARTIFACT_VERIFIER"
|
|
1394
|
+
run_codesign --force --sign "$CODESIGN_IDENTITY" --identifier com.hasna.recordings.bootstrap-preflight \
|
|
1395
|
+
--options runtime --timestamp --entitlements "$SOURCE_PACKAGE_ROOT/packaging/macos/Empty.entitlements" \
|
|
1396
|
+
"$BOOTSTRAP_PREFLIGHT_VERIFIER"
|
|
1397
|
+
RELEASE_SIGNED_UPDATER_CODE+=("$UPDATE_BROKER" "$ARTIFACT_VERIFIER" "$BOOTSTRAP_PREFLIGHT_VERIFIER")
|
|
1398
|
+
fi
|
|
1399
|
+
for updater_code in "${RELEASE_SIGNED_UPDATER_CODE[@]}"; do
|
|
1400
|
+
run_codesign --verify --strict --all-architectures --verbose=2 "$updater_code"
|
|
1401
|
+
done
|
|
1402
|
+
fi
|
|
1403
|
+
|
|
1404
|
+
verify_helper_entitlements() {
|
|
1405
|
+
local helper="$1"
|
|
1406
|
+
local entitlement_plist
|
|
1407
|
+
local entitlement_json
|
|
1408
|
+
entitlement_plist="$(run_sensitive_tool "$MKTEMP_EXECUTABLE" "$BUILD_WORK_DIR/helper-entitlements.XXXXXX")"
|
|
1409
|
+
if ! run_codesign -d --entitlements :- "$helper" >"$entitlement_plist" 2>/dev/null; then
|
|
1410
|
+
"$RM_EXECUTABLE" -f "$entitlement_plist"
|
|
1411
|
+
echo "Companion CLI signed entitlements could not be read back." >&2
|
|
1412
|
+
return 1
|
|
1413
|
+
fi
|
|
1414
|
+
entitlement_json="$(run_sensitive_tool "$PLUTIL" -convert json -o - "$entitlement_plist" | run_sensitive_tool "$TR_EXECUTABLE" -d '[:space:]')"
|
|
1415
|
+
"$RM_EXECUTABLE" -f "$entitlement_plist"
|
|
1416
|
+
if ! "$ENV_EXECUTABLE" -i \
|
|
1417
|
+
HOME="$BUILD_HOME" \
|
|
1418
|
+
PATH="$SANITIZED_PATH" \
|
|
1419
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
1420
|
+
ENTITLEMENT_JSON="$entitlement_json" \
|
|
1421
|
+
"$BUN_EXECUTABLE" -e '
|
|
1422
|
+
const actual = JSON.parse(process.env.ENTITLEMENT_JSON ?? "null");
|
|
1423
|
+
const expected = [
|
|
1424
|
+
"com.apple.security.cs.allow-jit",
|
|
1425
|
+
"com.apple.security.cs.allow-unsigned-executable-memory",
|
|
1426
|
+
];
|
|
1427
|
+
if (!actual || Array.isArray(actual)) process.exit(1);
|
|
1428
|
+
const keys = Object.keys(actual).sort();
|
|
1429
|
+
if (JSON.stringify(keys) !== JSON.stringify(expected)) process.exit(1);
|
|
1430
|
+
if (!keys.every((key) => actual[key] === true)) process.exit(1);
|
|
1431
|
+
'; then
|
|
1432
|
+
echo "Companion CLI has unexpected hardened-runtime entitlements: ${entitlement_json:-missing}." >&2
|
|
1433
|
+
return 1
|
|
1434
|
+
fi
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
has_hardened_runtime_flag() {
|
|
1438
|
+
local details="$1"
|
|
1439
|
+
local flags
|
|
1440
|
+
flags="$(printf '%s\n' "$details" | run_sensitive_tool "$SED_EXECUTABLE" -n 's/^CodeDirectory .*flags=[^(]*(\([^)]*\)).*/\1/p' | run_sensitive_tool "$HEAD_EXECUTABLE" -n 1)"
|
|
1441
|
+
case ",$flags," in
|
|
1442
|
+
*,runtime,*) return 0 ;;
|
|
1443
|
+
*) return 1 ;;
|
|
1444
|
+
esac
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
verify_hardened_helper() {
|
|
1448
|
+
local details
|
|
1449
|
+
run_codesign --verify --strict --verbose=2 "$HELPERS/recordings"
|
|
1450
|
+
details="$(run_codesign -d --verbose=4 "$HELPERS/recordings" 2>&1)"
|
|
1451
|
+
if ! has_hardened_runtime_flag "$details"; then
|
|
1452
|
+
echo "Companion CLI is missing hardened runtime signing." >&2
|
|
1453
|
+
exit 1
|
|
1454
|
+
fi
|
|
1455
|
+
verify_helper_entitlements "$HELPERS/recordings"
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
run_signed_helper_contract() {
|
|
1459
|
+
local contract_home
|
|
1460
|
+
local version
|
|
1461
|
+
local project_output
|
|
1462
|
+
local recording_output
|
|
1463
|
+
local helper_executable
|
|
1464
|
+
contract_home="$(run_sensitive_tool "$MKTEMP_EXECUTABLE" -d "$BUILD_WORK_DIR/signed-helper-contract.XXXXXX")"
|
|
1465
|
+
helper_executable="$HELPERS/recordings"
|
|
1466
|
+
local -a contract_environment=(
|
|
1467
|
+
"$ENV_EXECUTABLE" -i
|
|
1468
|
+
HOME="$contract_home"
|
|
1469
|
+
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
|
|
1470
|
+
TMPDIR="$contract_home"
|
|
1471
|
+
HASNA_RECORDINGS_STORAGE_MODE="local"
|
|
1472
|
+
RECORDINGS_STORAGE_MODE="local"
|
|
1473
|
+
HASNA_RECORDINGS_DB_PATH="$contract_home/recordings.db"
|
|
1474
|
+
RECORDINGS_AUDIO_DIR="$contract_home/audio"
|
|
1475
|
+
)
|
|
1476
|
+
contract_run() {
|
|
1477
|
+
(cd "$contract_home" && "${contract_environment[@]}" "$@")
|
|
1478
|
+
}
|
|
1479
|
+
if ! version="$(contract_run "$helper_executable" --version)" || \
|
|
1480
|
+
[ "$version" != "$VERSION" ] || \
|
|
1481
|
+
! project_output="$(contract_run "$helper_executable" --json project register \
|
|
1482
|
+
--name "Signed Helper Contract" \
|
|
1483
|
+
--path "recordings-app://build/signed-helper-contract")" || \
|
|
1484
|
+
[[ "$project_output" != *"Signed Helper Contract"* ]] || \
|
|
1485
|
+
! recording_output="$(contract_run "$helper_executable" --json save-text \
|
|
1486
|
+
"Signed helper contract" \
|
|
1487
|
+
--source "native_build_contract" \
|
|
1488
|
+
--post-processing off)" || \
|
|
1489
|
+
[[ "$recording_output" != *"Signed helper contract"* ]]; then
|
|
1490
|
+
"$RM_EXECUTABLE" -rf "$contract_home"
|
|
1491
|
+
echo "Post-sign signed companion CLI contract failed." >&2
|
|
1492
|
+
exit 1
|
|
1493
|
+
fi
|
|
1494
|
+
if ! "$ENV_EXECUTABLE" -i \
|
|
1495
|
+
HOME="$BUILD_HOME" \
|
|
1496
|
+
PATH="$SANITIZED_PATH" \
|
|
1497
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
1498
|
+
PROJECT_JSON="$project_output" \
|
|
1499
|
+
RECORDING_JSON="$recording_output" \
|
|
1500
|
+
"$BUN_EXECUTABLE" -e '
|
|
1501
|
+
const project = JSON.parse(process.env.PROJECT_JSON ?? "null");
|
|
1502
|
+
const recording = JSON.parse(process.env.RECORDING_JSON ?? "null");
|
|
1503
|
+
if (project?.name !== "Signed Helper Contract") process.exit(1);
|
|
1504
|
+
if (project?.path !== "recordings-app://build/signed-helper-contract") process.exit(1);
|
|
1505
|
+
if (recording?.raw_text !== "Signed helper contract") process.exit(1);
|
|
1506
|
+
'; then
|
|
1507
|
+
"$RM_EXECUTABLE" -rf "$contract_home"
|
|
1508
|
+
echo "Post-sign signed companion CLI contract returned invalid JSON." >&2
|
|
1509
|
+
exit 1
|
|
1510
|
+
fi
|
|
1511
|
+
"$RM_EXECUTABLE" -rf "$contract_home"
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
verify_hardened_helper
|
|
1515
|
+
run_signed_helper_contract
|
|
1516
|
+
if [ "$MODE" != "debug" ]; then
|
|
1517
|
+
verify_source_unchanged
|
|
1518
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" provenance \
|
|
1519
|
+
--app "$APP_DIR" \
|
|
1520
|
+
--source-sha "$SOURCE_SHA" \
|
|
1521
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
1522
|
+
--package-root "$PACKAGE_ROOT" \
|
|
1523
|
+
--artifact-policy "$ARTIFACT_POLICY" \
|
|
1524
|
+
--approved-target "$APPROVED_TARGET" \
|
|
1525
|
+
--approved-target-identity-kind "$APPROVED_TARGET_IDENTITY_KIND" \
|
|
1526
|
+
--approved-target-identity-sha256 "$APPROVED_TARGET_IDENTITY_SHA256" \
|
|
1527
|
+
--builder-identity-kind "$BUILDER_IDENTITY_KIND" \
|
|
1528
|
+
--builder-identity-sha256 "$BUILDER_IDENTITY_SHA256"
|
|
1529
|
+
PROVENANCE_FILE="$RESOURCES/recordings-build-provenance.json"
|
|
1530
|
+
if [ -e "$PROVENANCE_FILE" ] || [ -L "$PROVENANCE_FILE" ]; then
|
|
1531
|
+
normalize_unsigned_app_data_file_mode "$PROVENANCE_FILE" "App build provenance"
|
|
1532
|
+
elif [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1533
|
+
echo "App build provenance was not emitted: $PROVENANCE_FILE" >&2
|
|
1534
|
+
exit 1
|
|
1535
|
+
fi
|
|
1536
|
+
fi
|
|
1537
|
+
verify_app_bundle_modes "$APP_DIR"
|
|
1538
|
+
run_codesign "${APP_SIGN_ARGUMENTS[@]}" \
|
|
1539
|
+
--entitlements "$APP_ENTITLEMENTS" \
|
|
1540
|
+
"$APP_DIR"
|
|
1541
|
+
|
|
1542
|
+
verify_signed_code() {
|
|
1543
|
+
local code_path="$1"
|
|
1544
|
+
local label="$2"
|
|
1545
|
+
local details
|
|
1546
|
+
local authority
|
|
1547
|
+
local team_id
|
|
1548
|
+
local timestamp
|
|
1549
|
+
run_codesign --verify --strict --verbose=2 "$code_path"
|
|
1550
|
+
details="$(run_codesign -d --verbose=4 "$code_path" 2>&1)"
|
|
1551
|
+
authority="$(printf '%s\n' "$details" | run_sensitive_tool "$AWK_EXECUTABLE" -F= '/^Authority=/ { print $2; exit }')"
|
|
1552
|
+
team_id="$(printf '%s\n' "$details" | run_sensitive_tool "$AWK_EXECUTABLE" -F= '/^TeamIdentifier=/ { print $2; exit }')"
|
|
1553
|
+
timestamp="$(printf '%s\n' "$details" | run_sensitive_tool "$AWK_EXECUTABLE" -F= '/^Timestamp=/ { print $2; exit }')"
|
|
1554
|
+
if [[ "$authority" != "Developer ID Application:"* ]]; then
|
|
1555
|
+
echo "$label is not signed by a Developer ID Application authority." >&2
|
|
1556
|
+
exit 1
|
|
1557
|
+
fi
|
|
1558
|
+
if [ "$team_id" != "$EXPECTED_TEAM_ID" ]; then
|
|
1559
|
+
echo "$label TeamIdentifier ${team_id:-missing} does not match ${EXPECTED_TEAM_ID}." >&2
|
|
1560
|
+
exit 1
|
|
1561
|
+
fi
|
|
1562
|
+
if ! has_hardened_runtime_flag "$details"; then
|
|
1563
|
+
echo "$label is missing hardened runtime signing." >&2
|
|
1564
|
+
exit 1
|
|
1565
|
+
fi
|
|
1566
|
+
case "$timestamp" in
|
|
1567
|
+
''|none|None|NONE)
|
|
1568
|
+
echo "$label is missing a trusted signing timestamp." >&2
|
|
1569
|
+
exit 1
|
|
1570
|
+
;;
|
|
1571
|
+
esac
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
run_codesign --verify --deep --strict --verbose=2 "$APP_DIR"
|
|
1575
|
+
"$ENV_EXECUTABLE" -i \
|
|
1576
|
+
HOME="$OPERATOR_HOME" \
|
|
1577
|
+
PATH="$SANITIZED_PATH" \
|
|
1578
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
1579
|
+
SSH_CONNECTION="${SSH_CONNECTION:-}" \
|
|
1580
|
+
${SMOKE_TEST_ENVIRONMENT[0]+"${SMOKE_TEST_ENVIRONMENT[@]}"} \
|
|
1581
|
+
"$BASH_EXECUTABLE" "$SMOKE_SCRIPT" "$APP_DIR" "$BUN_EXECUTABLE"
|
|
1582
|
+
|
|
1583
|
+
publish_app_output() {
|
|
1584
|
+
local source_tree_digest
|
|
1585
|
+
local published_tree_digest
|
|
1586
|
+
if [ "$MODE" = "release" ]; then
|
|
1587
|
+
[ ! -e "$OUTPUT_APP_DIR" ] && [ ! -L "$OUTPUT_APP_DIR" ] || {
|
|
1588
|
+
echo "Release staging already contains an app output; refusing replacement." >&2
|
|
1589
|
+
exit 1
|
|
1590
|
+
}
|
|
1591
|
+
else
|
|
1592
|
+
"$RM_EXECUTABLE" -rf "$OUTPUT_APP_DIR"
|
|
1593
|
+
fi
|
|
1594
|
+
run_sensitive_tool "$DITTO_EXECUTABLE" "$APP_DIR" "$OUTPUT_APP_DIR"
|
|
1595
|
+
if [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1596
|
+
verify_app_bundle_modes "$OUTPUT_APP_DIR"
|
|
1597
|
+
require_app_tree_without_extended_acl "$OUTPUT_APP_DIR"
|
|
1598
|
+
source_tree_digest="$(run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" tree-digest --path "$APP_DIR")"
|
|
1599
|
+
published_tree_digest="$(run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" tree-digest --path "$OUTPUT_APP_DIR")"
|
|
1600
|
+
if [ "$published_tree_digest" != "$source_tree_digest" ]; then
|
|
1601
|
+
echo "Published app copy does not match the signed source tree." >&2
|
|
1602
|
+
exit 1
|
|
1603
|
+
fi
|
|
1604
|
+
run_codesign --verify --deep --strict --verbose=2 "$OUTPUT_APP_DIR"
|
|
1605
|
+
fi
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
verify_complete_release_staging() {
|
|
1609
|
+
local output
|
|
1610
|
+
for output in \
|
|
1611
|
+
"$OUTPUT_APP_DIR" \
|
|
1612
|
+
"$FINAL_ARCHIVE" \
|
|
1613
|
+
"$FINAL_MANIFEST" \
|
|
1614
|
+
"$NOTARY_SUBMISSION" \
|
|
1615
|
+
"$NOTARY_LOG"; do
|
|
1616
|
+
[ -e "$output" ] && [ ! -L "$output" ] || {
|
|
1617
|
+
echo "Release staging is incomplete or unsafe: $output" >&2
|
|
1618
|
+
exit 1
|
|
1619
|
+
}
|
|
1620
|
+
done
|
|
1621
|
+
[ -d "$OUTPUT_APP_DIR" ] || { echo "Release staging app is not a directory." >&2; exit 1; }
|
|
1622
|
+
for output in "$FINAL_ARCHIVE" "$FINAL_MANIFEST" "$NOTARY_SUBMISSION" "$NOTARY_LOG"; do
|
|
1623
|
+
[ -f "$output" ] && [ -s "$output" ] || {
|
|
1624
|
+
echo "Release staging file is empty or not regular: $output" >&2
|
|
1625
|
+
exit 1
|
|
1626
|
+
}
|
|
1627
|
+
"$CHMOD_EXECUTABLE" 0444 "$output"
|
|
1628
|
+
done
|
|
1629
|
+
if [ "$HOST_PLATFORM" = "Darwin" ]; then
|
|
1630
|
+
local manifest_archive_digest manifest_notary_log_digest manifest_submitted_digest
|
|
1631
|
+
manifest_archive_digest="$(run_release_sensitive_tool "$PLUTIL" -extract archive.sha256 raw -o - \
|
|
1632
|
+
"$FINAL_MANIFEST")"
|
|
1633
|
+
manifest_notary_log_digest="$(run_release_sensitive_tool "$PLUTIL" -extract notarization.log_sha256 raw -o - \
|
|
1634
|
+
"$FINAL_MANIFEST")"
|
|
1635
|
+
manifest_submitted_digest="$(run_release_sensitive_tool "$PLUTIL" -extract notarization.submitted_archive_sha256 raw -o - \
|
|
1636
|
+
"$FINAL_MANIFEST")"
|
|
1637
|
+
[ "$manifest_archive_digest" = "$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1638
|
+
"$FINAL_ARCHIVE" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')" ] || {
|
|
1639
|
+
echo "Release manifest no longer binds the finalized ZIP in staging." >&2
|
|
1640
|
+
exit 1
|
|
1641
|
+
}
|
|
1642
|
+
[ "$manifest_notary_log_digest" = "$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1643
|
+
"$NOTARY_LOG" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')" ] || {
|
|
1644
|
+
echo "Release manifest no longer binds the accepted notary log in staging." >&2
|
|
1645
|
+
exit 1
|
|
1646
|
+
}
|
|
1647
|
+
[ "$manifest_submitted_digest" = "$NOTARY_ARCHIVE_SHA256" ] || {
|
|
1648
|
+
echo "Release manifest no longer binds the locally submitted archive digest." >&2
|
|
1649
|
+
exit 1
|
|
1650
|
+
}
|
|
1651
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1652
|
+
local package_set="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.release"
|
|
1653
|
+
local pkg="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.pkg"
|
|
1654
|
+
local pkg_digest="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.pkg.sha256"
|
|
1655
|
+
[ -d "$package_set" ] && [ ! -L "$package_set" ] || {
|
|
1656
|
+
echo "Initial-bootstrap staging is missing the canonical updater package set." >&2
|
|
1657
|
+
exit 1
|
|
1658
|
+
}
|
|
1659
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" assert-release-publication-complete \
|
|
1660
|
+
--destination "$package_set" \
|
|
1661
|
+
--output-root "$OUTPUT_BUILD_DIR" \
|
|
1662
|
+
--publication-identity-sha256 "$PACKAGE_PUBLICATION_IDENTITY_SHA256"
|
|
1663
|
+
for output in \
|
|
1664
|
+
"$pkg" \
|
|
1665
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.notary-submit.json" \
|
|
1666
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.notary-log.json" \
|
|
1667
|
+
"$pkg_digest" \
|
|
1668
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.bootstrap-envelope.json" \
|
|
1669
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.compatible-cohort.json" \
|
|
1670
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.compatible-cohort.json.sha256"; do
|
|
1671
|
+
[ -f "$output" ] && [ ! -L "$output" ] && [ -s "$output" ] || {
|
|
1672
|
+
echo "Initial-bootstrap staging is missing a finalized package output: $output" >&2
|
|
1673
|
+
exit 1
|
|
1674
|
+
}
|
|
1675
|
+
[ "$("$STAT_EXECUTABLE" -f '%Lp' "$output")" = "444" ] || {
|
|
1676
|
+
echo "Finalized initial-bootstrap output is not read-only: $output" >&2
|
|
1677
|
+
exit 1
|
|
1678
|
+
}
|
|
1679
|
+
done
|
|
1680
|
+
[ ! -e "$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.update-envelope.json" ] && \
|
|
1681
|
+
[ ! -L "$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.update-envelope.json" ] || {
|
|
1682
|
+
echo "Initial-bootstrap staging must not contain an app-update envelope." >&2
|
|
1683
|
+
exit 1
|
|
1684
|
+
}
|
|
1685
|
+
local published_pkg_digest
|
|
1686
|
+
published_pkg_digest="$(run_release_sensitive_tool "$AWK_EXECUTABLE" 'NR == 1 { print $1 }' \
|
|
1687
|
+
"$pkg_digest")"
|
|
1688
|
+
[ "$published_pkg_digest" = "$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1689
|
+
"$pkg" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')" ] || {
|
|
1690
|
+
echo "Initial-bootstrap PKG digest does not match the finalized PKG." >&2
|
|
1691
|
+
exit 1
|
|
1692
|
+
}
|
|
1693
|
+
else
|
|
1694
|
+
local update_envelope="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.update-envelope.json"
|
|
1695
|
+
[ -f "$update_envelope" ] && [ ! -L "$update_envelope" ] && [ -s "$update_envelope" ] && \
|
|
1696
|
+
[ "$("$STAT_EXECUTABLE" -f '%Lp' "$update_envelope")" = "444" ] || {
|
|
1697
|
+
echo "App-update staging is missing its single finalized update envelope." >&2
|
|
1698
|
+
exit 1
|
|
1699
|
+
}
|
|
1700
|
+
for output in \
|
|
1701
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.release" \
|
|
1702
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.pkg" \
|
|
1703
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.bootstrap-envelope.json" \
|
|
1704
|
+
"$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-updater.compatible-cohort.json"; do
|
|
1705
|
+
[ ! -e "$output" ] && [ ! -L "$output" ] || {
|
|
1706
|
+
echo "App-update staging contains a forbidden bootstrap or root-cohort output: $output" >&2
|
|
1707
|
+
exit 1
|
|
1708
|
+
}
|
|
1709
|
+
done
|
|
1710
|
+
fi
|
|
1711
|
+
fi
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
publish_complete_release_set() {
|
|
1715
|
+
local output_alias
|
|
1716
|
+
local -a publication_arguments=()
|
|
1717
|
+
verify_complete_release_staging
|
|
1718
|
+
verify_source_unchanged
|
|
1719
|
+
[ ! -e "$RELEASE_FINAL_DIR" ] && [ ! -L "$RELEASE_FINAL_DIR" ] || {
|
|
1720
|
+
echo "Release destination appeared before immutable publication." >&2
|
|
1721
|
+
exit 1
|
|
1722
|
+
}
|
|
1723
|
+
for output_alias in "${RELEASE_OUTPUT_ALIASES[@]}"; do
|
|
1724
|
+
[ -f "$RELEASE_STAGING_DIR/$output_alias" ] || continue
|
|
1725
|
+
publication_arguments+=(--alias "$output_alias")
|
|
1726
|
+
done
|
|
1727
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1728
|
+
publication_arguments+=(
|
|
1729
|
+
--nested-publication
|
|
1730
|
+
"${ARTIFACT_BASENAME}-updater.release=$PACKAGE_PUBLICATION_IDENTITY_SHA256"
|
|
1731
|
+
)
|
|
1732
|
+
fi
|
|
1733
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" prepare-release-publication \
|
|
1734
|
+
--staging "$RELEASE_STAGING_DIR" \
|
|
1735
|
+
--destination "$RELEASE_FINAL_DIR" \
|
|
1736
|
+
--reservation "$RELEASE_RESERVATION" \
|
|
1737
|
+
--publication-identity-sha256 "$RELEASE_PUBLICATION_IDENTITY_SHA256" \
|
|
1738
|
+
"${publication_arguments[@]}"
|
|
1739
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" publish-release-directory \
|
|
1740
|
+
--staging "$RELEASE_STAGING_DIR" \
|
|
1741
|
+
--destination "$RELEASE_FINAL_DIR"
|
|
1742
|
+
RELEASE_DIRECTORY_PUBLISHED=1
|
|
1743
|
+
RELEASE_STAGING_DIR=""
|
|
1744
|
+
OUTPUT_BUILD_DIR="$RELEASE_FINAL_DIR"
|
|
1745
|
+
OUTPUT_APP_DIR="$OUTPUT_BUILD_DIR/Recordings.app"
|
|
1746
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" complete-release-publication \
|
|
1747
|
+
--destination "$RELEASE_FINAL_DIR" \
|
|
1748
|
+
--reservation "$RELEASE_RESERVATION" \
|
|
1749
|
+
--output-root "$RELEASE_OUTPUT_ROOT" \
|
|
1750
|
+
--publication-identity-sha256 "$RELEASE_PUBLICATION_IDENTITY_SHA256"
|
|
1751
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" assert-release-publication-complete \
|
|
1752
|
+
--destination "$RELEASE_FINAL_DIR" \
|
|
1753
|
+
--output-root "$RELEASE_OUTPUT_ROOT" \
|
|
1754
|
+
--publication-identity-sha256 "$RELEASE_PUBLICATION_IDENTITY_SHA256"
|
|
1755
|
+
RELEASE_RESERVATION_OWNED=0
|
|
1756
|
+
FINAL_ARCHIVE="$RELEASE_FINAL_DIR/${ARTIFACT_BASENAME}.zip"
|
|
1757
|
+
FINAL_MANIFEST="$RELEASE_FINAL_DIR/${ARTIFACT_BASENAME}.manifest.json"
|
|
1758
|
+
NOTARY_SUBMISSION="$RELEASE_FINAL_DIR/${ARTIFACT_BASENAME}.notary-submit.json"
|
|
1759
|
+
NOTARY_LOG="$RELEASE_FINAL_DIR/${ARTIFACT_BASENAME}.notary-log.json"
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
if [ "$MODE" = "debug" ]; then
|
|
1763
|
+
verify_source_unchanged
|
|
1764
|
+
publish_app_output
|
|
1765
|
+
verify_source_unchanged
|
|
1766
|
+
echo "Built non-distributable debug app: $OUTPUT_APP_DIR"
|
|
1767
|
+
exit 0
|
|
1768
|
+
fi
|
|
1769
|
+
|
|
1770
|
+
if [ "$MODE" = "local" ]; then
|
|
1771
|
+
ARTIFACT_BASENAME="Recordings-${VERSION}-macos-${APPROVED_TARGET}-local-only"
|
|
1772
|
+
FINAL_ARCHIVE="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.zip"
|
|
1773
|
+
FINAL_MANIFEST="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.manifest.json"
|
|
1774
|
+
"$RM_EXECUTABLE" -f "$FINAL_ARCHIVE" "$FINAL_MANIFEST"
|
|
1775
|
+
run_codesign --verify --deep --strict --all-architectures --verbose=2 "$APP_DIR"
|
|
1776
|
+
"$DITTO_EXECUTABLE" -c -k --sequesterRsrc --keepParent "$APP_DIR" "$FINAL_ARCHIVE"
|
|
1777
|
+
verify_source_unchanged
|
|
1778
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" finalize-local \
|
|
1779
|
+
--app "$APP_DIR" \
|
|
1780
|
+
--source-sha "$SOURCE_SHA" \
|
|
1781
|
+
--archive "$FINAL_ARCHIVE" \
|
|
1782
|
+
--manifest "$FINAL_MANIFEST" \
|
|
1783
|
+
--package-root "$PACKAGE_ROOT" \
|
|
1784
|
+
--approved-target "$APPROVED_TARGET" \
|
|
1785
|
+
--approved-target-identity-kind "$APPROVED_TARGET_IDENTITY_KIND" \
|
|
1786
|
+
--approved-target-identity-sha256 "$APPROVED_TARGET_IDENTITY_SHA256"
|
|
1787
|
+
publish_app_output
|
|
1788
|
+
verify_source_unchanged
|
|
1789
|
+
echo "Built immutable local-only app artifact: $FINAL_ARCHIVE"
|
|
1790
|
+
echo "Built local-only artifact manifest: $FINAL_MANIFEST"
|
|
1791
|
+
echo "This artifact is not notarized and is approved only for ${APPROVED_TARGET}."
|
|
1792
|
+
exit 0
|
|
1793
|
+
fi
|
|
1794
|
+
|
|
1795
|
+
verify_signed_code "$HELPERS/recordings" "Companion CLI"
|
|
1796
|
+
verify_signed_code "$APP_DIR" "Recordings.app"
|
|
1797
|
+
ARTIFACT_BASENAME="Recordings-${VERSION}-macos-${RELEASE_SUBTYPE}"
|
|
1798
|
+
NOTARY_ARCHIVE="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}-notarization.zip"
|
|
1799
|
+
FINAL_ARCHIVE="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.zip"
|
|
1800
|
+
FINAL_MANIFEST="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.manifest.json"
|
|
1801
|
+
NOTARY_SUBMISSION="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.notary-submit.json"
|
|
1802
|
+
NOTARY_LOG="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.notary-log.json"
|
|
1803
|
+
|
|
1804
|
+
for release_staging_output in \
|
|
1805
|
+
"$NOTARY_ARCHIVE" \
|
|
1806
|
+
"$FINAL_ARCHIVE" \
|
|
1807
|
+
"$FINAL_MANIFEST" \
|
|
1808
|
+
"$NOTARY_SUBMISSION" \
|
|
1809
|
+
"$NOTARY_LOG"; do
|
|
1810
|
+
[ ! -e "$release_staging_output" ] && [ ! -L "$release_staging_output" ] || {
|
|
1811
|
+
echo "Release staging output already exists; refusing replacement: $release_staging_output" >&2
|
|
1812
|
+
exit 1
|
|
1813
|
+
}
|
|
1814
|
+
done
|
|
1815
|
+
run_release_sensitive_tool "$DITTO_EXECUTABLE" -c -k --sequesterRsrc --keepParent "$APP_DIR" "$NOTARY_ARCHIVE"
|
|
1816
|
+
NOTARY_ARCHIVE_SHA256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 "$NOTARY_ARCHIVE" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{print $1}')"
|
|
1817
|
+
run_xcrun notarytool submit "$NOTARY_ARCHIVE" \
|
|
1818
|
+
--keychain-profile "$NOTARY_PROFILE" \
|
|
1819
|
+
--wait --output-format json >"$NOTARY_SUBMISSION"
|
|
1820
|
+
NOTARY_ID="$("$ENV_EXECUTABLE" -i \
|
|
1821
|
+
HOME="$BUILD_HOME" \
|
|
1822
|
+
PATH="$SANITIZED_PATH" \
|
|
1823
|
+
TMPDIR="$BUILD_WORK_DIR" \
|
|
1824
|
+
NOTARY_SUBMISSION_JSON="$(run_release_sensitive_tool "$CAT_EXECUTABLE" "$NOTARY_SUBMISSION")" \
|
|
1825
|
+
"$BUN_EXECUTABLE" -e '
|
|
1826
|
+
const value = JSON.parse(process.env.NOTARY_SUBMISSION_JSON ?? "null");
|
|
1827
|
+
if (value?.status !== "Accepted" || typeof value?.id !== "string" || !value.id) process.exit(1);
|
|
1828
|
+
process.stdout.write(value.id);
|
|
1829
|
+
')" || {
|
|
1830
|
+
echo "Notarization submission was not accepted or omitted its submission ID." >&2
|
|
1831
|
+
exit 1
|
|
1832
|
+
}
|
|
1833
|
+
run_xcrun notarytool log "$NOTARY_ID" \
|
|
1834
|
+
--keychain-profile "$NOTARY_PROFILE" >"$NOTARY_LOG"
|
|
1835
|
+
if ! run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" assert-notary-log \
|
|
1836
|
+
--notary-log "$NOTARY_LOG" \
|
|
1837
|
+
--submission-id "$NOTARY_ID" \
|
|
1838
|
+
--submitted-archive-sha256 "$NOTARY_ARCHIVE_SHA256"; then
|
|
1839
|
+
echo "Accepted notarization log contains a rejected status or reported issues." >&2
|
|
263
1840
|
exit 1
|
|
264
1841
|
fi
|
|
1842
|
+
"$RM_EXECUTABLE" -f "$NOTARY_ARCHIVE"
|
|
1843
|
+
run_xcrun stapler staple "$APP_DIR"
|
|
1844
|
+
run_xcrun stapler validate "$APP_DIR"
|
|
1845
|
+
run_release_sensitive_tool "$SPCTL_EXECUTABLE" --assess --type execute --verbose=2 "$APP_DIR"
|
|
1846
|
+
run_release_sensitive_tool "$SYSPOLICY_CHECK_EXECUTABLE" distribution "$APP_DIR"
|
|
1847
|
+
verify_signed_code "$HELPERS/recordings" "Companion CLI"
|
|
1848
|
+
verify_signed_code "$APP_DIR" "Recordings.app"
|
|
1849
|
+
run_codesign --verify --deep --strict --verbose=2 "$APP_DIR"
|
|
1850
|
+
|
|
1851
|
+
run_release_sensitive_tool "$DITTO_EXECUTABLE" -c -k --sequesterRsrc --keepParent "$APP_DIR" "$FINAL_ARCHIVE"
|
|
1852
|
+
verify_source_unchanged
|
|
1853
|
+
run_bun "$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" finalize \
|
|
1854
|
+
--app "$APP_DIR" \
|
|
1855
|
+
--source-sha "$SOURCE_SHA" \
|
|
1856
|
+
--archive "$FINAL_ARCHIVE" \
|
|
1857
|
+
--manifest "$FINAL_MANIFEST" \
|
|
1858
|
+
--package-root "$PACKAGE_ROOT" \
|
|
1859
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
1860
|
+
--notary-log "$NOTARY_LOG" \
|
|
1861
|
+
--notary-submission-id "$NOTARY_ID" \
|
|
1862
|
+
--submitted-archive-sha256 "$NOTARY_ARCHIVE_SHA256"
|
|
1863
|
+
|
|
1864
|
+
designated_requirement() {
|
|
1865
|
+
local code_path="$1"
|
|
1866
|
+
local requirement
|
|
1867
|
+
requirement="$(run_codesign -d -r- "$code_path" 2>&1 | \
|
|
1868
|
+
run_release_sensitive_tool "$SED_EXECUTABLE" -n 's/^designated => //p' | \
|
|
1869
|
+
run_release_sensitive_tool "$HEAD_EXECUTABLE" -n 1)"
|
|
1870
|
+
[ -n "$requirement" ] || {
|
|
1871
|
+
echo "Signed component is missing its designated requirement: $code_path" >&2
|
|
1872
|
+
return 1
|
|
1873
|
+
}
|
|
1874
|
+
printf '%s\n' "$requirement"
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
build_app_update_envelope() {
|
|
1878
|
+
local payload="$BUILD_WORK_DIR/app-update-envelope-payload.json"
|
|
1879
|
+
local update_envelope="$OUTPUT_BUILD_DIR/${ARTIFACT_BASENAME}.update-envelope.json"
|
|
1880
|
+
[ "$HOST_PLATFORM" = "Darwin" ] || [ -n "$TEST_SWIFT_EXECUTABLE" ] || {
|
|
1881
|
+
echo "App-update release envelopes can only be built on macOS." >&2
|
|
1882
|
+
return 1
|
|
1883
|
+
}
|
|
1884
|
+
[ -f "$COMPATIBLE_COHORT_SNAPSHOT" ] && [ ! -L "$COMPATIBLE_COHORT_SNAPSHOT" ] || {
|
|
1885
|
+
echo "App-update compatible-cohort snapshot is missing or unsafe." >&2
|
|
1886
|
+
return 1
|
|
1887
|
+
}
|
|
1888
|
+
[ ! -e "$update_envelope" ] && [ ! -L "$update_envelope" ] || {
|
|
1889
|
+
echo "App-update envelope output already exists; refusing replacement." >&2
|
|
1890
|
+
return 1
|
|
1891
|
+
}
|
|
1892
|
+
run_bun "$RELEASE_LIFECYCLE_HELPER" write-update-payload \
|
|
1893
|
+
--app-archive "$FINAL_ARCHIVE" \
|
|
1894
|
+
--application-designated-requirement "$(designated_requirement "$APP_DIR")" \
|
|
1895
|
+
--compatible-cohort-manifest "$COMPATIBLE_COHORT_SNAPSHOT" \
|
|
1896
|
+
--envelope-public-key "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
1897
|
+
--expires-at-utc "$ENVELOPE_EXPIRES_AT_UTC" \
|
|
1898
|
+
--key-epoch "$KEY_EPOCH" \
|
|
1899
|
+
--manifest "$FINAL_MANIFEST" \
|
|
1900
|
+
--output "$payload" \
|
|
1901
|
+
--release-sequence "$RELEASE_SEQUENCE" \
|
|
1902
|
+
--source-sha "$SOURCE_SHA" \
|
|
1903
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
1904
|
+
--update-client "$UPDATE_CLIENT" \
|
|
1905
|
+
--update-client-designated-requirement "$(designated_requirement "$UPDATE_CLIENT")" \
|
|
1906
|
+
--version "$VERSION"
|
|
1907
|
+
run_release_sensitive_tool "$ENVELOPE_SIGNER" \
|
|
1908
|
+
--payload "$payload" \
|
|
1909
|
+
--private-key "$ENVELOPE_PRIVATE_KEY" \
|
|
1910
|
+
--public-key "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
1911
|
+
--output "$update_envelope"
|
|
1912
|
+
[ -f "$update_envelope" ] && [ ! -L "$update_envelope" ] && [ -s "$update_envelope" ] || {
|
|
1913
|
+
echo "Envelope signer did not emit exactly one app-update sidecar." >&2
|
|
1914
|
+
return 1
|
|
1915
|
+
}
|
|
1916
|
+
"$CHMOD_EXECUTABLE" 0444 "$update_envelope"
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
verify_source_unchanged
|
|
1920
|
+
|
|
1921
|
+
compute_package_publication_identity() {
|
|
1922
|
+
local app_archive_sha256 manifest_sha256 broker_sha256 verifier_sha256
|
|
1923
|
+
local bootstrap_preflight_sha256 envelope_public_key_sha256 envelope_signer_sha256
|
|
1924
|
+
if [ "$HOST_PLATFORM" != "Darwin" ] && [ -n "$TEST_SWIFT_EXECUTABLE" ]; then
|
|
1925
|
+
PACKAGE_PUBLICATION_IDENTITY_SHA256="$(run_bun \
|
|
1926
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" release-publication-identity \
|
|
1927
|
+
--component "release_kind=initial-bootstrap-updater" \
|
|
1928
|
+
--component "source_sha=$SOURCE_SHA" \
|
|
1929
|
+
--component "version=$VERSION" \
|
|
1930
|
+
--component "fixture_mode=non-darwin-contract")"
|
|
1931
|
+
[[ "$PACKAGE_PUBLICATION_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]] || {
|
|
1932
|
+
echo "Updater package publication identity is invalid." >&2
|
|
1933
|
+
return 1
|
|
1934
|
+
}
|
|
1935
|
+
return
|
|
1936
|
+
fi
|
|
1937
|
+
app_archive_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1938
|
+
"$FINAL_ARCHIVE" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1939
|
+
manifest_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1940
|
+
"$FINAL_MANIFEST" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1941
|
+
broker_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1942
|
+
"$UPDATE_BROKER" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1943
|
+
verifier_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1944
|
+
"$ARTIFACT_VERIFIER" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1945
|
+
bootstrap_preflight_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1946
|
+
"$BOOTSTRAP_PREFLIGHT_VERIFIER" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1947
|
+
envelope_public_key_sha256="$ENVELOPE_PUBLIC_KEY_SHA256"
|
|
1948
|
+
envelope_signer_sha256="$(run_release_sensitive_tool "$SHASUM_EXECUTABLE" -a 256 \
|
|
1949
|
+
"$ENVELOPE_SIGNER" | run_release_sensitive_tool "$AWK_EXECUTABLE" '{ print $1 }')"
|
|
1950
|
+
PACKAGE_PUBLICATION_IDENTITY_SHA256="$(run_bun \
|
|
1951
|
+
"$SOURCE_PACKAGE_ROOT/scripts/macos_artifact.ts" release-publication-identity \
|
|
1952
|
+
--component "release_kind=initial-bootstrap-updater" \
|
|
1953
|
+
--component "source_sha=$SOURCE_SHA" \
|
|
1954
|
+
--component "version=$VERSION" \
|
|
1955
|
+
--component "team_id=$EXPECTED_TEAM_ID" \
|
|
1956
|
+
--component "installer_identity=$INSTALLER_IDENTITY" \
|
|
1957
|
+
--component "notary_profile=$NOTARY_PROFILE" \
|
|
1958
|
+
--component "release_sequence=$RELEASE_SEQUENCE" \
|
|
1959
|
+
--component "key_epoch=$KEY_EPOCH" \
|
|
1960
|
+
--component "expires_at_utc=$ENVELOPE_EXPIRES_AT_UTC" \
|
|
1961
|
+
--component "app_archive_sha256=$app_archive_sha256" \
|
|
1962
|
+
--component "manifest_sha256=$manifest_sha256" \
|
|
1963
|
+
--component "broker_sha256=$broker_sha256" \
|
|
1964
|
+
--component "artifact_verifier_sha256=$verifier_sha256" \
|
|
1965
|
+
--component "bootstrap_preflight_sha256=$bootstrap_preflight_sha256" \
|
|
1966
|
+
--component "envelope_public_key_sha256=$envelope_public_key_sha256" \
|
|
1967
|
+
--component "envelope_signer_sha256=$envelope_signer_sha256")"
|
|
1968
|
+
[[ "$PACKAGE_PUBLICATION_IDENTITY_SHA256" =~ ^[a-f0-9]{64}$ ]] || {
|
|
1969
|
+
echo "Updater package publication identity is invalid." >&2
|
|
1970
|
+
return 1
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
if [ "$RELEASE_SUBTYPE" = "initial-bootstrap" ]; then
|
|
1975
|
+
compute_package_publication_identity
|
|
1976
|
+
run_release_sensitive_tool "$BASH_EXECUTABLE" \
|
|
1977
|
+
"$SOURCE_PACKAGE_ROOT/packaging/macos/build_release_pkg.sh" \
|
|
1978
|
+
--source-root "$SOURCE_PACKAGE_ROOT" \
|
|
1979
|
+
--app "$APP_DIR" \
|
|
1980
|
+
--app-archive "$FINAL_ARCHIVE" \
|
|
1981
|
+
--artifact-basename "$ARTIFACT_BASENAME" \
|
|
1982
|
+
--manifest "$FINAL_MANIFEST" \
|
|
1983
|
+
--broker "$UPDATE_BROKER" \
|
|
1984
|
+
--verifier "$ARTIFACT_VERIFIER" \
|
|
1985
|
+
--public-key "$ENVELOPE_PUBLIC_KEY_SNAPSHOT" \
|
|
1986
|
+
--envelope-private-key "$ENVELOPE_PRIVATE_KEY" \
|
|
1987
|
+
--envelope-signer "$ENVELOPE_SIGNER" \
|
|
1988
|
+
--bootstrap-preflight-verifier "$BOOTSTRAP_PREFLIGHT_VERIFIER" \
|
|
1989
|
+
--bun-executable "$BUN_EXECUTABLE" \
|
|
1990
|
+
--version "$VERSION" \
|
|
1991
|
+
--source-sha "$SOURCE_SHA" \
|
|
1992
|
+
--team-id "$EXPECTED_TEAM_ID" \
|
|
1993
|
+
--installer-identity "$INSTALLER_IDENTITY" \
|
|
1994
|
+
--notary-profile "$NOTARY_PROFILE" \
|
|
1995
|
+
--release-sequence "$RELEASE_SEQUENCE" \
|
|
1996
|
+
--key-epoch "$KEY_EPOCH" \
|
|
1997
|
+
--expires-at-utc "$ENVELOPE_EXPIRES_AT_UTC" \
|
|
1998
|
+
--publication-identity-sha256 "$PACKAGE_PUBLICATION_IDENTITY_SHA256" \
|
|
1999
|
+
--output-dir "$OUTPUT_BUILD_DIR"
|
|
2000
|
+
else
|
|
2001
|
+
build_app_update_envelope
|
|
2002
|
+
fi
|
|
265
2003
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
echo "
|
|
270
|
-
echo ""
|
|
271
|
-
echo "
|
|
272
|
-
echo " open $APP_DIR"
|
|
2004
|
+
publish_app_output
|
|
2005
|
+
publish_complete_release_set
|
|
2006
|
+
verify_source_unchanged
|
|
2007
|
+
echo "Built immutable app artifact: $FINAL_ARCHIVE"
|
|
2008
|
+
echo "Built artifact manifest: $FINAL_MANIFEST"
|
|
2009
|
+
echo "Captured accepted notarization log: $NOTARY_LOG"
|