mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-13 17:07:40 +00:00
fix(release): unblock plugin bootstrap recovery (#108243)
This commit is contained in:
committed by
GitHub
parent
99c43b1647
commit
54f70c5d8c
4
.github/workflows/plugin-clawhub-new.yml
vendored
4
.github/workflows/plugin-clawhub-new.yml
vendored
@@ -667,6 +667,8 @@ jobs:
|
||||
ARTIFACT_SIZE: ${{ needs.pack_bootstrap_plugins.outputs.artifact_size }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
TARGET_SHA: ${{ needs.resolve_bootstrap_plan.outputs.ref_revision }}
|
||||
WORKFLOW_HEAD_BRANCH: ${{ github.ref_name }}
|
||||
WORKFLOW_REF: ${{ github.ref }}
|
||||
WORKFLOW_SHA: ${{ github.sha }}
|
||||
CLAWHUB_TOOLCHAIN_INTEGRITY: ${{ steps.clawhub_cli.outputs.integrity }}
|
||||
CLAWHUB_TOOLCHAIN_SHA256: ${{ steps.clawhub_cli.outputs.lock_sha256 }}
|
||||
@@ -688,6 +690,8 @@ jobs:
|
||||
--run-attempt "${ARTIFACT_RUN_ATTEMPT}" \
|
||||
--run-id "${ARTIFACT_RUN_ID}" \
|
||||
--target-sha "${TARGET_SHA}" \
|
||||
--workflow-head-branch "${WORKFLOW_HEAD_BRANCH}" \
|
||||
--workflow-ref "${WORKFLOW_REF}" \
|
||||
--workflow-sha "${WORKFLOW_SHA}"
|
||||
|
||||
- name: Rehash immutable ClawHub bootstrap artifacts
|
||||
|
||||
14
.github/workflows/plugin-npm-release.yml
vendored
14
.github/workflows/plugin-npm-release.yml
vendored
@@ -1240,7 +1240,7 @@ jobs:
|
||||
PACKAGE_VERSION: ${{ matrix.plugin.version }}
|
||||
run: node scripts/verify-plugin-npm-published-runtime.mjs "${PACKAGE_NAME}@${PACKAGE_VERSION}"
|
||||
|
||||
- name: Publish approved Meta bootstrap tarball
|
||||
- name: Publish approved bootstrap tarball
|
||||
if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap'
|
||||
env:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -1251,19 +1251,15 @@ jobs:
|
||||
TARBALL_PATH: ${{ steps.publication_evidence.outputs.tarball_path }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
[[ "$PACKAGE_NAME" == "@openclaw/meta-provider" && "$PACKAGE_DIR" == "extensions/meta" ]] || {
|
||||
echo "npm token bootstrap is restricted to the approved Meta provider package." >&2
|
||||
exit 1
|
||||
}
|
||||
[[ "$PACKAGE_VERSION" == *"-beta."* && "$PUBLISH_TAG" == "beta" ]] || {
|
||||
echo "Meta npm token bootstrap requires an approved beta package and beta tag." >&2
|
||||
echo "npm token bootstrap requires an approved beta package and beta tag." >&2
|
||||
exit 1
|
||||
}
|
||||
[[ -n "${NPM_TOKEN// }" ]] || {
|
||||
echo "Meta npm token bootstrap requires the protected npm release token." >&2
|
||||
echo "npm token bootstrap requires the protected npm release token." >&2
|
||||
exit 1
|
||||
}
|
||||
publish_home="$(mktemp -d "${RUNNER_TEMP}/meta-npm-bootstrap.XXXXXX")"
|
||||
publish_home="$(mktemp -d "${RUNNER_TEMP}/plugin-npm-bootstrap.XXXXXX")"
|
||||
cleanup() {
|
||||
rm -rf "$publish_home"
|
||||
}
|
||||
@@ -1287,7 +1283,7 @@ jobs:
|
||||
--provenance \
|
||||
--tag "$PUBLISH_TAG"
|
||||
|
||||
- name: Verify Meta bootstrap published runtime
|
||||
- name: Verify bootstrap published runtime
|
||||
if: steps.publication_evidence.outputs.publish_route == 'npm-token-bootstrap'
|
||||
env:
|
||||
PACKAGE_NAME: ${{ steps.publication_evidence.outputs.package_name }}
|
||||
|
||||
@@ -23,6 +23,8 @@ const REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
||||
const PACKAGE_NAME_PATTERN = /^@openclaw\/[a-z0-9][a-z0-9._-]*$/u;
|
||||
const PACKAGE_DIR_PATTERN = /^extensions\/[a-z0-9][a-z0-9._-]*$/u;
|
||||
const TAG_PATTERN = /^[a-z0-9][a-z0-9._-]*$/u;
|
||||
const PROTECTED_WORKFLOW_TAG_PATTERN =
|
||||
/^refs\/tags\/(release-publish\/([a-f0-9]{12})-[1-9][0-9]*)$/u;
|
||||
const VERSION_PATTERN =
|
||||
/^[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*(?:-(?:alpha|beta)\.[1-9][0-9]*|-[1-9][0-9]*)?$/u;
|
||||
const TOOLCHAIN_VERSION_PATTERN = /^(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)\.(?:0|[1-9][0-9]*)$/u;
|
||||
@@ -431,6 +433,17 @@ export async function downloadClawHubBootstrapArtifact(options) {
|
||||
const producerJobName = requireString(options.producerJobName, "producerJobName");
|
||||
const targetSha = requirePattern(options.targetSha, COMMIT_PATTERN, "targetSha");
|
||||
const workflowSha = requirePattern(options.workflowSha, COMMIT_PATTERN, "workflowSha");
|
||||
const workflowHeadBranch = requireString(options.workflowHeadBranch, "workflowHeadBranch");
|
||||
const workflowRef = requireString(options.workflowRef, "workflowRef");
|
||||
const protectedWorkflowTag = PROTECTED_WORKFLOW_TAG_PATTERN.exec(workflowRef);
|
||||
const trustedMain = workflowRef === "refs/heads/main" && workflowHeadBranch === "main";
|
||||
const trustedProtectedTag =
|
||||
protectedWorkflowTag !== null &&
|
||||
workflowHeadBranch === protectedWorkflowTag[1] &&
|
||||
protectedWorkflowTag[2] === workflowSha.slice(0, 12);
|
||||
if (!trustedMain && !trustedProtectedTag) {
|
||||
fail("workflowRef must be main or the SHA-pinned release-publish tag.");
|
||||
}
|
||||
const artifactDigest = requirePattern(options.artifactDigest, SHA256_PATTERN, "artifactDigest");
|
||||
const artifactName = requireString(options.artifactName, "artifactName");
|
||||
const repository = requirePattern(options.repository, REPOSITORY_PATTERN, "repository");
|
||||
@@ -488,7 +501,7 @@ export async function downloadClawHubBootstrapArtifact(options) {
|
||||
runAttempt,
|
||||
runId,
|
||||
workflowEvent: "workflow_dispatch",
|
||||
workflowHeadBranch: "main",
|
||||
workflowHeadBranch,
|
||||
workflowPath: ".github/workflows/plugin-clawhub-new.yml",
|
||||
workflowSha,
|
||||
},
|
||||
@@ -745,6 +758,8 @@ async function main() {
|
||||
targetSha: args.target_sha,
|
||||
token: process.env.GH_TOKEN,
|
||||
workflowSha: args.workflow_sha,
|
||||
workflowHeadBranch: args.workflow_head_branch,
|
||||
workflowRef: args.workflow_ref,
|
||||
});
|
||||
process.stdout.write(`${JSON.stringify(result)}\n`);
|
||||
return;
|
||||
|
||||
@@ -285,6 +285,8 @@ describe("ClawHub bootstrap artifact manifest", () => {
|
||||
it("rejects preexisting and symlinked download output roots before fetching", async () => {
|
||||
const paths = fixture();
|
||||
const downloadOptions = {
|
||||
workflowHeadBranch: "main",
|
||||
workflowRef: "refs/heads/main",
|
||||
artifactDigest: "d".repeat(64),
|
||||
artifactId: "456",
|
||||
artifactName: `clawhub-bootstrap-${targetSha.slice(0, 12)}-123-2`,
|
||||
@@ -313,6 +315,14 @@ describe("ClawHub bootstrap artifact manifest", () => {
|
||||
outputRoot: existingRoot,
|
||||
}),
|
||||
).rejects.toThrow("output directory must not already exist");
|
||||
await expect(
|
||||
downloadClawHubBootstrapArtifact({
|
||||
...downloadOptions,
|
||||
outputRoot: existingRoot,
|
||||
workflowHeadBranch: `release-publish/${workflowSha.slice(0, 12)}-123`,
|
||||
workflowRef: `refs/tags/release-publish/${workflowSha.slice(0, 12)}-123`,
|
||||
}),
|
||||
).rejects.toThrow("output directory must not already exist");
|
||||
|
||||
const symlinkTarget = join(paths.artifactRoot, "symlink-target");
|
||||
const symlinkRoot = join(paths.artifactRoot, "symlink-output");
|
||||
@@ -324,6 +334,15 @@ describe("ClawHub bootstrap artifact manifest", () => {
|
||||
outputRoot: symlinkRoot,
|
||||
}),
|
||||
).rejects.toThrow("output directory must not already exist");
|
||||
|
||||
await expect(
|
||||
downloadClawHubBootstrapArtifact({
|
||||
...downloadOptions,
|
||||
outputRoot: join(paths.artifactRoot, "unused-output"),
|
||||
workflowHeadBranch: `release-publish/${workflowSha.slice(0, 12)}-123`,
|
||||
workflowRef: `refs/heads/release-publish/${workflowSha.slice(0, 12)}-123`,
|
||||
}),
|
||||
).rejects.toThrow("workflowRef must be main or the SHA-pinned release-publish tag");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3963,6 +3963,10 @@ describe("package artifact reuse", () => {
|
||||
expect(clawHubNewWorkflow).toContain(
|
||||
"Download and verify immutable ClawHub bootstrap artifact",
|
||||
);
|
||||
expect(clawHubNewWorkflow).toContain("WORKFLOW_HEAD_BRANCH: ${{ github.ref_name }}");
|
||||
expect(clawHubNewWorkflow).toContain("WORKFLOW_REF: ${{ github.ref }}");
|
||||
expect(clawHubNewWorkflow).toContain('--workflow-head-branch "${WORKFLOW_HEAD_BRANCH}"');
|
||||
expect(clawHubNewWorkflow).toContain('--workflow-ref "${WORKFLOW_REF}"');
|
||||
expect(clawHubNewWorkflow).toContain("Rehash immutable ClawHub bootstrap artifacts");
|
||||
expect(clawHubNewWorkflow).toContain("Download parent ClawHub bootstrap approval");
|
||||
expect(clawHubNewWorkflow).toContain("RELEASE_APPROVAL_KIND: clawhub-bootstrap");
|
||||
|
||||
@@ -329,15 +329,11 @@ describe("plugin npm extended-stable workflow", () => {
|
||||
});
|
||||
expect(publish.env?.NODE_AUTH_TOKEN).toBeUndefined();
|
||||
expect(publish.env?.NPM_TOKEN).toBeUndefined();
|
||||
const bootstrap = step(
|
||||
parsed.jobs?.publish_plugins_npm,
|
||||
"Publish approved Meta bootstrap tarball",
|
||||
);
|
||||
const bootstrap = step(parsed.jobs?.publish_plugins_npm, "Publish approved bootstrap tarball");
|
||||
expect(bootstrap.if).toContain("npm-token-bootstrap");
|
||||
expect(bootstrap.env?.NPM_TOKEN).toBe("${{ secrets.NPM_TOKEN }}");
|
||||
expect(bootstrap.run).toContain(
|
||||
'[[ "$PACKAGE_NAME" == "@openclaw/meta-provider" && "$PACKAGE_DIR" == "extensions/meta" ]]',
|
||||
);
|
||||
expect(bootstrap.env?.PACKAGE_NAME).toContain("publication_evidence.outputs.package_name");
|
||||
expect(bootstrap.run).not.toContain("@openclaw/meta-provider");
|
||||
expect(bootstrap.run).toContain("NPM_CONFIG_USERCONFIG");
|
||||
expect(bootstrap.run).toContain("unset NODE_AUTH_TOKEN NPM_TOKEN NODE_OPTIONS");
|
||||
expect(bootstrap.run).toContain('npm publish "$TARBALL_PATH"');
|
||||
|
||||
Reference in New Issue
Block a user