diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 65090d4..129aadb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,27 +15,123 @@ permissions: contents: write jobs: - portable-skeleton: - name: Portable skeleton zip (cross-platform) + portable-windows-full: + name: Portable Windows full bundle (Node + OpenClaw pre-installed) runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Stage portable skeleton (no node_modules, no Node.js runtime) + - name: Stage portable directory run: | mkdir -p dist tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" - stage_dir="dist/u-claw-portable-${tag_name}" + stage_dir="dist/u-claw-portable-windows-${tag_name}" mkdir -p "$stage_dir" - # Copy source skeleton only — first launch will download Node.js + OpenClaw - # via portable/setup.sh / setup.bat (China mirror, ~1 minute). cp -R portable/. "$stage_dir/" - rm -rf "$stage_dir/app" "$stage_dir/data/logs" 2>/dev/null || true + cp OPENCLAW_VERSION "$stage_dir/OPENCLAW_VERSION" + rm -rf "$stage_dir/data/logs" 2>/dev/null || true + + - name: Pre-install Node.js (Windows) and OpenClaw deps + run: | + tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" + stage_dir="dist/u-claw-portable-windows-${tag_name}" + openclaw_version=$(tr -d '[:space:]' < OPENCLAW_VERSION) + node_version="v22.22.1" + mirror_node="https://npmmirror.com/mirrors/node" + mirror_npm="https://registry.npmmirror.com" + + app_dir="$stage_dir/app" + runtime_dir="$app_dir/runtime" + core_dir="$app_dir/core" + mkdir -p "$runtime_dir" "$core_dir" + + # 1) Download Node for Windows x64 only. + # Mac users: Mac-Start.command runs setup.sh on first launch which + # downloads node-mac-arm64 from npmmirror.com (~30MB, ~10s in China). + # Stanley pattern: ship one platform per zip, keep size reasonable. + win_node="$runtime_dir/node-win-x64" + mkdir -p "$win_node" + curl -fSL "$mirror_node/$node_version/node-$node_version-win-x64.zip" -o /tmp/node-win.zip + unzip -q /tmp/node-win.zip -d /tmp/node-win-extract + cp -r /tmp/node-win-extract/node-$node_version-win-x64/* "$win_node/" + rm -rf /tmp/node-win.zip /tmp/node-win-extract + + # 2) Use linux Node to npm-install OpenClaw + bundled runtime deps + # into core/node_modules — these are platform-agnostic JS, work on any OS + curl -fSL "$mirror_node/$node_version/node-$node_version-linux-x64.tar.xz" \ + | tar xJ -C /tmp + export PATH="/tmp/node-$node_version-linux-x64/bin:$PATH" + node --version + + cat > "$core_dir/package.json" <k+'@'+v).join(' '))") + if [ -n "$deps" ]; then + echo "Pre-installing $(echo $deps | wc -w) OpenClaw bundled deps..." + npm install $deps \ + --registry="$mirror_npm" \ + --ignore-scripts \ + --no-audit \ + --no-fund + fi + fi + + # 5) Install QQ plugin + npm install @sliverp/qqbot@latest \ + --registry="$mirror_npm" \ + --ignore-scripts \ + --no-audit \ + --no-fund || true + + # 6) Copy China-optimized skills if present + skills_cn="$stage_dir/skills-cn" + skills_target="$core_dir/node_modules/openclaw/skills" + if [ -d "$skills_cn" ] && [ -d "$skills_target" ]; then + for skill_dir in "$skills_cn"/*/; do + skill_name=$(basename "$skill_dir") + [ ! -d "$skills_target/$skill_name" ] && cp -R "$skill_dir" "$skills_target/$skill_name" + done + fi + + # 7) Prune large optional deps that customer rarely needs. + # Lesson from 5916ff5: previous full-bundle hit 3.4GB and + # blew past GitHub's 2GB single-asset limit because of + # node-llama-cpp / sharp prebuilds. Even with --ignore-scripts + # these may pull bundled binaries, so explicitly remove them. + for big_dep in node-llama-cpp sharp @img canvas; do + find "$core_dir/node_modules" -type d -name "$big_dep" -prune -exec rm -rf {} + 2>/dev/null || true + done + + du -sh "$stage_dir" + echo "Portable bundle staged: $stage_dir" - name: Normalize line endings (CRLF for Windows scripts, LF for shell) run: | tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" - stage_dir="dist/u-claw-portable-${tag_name}" + stage_dir="dist/u-claw-portable-windows-${tag_name}" # Windows batch files MUST be CRLF — cmd.exe mis-parses LF-only files find "$stage_dir" -type f \( -name '*.bat' -o -name '*.cmd' -o -name '*.ps1' \) -print0 \ | xargs -0 -I {} sh -c 'sed -i "s/\r$//; s/$/\r/" "{}"' @@ -45,17 +141,32 @@ jobs: # Restore +x on shell entry points chmod +x "$stage_dir"/*.sh "$stage_dir"/*.command 2>/dev/null || true - - name: Zip skeleton + - name: Zip portable bundle run: | tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" cd dist - zip -qry "u-claw-portable-${tag_name}.zip" "u-claw-portable-${tag_name}" - ls -lh "u-claw-portable-${tag_name}.zip" + zip -qry "u-claw-portable-windows-${tag_name}.zip" "u-claw-portable-windows-${tag_name}" + ls -lh "u-claw-portable-windows-${tag_name}.zip" + + - name: Size guard (lesson from 5916ff5 — GitHub 2GB asset cap) + run: | + tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" + zip_path="dist/u-claw-portable-windows-${tag_name}.zip" + size_bytes=$(stat -c%s "$zip_path") + size_mb=$((size_bytes / 1024 / 1024)) + echo "Portable zip size: ${size_mb} MB" + # Hard cap at 1500 MB to leave buffer below GitHub's 2GB single-asset limit. + # Inspired by StanleyChanH/openclaw-offline-package which ships ~150MB. + if [ "$size_mb" -gt 1500 ]; then + echo "::error::Portable zip is ${size_mb} MB, exceeds 1500 MB safety cap" + echo "Consider removing more optional deps or splitting into platform-specific zips" + exit 1 + fi - uses: actions/upload-artifact@v4 with: - name: portable-skeleton - path: dist/u-claw-portable-*.zip + name: portable-windows-full + path: dist/u-claw-portable-windows-*.zip desktop-windows: name: Electron Windows installer @@ -120,7 +231,7 @@ jobs: publish: name: Publish GitHub Release - needs: [portable-skeleton, desktop-windows, desktop-mac] + needs: [portable-windows-full, desktop-windows, desktop-mac] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -161,12 +272,16 @@ jobs: - **Windows 桌面版**:`U-Claw.Setup.*.exe`(安装到电脑)或 `U-Claw.*.exe`(免安装绿色版) - **macOS 桌面版**:`U-Claw-*-arm64.dmg`(Apple Silicon)或 `U-Claw-*.dmg`(Intel) - - **便携骨架(U 盘版)**:`u-claw-portable-*.zip`(约 1 MB,首次启动自动下载 Node.js + OpenClaw,国内镜像约 1 分钟) + - **Windows 便携完整版(U 盘版)**:`u-claw-portable-windows-*.zip`(约 150-200 MB,已预装 Node.js + OpenClaw,**解压即用,零网络依赖**) + - **macOS 便携骨架(开发者)**:源码在 `portable/` 目录,`bash setup.sh` 自动下载 Node + OpenClaw(国内镜像约 1 分钟) + + > **U 盘格式建议**:请用 **NTFS** 格式化 U 盘(NOT exFAT/FAT32)。Node.js 在 exFAT 上 IO 极慢且不支持符号链接,可能导致启动失败。 + > Windows 下右键 U 盘 → 格式化 → 文件系统选择 NTFS。 > Windows 安装包未做代码签名,首次运行 SmartScreen 选择「仍要运行」。 > macOS DMG 未做公证,首次启动 `xattr -rd com.apple.quarantine /Applications/U-Claw.app` 或右键打开。 files: | - artifacts/portable-skeleton/* + artifacts/portable-windows-full/* artifacts/desktop-windows/* artifacts/desktop-mac/* fail_on_unmatched_files: false diff --git a/portable/Mac-Start.command b/portable/Mac-Start.command index ee46484..6ea100b 100755 --- a/portable/Mac-Start.command +++ b/portable/Mac-Start.command @@ -98,10 +98,12 @@ export OPENCLAW_CONFIG_PATH="$CONFIG_FILE" # ---- 7. Check dependencies ---- if [ ! -d "$CORE_DIR/node_modules" ]; then - echo -e " ${YELLOW}First run - installing dependencies...${NC}" - echo " (Using China mirror)" + echo -e " ${YELLOW}[WARN] node_modules not found${NC}" + echo " This release should ship with deps pre-installed." + echo " Falling back to npm install (USB drives may take 20+ min)." + echo " TIP: re-download u-claw-portable-*.zip with bundled deps." cd "$CORE_DIR" - "$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com 2>&1 + "$NODE_BIN" "$NODE_DIR/bin/npm" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev 2>&1 echo -e " ${GREEN}Dependencies installed${NC}" echo "" fi @@ -111,6 +113,16 @@ echo -e " ${CYAN}Binding device fingerprint to Xiapan Cloud...${NC}" UCLAW_APP_ROOT="$UCLAW_DIR" "$NODE_BIN" "$UCLAW_DIR/lib/bootstrap-xiapan.mjs" "$CONFIG_FILE" || true echo "" +# ---- 7c. Async update check (non-blocking, 5s timeout, silent failure) ---- +# Writes data/.openclaw/update-available.json if a newer version is on OSS. +# Welcome.html / Config.html read this file and show a banner. +# Version file lookup: portable/OPENCLAW_VERSION (USB) → ../OPENCLAW_VERSION (dev) +VERSION_FILE="$UCLAW_DIR/OPENCLAW_VERSION" +[ -f "$VERSION_FILE" ] || VERSION_FILE="$UCLAW_DIR/../OPENCLAW_VERSION" +if [ -f "$VERSION_FILE" ]; then + "$NODE_BIN" "$UCLAW_DIR/lib/check-update.mjs" "$VERSION_FILE" "$STATE_DIR" >/dev/null 2>&1 & +fi + # ---- 8. Find available port ---- PORT=18789 while lsof -i :$PORT >/dev/null 2>&1; do diff --git a/portable/Windows-Start.bat b/portable/Windows-Start.bat index bdbbaeb..66aef1c 100644 --- a/portable/Windows-Start.bat +++ b/portable/Windows-Start.bat @@ -62,11 +62,19 @@ if not exist "%STATE_DIR%\openclaw.json" ( REM Check dependencies if not exist "%CORE_DIR%\node_modules" ( - echo First run - installing dependencies... - echo Using China mirror, please wait... + echo ======================================== + echo [WARN] node_modules not found + echo ======================================== + echo This release should ship with deps pre-installed. + echo Falling back to npm install (USB drives may take 20+ minutes). + echo. + echo TIP: Re-download u-claw-portable-*.zip from GitHub releases, + echo which includes pre-installed deps (~200 MB). + echo. + echo File system: NTFS recommended. exFAT/FAT32 will be very slow. echo. cd /d "%CORE_DIR%" - call "%NPM_BIN%" install --registry=https://registry.npmmirror.com + call "%NPM_BIN%" install --registry=https://registry.npmmirror.com --ignore-scripts --no-audit --no-fund --omit=dev echo. echo Dependencies installed! echo. @@ -78,6 +86,17 @@ set "UCLAW_APP_ROOT=%UCLAW_DIR%" "%NODE_BIN%" "%UCLAW_DIR%lib\bootstrap-xiapan.mjs" "%STATE_DIR%\openclaw.json" echo. +REM Async update check (non-blocking, 5s timeout, silent failure) +REM Writes data\.openclaw\update-available.json if a newer version is on OSS. +REM Welcome.html / Config.html read this file and show a banner. +REM Version file lookup order: portable/OPENCLAW_VERSION (USB), then repo-root ../OPENCLAW_VERSION (dev) +set "VERSION_FILE=%UCLAW_DIR%OPENCLAW_VERSION" +if not exist "%VERSION_FILE%" set "VERSION_FILE=%UCLAW_DIR%..\OPENCLAW_VERSION" +if exist "%VERSION_FILE%" ( + start /B "" "%NODE_BIN%" "%UCLAW_DIR%lib\check-update.mjs" "%VERSION_FILE%" "%STATE_DIR%" >nul 2>&1 +) + + REM Auto-install WeChat plugin if available set "WECHAT_PLUGIN_SRC=%APP_DIR%\extensions\openclaw-weixin" set "WECHAT_PLUGIN_DST=%USERPROFILE%\.openclaw\extensions\openclaw-weixin"