mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-05-07 04:52:53 +08:00
fix: R2归档解压路径修正(qingchencloud→@qingchencloud) + Docker构建脚本
This commit is contained in:
161
scripts/build-r2-archive.sh
Normal file
161
scripts/build-r2-archive.sh
Normal file
@@ -0,0 +1,161 @@
|
||||
#!/bin/bash
|
||||
# R2 CDN 归档构建脚本
|
||||
# 用 Docker 构建 linux-x64 和 linux-arm64 的 OpenClaw 预装归档
|
||||
# 用法:
|
||||
# ./scripts/build-r2-archive.sh linux-x64 # 构建 Linux x64
|
||||
# ./scripts/build-r2-archive.sh linux-arm64 # 构建 Linux ARM64(需要 QEMU/buildx)
|
||||
# ./scripts/build-r2-archive.sh all # 构建所有 Linux 平台
|
||||
#
|
||||
# 前置条件:
|
||||
# - Docker Desktop 已启动
|
||||
# - ARM64 构建需要: docker run --privileged --rm tonistiigi/binfmt --install all
|
||||
# - wrangler 已登录(用于上传到 R2)
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="${1:-linux-x64}"
|
||||
OPENCLAW_VERSION="2026.3.13-zh.1"
|
||||
OPENCLAW_PKG="@qingchencloud/openclaw-zh"
|
||||
R2_BUCKET="clawpanel-releases"
|
||||
R2_PATH="openclaw-zh/${OPENCLAW_VERSION}"
|
||||
OUTPUT_DIR="$(pwd)/r2-archives"
|
||||
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
build_archive() {
|
||||
local PLATFORM="$1" # linux-x64 or linux-arm64
|
||||
local DOCKER_PLATFORM="$2" # linux/amd64 or linux/arm64
|
||||
|
||||
echo "================================================"
|
||||
echo "构建 ${PLATFORM} 归档..."
|
||||
echo "OpenClaw: ${OPENCLAW_PKG}@${OPENCLAW_VERSION}"
|
||||
echo "Docker 平台: ${DOCKER_PLATFORM}"
|
||||
echo "================================================"
|
||||
|
||||
local CONTAINER_NAME="r2-build-${PLATFORM}"
|
||||
local ARCHIVE_NAME="${PLATFORM}.tgz"
|
||||
|
||||
# 清理旧容器
|
||||
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
|
||||
|
||||
# 在 Docker 中安装 OpenClaw 并打包
|
||||
docker run \
|
||||
--platform "$DOCKER_PLATFORM" \
|
||||
--name "$CONTAINER_NAME" \
|
||||
node:22-slim \
|
||||
bash -c "
|
||||
echo '>>> 安装 OpenClaw ${OPENCLAW_VERSION}...'
|
||||
npm install -g ${OPENCLAW_PKG}@${OPENCLAW_VERSION} --force --registry https://registry.npmmirror.com 2>&1 | tail -5
|
||||
echo '>>> 验证安装...'
|
||||
openclaw --version || echo '(版本检测跳过)'
|
||||
echo '>>> 打包 node_modules...'
|
||||
cd /usr/local/lib/node_modules
|
||||
tar -czf /tmp/archive.tgz @qingchencloud/
|
||||
ls -lh /tmp/archive.tgz
|
||||
echo '>>> 完成'
|
||||
"
|
||||
|
||||
# 从容器中复制归档
|
||||
docker cp "${CONTAINER_NAME}:/tmp/archive.tgz" "${OUTPUT_DIR}/${ARCHIVE_NAME}"
|
||||
docker rm -f "$CONTAINER_NAME"
|
||||
|
||||
# 计算 SHA256
|
||||
local SHA256=$(sha256sum "${OUTPUT_DIR}/${ARCHIVE_NAME}" | cut -d' ' -f1)
|
||||
local SIZE=$(stat -c%s "${OUTPUT_DIR}/${ARCHIVE_NAME}" 2>/dev/null || stat -f%z "${OUTPUT_DIR}/${ARCHIVE_NAME}")
|
||||
local SIZE_MB=$(echo "scale=1; $SIZE / 1048576" | bc)
|
||||
|
||||
echo ""
|
||||
echo "✅ ${PLATFORM} 归档构建完成:"
|
||||
echo " 文件: ${OUTPUT_DIR}/${ARCHIVE_NAME}"
|
||||
echo " 大小: ${SIZE_MB} MB (${SIZE} bytes)"
|
||||
echo " SHA256: ${SHA256}"
|
||||
echo ""
|
||||
|
||||
# 保存元数据
|
||||
echo "${SHA256}" > "${OUTPUT_DIR}/${PLATFORM}.sha256"
|
||||
echo "${SIZE}" > "${OUTPUT_DIR}/${PLATFORM}.size"
|
||||
}
|
||||
|
||||
upload_to_r2() {
|
||||
local PLATFORM="$1"
|
||||
local ARCHIVE_NAME="${PLATFORM}.tgz"
|
||||
|
||||
if [ ! -f "${OUTPUT_DIR}/${ARCHIVE_NAME}" ]; then
|
||||
echo "❌ ${ARCHIVE_NAME} 不存在,跳过上传"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo ">>> 上传 ${ARCHIVE_NAME} 到 R2..."
|
||||
npx wrangler r2 object put "${R2_BUCKET}/${R2_PATH}/${ARCHIVE_NAME}" \
|
||||
--file "${OUTPUT_DIR}/${ARCHIVE_NAME}" \
|
||||
--remote \
|
||||
--content-type "application/gzip"
|
||||
echo "✅ 上传完成: ${R2_PATH}/${ARCHIVE_NAME}"
|
||||
}
|
||||
|
||||
update_latest_json() {
|
||||
echo ">>> 生成 latest.json..."
|
||||
|
||||
local JSON='{"chinese":{"version":"'${OPENCLAW_VERSION}'","assets":{'
|
||||
local FIRST=true
|
||||
|
||||
for PLATFORM in win-x64 linux-x64 linux-arm64 darwin-arm64; do
|
||||
if [ -f "${OUTPUT_DIR}/${PLATFORM}.sha256" ] && [ -f "${OUTPUT_DIR}/${PLATFORM}.size" ]; then
|
||||
local SHA256=$(cat "${OUTPUT_DIR}/${PLATFORM}.sha256")
|
||||
local SIZE=$(cat "${OUTPUT_DIR}/${PLATFORM}.size")
|
||||
if [ "$FIRST" = true ]; then FIRST=false; else JSON+=','; fi
|
||||
JSON+='"'${PLATFORM}'":{"url":"https://dl.qrj.ai/'${R2_PATH}'/'${PLATFORM}'.tgz","size":'${SIZE}',"sha256":"'${SHA256}'"}'
|
||||
fi
|
||||
done
|
||||
|
||||
JSON+='}},"updatedAt":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}'
|
||||
|
||||
echo "$JSON" > "${OUTPUT_DIR}/latest.json"
|
||||
echo ">>> latest.json 已生成"
|
||||
cat "${OUTPUT_DIR}/latest.json" | python3 -m json.tool 2>/dev/null || cat "${OUTPUT_DIR}/latest.json"
|
||||
|
||||
echo ">>> 上传 latest.json 到 R2..."
|
||||
npx wrangler r2 object put "${R2_BUCKET}/openclaw-zh/latest.json" \
|
||||
--file "${OUTPUT_DIR}/latest.json" \
|
||||
--remote \
|
||||
--content-type "application/json"
|
||||
echo "✅ latest.json 已更新"
|
||||
}
|
||||
|
||||
# === 主流程 ===
|
||||
|
||||
case "$VERSION" in
|
||||
linux-x64)
|
||||
build_archive "linux-x64" "linux/amd64"
|
||||
;;
|
||||
linux-arm64)
|
||||
# ARM64 需要 QEMU 支持
|
||||
echo "确保 QEMU 已安装: docker run --privileged --rm tonistiigi/binfmt --install all"
|
||||
build_archive "linux-arm64" "linux/arm64"
|
||||
;;
|
||||
all)
|
||||
build_archive "linux-x64" "linux/amd64"
|
||||
build_archive "linux-arm64" "linux/arm64"
|
||||
;;
|
||||
upload)
|
||||
# 仅上传已构建的归档
|
||||
for P in win-x64 linux-x64 linux-arm64; do
|
||||
upload_to_r2 "$P" || true
|
||||
done
|
||||
update_latest_json
|
||||
;;
|
||||
*)
|
||||
echo "用法: $0 {linux-x64|linux-arm64|all|upload}"
|
||||
echo ""
|
||||
echo "示例:"
|
||||
echo " $0 linux-x64 # 构建 Linux x64 归档"
|
||||
echo " $0 linux-arm64 # 构建 Linux ARM64 归档"
|
||||
echo " $0 all # 构建所有 Linux 平台"
|
||||
echo " $0 upload # 上传所有已构建的归档到 R2"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
echo "=== 构建产物 ==="
|
||||
ls -lh "${OUTPUT_DIR}/"*.tgz 2>/dev/null || echo "(无 .tgz 文件)"
|
||||
@@ -206,6 +206,13 @@ async function _tryR2Install(version, source, logs) {
|
||||
logs.push(`解压到 ${modulesDir}`)
|
||||
execSync(`tar -xzf "${tmpPath}" -C "${modulesDir}"`, { timeout: 60000, windowsHide: true })
|
||||
|
||||
// 归档内目录可能是 qingchencloud/(Windows tar 不支持 @ 前缀),需要重命名
|
||||
const noAtDir = path.join(modulesDir, 'qingchencloud')
|
||||
if (fs.existsSync(noAtDir) && !fs.existsSync(qcDir)) {
|
||||
fs.renameSync(noAtDir, qcDir)
|
||||
logs.push('目录已修正: qingchencloud → @qingchencloud')
|
||||
}
|
||||
|
||||
// 创建 bin 链接
|
||||
let binDir
|
||||
if (isWindows) {
|
||||
|
||||
@@ -1171,6 +1171,14 @@ async fn try_r2_install(
|
||||
}
|
||||
}
|
||||
|
||||
// 归档内目录可能是 qingchencloud/(Windows tar 不支持 @ 前缀),需要重命名
|
||||
let no_at_dir = modules_dir.join("qingchencloud");
|
||||
if no_at_dir.exists() && !qc_dir.exists() {
|
||||
std::fs::rename(&no_at_dir, &qc_dir)
|
||||
.map_err(|e| format!("重命名 qingchencloud → @qingchencloud 失败: {e}"))?;
|
||||
let _ = app.emit("upgrade-log", "目录已修正: qingchencloud → @qingchencloud");
|
||||
}
|
||||
|
||||
let _ = app.emit("upgrade-progress", 85);
|
||||
let _ = app.emit("upgrade-log", "解压完成,创建 bin 链接...");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user