Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled
Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11), free to diverge. Tree copied sans upstream .git; upstream remote added for future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19. Preserves docs/ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
252
scripts/create-dmg.sh
Executable file
252
scripts/create-dmg.sh
Executable file
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Create a styled DMG containing the app bundle + /Applications symlink.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/create-dmg.sh <app_path> [output_dmg]
|
||||
#
|
||||
# Env:
|
||||
# DMG_VOLUME_NAME default: CFBundleName
|
||||
# DMG_BACKGROUND_PATH default: apps/macos/Packaging/dmg-background.png
|
||||
# DMG_BACKGROUND_SMALL default: apps/macos/Packaging/dmg-background-small.png (recommended)
|
||||
# DMG_WINDOW_BOUNDS default: "400 100 900 420" (500x320)
|
||||
# DMG_ICON_SIZE default: 128
|
||||
# DMG_APP_POS default: "125 160"
|
||||
# DMG_APPS_POS default: "375 160"
|
||||
# SKIP_DMG_STYLE=1 skip Finder styling
|
||||
# DMG_EXTRA_SECTORS extra sectors to keep when shrinking RW image (default: 2048)
|
||||
|
||||
APP_PATH="${1:-}"
|
||||
OUT_PATH="${2:-}"
|
||||
|
||||
if [[ -z "$APP_PATH" ]]; then
|
||||
echo "Usage: $0 <app_path> [output_dmg]" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -d "$APP_PATH" ]]; then
|
||||
echo "Error: App not found: $APP_PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
source "$ROOT_DIR/scripts/lib/plistbuddy.sh"
|
||||
|
||||
BUILD_DIR="$ROOT_DIR/dist"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
APP_NAME="$(plist_print_required "$APP_PATH/Contents/Info.plist" CFBundleName)"
|
||||
VERSION="$(plist_print_required "$APP_PATH/Contents/Info.plist" CFBundleShortVersionString)"
|
||||
|
||||
DMG_NAME="${APP_NAME}-${VERSION}.dmg"
|
||||
DMG_VOLUME_NAME="${DMG_VOLUME_NAME:-$APP_NAME}"
|
||||
DMG_BACKGROUND_SMALL="${DMG_BACKGROUND_SMALL:-$ROOT_DIR/apps/macos/Packaging/dmg-background-small.png}"
|
||||
DMG_BACKGROUND_PATH="${DMG_BACKGROUND_PATH:-$ROOT_DIR/apps/macos/Packaging/dmg-background.png}"
|
||||
|
||||
DMG_WINDOW_BOUNDS="${DMG_WINDOW_BOUNDS:-400 100 900 420}"
|
||||
DMG_ICON_SIZE="${DMG_ICON_SIZE:-128}"
|
||||
DMG_APP_POS="${DMG_APP_POS:-125 160}"
|
||||
DMG_APPS_POS="${DMG_APPS_POS:-375 160}"
|
||||
DMG_EXTRA_SECTORS="${DMG_EXTRA_SECTORS:-2048}"
|
||||
|
||||
require_integer_list() {
|
||||
local name="$1"
|
||||
local raw="$2"
|
||||
local expected_count="$3"
|
||||
local values=()
|
||||
local value
|
||||
|
||||
if [[ "$raw" == *$'\n'* || "$raw" == *$'\r'* ]]; then
|
||||
echo "Error: $name must be a single line of integer values: '$raw'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -r -a values <<< "$raw"
|
||||
if [[ "${#values[@]}" -ne "$expected_count" ]]; then
|
||||
echo "Error: $name must contain $expected_count integer value(s): '$raw'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for value in "${values[@]}"; do
|
||||
if [[ ! "$value" =~ ^-?[0-9]+$ ]]; then
|
||||
echo "Error: $name must contain only integer values: '$raw'" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
require_positive_integer() {
|
||||
local name="$1"
|
||||
local raw="$2"
|
||||
if [[ ! "$raw" =~ ^[1-9][0-9]*$ ]]; then
|
||||
echo "Error: $name must be a positive integer: '$raw'" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_nonnegative_integer() {
|
||||
local name="$1"
|
||||
local raw="$2"
|
||||
if [[ ! "$raw" =~ ^(0|[1-9][0-9]*)$ || "${#raw}" -gt 9 ]]; then
|
||||
echo "Error: $name must be a finite non-negative integer: '$raw'" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_integer_list DMG_WINDOW_BOUNDS "$DMG_WINDOW_BOUNDS" 4
|
||||
require_integer_list DMG_APP_POS "$DMG_APP_POS" 2
|
||||
require_integer_list DMG_APPS_POS "$DMG_APPS_POS" 2
|
||||
require_positive_integer DMG_ICON_SIZE "$DMG_ICON_SIZE"
|
||||
require_nonnegative_integer DMG_EXTRA_SECTORS "$DMG_EXTRA_SECTORS"
|
||||
|
||||
to_applescript_list4() {
|
||||
local raw="$1"
|
||||
echo "$raw" | awk '{ printf "%s, %s, %s, %s", $1, $2, $3, $4 }'
|
||||
}
|
||||
|
||||
to_applescript_pair() {
|
||||
local raw="$1"
|
||||
echo "$raw" | awk '{ printf "%s, %s", $1, $2 }'
|
||||
}
|
||||
|
||||
if [[ -z "$OUT_PATH" ]]; then
|
||||
OUT_PATH="$BUILD_DIR/$DMG_NAME"
|
||||
fi
|
||||
OUT_DIR="$(dirname "$OUT_PATH")"
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
echo "Creating DMG: $OUT_PATH"
|
||||
|
||||
DMG_TEMP="$(mktemp -d "${TMPDIR:-/tmp}/openclaw-dmg.XXXXXX")"
|
||||
DMG_SOURCE="$DMG_TEMP/source"
|
||||
MOUNT_POINT="$DMG_TEMP/mount"
|
||||
DMG_RW_PATH="$DMG_TEMP/image-rw.dmg"
|
||||
DMG_OUTPUT_TEMP=""
|
||||
DMG_FINAL_PATH=""
|
||||
MOUNTED=0
|
||||
|
||||
cleanup_dmg() {
|
||||
if [[ "$MOUNTED" == "1" ]]; then
|
||||
if hdiutil detach "$MOUNT_POINT" -force 2>/dev/null; then
|
||||
MOUNTED=0
|
||||
else
|
||||
echo "WARN: Preserving DMG temp root because mount is still attached: $DMG_TEMP" >&2
|
||||
return
|
||||
fi
|
||||
fi
|
||||
if [[ -n "$DMG_OUTPUT_TEMP" ]]; then
|
||||
rm -rf "$DMG_OUTPUT_TEMP" 2>/dev/null || true
|
||||
fi
|
||||
rm -rf "$DMG_TEMP" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup_dmg EXIT
|
||||
|
||||
detach_dmg() {
|
||||
local attempt
|
||||
for attempt in {1..15}; do
|
||||
if hdiutil detach "$MOUNT_POINT" -quiet 2>/dev/null; then
|
||||
MOUNTED=0
|
||||
return
|
||||
fi
|
||||
if (( attempt >= 3 )) && hdiutil detach "$MOUNT_POINT" -force 2>/dev/null; then
|
||||
MOUNTED=0
|
||||
return
|
||||
fi
|
||||
# Finder can retain the just-closed volume briefly on macOS runners.
|
||||
sleep 2
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
mkdir -p "$DMG_SOURCE" "$MOUNT_POINT"
|
||||
cp -R "$APP_PATH" "$DMG_SOURCE/"
|
||||
ln -s /Applications "$DMG_SOURCE/Applications"
|
||||
|
||||
APP_SIZE_MB=$(du -sm "$APP_PATH" | awk '{print $1}')
|
||||
DMG_SIZE_MB=$((APP_SIZE_MB + 80))
|
||||
|
||||
hdiutil create \
|
||||
-volname "$DMG_VOLUME_NAME" \
|
||||
-srcfolder "$DMG_SOURCE" \
|
||||
-ov \
|
||||
-format UDRW \
|
||||
-size "${DMG_SIZE_MB}m" \
|
||||
"$DMG_RW_PATH"
|
||||
|
||||
hdiutil attach "$DMG_RW_PATH" -mountpoint "$MOUNT_POINT" -nobrowse
|
||||
MOUNTED=1
|
||||
|
||||
if [[ "${SKIP_DMG_STYLE:-0}" != "1" ]]; then
|
||||
mkdir -p "$MOUNT_POINT/.background"
|
||||
if [[ -f "$DMG_BACKGROUND_SMALL" ]]; then
|
||||
cp "$DMG_BACKGROUND_SMALL" "$MOUNT_POINT/.background/background.png"
|
||||
elif [[ -f "$DMG_BACKGROUND_PATH" ]]; then
|
||||
cp "$DMG_BACKGROUND_PATH" "$MOUNT_POINT/.background/background.png"
|
||||
else
|
||||
echo "WARN: DMG background missing: $DMG_BACKGROUND_SMALL / $DMG_BACKGROUND_PATH" >&2
|
||||
fi
|
||||
|
||||
# Volume icon: reuse the app icon if available.
|
||||
ICON_SRC="$ROOT_DIR/apps/macos/Sources/OpenClaw/Resources/OpenClaw.icns"
|
||||
if [[ -f "$ICON_SRC" ]]; then
|
||||
cp "$ICON_SRC" "$MOUNT_POINT/.VolumeIcon.icns"
|
||||
if command -v SetFile >/dev/null 2>&1; then
|
||||
SetFile -a C "$MOUNT_POINT" 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
osascript <<EOF
|
||||
tell application "Finder"
|
||||
set dmgRoot to POSIX file "$MOUNT_POINT" as alias
|
||||
set dmgDisk to disk of dmgRoot
|
||||
tell dmgDisk
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {$(to_applescript_list4 "$DMG_WINDOW_BOUNDS")}
|
||||
set viewOptions to the icon view options of container window
|
||||
set arrangement of viewOptions to not arranged
|
||||
set icon size of viewOptions to ${DMG_ICON_SIZE}
|
||||
if exists file ".background:background.png" then
|
||||
set background picture of viewOptions to file ".background:background.png"
|
||||
end if
|
||||
set text size of viewOptions to 12
|
||||
set label position of viewOptions to bottom
|
||||
set shows item info of viewOptions to false
|
||||
set shows icon preview of viewOptions to true
|
||||
set position of item "${APP_NAME}.app" of container window to {$(to_applescript_pair "$DMG_APP_POS")}
|
||||
set position of item "Applications" of container window to {$(to_applescript_pair "$DMG_APPS_POS")}
|
||||
update without registering applications
|
||||
delay 2
|
||||
close
|
||||
open
|
||||
delay 1
|
||||
close container window
|
||||
end tell
|
||||
end tell
|
||||
EOF
|
||||
fi
|
||||
|
||||
if ! detach_dmg; then
|
||||
echo "Error: Failed to detach DMG mount: $MOUNT_POINT" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DMG_LIMITS_PATH="$DMG_TEMP/resize-limits.txt"
|
||||
hdiutil resize -limits "$DMG_RW_PATH" >"$DMG_LIMITS_PATH" 2>/dev/null || true
|
||||
MIN_SECTORS="$(tail -n 1 "$DMG_LIMITS_PATH" 2>/dev/null | awk '{print $1}')"
|
||||
if [[ "$MIN_SECTORS" =~ ^[0-9]+$ ]] && [[ "$DMG_EXTRA_SECTORS" =~ ^[0-9]+$ ]]; then
|
||||
TARGET_SECTORS=$((MIN_SECTORS + DMG_EXTRA_SECTORS))
|
||||
echo "Shrinking RW image: min sectors=$MIN_SECTORS (+$DMG_EXTRA_SECTORS) -> $TARGET_SECTORS"
|
||||
hdiutil resize -sectors "$TARGET_SECTORS" "$DMG_RW_PATH" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
DMG_OUTPUT_TEMP="$(mktemp -d "$(dirname "$OUT_PATH")/.openclaw-dmg.XXXXXX")"
|
||||
DMG_FINAL_PATH="$DMG_OUTPUT_TEMP/final.dmg"
|
||||
|
||||
hdiutil convert "$DMG_RW_PATH" -format ULMO -o "$DMG_FINAL_PATH" -ov
|
||||
|
||||
hdiutil verify "$DMG_FINAL_PATH" >/dev/null
|
||||
mv -f "$DMG_FINAL_PATH" "$OUT_PATH"
|
||||
echo "✅ DMG ready: $OUT_PATH"
|
||||
Reference in New Issue
Block a user