diff --git a/.github/workflows/plugin-clawhub-release.yml b/.github/workflows/plugin-clawhub-release.yml index ab041fa5544..50c2ef78cb0 100644 --- a/.github/workflows/plugin-clawhub-release.yml +++ b/.github/workflows/plugin-clawhub-release.yml @@ -32,7 +32,7 @@ env: CLAWHUB_REGISTRY: "https://clawhub.ai" CLAWHUB_REPOSITORY: "openclaw/clawhub" # Pinned to a reviewed ClawHub commit so release behavior stays reproducible. - CLAWHUB_REF: "48e66714ac2352d52b193a90ae911cd92463c20a" + CLAWHUB_REF: "199e6a0cdf32471702e0503e9899e8d24f06a527" jobs: preview_plugins_clawhub: @@ -178,7 +178,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} pnpm-version: ${{ env.PNPM_VERSION }} install-bun: "true" - install-deps: "false" + install-deps: "true" - name: Checkout ClawHub CLI source uses: actions/checkout@v6 @@ -239,7 +239,7 @@ jobs: node-version: ${{ env.NODE_VERSION }} pnpm-version: ${{ env.PNPM_VERSION }} install-bun: "true" - install-deps: "false" + install-deps: "true" - name: Checkout ClawHub CLI source uses: actions/checkout@v6 diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index d67151cf3ae..a0a658067f7 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -78,8 +78,9 @@ the maintainer-only release runbook. 9. For beta, tag `vYYYY.M.D-beta.N`, then run `OpenClaw Release Publish` from the matching `release/YYYY.M.D` branch. It verifies `pnpm plugins:sync:check`, publishes all publishable plugin packages to npm first, publishes the same - set to ClawHub second, and then promotes the prepared OpenClaw npm preflight - artifact with the matching dist-tag. After publish, run post-publish package + set to ClawHub second as ClawPack npm-pack tarballs, and then promotes the + prepared OpenClaw npm preflight artifact with the matching dist-tag. After + publish, run post-publish package acceptance against the published `openclaw@YYYY.M.D-beta.N` or `openclaw@beta` package. If a pushed or published prerelease needs a fix, cut the next matching prerelease number; do not delete or rewrite the old diff --git a/scripts/plugin-clawhub-publish.sh b/scripts/plugin-clawhub-publish.sh index 121fbd7f1e3..3beb9263765 100644 --- a/scripts/plugin-clawhub-publish.sh +++ b/scripts/plugin-clawhub-publish.sh @@ -30,6 +30,8 @@ if ! command -v clawhub >/dev/null 2>&1; then exit 1 fi +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd "${script_dir}/.." && pwd)" package_name="$(node -e 'const pkg = require(require("node:path").resolve(process.argv[1], "package.json")); console.log(pkg.name)' "${package_dir}")" package_version="$(node -e 'const pkg = require(require("node:path").resolve(process.argv[1], "package.json")); console.log(pkg.version)' "${package_dir}")" publish_tag="${PACKAGE_TAG:-latest}" @@ -37,17 +39,91 @@ source_repo="${SOURCE_REPO:-${GITHUB_REPOSITORY:-openclaw/openclaw}}" source_commit="${SOURCE_COMMIT:-$(git rev-parse HEAD)}" source_ref="${SOURCE_REF:-$(git symbolic-ref -q HEAD || true)}" clawhub_workdir="${CLAWDHUB_WORKDIR:-${CLAWHUB_WORKDIR:-$(pwd)}}" -publish_source="${package_dir}" +package_source="${package_dir}" -if [[ "${publish_source}" != /* && "${publish_source}" != ./* ]]; then - publish_source="./${publish_source}" +if [[ "${package_source}" != /* && "${package_source}" != ./* ]]; then + package_source="./${package_source}" +fi + +pack_dir="$(mktemp -d "${RUNNER_TEMP:-/tmp}/openclaw-clawhub-pack.XXXXXX")" +cleanup() { + rm -rf "${pack_dir}" +} +trap cleanup EXIT + +pack_cmd=( + clawhub + package + pack + "${package_source}" + --pack-destination + "${pack_dir}" + --json +) + +build_package_runtime() { + if [[ "${OPENCLAW_PLUGIN_NPM_RUNTIME_BUILD:-1}" == "0" || "${OPENCLAW_PLUGIN_NPM_RUNTIME_BUILD:-1}" == "false" ]]; then + echo "Package-local runtime build: skipped" + return + fi + echo "Package-local runtime build: ${package_dir}" + node "${repo_root}/scripts/lib/plugin-npm-runtime-build.mjs" "${package_dir}" >&2 +} + +echo "Resolved package dir: ${package_dir}" +echo "Resolved package source: ${package_source}" +echo "Resolved package name: ${package_name}" +echo "Resolved package version: ${package_version}" +echo "Resolved publish tag: ${publish_tag}" +echo "Resolved source repo: ${source_repo}" +echo "Resolved source commit: ${source_commit}" +echo "Resolved source ref: ${source_ref:-}" +echo "Resolved ClawHub workdir: ${clawhub_workdir}" +echo "Publish auth: GitHub Actions OIDC via ClawHub short-lived token" + +printf 'Pack command: CLAWHUB_WORKDIR=%q' "${clawhub_workdir}" +printf ' %q' "${pack_cmd[@]}" +printf '\n' + +build_package_runtime + +pack_json="${pack_dir}/pack.json" +CLAWHUB_WORKDIR="${clawhub_workdir}" \ + node "${repo_root}/scripts/lib/plugin-npm-package-manifest.mjs" --run "${package_dir}" -- \ + "${pack_cmd[@]}" > "${pack_json}" +pack_output="$(cat "${pack_json}")" +printf '%s\n' "${pack_output}" + +pack_path="$( + PACK_OUTPUT="${pack_output}" node --input-type=module <<'EOF' +import { resolve } from "node:path"; + +const raw = process.env.PACK_OUTPUT ?? ""; +let parsed; +try { + parsed = JSON.parse(raw); +} catch (error) { + console.error(`clawhub package pack did not return JSON: ${error instanceof Error ? error.message : String(error)}`); + process.exit(1); +} +if (!parsed || typeof parsed.path !== "string" || parsed.path.trim() === "") { + console.error("clawhub package pack output did not include a tarball path."); + process.exit(1); +} +console.log(resolve(parsed.path)); +EOF +)" + +if [[ ! -f "${pack_path}" ]]; then + echo "ClawPack tarball not found: ${pack_path}" >&2 + exit 1 fi publish_cmd=( clawhub package publish - "${publish_source}" + "${pack_path}" --tags "${publish_tag}" --source-repo @@ -65,16 +141,7 @@ if [[ -n "${source_ref}" ]]; then ) fi -echo "Resolved package dir: ${package_dir}" -echo "Resolved publish source: ${publish_source}" -echo "Resolved package name: ${package_name}" -echo "Resolved package version: ${package_version}" -echo "Resolved publish tag: ${publish_tag}" -echo "Resolved source repo: ${source_repo}" -echo "Resolved source commit: ${source_commit}" -echo "Resolved source ref: ${source_ref:-}" -echo "Resolved ClawHub workdir: ${clawhub_workdir}" -echo "Publish auth: GitHub Actions OIDC via ClawHub short-lived token" +echo "Resolved ClawPack: ${pack_path}" printf 'Publish command: CLAWHUB_WORKDIR=%q' "${clawhub_workdir}" printf ' %q' "${publish_cmd[@]}" diff --git a/test/plugin-clawhub-release.test.ts b/test/plugin-clawhub-release.test.ts index 055177c2a3a..89eb28aed9e 100644 --- a/test/plugin-clawhub-release.test.ts +++ b/test/plugin-clawhub-release.test.ts @@ -371,7 +371,29 @@ describe("plugin-clawhub-publish.sh", () => { const clawhubPath = join(binDir, "clawhub"); writeFileSync( clawhubPath, - `#!/usr/bin/env bash\nprintf '%s\\n' "$@" > ${JSON.stringify(markerPath)}\nexit 0\n`, + `#!/usr/bin/env bash +set -euo pipefail +printf '%s\\n' "$*" >> ${JSON.stringify(markerPath)} +if [[ "\${1:-}" == "package" && "\${2:-}" == "pack" ]]; then + pack_destination="" + while [[ "$#" -gt 0 ]]; do + case "$1" in + --pack-destination) + pack_destination="\${2:-}" + shift 2 + ;; + *) + shift + ;; + esac + done + mkdir -p "$pack_destination" + pack_path="$pack_destination/openclaw-demo-plugin-2026.4.1.tgz" + printf 'fake tgz\\n' > "$pack_path" + printf '{"path":"%s","name":"@openclaw/demo-plugin","version":"2026.4.1"}\\n' "$pack_path" +fi +exit 0 +`, ); chmodSync(clawhubPath, 0o755); @@ -387,13 +409,19 @@ describe("plugin-clawhub-publish.sh", () => { encoding: "utf8", env: { ...process.env, + OPENCLAW_PLUGIN_NPM_RUNTIME_BUILD: "0", PATH: `${binDir}${delimiter}${process.env.PATH ?? ""}`, }, }, ); expect(output).toContain("Publish command: CLAWHUB_WORKDIR="); - expect(readFileSync(markerPath, "utf8")).toContain("--dry-run"); + expect(output).toContain("Resolved ClawPack:"); + const invocations = readFileSync(markerPath, "utf8"); + expect(invocations).toContain("package pack ./extensions/demo-plugin"); + expect(invocations).toContain("package publish "); + expect(invocations).toContain(".tgz --tags latest"); + expect(invocations).toContain("--dry-run"); }); });