name: Build Node.js ARM64 musl on: workflow_dispatch: inputs: build_v1: description: 'Build V1 (22.15.1) - Legacy compatibility' required: false default: true type: boolean build_v2: description: 'Build V2 (24.14.1) - Current LTS version' required: false default: true type: boolean jobs: # ── V1 构建: 使用 Alpine apk 模式 (22.15.1) ── build-v1: name: Build Node.js V1 (Legacy) if: ${{ inputs.build_v1 }} runs-on: ubuntu-latest permissions: contents: write outputs: artifact_name: ${{ steps.build_info.outputs.artifact_name }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: linux/arm64 - name: Build Node.js V1 ARM64 musl (apk lts mode) run: | NODE_VER="22.15.1" mkdir -p dist echo "=== Building Node.js v${NODE_VER} ARM64 musl (apk lts mode) ===" docker run --rm --platform linux/arm64 \ -v "$PWD/dist:/output" \ -v "$PWD/scripts/build-node-musl.sh:/build-node-musl.sh:ro" \ -e "NODE_VER=${NODE_VER}" \ -e "BUILD_MODE=apk" \ -e "PKG_TYPE=lts" \ alpine:3.21 sh /build-node-musl.sh - name: Build info id: build_info run: | ARTIFACT_NAME=$(ls dist/*.tar.xz | head -1 | xargs basename) echo "artifact_name=${ARTIFACT_NAME}" >> "$GITHUB_OUTPUT" echo "Artifact: ${ARTIFACT_NAME}" - name: Verify tarball run: | echo "=== Output files ===" ls -lh dist/ echo "" echo "=== Tarball contents (first 20 entries) ===" tar tJf dist/*.tar.xz | head -20 - name: Verify relocatable tarball run: | docker run --rm --platform linux/arm64 \ -v "$PWD/dist:/dist:ro" \ alpine:3.21 sh -euxc ' apk add --no-cache xz icu-data-full tar TARBALL=$(ls /dist/*.tar.xz | head -1) verify_prefix() { prefix="$1" rm -rf "$prefix" mkdir -p "$prefix" tar -xJf "$TARBALL" --strip-components=1 -C "$prefix" "$prefix/bin/node" --version exec_path=$("$prefix/bin/node" -e "process.stdout.write(process.execPath)") [ "$exec_path" = "$prefix/bin/node" ] NODE_ICU_DATA="$prefix/share/icu" "$prefix/bin/npm" --version >/dev/null } verify_prefix /opt/openclaw/node verify_prefix /tmp/custom-openclaw-root/openclaw/node ' - name: Upload artifact uses: actions/upload-artifact@v4 with: name: node-arm64-musl-v1 path: dist/*.tar.xz # ── V2 构建: 使用 Alpine edge apk 模式 (24.14.1 LTS) ── build-v2: name: Build Node.js V2 (Current) if: ${{ inputs.build_v2 }} runs-on: ubuntu-latest permissions: contents: write outputs: artifact_name: ${{ steps.build_info.outputs.artifact_name }} steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 with: platforms: linux/arm64 - name: Build Node.js V2 ARM64 musl (apk lts mode) run: | NODE_VER="24.14.1" mkdir -p dist echo "=== Building Node.js v${NODE_VER} ARM64 musl (apk lts mode) ===" docker run --rm --platform linux/arm64 \ -v "$PWD/dist:/output" \ -v "$PWD/scripts/build-node-musl.sh:/build-node-musl.sh:ro" \ -e "NODE_VER=${NODE_VER}" \ -e "BUILD_MODE=apk" \ -e "PKG_TYPE=lts" \ alpine:edge sh /build-node-musl.sh - name: Build info id: build_info run: | EXPECTED_ARTIFACT="node-v24.14.1-linux-arm64-musl.tar.xz" test -f "dist/${EXPECTED_ARTIFACT}" ARTIFACT_NAME=$(ls dist/*.tar.xz | head -1 | xargs basename) test "${ARTIFACT_NAME}" = "${EXPECTED_ARTIFACT}" echo "artifact_name=${ARTIFACT_NAME}" >> "$GITHUB_OUTPUT" echo "Artifact: ${ARTIFACT_NAME}" - name: Verify tarball run: | echo "=== Output files ===" ls -lh dist/ echo "" echo "=== Tarball contents (first 20 entries) ===" tar tJf dist/*.tar.xz | head -20 - name: Verify relocatable tarball run: | docker run --rm --platform linux/arm64 \ -v "$PWD/dist:/dist:ro" \ alpine:3.21 sh -euxc ' apk add --no-cache xz icu-data-full tar TARBALL=$(ls /dist/*.tar.xz | head -1) verify_prefix() { prefix="$1" rm -rf "$prefix" mkdir -p "$prefix" tar -xJf "$TARBALL" --strip-components=1 -C "$prefix" "$prefix/bin/node" --version exec_path=$("$prefix/bin/node" -e "process.stdout.write(process.execPath)") [ "$exec_path" = "$prefix/bin/node" ] NODE_ICU_DATA="$prefix/share/icu" "$prefix/bin/npm" --version >/dev/null } verify_prefix /opt/openclaw/node verify_prefix /tmp/custom-openclaw-root/openclaw/node ' - name: Upload artifact uses: actions/upload-artifact@v4 with: name: node-arm64-musl-v2 path: dist/*.tar.xz # ── 发布到 GitHub Release ── release: name: Create/Update Release needs: [build-v1, build-v2] if: always() && (needs.build-v1.result == 'success' || needs.build-v2.result == 'success') runs-on: ubuntu-latest permissions: contents: write steps: - name: Download V1 artifact if: ${{ needs.build-v1.result == 'success' }} uses: actions/download-artifact@v4 with: name: node-arm64-musl-v1 path: dist/ - name: Download V2 artifact if: ${{ needs.build-v2.result == 'success' }} uses: actions/download-artifact@v4 with: name: node-arm64-musl-v2 path: dist/ - name: List all artifacts run: | echo "=== All artifacts ===" ls -lh dist/ - name: Create/Update Release uses: softprops/action-gh-release@v2 with: tag_name: node-bins name: "Node.js Prebuilt Binaries (musl)" files: dist/*.tar.xz draft: false prerelease: false make_latest: false body: | 预编译的 Node.js ARM64 musl 二进制文件,供 ARM64 OpenWrt/iStoreOS 设备使用。 Node.js 官方 [unofficial-builds](https://unofficial-builds.nodejs.org/) 仅提供 x64 musl 构建,不提供 ARM64 musl。 此 Release 使用 Alpine Linux ARM64 (musl libc) 环境打包。 ## 版本说明 | 文件 | 版本 | 用途 | |------|------|------| | `node-v24.14.1-linux-arm64-musl.tar.xz` | V2 (当前默认) | OpenClaw v2026.3.11+ (默认下载,满足 >= 22.16.0) | | `node-v22.x.x-linux-arm64-musl.tar.xz` | V1 (旧版) | OpenClaw v2026.3.8 及更早版本 | ## 构建模式 两种版本均使用 **Alpine apk 模式** 构建: - **V2**: 使用 Alpine `edge` 的 `nodejs` LTS 包 (v24.x),当前默认发布 `node-v24.14.1-linux-arm64-musl.tar.xz` - **V1**: 使用 Alpine `nodejs` LTS 包 (v22.x),兼容旧版 OpenClaw `openclaw-env setup` 会在 ARM64 musl 设备上默认直连下载 V2 当前版本,必要时再回退到 Release 元数据解析。