cc-haha/desktop/scripts/install-macos-unsigned.sh
程序员阿江(Relakkes) 8bca6985fe docs(release): rewrite v0.4.0 notes and refresh unsigned install docs
- Rewrite release-notes/v0.4.0.md to match the historical style (no emoji,
  Highlights/Fixes/Notes), keep it user-facing only (drop release-process
  notes), and correct Linux (supported since the Tauri builds, not new).
- README.md / README.en.md: replace Tauri references with Electron, list
  macOS / Windows / Linux, and point first-launch approval to the guide.
- Rewrite docs/desktop/04-installation.md for Electron: Electron asset
  names, Linux section, and the unsigned-macOS flow (clear the DMG quarantine
  before double-clicking; drop the WebView2/right-click-open leftovers).
- install-macos-unsigned.sh: keep the same-folder DMG flow, no online download.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:47:07 +08:00

120 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# install-macos-unsigned.sh
#
# Installs the unsigned macOS build of Claude Code Haha from a DMG that sits
# next to this script (download both from the same GitHub Release into one
# folder, e.g. ~/Downloads, then run this script).
#
# Because this release ships without an Apple Developer ID signature, macOS
# quarantines the download and Gatekeeper refuses to launch it ("damaged" /
# "cannot verify developer"). This script installs the app to /Applications
# and removes the com.apple.quarantine attribute so it opens normally.
#
# Usage:
# bash install-macos-unsigned.sh # auto-pick the DMG next to this script
# bash install-macos-unsigned.sh /path/to/Claude-Code-Haha-0.4.0-mac-arm64.dmg
APP_NAME="Claude Code Haha.app"
APP_PATH="/Applications/${APP_NAME}"
script_dir() {
cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1
pwd
}
find_dmg() {
local dir="$1"
local arch
arch="$(uname -m)"
shopt -s nullglob
local matches=("${dir}"/Claude-Code-Haha-*-mac-*.dmg)
shopt -u nullglob
if [ "${#matches[@]}" -eq 0 ]; then
return 1
fi
if [ "${#matches[@]}" -eq 1 ]; then
printf '%s\n' "${matches[0]}"
return 0
fi
local wanted="mac-x64"
if [ "$arch" = "arm64" ]; then
wanted="mac-arm64"
fi
local dmg
for dmg in "${matches[@]}"; do
if [[ "$(basename "$dmg")" == *"${wanted}.dmg" ]]; then
printf '%s\n' "$dmg"
return 0
fi
done
printf '%s\n' "${matches[0]}"
}
main() {
local base_dir
base_dir="$(script_dir)"
local dmg="${1:-}"
if [ -z "$dmg" ]; then
if ! dmg="$(find_dmg "$base_dir")"; then
echo "No Claude Code Haha macOS DMG found next to this script."
echo "Download the DMG into the same folder as this script, then run it again."
echo "Usage: bash install-macos-unsigned.sh /path/to/Claude-Code-Haha-0.4.0-mac-arm64.dmg"
exit 1
fi
fi
if [ ! -f "$dmg" ]; then
echo "DMG not found: $dmg"
exit 1
fi
echo "Using DMG: $dmg"
xattr -dr com.apple.quarantine "$dmg" 2>/dev/null || true
local mount_output volume app_in_volume
mount_output="$(hdiutil attach "$dmg" -nobrowse -readonly)"
volume="$(printf '%s\n' "$mount_output" | sed -n 's#^.*\(/Volumes/.*\)$#\1#p' | head -n 1)"
if [ -z "$volume" ] || [ ! -d "$volume" ]; then
echo "Could not find mounted DMG volume."
exit 1
fi
cleanup() {
hdiutil detach "$volume" >/dev/null 2>&1 || true
}
trap cleanup EXIT
app_in_volume="${volume}/${APP_NAME}"
if [ ! -d "$app_in_volume" ]; then
echo "Could not find ${APP_NAME} in mounted DMG: $volume"
exit 1
fi
osascript -e 'quit app "Claude Code Haha"' >/dev/null 2>&1 || true
if [ -d "$APP_PATH" ]; then
local backup
backup="${HOME}/.Trash/Claude Code Haha.$(date +%Y%m%d%H%M%S).app"
echo "Moving existing app to: $backup"
mv "$APP_PATH" "$backup"
fi
echo "Installing to: $APP_PATH"
ditto "$app_in_volume" "$APP_PATH"
xattr -dr com.apple.quarantine "$APP_PATH" 2>/dev/null || true
echo "Opening Claude Code Haha..."
open "$APP_PATH"
}
main "$@"