feat: harden local shell, SFTP, and desktop session reliability
Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
188f3556ed
commit
08696896f9
223 files changed
+13493
-3423
No files matched your search
Executable
+220
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# OpsXShell v2.1.x native release builder.
|
||||
# Builds only for the current host OS; macOS and Windows installers must be
|
||||
# produced and verified on their respective systems.
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
VERSION_ARG=""
|
||||
TARGET="auto"
|
||||
CHECK_MODE="full"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
OpsXShell 原生安装包构建脚本
|
||||
|
||||
用法:
|
||||
./scripts/build.sh [v2.1.x] [目标] [选项]
|
||||
|
||||
目标:
|
||||
auto 根据当前系统自动选择(默认)
|
||||
mac-arm64 macOS Apple Silicon,生成 .app 和 .dmg
|
||||
mac-x64 macOS Intel,生成 .app 和 .dmg
|
||||
win-x64 Windows x86_64,生成 NSIS/MSI 安装包
|
||||
|
||||
选项:
|
||||
--quick 构建前只运行快速门禁
|
||||
--skip-checks 跳过门禁,仅用于本地重复打包
|
||||
-h, --help 显示帮助
|
||||
|
||||
示例:
|
||||
./scripts/build.sh
|
||||
./scripts/build.sh v2.1.0 mac-arm64
|
||||
./scripts/build.sh v2.1.1 mac-arm64 --quick
|
||||
|
||||
说明:
|
||||
- 显式传入新版本时,会同步 package.json、package-lock.json、
|
||||
Cargo.toml、Cargo.lock 和 tauri.conf.json,但不会创建或推送 Git tag。
|
||||
- Windows 包必须在 Windows 上构建;macOS 包必须在 macOS 上构建。
|
||||
- 正式签名构建请先设置 REQUIRE_SIGNING=true 及对应签名环境变量。
|
||||
EOF
|
||||
}
|
||||
|
||||
fail() {
|
||||
printf '\n[build] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
log() {
|
||||
printf '\n[build] %s\n' "$*"
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
--quick)
|
||||
CHECK_MODE="quick"
|
||||
;;
|
||||
--skip-checks)
|
||||
CHECK_MODE="none"
|
||||
;;
|
||||
auto|mac-arm64|mac-x64|win-x64)
|
||||
TARGET="$1"
|
||||
;;
|
||||
v[0-9]*|[0-9]*)
|
||||
[ -z "$VERSION_ARG" ] || fail "只能指定一个版本号"
|
||||
VERSION_ARG="${1#v}"
|
||||
;;
|
||||
*)
|
||||
fail "未知参数:$1(使用 ./scripts/build.sh --help 查看用法)"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
for command in node npm npx cargo rustc; do
|
||||
command -v "$command" >/dev/null 2>&1 || fail "缺少构建依赖:$command"
|
||||
done
|
||||
|
||||
CURRENT_VERSION="$(node -p "require('./package.json').version")"
|
||||
VERSION="${VERSION_ARG:-$CURRENT_VERSION}"
|
||||
|
||||
if ! printf '%s' "$VERSION" | grep -Eq '^2\.1\.[0-9]+([.-][0-9A-Za-z.-]+)?$'; then
|
||||
fail "版本必须是 v2.1.x 语义版本,当前输入:$VERSION"
|
||||
fi
|
||||
|
||||
HOST_OS="$(uname -s)"
|
||||
HOST_ARCH="$(uname -m)"
|
||||
if [ "$TARGET" = "auto" ]; then
|
||||
case "$HOST_OS/$HOST_ARCH" in
|
||||
Darwin/arm64|Darwin/aarch64) TARGET="mac-arm64" ;;
|
||||
Darwin/x86_64) TARGET="mac-x64" ;;
|
||||
MINGW*/*|MSYS*/*|CYGWIN*/*) TARGET="win-x64" ;;
|
||||
*) fail "当前平台 $HOST_OS/$HOST_ARCH 不支持自动打包,请显式指定目标" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
case "$TARGET" in
|
||||
mac-arm64)
|
||||
[ "$HOST_OS" = "Darwin" ] || fail "mac-arm64 必须在 macOS 上构建"
|
||||
RUST_TARGET="aarch64-apple-darwin"
|
||||
RELEASE_PLATFORM="macos"
|
||||
OUTPUT_PLATFORM="macos-arm64"
|
||||
;;
|
||||
mac-x64)
|
||||
[ "$HOST_OS" = "Darwin" ] || fail "mac-x64 必须在 macOS 上构建"
|
||||
RUST_TARGET="x86_64-apple-darwin"
|
||||
RELEASE_PLATFORM="macos"
|
||||
OUTPUT_PLATFORM="macos-x64"
|
||||
;;
|
||||
win-x64)
|
||||
case "$HOST_OS" in
|
||||
MINGW*|MSYS*|CYGWIN*) ;;
|
||||
*) fail "win-x64 必须在 Windows 上构建,macOS 交叉编译不能替代 Windows 实机打包" ;;
|
||||
esac
|
||||
RUST_TARGET="x86_64-pc-windows-msvc"
|
||||
RELEASE_PLATFORM="windows"
|
||||
OUTPUT_PLATFORM="windows-x64"
|
||||
;;
|
||||
esac
|
||||
|
||||
if command -v rustup >/dev/null 2>&1 && ! rustup target list --installed | grep -Fxq "$RUST_TARGET"; then
|
||||
fail "Rust target 未安装:$RUST_TARGET;请先运行 rustup target add $RUST_TARGET"
|
||||
fi
|
||||
|
||||
if [ -n "$VERSION_ARG" ] && [ "$VERSION" != "$CURRENT_VERSION" ]; then
|
||||
log "同步版本号到 v$VERSION"
|
||||
node scripts/set-version.mjs "$VERSION"
|
||||
fi
|
||||
|
||||
node scripts/set-version.mjs --check "$VERSION"
|
||||
|
||||
if [ ! -d node_modules ]; then
|
||||
log "安装锁定依赖"
|
||||
npm ci
|
||||
fi
|
||||
|
||||
log "OpsXShell v$VERSION · $TARGET · $(git rev-parse --short HEAD 2>/dev/null || printf unknown)"
|
||||
|
||||
case "$CHECK_MODE" in
|
||||
full)
|
||||
log "运行完整发布门禁"
|
||||
npm run acceptance:full
|
||||
;;
|
||||
quick)
|
||||
log "运行快速发布门禁"
|
||||
npm run acceptance:quick
|
||||
;;
|
||||
none)
|
||||
log "已按参数跳过构建前门禁"
|
||||
;;
|
||||
esac
|
||||
|
||||
node scripts/release-preflight.mjs "$RELEASE_PLATFORM"
|
||||
|
||||
log "构建原生安装包"
|
||||
if [ "$RELEASE_PLATFORM" = "macos" ]; then
|
||||
npx tauri build --ci --target "$RUST_TARGET" --bundles app,dmg
|
||||
else
|
||||
npx tauri build --ci --target "$RUST_TARGET"
|
||||
fi
|
||||
|
||||
BUNDLE_ROOT="${CARGO_TARGET_DIR:-$ROOT_DIR/src-tauri/target}/$RUST_TARGET/release/bundle"
|
||||
node scripts/verify-release.mjs "$RELEASE_PLATFORM" "$BUNDLE_ROOT"
|
||||
|
||||
OUTPUT_DIR="$ROOT_DIR/release/v$VERSION/$OUTPUT_PLATFORM"
|
||||
rm -rf "$OUTPUT_DIR"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
shopt -s nullglob
|
||||
|
||||
COPIED_ARTIFACTS=()
|
||||
if [ "$RELEASE_PLATFORM" = "macos" ]; then
|
||||
SOURCE_ARTIFACTS=("$BUNDLE_ROOT"/dmg/*.dmg)
|
||||
else
|
||||
SOURCE_ARTIFACTS=("$BUNDLE_ROOT"/nsis/*.exe "$BUNDLE_ROOT"/msi/*.msi "$BUNDLE_ROOT"/wix/*.msi)
|
||||
fi
|
||||
|
||||
[ "${#SOURCE_ARTIFACTS[@]}" -gt 0 ] || fail "构建完成但未找到安装包:$BUNDLE_ROOT"
|
||||
for artifact in "${SOURCE_ARTIFACTS[@]}"; do
|
||||
destination="$OUTPUT_DIR/$(basename "$artifact")"
|
||||
cp "$artifact" "$destination"
|
||||
COPIED_ARTIFACTS+=("$destination")
|
||||
done
|
||||
|
||||
CHECKSUM_FILE="$OUTPUT_DIR/SHA256SUMS.txt"
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
(
|
||||
cd "$OUTPUT_DIR"
|
||||
names=()
|
||||
for artifact in "${COPIED_ARTIFACTS[@]}"; do names+=("$(basename "$artifact")"); done
|
||||
shasum -a 256 "${names[@]}" > SHA256SUMS.txt
|
||||
)
|
||||
elif command -v sha256sum >/dev/null 2>&1; then
|
||||
(
|
||||
cd "$OUTPUT_DIR"
|
||||
names=()
|
||||
for artifact in "${COPIED_ARTIFACTS[@]}"; do names+=("$(basename "$artifact")"); done
|
||||
sha256sum "${names[@]}" > SHA256SUMS.txt
|
||||
)
|
||||
else
|
||||
node --input-type=module -e '
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { basename } from "node:path";
|
||||
for (const path of process.argv.slice(1)) {
|
||||
console.log(`${createHash("sha256").update(readFileSync(path)).digest("hex")} ${basename(path)}`);
|
||||
}
|
||||
' "${COPIED_ARTIFACTS[@]}" > "$CHECKSUM_FILE"
|
||||
fi
|
||||
|
||||
log "构建完成:$OUTPUT_DIR"
|
||||
ls -lh "$OUTPUT_DIR"
|
||||
printf '\n[build] SHA-256\n'
|
||||
cat "$CHECKSUM_FILE"
|
||||
Reference in new issue
Block a user