153 lines
5.6 KiB
Bash
153 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
script_dir="$(cd "$(dirname "$0")" && pwd -P)"
|
||
project_root="$(cd "$script_dir/.." && pwd -P)"
|
||
# shellcheck source=macos_helpers.sh
|
||
source "$script_dir/macos_helpers.sh"
|
||
macos_install_exit_trap 1
|
||
macos_require_darwin
|
||
/bin/bash "$script_dir/check_macos_entrypoints.sh"
|
||
|
||
NODE_BIN=""
|
||
NPM_BIN=""
|
||
|
||
use_node_pair() {
|
||
local node_candidate="$1"
|
||
local npm_candidate="$2"
|
||
local major version
|
||
[[ -x "$node_candidate" && -x "$npm_candidate" ]] || return 1
|
||
version="$("$node_candidate" --version 2>/dev/null || true)"
|
||
major="${version#v}"
|
||
major="${major%%.*}"
|
||
case "$major" in
|
||
''|*[!0-9]*) return 1 ;;
|
||
esac
|
||
[[ "$major" -ge 20 ]] || return 1
|
||
NODE_BIN="$node_candidate"
|
||
NPM_BIN="$npm_candidate"
|
||
export PATH="$(dirname "$NODE_BIN"):$PATH"
|
||
}
|
||
|
||
locate_node() {
|
||
local bin_dir node_candidate npm_candidate
|
||
node_candidate="$(command -v node 2>/dev/null || true)"
|
||
npm_candidate="$(command -v npm 2>/dev/null || true)"
|
||
if use_node_pair "$node_candidate" "$npm_candidate"; then
|
||
return 0
|
||
fi
|
||
|
||
for bin_dir in /opt/homebrew/bin /usr/local/bin; do
|
||
if use_node_pair "$bin_dir/node" "$bin_dir/npm"; then
|
||
return 0
|
||
fi
|
||
done
|
||
|
||
if [[ -n "${HOME:-}" ]]; then
|
||
for node_candidate in "$HOME"/.nvm/versions/node/*/bin/node; do
|
||
[[ -x "$node_candidate" ]] || continue
|
||
npm_candidate="$(dirname "$node_candidate")/npm"
|
||
if use_node_pair "$node_candidate" "$npm_candidate"; then
|
||
return 0
|
||
fi
|
||
done
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
install_local_node() {
|
||
local architecture archive archive_path cache_root curl_bin download_dir
|
||
local expected extracted install_dir node_version shasums_path actual
|
||
node_version="${DOCTOR_NODE_VERSION:-22.14.0}"
|
||
case "$(uname -m)" in
|
||
arm64) architecture="arm64" ;;
|
||
x86_64) architecture="x64" ;;
|
||
*) macos_die "不支持的 Mac CPU 架构:$(uname -m)" ;;
|
||
esac
|
||
|
||
if [[ -n "${XDG_CACHE_HOME:-}" ]]; then
|
||
cache_root="$XDG_CACHE_HOME/DoctorWorkstation/tools"
|
||
elif [[ -n "${HOME:-}" ]]; then
|
||
cache_root="$HOME/Library/Caches/DoctorWorkstation/tools"
|
||
else
|
||
macos_die "无法确定 Node 工具缓存目录:HOME 与 XDG_CACHE_HOME 均未设置。"
|
||
fi
|
||
install_dir="$cache_root/node-v$node_version-darwin-$architecture"
|
||
if use_node_pair "$install_dir/bin/node" "$install_dir/bin/npm"; then
|
||
return 0
|
||
fi
|
||
if [[ -e "$install_dir" ]]; then
|
||
macos_die "Node 缓存不完整,请删除后重试:$install_dir"
|
||
fi
|
||
|
||
curl_bin="$(command -v curl 2>/dev/null || true)"
|
||
[[ -n "$curl_bin" ]] || macos_die "未找到用于下载 Node.js 的 curl。"
|
||
mkdir -p "$cache_root"
|
||
download_dir="$(mktemp -d "$cache_root/doctor-node.XXXXXX")"
|
||
macos_register_temp_dir "$download_dir"
|
||
archive="node-v$node_version-darwin-$architecture.tar.gz"
|
||
archive_path="$download_dir/$archive"
|
||
shasums_path="$download_dir/SHASUMS256.txt"
|
||
|
||
printf '未检测到 Node.js 20+ 与 npm,正在下载 Node.js %s(%s)…\n' \
|
||
"$node_version" "$architecture"
|
||
if ! "$curl_bin" --proto '=https' --tlsv1.2 -fsSL \
|
||
"https://nodejs.org/dist/v$node_version/$archive" -o "$archive_path"; then
|
||
macos_die "Node.js 下载失败,请检查网络后重试。"
|
||
fi
|
||
if ! "$curl_bin" --proto '=https' --tlsv1.2 -fsSL \
|
||
"https://nodejs.org/dist/v$node_version/SHASUMS256.txt" -o "$shasums_path"; then
|
||
macos_die "Node.js 校验文件下载失败。"
|
||
fi
|
||
expected="$(/usr/bin/awk -v file="$archive" '$2 == file { print $1; exit }' "$shasums_path")"
|
||
actual="$(/usr/bin/shasum -a 256 "$archive_path" | /usr/bin/awk '{print $1}')"
|
||
[[ -n "$expected" && "$actual" == "$expected" ]] || macos_die "Node.js 下载包 SHA-256 校验失败。"
|
||
|
||
/usr/bin/tar -xzf "$archive_path" -C "$download_dir"
|
||
extracted="$download_dir/node-v$node_version-darwin-$architecture"
|
||
[[ -d "$extracted" ]] || macos_die "Node.js 下载包结构无效。"
|
||
if [[ ! -e "$install_dir" ]]; then
|
||
/bin/mv "$extracted" "$install_dir"
|
||
fi
|
||
use_node_pair "$install_dir/bin/node" "$install_dir/bin/npm" || \
|
||
macos_die "Node.js 安装完成,但 node/npm 无法执行。"
|
||
}
|
||
|
||
macos_ensure_uv
|
||
cd "$project_root"
|
||
printf '正在同步 Python 与 PyInstaller 构建依赖…\n'
|
||
"$MACOS_UV_BIN" sync --locked --extra build
|
||
python_bin="$project_root/.venv/bin/python"
|
||
[[ -x "$python_bin" ]] || macos_die "uv 未生成可用的构建 Python:$python_bin"
|
||
|
||
locate_node || install_local_node
|
||
printf 'Node.js: %s;npm: %s\n' \
|
||
"$("$NODE_BIN" --version)" "$("$NPM_BIN" --version)"
|
||
printf '正在安装锁定的 companion 依赖…\n'
|
||
"$NPM_BIN" ci --prefix "$project_root/video_companion" --no-audit --no-fund
|
||
|
||
printf '正在构建 DoctorWorkstation.app…\n'
|
||
PYINSTALLER_PYTHON="$python_bin" SKIP_FRONTEND_INSTALL=1 \
|
||
/bin/bash "$script_dir/build_macos.sh"
|
||
|
||
artifact="$project_root/dist/DoctorWorkstation.app"
|
||
project_version="$(/usr/bin/awk -F '"' '/^version = "/ { print $2; exit }' \
|
||
"$project_root/pyproject.toml")"
|
||
[[ -n "$project_version" ]] || macos_die "无法从 pyproject.toml 读取版本号。"
|
||
case "$(uname -m)" in
|
||
arm64) release_arch="arm64" ;;
|
||
x86_64) release_arch="x64" ;;
|
||
*) macos_die "不支持的 Mac CPU 架构:$(uname -m)" ;;
|
||
esac
|
||
release_zip="$project_root/dist/DoctorWorkstation-macOS-$release_arch-$project_version.zip"
|
||
checksum_file="$release_zip.sha256"
|
||
rm -f -- "$release_zip" "$checksum_file"
|
||
printf '正在生成可分发 ZIP…\n'
|
||
/usr/bin/ditto -c -k --sequesterRsrc --keepParent "$artifact" "$release_zip"
|
||
release_hash="$(/usr/bin/shasum -a 256 "$release_zip" | /usr/bin/awk '{print $1}')"
|
||
printf '%s %s\n' "$release_hash" "$(basename "$release_zip")" >"$checksum_file"
|
||
|
||
printf '\n打包成功:%s\n' "$artifact"
|
||
printf '分发包:%s\n' "$release_zip"
|
||
printf 'SHA-256:%s\n' "$release_hash"
|