fix(mac): fail closed on dist plist reads

This commit is contained in:
Vincent Koc
2026-05-26 11:43:55 +02:00
parent 4c8e9da033
commit 8e110a2122
3 changed files with 112 additions and 6 deletions

View File

@@ -33,3 +33,9 @@ plist_set_or_add_bool() {
/usr/libexec/PlistBuddy -c "Set :$key $value" "$plist" ||
/usr/libexec/PlistBuddy -c "Add :$key bool $value" "$plist"
}
plist_print_required() {
local plist="$1"
local key="$2"
/usr/libexec/PlistBuddy -c "Print :$key" "$plist"
}

View File

@@ -9,6 +9,8 @@ set -euo pipefail
# - dist/OpenClaw-<version>.dmg
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
source "$ROOT_DIR/scripts/lib/plistbuddy.sh"
BUILD_ROOT="$ROOT_DIR/apps/macos/.build"
PRODUCT="OpenClaw"
BUILD_CONFIG="${BUILD_CONFIG:-release}"
@@ -64,10 +66,10 @@ if [[ ! -d "$APP" ]]; then
exit 1
fi
VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$APP/Contents/Info.plist" 2>/dev/null || echo "0.0.0")
BUNDLE_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$APP/Contents/Info.plist" 2>/dev/null || echo "")
ACTUAL_BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "$APP/Contents/Info.plist" 2>/dev/null || echo "")
ACTUAL_FEED_URL=$(/usr/libexec/PlistBuddy -c "Print SUFeedURL" "$APP/Contents/Info.plist" 2>/dev/null || echo "")
VERSION="$(plist_print_required "$APP/Contents/Info.plist" CFBundleShortVersionString)"
BUNDLE_VERSION="$(plist_print_required "$APP/Contents/Info.plist" CFBundleVersion)"
ACTUAL_BUNDLE_ID="$(plist_print_required "$APP/Contents/Info.plist" CFBundleIdentifier)"
ACTUAL_FEED_URL="$(plist_print_required "$APP/Contents/Info.plist" SUFeedURL)"
ZIP="$ROOT_DIR/dist/OpenClaw-$VERSION.zip"
DMG="$ROOT_DIR/dist/OpenClaw-$VERSION.dmg"
NOTARY_ZIP="$ROOT_DIR/dist/OpenClaw-$VERSION.notary.zip"
@@ -82,8 +84,8 @@ if [[ "$SKIP_NOTARIZE" == "1" ]]; then
fi
if [[ "$BUILD_CONFIG" == "release" ]]; then
if [[ "$ACTUAL_BUNDLE_ID" == *.debug ]]; then
echo "Error: release packaging produced debug bundle id '$ACTUAL_BUNDLE_ID'." >&2
if [[ "$ACTUAL_BUNDLE_ID" != "$BUNDLE_ID" ]]; then
echo "Error: release packaging produced bundle id '$ACTUAL_BUNDLE_ID', expected '$BUNDLE_ID'." >&2
exit 1
fi

View File

@@ -0,0 +1,98 @@
import { spawnSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
const tempDirs: string[] = [];
const scriptPath = "scripts/package-mac-dist.sh";
function makePlist(): string {
const dir = mkdtempSync(path.join(tmpdir(), "openclaw-dist-plist-"));
tempDirs.push(dir);
const plist = path.join(dir, "Info.plist");
writeFileSync(
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>CFBundleShortVersionString</key>",
"<string>1.2.3</string>",
"</dict>",
"</plist>",
"",
].join("\n"),
"utf8",
);
return plist;
}
function runHelper(script: string) {
return spawnSync("bash", ["-lc", script], {
cwd: process.cwd(),
encoding: "utf8",
});
}
afterEach(() => {
for (const dir of tempDirs.splice(0)) {
rmSync(dir, { recursive: true, force: true });
}
});
describe("package-mac-dist plist validation", () => {
it("fails closed for required Info.plist reads", () => {
const script = readFileSync(scriptPath, "utf8");
const readBlock = script.slice(
script.indexOf("VERSION="),
script.indexOf('ZIP="$ROOT_DIR/dist/OpenClaw-$VERSION.zip"'),
);
expect(script).toContain('source "$ROOT_DIR/scripts/lib/plistbuddy.sh"');
expect(readBlock).toContain(
'VERSION="$(plist_print_required "$APP/Contents/Info.plist" CFBundleShortVersionString)"',
);
expect(readBlock).toContain(
'BUNDLE_VERSION="$(plist_print_required "$APP/Contents/Info.plist" CFBundleVersion)"',
);
expect(readBlock).toContain(
'ACTUAL_BUNDLE_ID="$(plist_print_required "$APP/Contents/Info.plist" CFBundleIdentifier)"',
);
expect(readBlock).toContain(
'ACTUAL_FEED_URL="$(plist_print_required "$APP/Contents/Info.plist" SUFeedURL)"',
);
expect(readBlock).not.toContain("PlistBuddy");
expect(readBlock).not.toContain("|| echo");
});
it("requires the release bundle id to match the configured bundle id", () => {
const script = readFileSync(scriptPath, "utf8");
const releaseBlock = script.slice(
script.indexOf('if [[ "$BUILD_CONFIG" == "release" ]]'),
script.indexOf('if [[ "$NOTARIZE" == "1" ]]'),
);
expect(releaseBlock).toContain('if [[ "$ACTUAL_BUNDLE_ID" != "$BUNDLE_ID" ]]');
expect(releaseBlock).toContain("expected '$BUNDLE_ID'");
expect(releaseBlock).not.toContain("*.debug");
});
it.runIf(process.platform === "darwin")(
"prints required plist keys and fails when a key is missing",
() => {
const plist = makePlist();
const result = runHelper(`
set -euo pipefail
source scripts/lib/plistbuddy.sh
plist_print_required ${JSON.stringify(plist)} CFBundleShortVersionString
plist_print_required ${JSON.stringify(plist)} CFBundleVersion
`);
expect(result.status).toBe(1);
expect(result.stdout).toContain("1.2.3");
expect(result.stderr).toContain("Does Not Exist");
},
);
});