



















@@ -1,5 +1,6 @@
11import { readFileSync } from "node:fs";
22import { describe, expect, it } from "vitest";
3+import { parse } from "yaml";
3445const PACKAGE_ACCEPTANCE_WORKFLOW = ".github/workflows/package-acceptance.yml";
56const LIVE_E2E_WORKFLOW = ".github/workflows/openclaw-live-and-e2e-checks-reusable.yml";
@@ -10,6 +11,50 @@ const FULL_RELEASE_VALIDATION_WORKFLOW = ".github/workflows/full-release-validat
1011const QA_LIVE_TRANSPORTS_WORKFLOW = ".github/workflows/qa-live-transports-convex.yml";
1112const UPGRADE_SURVIVOR_RUN_SCRIPT = "scripts/e2e/lib/upgrade-survivor/run.sh";
121314+type WorkflowStep = {
15+env?: Record<string, string>;
16+if?: string;
17+name?: string;
18+run?: string;
19+uses?: string;
20+with?: Record<string, string>;
21+};
22+23+type WorkflowJob = {
24+env?: Record<string, string>;
25+if?: string;
26+name?: string;
27+needs?: string | string[];
28+steps?: WorkflowStep[];
29+};
30+31+type Workflow = {
32+jobs?: Record<string, WorkflowJob>;
33+};
34+35+function readWorkflow(path: string): Workflow {
36+return parse(readFileSync(path, "utf8")) as Workflow;
37+}
38+39+function workflowJob(path: string, jobName: string): WorkflowJob {
40+const job = readWorkflow(path).jobs?.[jobName];
41+expect(job, `expected workflow job ${jobName}`).toBeDefined();
42+return job!;
43+}
44+45+function workflowStep(job: WorkflowJob, stepName: string): WorkflowStep {
46+const step = job.steps?.find((candidate) => candidate.name === stepName);
47+expect(step, `expected workflow step ${stepName}`).toBeDefined();
48+return step!;
49+}
50+51+function expectTextToIncludeAll(text: string | undefined, snippets: string[]): void {
52+expect(text).toBeDefined();
53+for (const snippet of snippets) {
54+expect(text).toContain(snippet);
55+}
56+}
57+1358describe("package acceptance workflow", () => {
1459it("resolves candidate package sources before reusing Docker E2E lanes", () => {
1560const workflow = readFileSync(PACKAGE_ACCEPTANCE_WORKFLOW, "utf8");
@@ -447,32 +492,101 @@ describe("package artifact reuse", () => {
447492448493it("runs full release children from the trusted workflow ref", () => {
449494const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8");
495+const npmTelegramJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "npm_telegram");
496+const dispatchStep = workflowStep(npmTelegramJob, "Dispatch and monitor npm Telegram E2E");
450497451498expect(workflow).toContain("CHILD_WORKFLOW_REF: ${{ github.ref_name }}");
452499expect(workflow).toContain('gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@"');
453-expect(workflow).toContain(
500+expect(npmTelegramJob.name).toBe("Run package Telegram E2E");
501+expect(npmTelegramJob.needs).toEqual(["resolve_target", "release_checks"]);
502+expect(npmTelegramJob.if).toContain(
503+"inputs.rerun_group == 'all' && inputs.release_profile == 'full'",
504+);
505+expect(dispatchStep.env).toMatchObject({
506+RELEASE_CHECKS_RUN_ID: "${{ needs.release_checks.outputs.run_id }}",
507+TARGET_SHA: "${{ needs.resolve_target.outputs.sha }}",
508+});
509+expectTextToIncludeAll(dispatchStep.run, [
454510'gh workflow run npm-telegram-beta-e2e.yml --ref "$CHILD_WORKFLOW_REF" "${args[@]}"',
455-);
456-expect(workflow).toContain('-f harness_ref="$TARGET_SHA"');
457-expect(workflow).toContain("needs: [resolve_target, release_checks]");
458-expect(workflow).toContain("inputs.rerun_group == 'all' && inputs.release_profile == 'full'");
459-expect(workflow).toContain("RELEASE_CHECKS_RUN_ID: ${{ needs.release_checks.outputs.run_id }}");
460-expect(workflow).toContain("-f package_artifact_name=release-package-under-test");
461-expect(workflow).toContain('-f package_artifact_run_id="$RELEASE_CHECKS_RUN_ID"');
462-expect(workflow).toContain('-f package_label="full-release-${TARGET_SHA:0:12}"');
463-expect(workflow).toContain("child_rerun_group=all");
464-expect(workflow).toContain('-f rerun_group="$child_rerun_group"');
465-expect(workflow).toContain('args+=(-f live_suite_filter="$LIVE_SUITE_FILTER")');
466-expect(workflow).toContain(
511+ '-f harness_ref="$TARGET_SHA"',
512+ 'args=(-f package_spec="${PACKAGE_SPEC:-openclaw@beta}"',
513+ 'if [[ -z "${PACKAGE_SPEC// }" ]]; then',
514+ "-f package_artifact_name=release-package-under-test",
515+ '-f package_artifact_run_id="$RELEASE_CHECKS_RUN_ID"',
516+ '-f package_label="full-release-${TARGET_SHA:0:12}"',
517+ 'args+=(-f scenario="$SCENARIO")',
518+]);
519+expectTextToIncludeAll(workflow, [
520+ "child_rerun_group=all",
521+ '-f rerun_group="$child_rerun_group"',
522+ 'args+=(-f live_suite_filter="$LIVE_SUITE_FILTER")',
467523"cancel-in-progress: ${{ inputs.ref == 'main' && inputs.rerun_group == 'all' }}",
468-);
469-expect(workflow).toContain("gh run cancel");
524+"gh run cancel",
525+"NORMAL_CI_RESULT: ${{ needs.normal_ci.result }}",
526+]);
470527expect(workflow).not.toContain("force-cancel");
471-expect(workflow).toContain("NORMAL_CI_RESULT: ${{ needs.normal_ci.result }}");
472528expect(workflow).not.toContain("workflow_ref:");
473529expect(workflow).not.toContain("inputs.workflow_ref");
474530});
475531532+it("documents the full-release Telegram package path in operator summaries", () => {
533+const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8");
534+const releaseDocs = readFileSync("docs/reference/RELEASING.md", "utf8");
535+const fullReleaseDocs = readFileSync("docs/reference/full-release-validation.md", "utf8");
536+537+expectTextToIncludeAll(workflow, [
538+"Published-package Telegram E2E:",
539+"Package Telegram E2E: release package artifact from \\`OpenClaw Release Checks\\`",
540+"Package Telegram E2E: skipped unless \\`release_profile=full\\` or \\`npm_telegram_package_spec\\` is provided",
541+]);
542+expect(releaseDocs).toContain(
543+"Focused `npm-telegram` reruns require `npm_telegram_package_spec`",
544+);
545+expectTextToIncludeAll(fullReleaseDocs, [
546+"full pre-publish candidate",
547+"silently skip that",
548+"Telegram package lane",
549+"| `npm-telegram` | Published-package Telegram E2E; requires `npm_telegram_package_spec`. |",
550+]);
551+});
552+553+it("lets npm Telegram consume current-run or release-run package artifacts", () => {
554+const job = workflowJob(NPM_TELEGRAM_WORKFLOW, "run_package_telegram_e2e");
555+const currentRunDownload = workflowStep(job, "Download package-under-test artifact");
556+const releaseRunDownload = workflowStep(
557+job,
558+"Download package-under-test artifact from release run",
559+);
560+const validateStep = workflowStep(job, "Validate inputs and secrets");
561+const runStep = workflowStep(job, "Run package Telegram E2E");
562+563+expect(currentRunDownload).toMatchObject({
564+if: "inputs.package_artifact_name != '' && inputs.package_artifact_run_id == ''",
565+uses: "actions/download-artifact@v8",
566+with: {
567+name: "${{ inputs.package_artifact_name }}",
568+path: ".artifacts/telegram-package-under-test",
569+},
570+});
571+expect(releaseRunDownload).toMatchObject({
572+if: "inputs.package_artifact_name != '' && inputs.package_artifact_run_id != ''",
573+uses: "actions/download-artifact@v8",
574+with: {
575+"github-token": "${{ github.token }}",
576+name: "${{ inputs.package_artifact_name }}",
577+path: ".artifacts/telegram-package-under-test",
578+"run-id": "${{ inputs.package_artifact_run_id }}",
579+},
580+});
581+expectTextToIncludeAll(validateStep.run, [
582+'if [[ -z "${PACKAGE_ARTIFACT_NAME// }" ]]; then',
583+"package_spec must be openclaw@beta",
584+]);
585+expectTextToIncludeAll(runStep.run, [
586+'export OPENCLAW_NPM_TELEGRAM_PACKAGE_TGZ="${package_tgzs[0]}"',
587+]);
588+});
589+476590it("keeps release QA and repo E2E lanes off scarce 32-core runners", () => {
477591const releaseChecksWorkflow = readFileSync(RELEASE_CHECKS_WORKFLOW, "utf8");
478592const qaWorkflow = readFileSync(QA_LIVE_TRANSPORTS_WORKFLOW, "utf8");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。