From 522f3296a71c56b434be241a715d3a759a2db4dd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 10 May 2026 20:38:29 +0100 Subject: [PATCH] ci: forward-port release validation fixes --- .github/workflows/full-release-validation.yml | 3 ++- .../workflows/openclaw-release-publish.yml | 21 ++++++++++++------- .../OpenClawProtocol/GatewayModels.swift | 4 ---- scripts/openclaw-npm-postpublish-verify.ts | 5 ++++- test/openclaw-npm-postpublish-verify.test.ts | 10 +++++++-- .../package-acceptance-workflow.test.ts | 11 ++++++---- 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/.github/workflows/full-release-validation.yml b/.github/workflows/full-release-validation.yml index 49990354ff0..eed0646fc46 100644 --- a/.github/workflows/full-release-validation.yml +++ b/.github/workflows/full-release-validation.yml @@ -783,6 +783,7 @@ jobs: RELEASE_CHECKS_RESULT: ${{ needs.release_checks.result }} NPM_TELEGRAM_RESULT: ${{ needs.npm_telegram.result }} TARGET_SHA: ${{ needs.resolve_target.outputs.sha }} + CHILD_WORKFLOW_REF: ${{ github.ref_name }} run: | set -euo pipefail @@ -809,7 +810,7 @@ jobs: head_sha="$(jq -r '.headSha // ""' <<< "$run_json")" echo "${label}: ${status}/${conclusion} attempt ${attempt} head ${head_sha}: ${url}" - if [[ -n "${TARGET_SHA// }" && "$head_sha" != "$TARGET_SHA" ]]; then + if [[ "$CHILD_WORKFLOW_REF" == release-ci/* && -n "${TARGET_SHA// }" && "$head_sha" != "$TARGET_SHA" ]]; then echo "::error::${label} child run used ${head_sha}, expected ${TARGET_SHA}. Dispatch Full Release Validation from a ref pinned to the target SHA, not a moving branch." return 1 fi diff --git a/.github/workflows/openclaw-release-publish.yml b/.github/workflows/openclaw-release-publish.yml index d8c15100a8c..35e8b876170 100644 --- a/.github/workflows/openclaw-release-publish.yml +++ b/.github/workflows/openclaw-release-publish.yml @@ -126,7 +126,7 @@ jobs: uses: actions/download-artifact@v8 with: name: openclaw-npm-preflight-${{ inputs.tag }} - path: preflight-manifest + path: ${{ runner.temp }}/openclaw-npm-preflight-manifest repository: ${{ github.repository }} run-id: ${{ inputs.preflight_run_id }} github-token: ${{ github.token }} @@ -151,10 +151,11 @@ jobs: EXPECTED_SHA: ${{ steps.ref.outputs.sha }} run: | set -euo pipefail - manifest="preflight-manifest/preflight-manifest.json" + preflight_dir="${RUNNER_TEMP}/openclaw-npm-preflight-manifest" + manifest="${preflight_dir}/preflight-manifest.json" if [[ ! -f "$manifest" ]]; then echo "OpenClaw npm preflight manifest is missing." >&2 - ls -la preflight-manifest >&2 || true + ls -la "$preflight_dir" >&2 || true exit 1 fi release_tag="$(jq -r '.releaseTag // ""' "$manifest")" @@ -174,11 +175,11 @@ jobs: echo "Preflight manifest npm dist-tag mismatch: expected $RELEASE_NPM_DIST_TAG, got $npm_dist_tag" >&2 exit 1 fi - if [[ -z "$tarball_name" || ! -f "preflight-manifest/$tarball_name" ]]; then + if [[ -z "$tarball_name" || ! -f "${preflight_dir}/${tarball_name}" ]]; then echo "Preflight manifest tarball is missing: $tarball_name" >&2 exit 1 fi - actual_tarball_sha256="$(sha256sum "preflight-manifest/$tarball_name" | awk '{print $1}')" + actual_tarball_sha256="$(sha256sum "${preflight_dir}/${tarball_name}" | awk '{print $1}')" if [[ "$actual_tarball_sha256" != "$tarball_sha256" ]]; then echo "Preflight manifest tarball digest mismatch." >&2 exit 1 @@ -222,6 +223,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 steps: + - name: Checkout release SHA + uses: actions/checkout@v6 + with: + ref: ${{ needs.resolve_release_target.outputs.sha }} + fetch-depth: 1 + persist-credentials: false + - name: Dispatch publish workflows env: GH_TOKEN: ${{ github.token }} @@ -336,8 +344,7 @@ jobs: changelog_file="${RUNNER_TEMP}/CHANGELOG.md" notes_file="${RUNNER_TEMP}/release-notes.md" - gh api "repos/${GITHUB_REPOSITORY}/contents/CHANGELOG.md?ref=${TARGET_SHA}" \ - --jq '.content' | base64 --decode > "${changelog_file}" + git show "${TARGET_SHA}:CHANGELOG.md" > "${changelog_file}" awk -v version="${notes_version}" ' $0 == "## " version { in_section = 1; next } /^## / && in_section { exit } diff --git a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift index 86f813cc979..2020b92481a 100644 --- a/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift +++ b/apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift @@ -716,7 +716,6 @@ public struct AgentParams: Codable, Sendable { public let bootstrapcontextmode: AnyCodable? public let bootstrapcontextrunkind: AnyCodable? public let acpturnsource: String? - public let internalruntimehandoffid: String? public let internalevents: [[String: AnyCodable]]? public let inputprovenance: [String: AnyCodable]? public let voicewaketrigger: String? @@ -753,7 +752,6 @@ public struct AgentParams: Codable, Sendable { bootstrapcontextmode: AnyCodable?, bootstrapcontextrunkind: AnyCodable?, acpturnsource: String?, - internalruntimehandoffid: String?, internalevents: [[String: AnyCodable]]?, inputprovenance: [String: AnyCodable]?, voicewaketrigger: String?, @@ -789,7 +787,6 @@ public struct AgentParams: Codable, Sendable { self.bootstrapcontextmode = bootstrapcontextmode self.bootstrapcontextrunkind = bootstrapcontextrunkind self.acpturnsource = acpturnsource - self.internalruntimehandoffid = internalruntimehandoffid self.internalevents = internalevents self.inputprovenance = inputprovenance self.voicewaketrigger = voicewaketrigger @@ -827,7 +824,6 @@ public struct AgentParams: Codable, Sendable { case bootstrapcontextmode = "bootstrapContextMode" case bootstrapcontextrunkind = "bootstrapContextRunKind" case acpturnsource = "acpTurnSource" - case internalruntimehandoffid = "internalRuntimeHandoffId" case internalevents = "internalEvents" case inputprovenance = "inputProvenance" case voicewaketrigger = "voiceWakeTrigger" diff --git a/scripts/openclaw-npm-postpublish-verify.ts b/scripts/openclaw-npm-postpublish-verify.ts index 45fc357f10d..d4d33a73165 100644 --- a/scripts/openclaw-npm-postpublish-verify.ts +++ b/scripts/openclaw-npm-postpublish-verify.ts @@ -50,7 +50,7 @@ const PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS = BUNDLED_RUNTIME_SIDECAR_PATHS.fi ); const NODE_BUILTIN_MODULES = new Set(builtinModules.map((name) => name.replace(/^node:/u, ""))); const MAX_INSTALLED_ROOT_PACKAGE_JSON_BYTES = 1024 * 1024; -const MAX_INSTALLED_ROOT_DIST_JS_BYTES = 4 * 1024 * 1024; +const MAX_INSTALLED_ROOT_DIST_JS_BYTES = 6 * 1024 * 1024; const MAX_INSTALLED_ROOT_DIST_JS_FILES = 5000; const ROOT_DIST_JAVASCRIPT_MODULE_FILE_RE = /\.(?:c|m)?js$/u; const OPTIONAL_OR_EXTERNALIZED_RUNTIME_IMPORTS = new Set([ @@ -69,6 +69,9 @@ const OPTIONAL_OR_EXTERNALIZED_RUNTIME_IMPORTS = new Set([ // Discord voice decoder fallback. The root chunk catches missing decoders and the owning // Discord plugin remains externalized from the root package. "opusscript", + // Public plugin SDK contract helpers are intentionally test-only entrypoints. + // Consumers importing them run under their own Vitest dev dependency. + "vitest", ]); const require = createRequire(import.meta.url); const acorn = require("acorn") as typeof import("acorn"); diff --git a/test/openclaw-npm-postpublish-verify.test.ts b/test/openclaw-npm-postpublish-verify.test.ts index 6c97df75df7..57fb4f53c74 100644 --- a/test/openclaw-npm-postpublish-verify.test.ts +++ b/test/openclaw-npm-postpublish-verify.test.ts @@ -250,6 +250,12 @@ describe("collectInstalledRootDependencyManifestErrors", () => { 'import * as lark from "@larksuiteoapi/node-sdk";\nexport { lark };\n', "utf8", ); + mkdirSync(join(packageRoot, "dist", "plugin-sdk"), { recursive: true }); + writeFileSync( + join(packageRoot, "dist", "plugin-sdk/channel-test-helpers.js"), + 'import { expect, it } from "vitest";\nexport { expect, it };\n', + "utf8", + ); expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toStrictEqual([]); } finally { @@ -363,12 +369,12 @@ describe("collectInstalledRootDependencyManifestErrors", () => { mkdirSync(join(packageRoot, "dist"), { recursive: true }); writeFileSync( join(packageRoot, "dist", "oversized.js"), - "x".repeat(4 * 1024 * 1024 + 1), + "x".repeat(6 * 1024 * 1024 + 1), "utf8", ); expect(collectInstalledRootDependencyManifestErrors(packageRoot)).toEqual([ - "installed package root dist file 'oversized.js' is invalid or exceeds 4194304 bytes.", + "installed package root dist file 'oversized.js' is invalid or exceeds 6291456 bytes.", ]); } finally { rmSync(packageRoot, { recursive: true, force: true }); diff --git a/test/scripts/package-acceptance-workflow.test.ts b/test/scripts/package-acceptance-workflow.test.ts index 7edbbf357ca..ea45ad39785 100644 --- a/test/scripts/package-acceptance-workflow.test.ts +++ b/test/scripts/package-acceptance-workflow.test.ts @@ -150,16 +150,20 @@ describe("package acceptance workflow", () => { expect(workflow).toContain("Published upgrade survivor scenarios:"); }); - it("requires full release child workflows to run at the resolved target SHA", () => { + it("requires pinned full release child workflows to run at the resolved target SHA", () => { const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8"); const releaseChecksWorkflow = readFileSync(RELEASE_CHECKS_WORKFLOW, "utf8"); expect(workflow).toContain("TARGET_SHA: ${{ needs.resolve_target.outputs.sha }}"); + expect(workflow).toContain("CHILD_WORKFLOW_REF: ${{ github.ref_name }}"); expect(workflow).toContain("package_acceptance_package_spec:"); expect(workflow).toContain( 'args+=(-f package_acceptance_package_spec="$PACKAGE_ACCEPTANCE_PACKAGE_SPEC")', ); expect(workflow).toContain("--json status,conclusion,url,attempt,headSha,jobs"); + expect(workflow).toContain( + '[[ "$CHILD_WORKFLOW_REF" == release-ci/* && -n "${TARGET_SHA// }" && "$head_sha" != "$TARGET_SHA" ]]', + ); expect(workflow).toContain("child run used ${head_sha}, expected ${TARGET_SHA}"); expect(workflow).toContain( "Dispatch Full Release Validation from a ref pinned to the target SHA", @@ -842,9 +846,8 @@ describe("package artifact reuse", () => { expect(workflow).toContain("preflight-manifest.json"); expect(npmWorkflow).toContain("preflight-manifest.json"); expect(npmWorkflow).toContain("tarballSha256"); - expect(workflow).toContain( - 'gh api "repos/${GITHUB_REPOSITORY}/contents/CHANGELOG.md?ref=${TARGET_SHA}"', - ); + expect(workflow).toContain("Checkout release SHA"); + expect(workflow).toContain('git show "${TARGET_SHA}:CHANGELOG.md" > "${changelog_file}"'); expect(workflow).toContain('$0 == "## Unreleased" { in_section = 1; next }'); expect(workflow).toContain("Unreleased prerelease fallback"); expect(workflow).not.toContain("gh api --repo");