mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-10-06 08:02:04 +00:00
build(macos): bundle name, id and paths overridable; notarize with an App Store Connect key (#1067)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dd4bd806c6
commit
3602daff5b
@@ -27,18 +27,24 @@ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
PACKAGE_UPDATE_ZIP="${TTY7_PACKAGE_UPDATE_ZIP:-1}"
|
||||
APP="dist/tty7.app"
|
||||
# A rebranded or local build sets its own name and id, binary dir and output
|
||||
# dir. TTY7_BUNDLE_ONLY stops after the .app, before the update zip and DMG.
|
||||
APP_NAME="${TTY7_APP_NAME:-tty7}"
|
||||
BUNDLE_ID="${TTY7_BUNDLE_ID:-com.github.tty7}"
|
||||
BIN_DIR="${TTY7_BIN_DIR:-target/${TARGET}/release}"
|
||||
DIST="${TTY7_DIST:-dist}"
|
||||
APP="$DIST/$APP_NAME.app"
|
||||
|
||||
rm -rf dist
|
||||
rm -rf "$DIST"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||
cp "target/${TARGET}/release/tty7-app" "$APP/Contents/MacOS/tty7-app"
|
||||
cp "$BIN_DIR/tty7-app" "$APP/Contents/MacOS/tty7-app"
|
||||
chmod +x "$APP/Contents/MacOS/tty7-app"
|
||||
# The CLI rides inside the bundle rather than beside it: a DMG is drag-to-
|
||||
# Applications, so anything not in the .app never reaches the user's disk. The
|
||||
# GUI symlinks it onto PATH at launch (see core::cli_install), which is why it
|
||||
# sits next to tty7-app under MacOS/ — that is the directory the GUI resolves
|
||||
# relative to its own executable.
|
||||
cp "target/${TARGET}/release/tty7" "$APP/Contents/MacOS/tty7"
|
||||
cp "$BIN_DIR/tty7" "$APP/Contents/MacOS/tty7"
|
||||
chmod +x "$APP/Contents/MacOS/tty7"
|
||||
if [[ "$PACKAGE_UPDATE_ZIP" != "0" ]]; then
|
||||
# A focused out-of-process updater can replace the bundle after the GUI
|
||||
@@ -47,7 +53,7 @@ if [[ "$PACKAGE_UPDATE_ZIP" != "0" ]]; then
|
||||
# is covered by the outer bundle — including Nightly, whose users are
|
||||
# offered the stable release that supersedes their prerelease and need a
|
||||
# working helper to get there.
|
||||
cp "target/${TARGET}/release/tty7-updater" "$APP/Contents/MacOS/tty7-updater"
|
||||
cp "$BIN_DIR/tty7-updater" "$APP/Contents/MacOS/tty7-updater"
|
||||
chmod +x "$APP/Contents/MacOS/tty7-updater"
|
||||
fi
|
||||
cp assets/tty7.icns "$APP/Contents/Resources/tty7.icns"
|
||||
@@ -56,15 +62,19 @@ cp assets/tty7.icns "$APP/Contents/Resources/tty7.icns"
|
||||
mkdir -p "$APP/Contents/Resources/completions"
|
||||
cp assets/completions/*.json "$APP/Contents/Resources/completions/"
|
||||
printf 'APPL????' > "$APP/Contents/PkgInfo"
|
||||
if [[ -n "${TTY7_LOCAL_BUILD_ID:-}" ]]; then
|
||||
# A local install's build id, which a running app can watch to offer a restart.
|
||||
printf '%s\n' "$TTY7_LOCAL_BUILD_ID" > "$APP/Contents/Resources/local-build-id"
|
||||
fi
|
||||
|
||||
cat > "$APP/Contents/Info.plist" <<PLIST
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key><string>tty7</string>
|
||||
<key>CFBundleDisplayName</key><string>tty7</string>
|
||||
<key>CFBundleIdentifier</key><string>com.github.tty7</string>
|
||||
<key>CFBundleName</key><string>${APP_NAME}</string>
|
||||
<key>CFBundleDisplayName</key><string>${APP_NAME}</string>
|
||||
<key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>
|
||||
<key>CFBundleVersion</key><string>${VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key><string>${VERSION}</string>
|
||||
<key>CFBundleExecutable</key><string>tty7-app</string>
|
||||
@@ -145,7 +155,12 @@ PLIST
|
||||
|
||||
SIGN_ID="${APPLE_SIGNING_IDENTITY:-}"
|
||||
|
||||
if [[ -n "$SIGN_ID" && -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
if [[ -n "$SIGN_ID" ]]; then
|
||||
# A local build signs with an identity already in the login keychain; only CI
|
||||
# imports one, and only CI pays for a secure timestamp.
|
||||
TIMESTAMP=--timestamp=none
|
||||
if [[ -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
TIMESTAMP=--timestamp
|
||||
# ---- Developer ID signing ------------------------------------------------
|
||||
# Import the cert into a throwaway keychain so we never touch the login one.
|
||||
KEYCHAIN="${RUNNER_TEMP:-/tmp}/tty7-sign.keychain-db"
|
||||
@@ -154,7 +169,7 @@ if [[ -n "$SIGN_ID" && -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
# Scrub the decoded cert + temp keychain on any exit path.
|
||||
cleanup() {
|
||||
security delete-keychain "$KEYCHAIN" >/dev/null 2>&1 || true
|
||||
rm -f "$CERT_PATH"
|
||||
rm -f "$CERT_PATH" "${ASC_KEY_PATH:-}"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
@@ -167,11 +182,12 @@ if [[ -n "$SIGN_ID" && -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: \
|
||||
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN" >/dev/null
|
||||
security list-keychains -d user -s "$KEYCHAIN" login.keychain
|
||||
fi
|
||||
|
||||
# Hardened runtime forbids JIT / unsigned executable memory by default; the
|
||||
# GPU/Metal path gpui uses needs them, so grant them explicitly or the
|
||||
# notarized build crashes on launch.
|
||||
ENTITLEMENTS="dist/entitlements.plist"
|
||||
ENTITLEMENTS="$DIST/entitlements.plist"
|
||||
cat > "$ENTITLEMENTS" <<'ENT'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
@@ -205,28 +221,35 @@ ENT
|
||||
# entitlements: the JIT and library-validation exemptions exist for gpui's
|
||||
# Metal path, and a CLI that never renders anything has no business holding
|
||||
# them.
|
||||
codesign --force --options runtime --timestamp \
|
||||
codesign --force --options runtime "$TIMESTAMP" \
|
||||
--sign "$SIGN_ID" "$APP/Contents/MacOS/tty7"
|
||||
if [[ "$PACKAGE_UPDATE_ZIP" != "0" ]]; then
|
||||
codesign --force --options runtime --timestamp \
|
||||
codesign --force --options runtime "$TIMESTAMP" \
|
||||
--sign "$SIGN_ID" "$APP/Contents/MacOS/tty7-updater"
|
||||
fi
|
||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" \
|
||||
codesign --force --options runtime "$TIMESTAMP" --entitlements "$ENTITLEMENTS" \
|
||||
--sign "$SIGN_ID" "$APP/Contents/MacOS/tty7-app"
|
||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" \
|
||||
codesign --force --options runtime "$TIMESTAMP" --entitlements "$ENTITLEMENTS" \
|
||||
--sign "$SIGN_ID" "$APP"
|
||||
codesign --verify --strict --verbose=2 "$APP"
|
||||
|
||||
# ---- Notarization --------------------------------------------------------
|
||||
if [[ -n "${APPLE_ID:-}" && -n "${APPLE_PASSWORD:-}" && -n "${APPLE_TEAM_ID:-}" ]]; then
|
||||
NOTARY_AUTH=()
|
||||
if [[ -n "${ASC_KEY_P8:-}" && -n "${ASC_KEY_ID:-}" && -n "${ASC_ISSUER_ID:-}" ]]; then
|
||||
# An App Store Connect API key, the alternative to an Apple ID password.
|
||||
ASC_KEY_PATH="${RUNNER_TEMP:-/tmp}/AuthKey_${ASC_KEY_ID}.p8"
|
||||
printf '%s\n' "$ASC_KEY_P8" > "$ASC_KEY_PATH"
|
||||
NOTARY_AUTH=(--key "$ASC_KEY_PATH" --key-id "$ASC_KEY_ID" --issuer "$ASC_ISSUER_ID")
|
||||
elif [[ -n "${APPLE_ID:-}" && -n "${APPLE_PASSWORD:-}" && -n "${APPLE_TEAM_ID:-}" ]]; then
|
||||
NOTARY_AUTH=(--apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" --team-id "$APPLE_TEAM_ID")
|
||||
fi
|
||||
if [[ ${#NOTARY_AUTH[@]} -gt 0 ]]; then
|
||||
# Submit a zip of the .app; on success staple the ticket onto the bundle
|
||||
# so it validates offline (the distributed zip below then carries it).
|
||||
ditto -c -k --keepParent "$APP" "dist/notarize.zip"
|
||||
xcrun notarytool submit "dist/notarize.zip" \
|
||||
--apple-id "$APPLE_ID" --password "$APPLE_PASSWORD" \
|
||||
--team-id "$APPLE_TEAM_ID" --wait
|
||||
ditto -c -k --keepParent "$APP" "$DIST/notarize.zip"
|
||||
xcrun notarytool submit "$DIST/notarize.zip" "${NOTARY_AUTH[@]}" --wait
|
||||
xcrun stapler staple "$APP"
|
||||
rm -f "dist/notarize.zip"
|
||||
rm -f "$DIST/notarize.zip"
|
||||
echo "✅ signed + notarized + stapled"
|
||||
else
|
||||
echo "⚠️ signed with Developer ID but notarization secrets missing — skipping notarize"
|
||||
@@ -317,6 +340,9 @@ if [[ "$BUNDLE_FAIL" -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ every Mach-O in $APP is a thin ${ARCH} binary (${SWEEP_SEEN} checked)"
|
||||
if [[ -n "${TTY7_BUNDLE_ONLY:-}" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The in-app updater needs the signed, notarized .app itself rather than a disk
|
||||
# image that requires Finder interaction. The helper re-reads the full embedded
|
||||
@@ -324,13 +350,13 @@ echo "✅ every Mach-O in $APP is a thin ${ARCH} binary (${SWEEP_SEEN} checked)"
|
||||
# it was told to install.
|
||||
ZIP=""
|
||||
if [[ "$PACKAGE_UPDATE_ZIP" != "0" ]]; then
|
||||
ZIP="dist/tty7-${VERSION}-macos-${ARCH}.zip"
|
||||
ZIP="$DIST/tty7-${VERSION}-macos-${ARCH}.zip"
|
||||
ditto -c -k --keepParent "$APP" "$ZIP"
|
||||
fi
|
||||
|
||||
# Package the (now stapled) bundle as a drag-to-Applications DMG.
|
||||
DMG="dist/tty7-${VERSION}-macos-${ARCH}.dmg"
|
||||
STAGE="dist/dmg-stage"
|
||||
DMG="$DIST/tty7-${VERSION}-macos-${ARCH}.dmg"
|
||||
STAGE="$DIST/dmg-stage"
|
||||
rm -rf "$STAGE"
|
||||
mkdir "$STAGE"
|
||||
# `mv`, not `cp -R`: this is the peak, and a second full copy of the bundle is
|
||||
@@ -354,7 +380,7 @@ ln -s /Applications "$STAGE/Applications"
|
||||
# measured against a stage of this shape, 127 MiB of empty volume cost 672 KiB
|
||||
# in the published DMG.
|
||||
STAGE_KB="$(du -sk "$STAGE" | awk '{print $1}')"
|
||||
hdiutil create -volname "tty7" -srcfolder "$STAGE" -ov -format UDZO \
|
||||
hdiutil create -volname "$APP_NAME" -srcfolder "$STAGE" -ov -format UDZO \
|
||||
-size "$(( STAGE_KB * 2 + 65536 ))k" "$DMG"
|
||||
rm -rf "$STAGE"
|
||||
if [[ -n "$SIGN_ID" && -n "${APPLE_CERTIFICATE:-}" ]]; then
|
||||
|
||||
Reference in New Issue
Block a user