Files
u-claw/.github/workflows/release.yml
zheng 0be8c3e3fe
All checks were successful
Tests / test (push) Successful in 55s
ci: 将 workflow 的 runs-on 改为 internal_docker
自托管 Gitea runner 只注册了 internal_docker 标签,ubuntu-latest 会导致 job 永远排队。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 14:03:48 +08:00

294 lines
13 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to publish (e.g. v2.1.0). Leave empty to use the most recent tag.'
required: false
default: ''
permissions:
contents: write
# 产品只发「便携 U 盘版」(portable/ — .bat/.command 解压即用)。
# Electron 桌面版 (u-claw-app/) 已于 2026-06-19 停发并归档,详见 u-claw-app/DEPRECATED.md。
jobs:
portable-windows-full:
name: Portable Windows full bundle (Node + OpenClaw pre-installed)
runs-on: internal_docker
steps:
- uses: actions/checkout@v4
- name: Stage portable directory
run: |
mkdir -p dist
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
stage_dir="dist/u-claw-portable-windows-${tag_name}"
mkdir -p "$stage_dir"
cp -R portable/. "$stage_dir/"
cp OPENCLAW_VERSION "$stage_dir/OPENCLAW_VERSION"
cp NODE_VERSION "$stage_dir/NODE_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 }}}"
# Absolute paths so `cd "$core_dir"` later doesn't break `du`/`find`
stage_dir="$(pwd)/dist/u-claw-portable-windows-${tag_name}"
openclaw_version=$(tr -d '[:space:]' < OPENCLAW_VERSION)
node_version=$(tr -d "[:space:]" < NODE_VERSION)
mirror_node="https://nodejs.org/dist"
mirror_npm="https://registry.npmjs.org"
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: START HERE - Mac.command runs setup.sh on first launch which
# downloads node-mac-arm64 from nodejs.org (~30MB).
# 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" <<PKGJSON
{
"name": "u-claw-core",
"version": "1.0.0",
"private": true,
"dependencies": {
"openclaw": "$openclaw_version"
}
}
PKGJSON
# Install OpenClaw + ALL bundled runtime deps in one shot, no native scripts
# (--ignore-scripts skips sharp/playwright/sqlite-vec native postinstall —
# those are not needed for OpenClaw gateway core path)
cd "$core_dir"
npm install \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund \
--omit=dev
# Note: do NOT pre-install OpenClaw's bundled runtime deps.
# First v2.1.3 attempt did this and the zip ballooned to 540MB
# because clawdbot drags in @node-llama-cpp (701MB local LLM
# binaries). Stanley's v2026.5.2 package only installs `openclaw`
# main package (153MB total) and lets OpenClaw lazy-stage the
# subset of bundled deps it actually needs at runtime.
# 5) Install QQ plugin
npm install @sliverp/qqbot@latest \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund || true
# 5b) Pre-install China channel plugins (钉钉/飞书中国版/企业微信)
# 借鉴自 BytePioneer-AI/openclaw-china 生态:这些是非官方主仓库插件,
# 官方镜像默认不带 → U-Claw 离线预置 = 最大差异化价值。国内客户开箱即用,
# 不用自己 npm install很多客户机没法翻墙装非镜像包
# 钉钉走 WebSocket 出站免公网 IP是国内最易接入的渠道。
# 全部 || true装不上不阻断发版个别包偶发 404 时便携版仍可用微信/QQ
for cn_pkg in \
"@openclaw-china/dingtalk" \
"@openclaw-china/feishu-china" \
"@openclaw-china/wecom" \
"@openclaw-china/qqbot"; do
echo "Installing China channel plugin: $cn_pkg"
npm install "${cn_pkg}@latest" \
--registry="$mirror_npm" \
--ignore-scripts \
--no-audit \
--no-fund || echo "WARN: $cn_pkg install failed, skipping — the drive still works with the other channels"
done
# 6) Install skills from the manifest.
# skills/ is the single source of truth (see skills/README.md). Ship it
# plus the installer onto the USB so the drive can re-install or switch
# locale offline, then populate OpenClaw's skills dir from it.
skills_target="$core_dir/node_modules/openclaw/skills"
mkdir -p "$stage_dir/lib" "$skills_target"
cp -R skills "$stage_dir/skills"
cp lib/install-skills.mjs "$stage_dir/lib/install-skills.mjs"
node "$stage_dir/lib/install-skills.mjs" --target "$skills_target" --locale en
echo "Skills on the drive: $(ls -1 "$skills_target" | wc -l)"
# 6b) Install WeChat (Weixin) plugin into app/extensions/openclaw-weixin
# 官方 npm 包 @tencent-weixin/openclaw-weixin 自带 dist/index.js(发布时已编译,无需 build)。
# config-server/server.js 把 app/extensions/openclaw-weixin 当作插件源,扫码登录后整目录
# copy 到 ~/.openclaw/extensions/。它硬要求插件目录内 node_modules/qrcode-terminal 存在
# (server.js:52),而 npm 会把该依赖提升到顶层,故下面显式 copy 进插件的 node_modules。
wx_tmp="$(mktemp -d)"
(
cd "$wx_tmp"
echo '{"name":"wx-tmp","version":"1.0.0","private":true}' > package.json
npm install @tencent-weixin/openclaw-weixin@latest \
--registry="$mirror_npm" --no-audit --no-fund
)
wx_pkg="$wx_tmp/node_modules/@tencent-weixin/openclaw-weixin"
if [ -f "$wx_pkg/dist/index.js" ] && [ -f "$wx_pkg/openclaw.plugin.json" ]; then
wx_dest="$app_dir/extensions/openclaw-weixin"
mkdir -p "$wx_dest"
cp -R "$wx_pkg/." "$wx_dest/"
# 把 qrcode-terminal(被 npm 提升到顶层)放进插件自己的 node_modules
mkdir -p "$wx_dest/node_modules"
cp -R "$wx_tmp/node_modules/qrcode-terminal" "$wx_dest/node_modules/qrcode-terminal"
echo "WeChat plugin installed: $(du -sm "$wx_dest" | cut -f1) MB"
else
echo "WARN: WeChat plugin install failed, skipping (dist/index.js or openclaw.plugin.json missing)"
fi
rm -rf "$wx_tmp"
# 7) Belt-and-suspenders: even though we don't pre-install bundled
# deps, openclaw's own dependencies might still pull large native
# binaries. Prune known offenders just in case.
echo "Before prune: $(du -sm "$core_dir/node_modules" | cut -f1) MB"
for big_pat in '@node-llama-cpp' 'node-llama-cpp'; do
find "$core_dir/node_modules" -maxdepth 3 -type d -name "$big_pat" -prune -exec rm -rf {} + 2>/dev/null || true
done
echo "After prune: $(du -sm "$core_dir/node_modules" | cut -f1) MB"
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-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/" "{}"'
# Strip UTF-8 BOM from .bat files — cmd.exe interprets BOM as
# part of the first command and reports "not internal/external command"
find "$stage_dir" -type f \( -name '*.bat' -o -name '*.cmd' \) -print0 \
| xargs -0 -I {} sh -c 'sed -i "1s/^\xef\xbb\xbf//" "{}"'
# Ensure shell scripts stay LF (mac/linux double-click won't run otherwise)
find "$stage_dir" -type f \( -name '*.sh' -o -name '*.command' -o -name '*.mjs' -o -name '*.js' \) -print0 \
| xargs -0 -I {} sh -c 'sed -i "s/\r$//" "{}"'
# Restore +x on shell entry points
chmod +x "$stage_dir"/*.sh "$stage_dir"/*.command 2>/dev/null || true
- name: Zip portable bundle
run: |
tag_name="${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}"
cd dist
zip -qry "u-claw-portable-windows-${tag_name}.zip" "u-claw-portable-windows-${tag_name}"
# Publish a checksum next to every asset so installers can verify what
# they downloaded instead of trusting "the file is bigger than 1MB".
sha256sum "u-claw-portable-windows-${tag_name}.zip" > "u-claw-portable-windows-${tag_name}.zip.sha256"
ls -lh "u-claw-portable-windows-${tag_name}.zip"
cat "u-claw-portable-windows-${tag_name}.zip.sha256"
- 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-windows-full
path: |
dist/u-claw-portable-windows-*.zip
dist/u-claw-portable-windows-*.zip.sha256
publish:
name: Publish GitHub Release
needs: [portable-windows-full]
runs-on: internal_docker
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
path: artifacts
- name: Resolve tag
id: tag
run: |
if [ -n "${{ github.event.inputs.tag }}" ]; then
tag="${{ github.event.inputs.tag }}"
else
tag="${GITHUB_REF_NAME}"
fi
echo "tag=$tag" >> "$GITHUB_OUTPUT"
- name: Read OpenClaw version
id: openclaw_version
run: |
version=$(tr -d '[:space:]' < OPENCLAW_VERSION)
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Create or update release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: U-Claw ${{ steps.tag.outputs.tag }}
body: |
## U-Claw ${{ steps.tag.outputs.tag }}
An AI assistant on a USB drive. Plug it into any Mac or Windows
machine — no installer, no admin rights, nothing left behind.
- Paste one API key and you are running. U-Claw works out the
provider from the key itself and checks it before accepting it.
- Your settings, keys and memory live on the drive, so they travel
with you between machines.
- OpenClaw runtime: `${{ steps.openclaw_version.outputs.version }}`
### Downloads
- **Windows** — `u-claw-portable-windows-*.zip`, roughly 150200 MB
with Node.js and OpenClaw already inside. Unzip onto the drive and
double-click `START HERE - Windows.bat`. No network needed to start.
- **macOS** — clone the repo, run `bash portable/advanced/setup.sh`
(about a minute to fetch Node and OpenClaw), then double-click
`START HERE - Mac.command`.
Each asset has a `.sha256` next to it. Check it before you run it.
### Format the drive as exFAT
Not NTFS. **macOS can only read NTFS, not write to it**, so an
NTFS drive silently breaks the one thing this product is for —
your settings will not follow you to a Mac.
exFAT is slower with many small files, which is why U-Claw moves its
rebuildable caches onto the host's local disk at startup. Your actual
data stays on the drive.
files: |
artifacts/portable-windows-full/*
fail_on_unmatched_files: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}