mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
ci: forward-port release validation fixes
This commit is contained in:
@@ -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
|
||||
|
||||
21
.github/workflows/openclaw-release-publish.yml
vendored
21
.github/workflows/openclaw-release-publish.yml
vendored
@@ -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 }
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user