mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-14 09:36:09 +00:00
fix: stage signed Mac app replacements (#104345)
This commit is contained in:
committed by
GitHub
parent
518c5823d8
commit
ee5834f7c4
@@ -8,7 +8,15 @@ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/plistbuddy.sh"
|
||||
source "$ROOT_DIR/scripts/lib/swift-toolchain.sh"
|
||||
source "$ROOT_DIR/scripts/lib/build-metadata.sh"
|
||||
APP_ROOT="$ROOT_DIR/dist/OpenClaw.app"
|
||||
DEFAULT_APP_ROOT="$ROOT_DIR/dist/OpenClaw.app"
|
||||
APP_ROOT="${OPENCLAW_PACKAGE_APP_ROOT:-$DEFAULT_APP_ROOT}"
|
||||
case "$APP_ROOT" in
|
||||
"$ROOT_DIR/dist/"*) ;;
|
||||
*)
|
||||
echo "ERROR: OPENCLAW_PACKAGE_APP_ROOT must stay under $ROOT_DIR/dist" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
BUILD_ROOT="$ROOT_DIR/apps/macos/.build"
|
||||
PRODUCT="OpenClaw"
|
||||
MLX_TTS_HELPER_PRODUCT="openclaw-mlx-tts"
|
||||
|
||||
@@ -27,6 +27,8 @@ TARGET_ONLY=0
|
||||
TARGET_APP_BUNDLE="${ROOT_DIR}/dist/OpenClaw.app"
|
||||
TARGET_EXECUTABLE="${TARGET_APP_BUNDLE}/${APP_EXECUTABLE_RELATIVE_PATH}"
|
||||
INSTALLED_EXECUTABLE="/Applications/OpenClaw.app/${APP_EXECUTABLE_RELATIVE_PATH}"
|
||||
STAGED_APP_DIR="${ROOT_DIR}/dist/.openclaw-replacement-${LOCK_KEY}-$$"
|
||||
STAGED_APP_BUNDLE="${STAGED_APP_DIR}/OpenClaw.app"
|
||||
|
||||
log() { printf '%s\n' "$*"; }
|
||||
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
@@ -43,6 +45,9 @@ run_step() {
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${STAGED_APP_DIR:-}" ]]; then
|
||||
rm -rf "${STAGED_APP_DIR}"
|
||||
fi
|
||||
if [[ "${LOCK_HELD}" != "1" || ! -d "${LOCK_DIR}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
@@ -314,8 +319,28 @@ elif [ "$SIGN" -eq 1 ]; then
|
||||
unset SIGN_IDENTITY
|
||||
fi
|
||||
|
||||
# 3) Package app (no embedded gateway).
|
||||
run_step "package app" bash -lc "cd '${ROOT_DIR}' && SKIP_TSC=${SKIP_TSC:-1} '${ROOT_DIR}/scripts/package-mac-app.sh'"
|
||||
# 3) Package and sign outside the live bundle. A failed package/sign operation
|
||||
# must leave the currently running and on-disk app untouched.
|
||||
run_step "package app" env \
|
||||
SKIP_TSC="${SKIP_TSC:-1}" \
|
||||
OPENCLAW_PACKAGE_APP_ROOT="${STAGED_APP_BUNDLE}" \
|
||||
"${ROOT_DIR}/scripts/package-mac-app.sh"
|
||||
run_step "verify packaged app" /usr/bin/codesign --verify --deep --strict "${STAGED_APP_BUNDLE}"
|
||||
|
||||
install_staged_app() {
|
||||
local previous="${ROOT_DIR}/dist/.OpenClaw.app.previous-$$"
|
||||
rm -rf "${previous}"
|
||||
if [[ -d "${TARGET_APP_BUNDLE}" ]]; then
|
||||
mv "${TARGET_APP_BUNDLE}" "${previous}"
|
||||
fi
|
||||
if ! mv "${STAGED_APP_BUNDLE}" "${TARGET_APP_BUNDLE}"; then
|
||||
if [[ -d "${previous}" && ! -d "${TARGET_APP_BUNDLE}" ]]; then
|
||||
mv "${previous}" "${TARGET_APP_BUNDLE}"
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
rm -rf "${previous}" "${STAGED_APP_DIR}"
|
||||
}
|
||||
|
||||
choose_app_bundle() {
|
||||
if [[ -n "${APP_BUNDLE}" ]]; then
|
||||
@@ -339,8 +364,6 @@ choose_app_bundle() {
|
||||
fail "App bundle not found. Set OPENCLAW_APP_BUNDLE to your installed OpenClaw.app"
|
||||
}
|
||||
|
||||
choose_app_bundle
|
||||
|
||||
# When signed, clear any previous launchagent override marker.
|
||||
if [[ "$NO_SIGN" -ne 1 && "$ATTACH_ONLY" -ne 1 && -f "${LAUNCHAGENT_DISABLE_MARKER}" ]]; then
|
||||
run_step "clear launchagent disable marker" /bin/rm -f "${LAUNCHAGENT_DISABLE_MARKER}"
|
||||
@@ -386,6 +409,9 @@ if [[ "$TARGET_ONLY" -eq 1 ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
run_step "install packaged app" install_staged_app
|
||||
choose_app_bundle
|
||||
|
||||
# 4) Launch the installed app in the foreground so the menu bar extra appears.
|
||||
# LaunchServices can inherit a huge environment from this shell (secrets, prompt vars, etc.).
|
||||
# That can cause launchd spawn failures and is undesirable for a GUI app anyway.
|
||||
|
||||
@@ -395,14 +395,32 @@ describe("scripts/restart-mac.sh", () => {
|
||||
it("keeps the managed app alive until the signed replacement is ready", () => {
|
||||
const script = readFileSync(restartScriptPath, "utf8");
|
||||
const packageIndex = script.indexOf('run_step "package app"');
|
||||
const verifyIndex = script.indexOf('run_step "verify packaged app"');
|
||||
const switchIndex = script.indexOf('log "==> Switching managed installed');
|
||||
const installIndex = script.indexOf('run_step "install packaged app"');
|
||||
const launchIndex = script.indexOf('run_step "launch app"');
|
||||
|
||||
expect(packageIndex).toBeGreaterThan(-1);
|
||||
expect(script).toContain('OPENCLAW_PACKAGE_APP_ROOT="${STAGED_APP_BUNDLE}"');
|
||||
expect(verifyIndex).toBeGreaterThan(packageIndex);
|
||||
expect(switchIndex).toBeGreaterThan(packageIndex);
|
||||
expect(installIndex).toBeGreaterThan(switchIndex);
|
||||
expect(launchIndex).toBeGreaterThan(installIndex);
|
||||
expect(launchIndex).toBeGreaterThan(switchIndex);
|
||||
});
|
||||
|
||||
it("restores the previous bundle if the staged install cannot complete", () => {
|
||||
const script = readFileSync(restartScriptPath, "utf8");
|
||||
const installBlock = script.slice(
|
||||
script.indexOf("install_staged_app()"),
|
||||
script.indexOf("choose_app_bundle()"),
|
||||
);
|
||||
|
||||
expect(installBlock).toContain('mv "${TARGET_APP_BUNDLE}" "${previous}"');
|
||||
expect(installBlock).toContain('if ! mv "${STAGED_APP_BUNDLE}" "${TARGET_APP_BUNDLE}"');
|
||||
expect(installBlock).toContain('mv "${previous}" "${TARGET_APP_BUNDLE}"');
|
||||
});
|
||||
|
||||
it("escalates only exact managed app processes when graceful shutdown stalls", () => {
|
||||
const script = readFileSync(restartScriptPath, "utf8");
|
||||
const managedKillBlock = script.slice(
|
||||
|
||||
Reference in New Issue
Block a user