mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(release): bootstrap typed Docker planner (#121088)
* fix(release): bootstrap typed Docker planner * test(release): handle optional workflow steps * fix(release): isolate trusted planner dependencies Punchcard-Session: amber-workshop-workshop-36 * test(release): update trusted harness contract Punchcard-Session: amber-workshop-workshop-36 * fix(release): bootstrap every trusted Docker planner Punchcard-Session: amber-workshop-workshop-36 * fix(release): centralize trusted harness bootstrap --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
committed by
GitHub
parent
b0a1a2ab5c
commit
a828bfe04e
@@ -0,0 +1,23 @@
|
||||
name: Setup trusted release harness
|
||||
description: Prepare the trusted release harness package manager and dependencies.
|
||||
inputs:
|
||||
node-version:
|
||||
description: Node.js version required by the release harness.
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup trusted release harness package manager
|
||||
uses: ./.release-harness/.github/actions/setup-pnpm-store-cache
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
package-manager-file: .release-harness/package.json
|
||||
use-actions-cache: "false"
|
||||
|
||||
- name: Install trusted release harness dependencies
|
||||
shell: bash
|
||||
working-directory: .release-harness
|
||||
env:
|
||||
CI: "true"
|
||||
# Corepack resolves packageManager from cwd before pnpm processes --dir.
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
|
||||
@@ -1256,6 +1256,12 @@ jobs:
|
||||
fetch-depth: 1
|
||||
path: .release-harness
|
||||
|
||||
- name: Setup trusted release harness
|
||||
if: contains(matrix.profiles, inputs.release_test_profile)
|
||||
uses: ./.release-harness/.github/actions/setup-release-harness
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Log in to GHCR for shared Docker E2E image
|
||||
if: contains(matrix.profiles, inputs.release_test_profile) && inputs.shared_image_policy != 'no-push-artifact'
|
||||
run: bash .release-harness/scripts/ci-docker-login-ghcr.sh
|
||||
@@ -1632,6 +1638,11 @@ jobs:
|
||||
fetch-depth: 1
|
||||
path: .release-harness
|
||||
|
||||
- name: Setup trusted release harness
|
||||
uses: ./.release-harness/.github/actions/setup-release-harness
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Log in to GHCR for shared Docker E2E image
|
||||
if: inputs.shared_image_policy != 'no-push-artifact'
|
||||
run: bash .release-harness/scripts/ci-docker-login-ghcr.sh
|
||||
@@ -1919,6 +1930,11 @@ jobs:
|
||||
path: .release-harness
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup trusted release harness
|
||||
uses: ./.release-harness/.github/actions/setup-release-harness
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Log in to GHCR for shared Docker E2E image
|
||||
if: inputs.shared_image_policy != 'no-push-artifact'
|
||||
run: bash .release-harness/scripts/ci-docker-login-ghcr.sh
|
||||
@@ -2141,6 +2157,11 @@ jobs:
|
||||
path: .release-harness
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup trusted release harness
|
||||
uses: ./.release-harness/.github/actions/setup-release-harness
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- name: Plan Docker E2E images
|
||||
id: plan
|
||||
shell: bash
|
||||
@@ -2187,24 +2208,6 @@ jobs:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
install-bun: "true"
|
||||
|
||||
- name: Setup artifact package validation environment
|
||||
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_id != ''
|
||||
uses: ./.release-harness/.github/actions/setup-pnpm-store-cache
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
package-manager-file: .release-harness/package.json
|
||||
use-actions-cache: "false"
|
||||
|
||||
- name: Install trusted package validation dependencies
|
||||
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_id != ''
|
||||
env:
|
||||
CI: "true"
|
||||
# Corepack resolves packageManager from the process cwd before pnpm can
|
||||
# process --dir. Keep this in the trusted harness so a frozen candidate
|
||||
# pin cannot select a different pnpm binary for its harness install.
|
||||
working-directory: .release-harness
|
||||
run: pnpm install --frozen-lockfile --prefer-offline --ignore-scripts
|
||||
|
||||
- name: Validate OpenClaw package artifact identity
|
||||
id: input_package_artifact
|
||||
if: steps.plan.outputs.needs_package == '1' && inputs.package_artifact_id != ''
|
||||
|
||||
@@ -31,6 +31,7 @@ const MANTIS_TELEGRAM_LIVE_WORKFLOW = ".github/workflows/mantis-telegram-live.ym
|
||||
const MANTIS_WEB_UI_CHAT_PROOF_WORKFLOW = ".github/workflows/mantis-web-ui-chat-proof.yml";
|
||||
const PACKAGE_JSON = "package.json";
|
||||
const SETUP_PNPM_STORE_CACHE_ACTION = ".github/actions/setup-pnpm-store-cache/action.yml";
|
||||
const SETUP_RELEASE_HARNESS_ACTION = ".github/actions/setup-release-harness/action.yml";
|
||||
const RELEASE_CHECKS_WORKFLOW = ".github/workflows/openclaw-release-checks.yml";
|
||||
const RELEASE_TELEGRAM_QA_WORKFLOW = ".github/workflows/openclaw-release-telegram-qa.yml";
|
||||
const RELEASE_PUBLISH_WORKFLOW = ".github/workflows/openclaw-release-publish.yml";
|
||||
@@ -118,6 +119,7 @@ type WorkflowStep = {
|
||||
shell?: string;
|
||||
uses?: string;
|
||||
with?: Record<string, string>;
|
||||
"working-directory"?: string;
|
||||
};
|
||||
|
||||
type WorkflowMatrixEntry = {
|
||||
@@ -1350,6 +1352,26 @@ describe("package acceptance workflow", () => {
|
||||
expect(setupPnpmAction).toContain('corepack enable --install-directory "$PNPM_HOME"');
|
||||
expect(setupPnpmAction).toContain('echo "PNPM_HOME=$PNPM_HOME" >> "$GITHUB_ENV"');
|
||||
|
||||
const setupReleaseHarnessAction = readFileSync(SETUP_RELEASE_HARNESS_ACTION, "utf8");
|
||||
const setupHarnessPackageManagerIndex = setupReleaseHarnessAction.indexOf(
|
||||
"Setup trusted release harness package manager",
|
||||
);
|
||||
const installHarnessDependenciesIndex = setupReleaseHarnessAction.indexOf(
|
||||
"Install trusted release harness dependencies",
|
||||
);
|
||||
expect(setupHarnessPackageManagerIndex).toBeGreaterThan(-1);
|
||||
expect(installHarnessDependenciesIndex).toBeGreaterThan(setupHarnessPackageManagerIndex);
|
||||
expect(setupReleaseHarnessAction).toContain(
|
||||
"uses: ./.release-harness/.github/actions/setup-pnpm-store-cache",
|
||||
);
|
||||
expect(setupReleaseHarnessAction).toContain(
|
||||
"package-manager-file: .release-harness/package.json",
|
||||
);
|
||||
expect(setupReleaseHarnessAction).toContain("working-directory: .release-harness");
|
||||
expect(setupReleaseHarnessAction).toContain(
|
||||
"pnpm install --frozen-lockfile --prefer-offline --ignore-scripts",
|
||||
);
|
||||
|
||||
const setupNodeAction = readFileSync(".github/actions/setup-node-env/action.yml", "utf8");
|
||||
expect(setupNodeAction).toContain("Normalize container toolcache");
|
||||
expect(setupNodeAction).toContain("ln -s /__t /opt/hostedtoolcache");
|
||||
@@ -2408,6 +2430,7 @@ describe("package artifact reuse", () => {
|
||||
it("lets reusable Docker E2E consume an already resolved package artifact", () => {
|
||||
const workflow = readFileSync(LIVE_E2E_WORKFLOW, "utf8");
|
||||
const parsedWorkflow = parse(workflow) as {
|
||||
jobs?: Record<string, WorkflowJob>;
|
||||
on?: { workflow_call?: { inputs?: Record<string, unknown> } };
|
||||
};
|
||||
const packageJson = readFileSync(PACKAGE_JSON, "utf8");
|
||||
@@ -2495,8 +2518,80 @@ describe("package artifact reuse", () => {
|
||||
?.prepublish_plugin_registry_artifact_id,
|
||||
).toContain("inputs.enable_prepublish_plugin_registry");
|
||||
expect(workflow).toContain("bash .release-harness/scripts/ci-docker-pull-retry.sh");
|
||||
const setupHarnessStepName = "Setup trusted release harness";
|
||||
const harnessJobCases = [
|
||||
{
|
||||
jobId: "validate_docker_e2e",
|
||||
planStepName: "Plan Docker E2E chunk",
|
||||
setupIf: "contains(matrix.profiles, inputs.release_test_profile)",
|
||||
},
|
||||
{
|
||||
jobId: "validate_docker_lanes",
|
||||
planStepName: "Plan targeted Docker E2E lanes",
|
||||
setupIf: undefined,
|
||||
},
|
||||
{
|
||||
jobId: "validate_docker_openwebui",
|
||||
planStepName: "Plan Open WebUI Docker E2E chunk",
|
||||
setupIf: undefined,
|
||||
},
|
||||
{
|
||||
jobId: "prepare_docker_e2e_image",
|
||||
planStepName: "Plan Docker E2E images",
|
||||
setupIf: undefined,
|
||||
},
|
||||
] as const;
|
||||
const typedHarnessJobIds = Object.entries(parsedWorkflow.jobs ?? {})
|
||||
.filter(([, job]) =>
|
||||
(job.steps ?? []).some(
|
||||
(step) =>
|
||||
step.run?.includes("node .release-harness/scripts/test-docker-all.mjs") ||
|
||||
step.run?.includes("node .release-harness/scripts/docker-e2e.mjs"),
|
||||
),
|
||||
)
|
||||
.map(([jobId]) => jobId)
|
||||
.sort();
|
||||
expect(typedHarnessJobIds).toEqual(harnessJobCases.map(({ jobId }) => jobId).sort());
|
||||
|
||||
for (const { jobId, planStepName, setupIf } of harnessJobCases) {
|
||||
const harnessJob = workflowJob(LIVE_E2E_WORKFLOW, jobId);
|
||||
const harnessJobSteps = harnessJob.steps ?? [];
|
||||
const harnessJobStepNames = harnessJobSteps.map((step) => step.name);
|
||||
const checkoutIndex = harnessJobStepNames.indexOf("Checkout trusted release harness");
|
||||
const setupIndex = harnessJobStepNames.indexOf(setupHarnessStepName);
|
||||
const typedHarnessIndex = harnessJobSteps.findIndex(
|
||||
(step) =>
|
||||
step.run?.includes("node .release-harness/scripts/test-docker-all.mjs") ||
|
||||
step.run?.includes("node .release-harness/scripts/docker-e2e.mjs"),
|
||||
);
|
||||
expect(harnessJobStepNames.filter((name) => name === setupHarnessStepName)).toHaveLength(1);
|
||||
expect(checkoutIndex).toBeGreaterThan(-1);
|
||||
expect(setupIndex).toBeGreaterThan(checkoutIndex);
|
||||
expect(typedHarnessIndex).toBeGreaterThan(setupIndex);
|
||||
expect(workflowStep(harnessJob, planStepName)).toBeDefined();
|
||||
const setupHarnessStep = workflowStep(harnessJob, setupHarnessStepName);
|
||||
expect(setupHarnessStep).toMatchObject({
|
||||
uses: "./.release-harness/.github/actions/setup-release-harness",
|
||||
with: { "node-version": "${{ env.NODE_VERSION }}" },
|
||||
});
|
||||
expect(setupHarnessStep.if).toBe(setupIf);
|
||||
}
|
||||
|
||||
const prepareDockerImage = workflowJob(LIVE_E2E_WORKFLOW, "prepare_docker_e2e_image");
|
||||
expect(workflowStep(prepareDockerImage, "Plan Docker E2E images").env).toEqual({
|
||||
const prepareDockerImageStepNames = (prepareDockerImage.steps ?? []).map((step) => step.name);
|
||||
const planStepName = "Plan Docker E2E images";
|
||||
const setupCandidateStepName = "Setup Node environment";
|
||||
expect(
|
||||
prepareDockerImageStepNames.filter((name) => name === setupCandidateStepName),
|
||||
).toHaveLength(1);
|
||||
expect(prepareDockerImageStepNames.indexOf(setupCandidateStepName)).toBeGreaterThan(
|
||||
prepareDockerImageStepNames.indexOf(planStepName),
|
||||
);
|
||||
expect(workflowStep(prepareDockerImage, setupCandidateStepName)).toMatchObject({
|
||||
if: "(steps.plan.outputs.needs_package == '1' && inputs.package_artifact_name == '' && inputs.package_artifact_run_id == '') || (inputs.enable_prepublish_plugin_registry && steps.plan.outputs.needs_prepublish_plugin_registry == '1' && inputs.prepublish_plugin_registry_artifact_id == '')",
|
||||
uses: "./.github/actions/setup-node-env",
|
||||
});
|
||||
expect(workflowStep(prepareDockerImage, planStepName).env).toEqual({
|
||||
INCLUDE_OPENWEBUI: "${{ inputs.include_openwebui }}",
|
||||
INCLUDE_RELEASE_PATH_SUITES: "${{ inputs.include_release_path_suites }}",
|
||||
LANES: "${{ inputs.docker_lanes }}",
|
||||
|
||||
@@ -693,19 +693,11 @@ describe("release validation no-push transport", () => {
|
||||
);
|
||||
expect(packDockerArtifact.run).toContain("archive_sha256=");
|
||||
const validatePackage = step(dockerProducer, "Validate OpenClaw Docker E2E package");
|
||||
expect(step(dockerProducer, "Setup artifact package validation environment")).toMatchObject({
|
||||
if: "steps.plan.outputs.needs_package == '1' && inputs.package_artifact_id != ''",
|
||||
uses: "./.release-harness/.github/actions/setup-pnpm-store-cache",
|
||||
with: {
|
||||
"package-manager-file": ".release-harness/package.json",
|
||||
"use-actions-cache": "false",
|
||||
},
|
||||
});
|
||||
expect(step(dockerProducer, "Install trusted package validation dependencies")).toMatchObject({
|
||||
if: "steps.plan.outputs.needs_package == '1' && inputs.package_artifact_id != ''",
|
||||
"working-directory": ".release-harness",
|
||||
run: "pnpm install --frozen-lockfile --prefer-offline --ignore-scripts",
|
||||
expect(step(dockerProducer, "Setup trusted release harness")).toMatchObject({
|
||||
uses: "./.release-harness/.github/actions/setup-release-harness",
|
||||
with: { "node-version": "${{ env.NODE_VERSION }}" },
|
||||
});
|
||||
expect(step(dockerProducer, "Setup trusted release harness").if).toBeUndefined();
|
||||
expect(validatePackage.env).toMatchObject({
|
||||
EXPECTED_PACKAGE_FILE_NAME: "${{ inputs.package_file_name }}",
|
||||
EXPECTED_PACKAGE_SHA256: "${{ inputs.package_sha256 }}",
|
||||
|
||||
Reference in New Issue
Block a user