mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
feat: release script builds Universal + ARM64 variants
Each release now produces two distribution zips: - Scarf-vX.X.X-Universal.zip (arm64 + x86_64, recommended) - Scarf-vX.X.X-ARM64.zip (arm64 only, ~14% smaller) Both are independently archived, exported with Developer ID, notarized, and stapled via a new build_variant helper. The appcast still points at the Universal zip since it works on all supported macs; ARM64 is an alternative manual download for Apple Silicon users who want the smaller file. README updated to list both variants. Prompted by the v1.6.1 release shipping only Universal; the ARM64 zip for v1.6.1 was produced ad-hoc and uploaded to the existing release. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -101,6 +101,7 @@ If a Hermes update changes the database schema or CLI output format, Scarf may n
|
|||||||
Download the latest build from [Releases](https://github.com/awizemann/scarf/releases):
|
Download the latest build from [Releases](https://github.com/awizemann/scarf/releases):
|
||||||
|
|
||||||
- `Scarf-vX.X.X-Universal.zip` — Apple Silicon + Intel (recommended)
|
- `Scarf-vX.X.X-Universal.zip` — Apple Silicon + Intel (recommended)
|
||||||
|
- `Scarf-vX.X.X-ARM64.zip` — Apple Silicon only (smaller download)
|
||||||
|
|
||||||
1. Unzip and drag **Scarf.app** to Applications
|
1. Unzip and drag **Scarf.app** to Applications
|
||||||
2. Launch normally — builds are Developer ID signed and notarized, so Gatekeeper accepts them on first launch
|
2. Launch normally — builds are Developer ID signed and notarized, so Gatekeeper accepts them on first launch
|
||||||
|
|||||||
+56
-39
@@ -54,8 +54,6 @@ APPCAST_URL="https://awizemann.github.io/scarf/appcast.xml"
|
|||||||
DOWNLOAD_URL_BASE="https://github.com/awizemann/scarf/releases/download"
|
DOWNLOAD_URL_BASE="https://github.com/awizemann/scarf/releases/download"
|
||||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
BUILD_DIR="$REPO_ROOT/build"
|
BUILD_DIR="$REPO_ROOT/build"
|
||||||
ARCHIVE_PATH="$BUILD_DIR/scarf.xcarchive"
|
|
||||||
EXPORT_DIR="$BUILD_DIR/export"
|
|
||||||
EXPORT_OPTIONS="$REPO_ROOT/scripts/ExportOptions.plist"
|
EXPORT_OPTIONS="$REPO_ROOT/scripts/ExportOptions.plist"
|
||||||
RELEASE_DIR="$REPO_ROOT/releases/v${VERSION}"
|
RELEASE_DIR="$REPO_ROOT/releases/v${VERSION}"
|
||||||
GH_PAGES_WORKTREE="${GH_PAGES_WORKTREE:-$REPO_ROOT/.gh-pages-worktree}"
|
GH_PAGES_WORKTREE="${GH_PAGES_WORKTREE:-$REPO_ROOT/.gh-pages-worktree}"
|
||||||
@@ -111,68 +109,86 @@ if [[ -f "$NOTES_FILE" ]]; then
|
|||||||
fi
|
fi
|
||||||
git commit -m "chore: Bump version to ${VERSION}"
|
git commit -m "chore: Bump version to ${VERSION}"
|
||||||
|
|
||||||
# ---------- build ----------
|
# ---------- build variants ----------
|
||||||
|
# Each release produces two zips: a Universal binary (recommended — works on
|
||||||
|
# both Apple Silicon and Intel) and an ARM64-only variant (smaller download for
|
||||||
|
# users who know they're on M-series silicon). Each variant is independently
|
||||||
|
# notarized and stapled. The appcast only references the Universal zip since
|
||||||
|
# it works everywhere; ARM64 is an alternative manual download.
|
||||||
|
|
||||||
log "Clean build directory"
|
log "Clean build directory"
|
||||||
rm -rf "$BUILD_DIR"
|
rm -rf "$BUILD_DIR"
|
||||||
mkdir -p "$BUILD_DIR"
|
mkdir -p "$BUILD_DIR" "$RELEASE_DIR"
|
||||||
|
|
||||||
log "Archive (universal arm64+x86_64)"
|
# build_variant <label> <archs> <output_zip>
|
||||||
|
# label e.g. "Universal" or "ARM64" (used as subdir name + log prefix)
|
||||||
|
# archs e.g. "arm64 x86_64" or "arm64" (space-separated ARCHS value)
|
||||||
|
# output_zip absolute path where the stapled, distribution-ready zip is written
|
||||||
|
build_variant() {
|
||||||
|
local label="$1"
|
||||||
|
local archs="$2"
|
||||||
|
local out_zip="$3"
|
||||||
|
local variant_dir="$BUILD_DIR/$label"
|
||||||
|
local archive_path="$variant_dir/scarf.xcarchive"
|
||||||
|
local export_dir="$variant_dir/export"
|
||||||
|
local app_path="$export_dir/Scarf.app"
|
||||||
|
local notarize_zip="$variant_dir/Scarf-notarize.zip"
|
||||||
|
|
||||||
|
mkdir -p "$variant_dir"
|
||||||
|
|
||||||
|
log "[$label] Archive (archs: $archs)"
|
||||||
xcodebuild \
|
xcodebuild \
|
||||||
-project "$PROJECT" \
|
-project "$PROJECT" \
|
||||||
-scheme "$SCHEME" \
|
-scheme "$SCHEME" \
|
||||||
-configuration Release \
|
-configuration Release \
|
||||||
-archivePath "$ARCHIVE_PATH" \
|
-archivePath "$archive_path" \
|
||||||
-destination "generic/platform=macOS" \
|
-destination "generic/platform=macOS" \
|
||||||
ONLY_ACTIVE_ARCH=NO \
|
ONLY_ACTIVE_ARCH=NO \
|
||||||
ARCHS="arm64 x86_64" \
|
ARCHS="$archs" \
|
||||||
archive
|
archive
|
||||||
|
|
||||||
log "Export signed .app"
|
log "[$label] Export signed .app"
|
||||||
xcodebuild \
|
xcodebuild \
|
||||||
-exportArchive \
|
-exportArchive \
|
||||||
-archivePath "$ARCHIVE_PATH" \
|
-archivePath "$archive_path" \
|
||||||
-exportPath "$EXPORT_DIR" \
|
-exportPath "$export_dir" \
|
||||||
-exportOptionsPlist "$EXPORT_OPTIONS"
|
-exportOptionsPlist "$EXPORT_OPTIONS"
|
||||||
|
|
||||||
# Xcode exports as scarf.app (PRODUCT_NAME = $TARGET_NAME = "scarf"). Rename the
|
# Xcode exports as scarf.app (PRODUCT_NAME = $TARGET_NAME = "scarf"). Rename so
|
||||||
# wrapper to Scarf.app so users see properly-cased app in /Applications. Renaming
|
# users see properly-cased Scarf.app in /Applications. Renaming the bundle
|
||||||
# the bundle directory does NOT invalidate the signature (codesign signs contents,
|
# wrapper does NOT invalidate the signature — codesign signs contents, not the
|
||||||
# not the wrapper folder name).
|
# wrapper folder name.
|
||||||
if [[ -d "$EXPORT_DIR/scarf.app" && ! -d "$EXPORT_DIR/Scarf.app" ]]; then
|
if [[ -d "$export_dir/scarf.app" && ! -d "$app_path" ]]; then
|
||||||
mv "$EXPORT_DIR/scarf.app" "$EXPORT_DIR/Scarf.app"
|
mv "$export_dir/scarf.app" "$app_path"
|
||||||
fi
|
fi
|
||||||
APP_PATH="$EXPORT_DIR/Scarf.app"
|
[[ -d "$app_path" ]] || die "[$label] exported app not found at $app_path"
|
||||||
[[ -d "$APP_PATH" ]] || die "exported app not found at $APP_PATH"
|
|
||||||
|
|
||||||
# ---------- verify signature ----------
|
log "[$label] Verify signature"
|
||||||
log "Verify signature"
|
codesign --verify --deep --strict --verbose=2 "$app_path"
|
||||||
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
|
|
||||||
# spctl will fail here (not yet notarized) — that's fine, we check after stapling
|
|
||||||
spctl --assess --type execute --verbose "$APP_PATH" || true
|
|
||||||
|
|
||||||
# ---------- notarize ----------
|
log "[$label] Zip for notarization"
|
||||||
log "Zip for notarization"
|
ditto -c -k --keepParent "$app_path" "$notarize_zip"
|
||||||
NOTARIZE_ZIP="$BUILD_DIR/Scarf-notarize.zip"
|
|
||||||
ditto -c -k --keepParent "$APP_PATH" "$NOTARIZE_ZIP"
|
|
||||||
|
|
||||||
log "Submit to notarytool (blocking)"
|
log "[$label] Submit to notarytool (blocking)"
|
||||||
xcrun notarytool submit "$NOTARIZE_ZIP" \
|
xcrun notarytool submit "$notarize_zip" \
|
||||||
--keychain-profile "$NOTARY_PROFILE" \
|
--keychain-profile "$NOTARY_PROFILE" \
|
||||||
--wait \
|
--wait \
|
||||||
--timeout 30m
|
--timeout 30m
|
||||||
|
|
||||||
log "Staple notarization ticket"
|
log "[$label] Staple + validate"
|
||||||
xcrun stapler staple "$APP_PATH"
|
xcrun stapler staple "$app_path"
|
||||||
xcrun stapler validate "$APP_PATH"
|
xcrun stapler validate "$app_path"
|
||||||
|
spctl --assess --type execute --verbose "$app_path"
|
||||||
|
|
||||||
log "Final gatekeeper assessment"
|
log "[$label] Package $(basename "$out_zip")"
|
||||||
spctl --assess --type execute --verbose "$APP_PATH"
|
ditto -c -k --keepParent "$app_path" "$out_zip"
|
||||||
|
}
|
||||||
|
|
||||||
# ---------- package distribution artifacts ----------
|
|
||||||
log "Package distribution zips"
|
|
||||||
mkdir -p "$RELEASE_DIR"
|
|
||||||
UNIVERSAL_ZIP="$RELEASE_DIR/Scarf-v${VERSION}-Universal.zip"
|
UNIVERSAL_ZIP="$RELEASE_DIR/Scarf-v${VERSION}-Universal.zip"
|
||||||
ditto -c -k --keepParent "$APP_PATH" "$UNIVERSAL_ZIP"
|
ARM64_ZIP="$RELEASE_DIR/Scarf-v${VERSION}-ARM64.zip"
|
||||||
|
|
||||||
|
build_variant "Universal" "arm64 x86_64" "$UNIVERSAL_ZIP"
|
||||||
|
build_variant "ARM64" "arm64" "$ARM64_ZIP"
|
||||||
|
|
||||||
# ---------- sign appcast entry ----------
|
# ---------- sign appcast entry ----------
|
||||||
log "Sign appcast entry with EdDSA"
|
log "Sign appcast entry with EdDSA"
|
||||||
@@ -241,7 +257,8 @@ fi
|
|||||||
gh release create "v${VERSION}" \
|
gh release create "v${VERSION}" \
|
||||||
--title "Scarf v${VERSION}" \
|
--title "Scarf v${VERSION}" \
|
||||||
"${GH_FLAGS[@]}" \
|
"${GH_FLAGS[@]}" \
|
||||||
"$UNIVERSAL_ZIP"
|
"$UNIVERSAL_ZIP" \
|
||||||
|
"$ARM64_ZIP"
|
||||||
|
|
||||||
# ---------- tag main (skipped for drafts) ----------
|
# ---------- tag main (skipped for drafts) ----------
|
||||||
if [[ $DRAFT -eq 0 ]]; then
|
if [[ $DRAFT -eq 0 ]]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user