fix(release): unblock deferred Telegram beta validation (#120630)

* fix(release): allow deferring package Telegram validation

* fix(qa): mount taxonomy in package Telegram harness

* fix(release): restrict Telegram deferral to beta
This commit is contained in:
Vincent Koc
2026-08-09 01:03:02 +08:00
committed by GitHub
parent 0fd3ee6929
commit 49161dee60
8 changed files with 271 additions and 3 deletions

View File

@@ -18,6 +18,11 @@ on:
required: false
default: false
type: boolean
skip_package_telegram_e2e:
description: Defer beta Package Acceptance Telegram E2E while preserving focused npm Telegram validation
required: false
default: false
type: boolean
provider:
description: Provider lane for cross-OS onboarding and the end-to-end agent turn
required: false
@@ -175,6 +180,17 @@ jobs:
--ref "$TARGET_REF" \
--github-output "$GITHUB_OUTPUT"
- name: Validate release inputs
env:
RELEASE_PROFILE: ${{ inputs.release_profile }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
run: |
set -euo pipefail
if [[ "$SKIP_PACKAGE_TELEGRAM_E2E" == "true" && "$RELEASE_PROFILE" != "beta" ]]; then
echo "skip_package_telegram_e2e is allowed only for release_profile=beta." >&2
exit 1
fi
- name: Summarize target
env:
TARGET_REF: ${{ inputs.ref }}
@@ -188,6 +204,7 @@ jobs:
RELEASE_PROFILE: ${{ inputs.release_profile }}
RUN_RELEASE_SOAK: ${{ inputs.run_release_soak || inputs.release_profile == 'stable' || inputs.release_profile == 'full' }}
FAIL_FAST: ${{ inputs.fail_fast }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
ALLOW_UNRELEASED_CHANGELOG: ${{ inputs.allow_unreleased_changelog || (inputs.target_context_ref == '' && (inputs.ref == 'main' || inputs.ref == 'refs/heads/main')) }}
RERUN_GROUP: ${{ inputs.rerun_group }}
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
@@ -201,6 +218,7 @@ jobs:
echo "- Child workflow ref: \`${CHILD_WORKFLOW_REF}\`"
echo "- Release soak lanes: \`${RUN_RELEASE_SOAK}\`"
echo "- Fail fast: \`${FAIL_FAST}\`"
echo "- Package Acceptance Telegram E2E deferred: \`${SKIP_PACKAGE_TELEGRAM_E2E}\`"
echo "- Allow Unreleased changelog packaging: \`${ALLOW_UNRELEASED_CHANGELOG}\`"
echo "- Rerun group: \`${RERUN_GROUP}\`"
if [[ -n "${LIVE_SUITE_FILTER// }" ]]; then
@@ -239,7 +257,11 @@ jobs:
elif [[ "$RERUN_GROUP" == "npm-telegram" ]]; then
echo "- Package Telegram E2E: focused rerun requires \`release_package_spec\` or \`npm_telegram_package_spec\`"
elif [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "release-checks" || "$RERUN_GROUP" == "package" ]]; then
echo "- Package Telegram E2E: OpenClaw Release Checks Package Acceptance"
if [[ "$SKIP_PACKAGE_TELEGRAM_E2E" == "true" ]]; then
echo "- Package Telegram E2E: deferred by \`skip_package_telegram_e2e\`"
else
echo "- Package Telegram E2E: OpenClaw Release Checks Package Acceptance"
fi
else
echo "- Package Telegram E2E: skipped by rerun group"
fi
@@ -311,6 +333,7 @@ jobs:
NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
NPM_TELEGRAM_PROVIDER_MODE: ${{ inputs.npm_telegram_provider_mode }}
NPM_TELEGRAM_SCENARIO: ${{ inputs.npm_telegram_scenario }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
ALLOW_UNRELEASED_CHANGELOG: ${{ inputs.allow_unreleased_changelog || (inputs.target_context_ref == '' && (inputs.ref == 'main' || inputs.ref == 'refs/heads/main')) }}
run: |
set -euo pipefail
@@ -328,6 +351,7 @@ jobs:
--arg npmTelegramPackageSpec "$NPM_TELEGRAM_PACKAGE_SPEC" \
--arg npmTelegramProviderMode "$NPM_TELEGRAM_PROVIDER_MODE" \
--arg npmTelegramScenario "$NPM_TELEGRAM_SCENARIO" \
--arg skipPackageTelegramE2e "$SKIP_PACKAGE_TELEGRAM_E2E" \
--arg allowUnreleasedChangelog "$ALLOW_UNRELEASED_CHANGELOG" \
'{
provider: $provider,
@@ -341,6 +365,7 @@ jobs:
npmTelegramPackageSpec: $npmTelegramPackageSpec,
npmTelegramProviderMode: $npmTelegramProviderMode,
npmTelegramScenario: $npmTelegramScenario,
skipPackageTelegramE2e: $skipPackageTelegramE2e,
allowUnreleasedChangelog: $allowUnreleasedChangelog
}')"
bash workflow/scripts/github/find-reusable-release-validation.sh \
@@ -857,6 +882,7 @@ jobs:
if [[ -n "${CODEX_PLUGIN_SPEC// }" ]]; then
echo "- Codex plugin spec: \`${CODEX_PLUGIN_SPEC}\`"
fi
echo "- Package Telegram E2E deferred: \`${SKIP_PACKAGE_TELEGRAM_E2E}\`"
} >> "$GITHUB_STEP_SUMMARY"
child_rerun_group="$RERUN_GROUP"
if [[ "$child_rerun_group" == "release-checks" ]]; then
@@ -872,6 +898,7 @@ jobs:
-f run_release_soak="$RUN_RELEASE_SOAK"
-f fail_fast="$FAIL_FAST"
-f allow_unreleased_changelog="$ALLOW_UNRELEASED_CHANGELOG"
-f skip_package_telegram_e2e="$SKIP_PACKAGE_TELEGRAM_E2E"
-f rerun_group="$child_rerun_group"
)
if [[ -n "${TARGET_CONTEXT_REF// }" ]]; then
@@ -1011,6 +1038,7 @@ jobs:
PACKAGE_ACCEPTANCE_PACKAGE_SPEC: ${{ inputs.package_acceptance_package_spec }}
CODEX_PLUGIN_SPEC: ${{ inputs.codex_plugin_spec }}
CANDIDATE_ARTIFACT_JSON: ${{ needs.prepare_release_candidate.outputs.candidate_artifact_json }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
run: *full_release_child_dispatch
npm_telegram:
@@ -1099,6 +1127,7 @@ jobs:
RELEASE_PACKAGE_SPEC: ${{ inputs.release_package_spec }}
NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
PACKAGE_ACCEPTANCE_PACKAGE_SPEC: ${{ inputs.package_acceptance_package_spec }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
EVIDENCE_REUSE: ${{ needs.evidence_reuse.outputs.reuse }}
EVIDENCE_ROOT_RUN_ID: ${{ needs.evidence_reuse.outputs.evidence_root_run_id }}
EVIDENCE_RUN_URL: ${{ needs.evidence_reuse.outputs.evidence_run_url }}
@@ -1374,6 +1403,7 @@ jobs:
if [[ "$RERUN_GROUP" == "npm-telegram" || ( "$RERUN_GROUP" == "all" && ( -n "${NPM_TELEGRAM_PACKAGE_SPEC// }" || -n "${RELEASE_PACKAGE_SPEC// }" ) ) ]]; then
npm_telegram_required=1
fi
echo "- Package Telegram E2E deferred: \`${SKIP_PACKAGE_TELEGRAM_E2E}\`" >> "$GITHUB_STEP_SUMMARY"
if [[ -z "${RELEASE_PACKAGE_SPEC// }" && -z "${PACKAGE_ACCEPTANCE_PACKAGE_SPEC// }" ]]; then
case "$RERUN_GROUP" in
all|plugin-prerelease|release-checks|cross-os|live-e2e|package)
@@ -1625,6 +1655,7 @@ jobs:
NPM_TELEGRAM_PACKAGE_SPEC: ${{ inputs.npm_telegram_package_spec }}
NPM_TELEGRAM_PROVIDER_MODE: ${{ inputs.npm_telegram_provider_mode }}
NPM_TELEGRAM_SCENARIO: ${{ inputs.npm_telegram_scenario }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ inputs.skip_package_telegram_e2e }}
ALLOW_UNRELEASED_CHANGELOG: ${{ inputs.allow_unreleased_changelog || (inputs.target_context_ref == '' && (inputs.ref == 'main' || inputs.ref == 'refs/heads/main')) }}
run: |
set -euo pipefail
@@ -1700,6 +1731,7 @@ jobs:
--arg npmTelegramPackageSpec "$NPM_TELEGRAM_PACKAGE_SPEC" \
--arg npmTelegramProviderMode "$NPM_TELEGRAM_PROVIDER_MODE" \
--arg npmTelegramScenario "$NPM_TELEGRAM_SCENARIO" \
--arg skipPackageTelegramE2e "$SKIP_PACKAGE_TELEGRAM_E2E" \
--arg allowUnreleasedChangelog "$ALLOW_UNRELEASED_CHANGELOG" \
'{
version: 3,
@@ -1727,6 +1759,7 @@ jobs:
npmTelegramPackageSpec: $npmTelegramPackageSpec,
npmTelegramProviderMode: $npmTelegramProviderMode,
npmTelegramScenario: $npmTelegramScenario,
skipPackageTelegramE2e: $skipPackageTelegramE2e,
allowUnreleasedChangelog: $allowUnreleasedChangelog
},
controls: {

View File

@@ -61,6 +61,11 @@ on:
required: false
default: false
type: boolean
skip_package_telegram_e2e:
description: Defer beta Package Acceptance Telegram E2E
required: false
default: false
type: boolean
allow_frozen_target_scenario_omissions:
description: Allow trusted compatibility filtering for a canonical frozen release target
required: false
@@ -141,6 +146,7 @@ jobs:
fail_fast: ${{ steps.inputs.outputs.fail_fast }}
run_maturity_scorecard: ${{ steps.inputs.outputs.run_maturity_scorecard }}
allow_unreleased_changelog: ${{ steps.inputs.outputs.allow_unreleased_changelog }}
skip_package_telegram_e2e: ${{ steps.inputs.outputs.skip_package_telegram_e2e }}
rerun_group: ${{ steps.inputs.outputs.rerun_group }}
live_suite_filter: ${{ steps.inputs.outputs.live_suite_filter }}
repo_live_suite_filter: ${{ steps.inputs.outputs.repo_live_suite_filter }}
@@ -324,6 +330,7 @@ jobs:
RELEASE_FAIL_FAST_INPUT: ${{ inputs.fail_fast }}
RELEASE_RUN_MATURITY_SCORECARD_INPUT: ${{ inputs.run_maturity_scorecard }}
RELEASE_ALLOW_UNRELEASED_CHANGELOG_INPUT: ${{ inputs.allow_unreleased_changelog }}
RELEASE_SKIP_PACKAGE_TELEGRAM_E2E_INPUT: ${{ inputs.skip_package_telegram_e2e }}
RELEASE_RERUN_GROUP_INPUT: ${{ inputs.rerun_group }}
RELEASE_LIVE_SUITE_FILTER_INPUT: ${{ inputs.live_suite_filter }}
RELEASE_CROSS_OS_SUITE_FILTER_INPUT: ${{ inputs.cross_os_suite_filter }}
@@ -408,6 +415,16 @@ jobs:
else
allow_unreleased_changelog=false
fi
skip_package_telegram_e2e="$(printf '%s' "$RELEASE_SKIP_PACKAGE_TELEGRAM_E2E_INPUT" | tr '[:upper:]' '[:lower:]')"
if [[ "$skip_package_telegram_e2e" != "true" && "$skip_package_telegram_e2e" != "1" && "$skip_package_telegram_e2e" != "yes" ]]; then
skip_package_telegram_e2e=false
else
skip_package_telegram_e2e=true
fi
if [[ "$skip_package_telegram_e2e" == "true" && "$release_profile" != "beta" ]]; then
echo "skip_package_telegram_e2e is allowed only for release_profile=beta." >&2
exit 1
fi
codex_plugin_spec="$RELEASE_CODEX_PLUGIN_SPEC_INPUT"
if [[ -z "${codex_plugin_spec// }" && "$RELEASE_PACKAGE_SPEC_INPUT" =~ ^openclaw@(.+)$ ]]; then
codex_plugin_spec="npm:@openclaw/codex@${BASH_REMATCH[1]}"
@@ -528,6 +545,7 @@ jobs:
printf 'fail_fast=%s\n' "$fail_fast"
printf 'run_maturity_scorecard=%s\n' "$run_maturity_scorecard"
printf 'allow_unreleased_changelog=%s\n' "$allow_unreleased_changelog"
printf 'skip_package_telegram_e2e=%s\n' "$skip_package_telegram_e2e"
printf 'rerun_group=%s\n' "$RELEASE_RERUN_GROUP_INPUT"
printf 'live_suite_filter=%s\n' "$RELEASE_LIVE_SUITE_FILTER_INPUT"
printf 'repo_live_suite_filter=%s\n' "$repo_live_suite_filter"
@@ -556,6 +574,7 @@ jobs:
RUN_RELEASE_SOAK: ${{ steps.inputs.outputs.run_release_soak }}
FAIL_FAST: ${{ steps.inputs.outputs.fail_fast }}
RUN_MATURITY_SCORECARD: ${{ steps.inputs.outputs.run_maturity_scorecard }}
SKIP_PACKAGE_TELEGRAM_E2E: ${{ steps.inputs.outputs.skip_package_telegram_e2e }}
ALLOW_UNRELEASED_CHANGELOG: ${{ steps.inputs.outputs.allow_unreleased_changelog }}
RELEASE_RERUN_GROUP: ${{ inputs.rerun_group }}
RELEASE_LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
@@ -576,6 +595,7 @@ jobs:
echo "- Release soak lanes: \`${RUN_RELEASE_SOAK}\`"
echo "- Matrix QA fail fast: \`${FAIL_FAST}\`"
echo "- Maturity scorecard docs: \`${RUN_MATURITY_SCORECARD}\`"
echo "- Package Acceptance Telegram E2E deferred: \`${SKIP_PACKAGE_TELEGRAM_E2E}\`"
echo "- Allow Unreleased changelog packaging: \`${ALLOW_UNRELEASED_CHANGELOG}\`"
echo "- Rerun group: \`${RELEASE_RERUN_GROUP}\`"
if [[ -n "${RELEASE_LIVE_SUITE_FILTER// }" ]]; then
@@ -1117,7 +1137,7 @@ jobs:
docker_lanes: release-typed-onboarding doctor-switch update-channel-switch skill-install update-corrupt-plugin upgrade-survivor published-upgrade-survivor root-managed-vps-upgrade update-restart-auth plugins-offline plugin-update plugin-binding-command-escape
published_upgrade_survivor_baselines: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'last-stable-4 2026.4.23 2026.5.2 2026.4.15' || '' }}
published_upgrade_survivor_scenarios: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'reported-issues' || '' }}
telegram_mode: mock-openai
telegram_mode: ${{ needs.resolve_target.outputs.skip_package_telegram_e2e == 'true' && 'none' || 'mock-openai' }}
telegram_advisory: ${{ needs.resolve_target.outputs.release_profile == 'beta' }}
shared_image_artifact_namespace: release-package
candidate_artifact_json: ${{ inputs.candidate_artifact_json }}

View File

@@ -89,6 +89,11 @@ Codex `final`, reads randomized workspace inputs, writes their exact artifact,
and sends explicit completion. This catches the v2026.7.1 regression where an
ordinary progress send terminated the turn.
Use `-f skip_package_telegram_e2e=true` only when the release owner explicitly
defers the Package Acceptance Telegram E2E to a follow-up beta. The input is
rejected for `stable` and `full`, recorded in validation evidence, and does not disable the focused
`rerun_group=npm-telegram` workflow.
## Top-level stages
For `rerun_group=all`, a `Check for reusable validation evidence` job runs

View File

@@ -417,6 +417,7 @@ run_logged_print_heartbeat "npm-telegram-live-suite" 60 docker_e2e_run_with_harn
-v "$ROOT_DIR/dist:/app/.openclaw-qa-harness-dist:ro" \
-v "$ROOT_DIR/extensions/qa-lab:/app/extensions/qa-lab:ro" \
-v "$ROOT_DIR/qa/scenarios:/app/qa/scenarios:ro" \
-v "$ROOT_DIR/taxonomy.yaml:/app/taxonomy.yaml:ro" \
-v "$npm_prefix_host:/npm-global" \
-i "$IMAGE_NAME" bash -s <<'EOF'
set -euo pipefail

View File

@@ -29,6 +29,7 @@ const DEFAULT_INPUTS = {
npmTelegramPackageSpec: "",
npmTelegramProviderMode: "mock-openai",
npmTelegramScenario: "",
skipPackageTelegramE2e: "false",
allowUnreleasedChangelog: "false",
};
@@ -906,6 +907,14 @@ describe("scripts/github/find-reusable-release-validation.sh", () => {
},
resolverOptions: {},
},
{
expected: "validation inputs differ",
label: "different package Telegram deferral",
recordOptions: {
validationInputs: { ...DEFAULT_INPUTS, skipPackageTelegramE2e: "true" },
},
resolverOptions: {},
},
{
expected: "validation inputs differ",
label: "different unreleased changelog policy",

View File

@@ -228,6 +228,13 @@ describe("package Telegram live Docker E2E", () => {
});
});
it("mounts the QA taxonomy without exposing the repository root", () => {
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
expect(script).toContain('-v "$ROOT_DIR/taxonomy.yaml:/app/taxonomy.yaml:ro"');
expect(script).not.toContain('-v "$ROOT_DIR:/app');
});
it("mounts configured output paths before entering the container", () => {
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
const dockerEnvStart = script.indexOf("docker_env=(");

View File

@@ -165,6 +165,9 @@ type Workflow = {
workflow_call?: {
inputs?: Record<string, unknown>;
};
workflow_dispatch?: {
inputs?: Record<string, unknown>;
};
};
};
@@ -194,6 +197,78 @@ function workflowStep(job: WorkflowJob, stepName: string): WorkflowStep {
return step;
}
function runFullReleaseInputValidation(releaseProfile: string, skipTelegram: string) {
const step = workflowStep(
workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "resolve_target"),
"Validate release inputs",
);
return spawnSync("bash", ["-c", step.run ?? ""], {
encoding: "utf8",
env: {
PATH: process.env.PATH,
RELEASE_PROFILE: releaseProfile,
SKIP_PACKAGE_TELEGRAM_E2E: skipTelegram,
},
});
}
function runReleaseChecksInputValidation(releaseProfile: string, skipTelegram: string) {
const step = workflowStep(
workflowJob(RELEASE_CHECKS_WORKFLOW, "resolve_target"),
"Capture selected inputs",
);
const workdir = tempDirs.make("release-checks-input-validation-");
const outputPath = resolve(workdir, "github-output");
const stepEnv = Object.fromEntries(Object.keys(step.env ?? {}).map((name) => [name, ""]));
const result = spawnSync("bash", ["-c", step.run ?? ""], {
encoding: "utf8",
env: {
...stepEnv,
CANDIDATE_ARTIFACT_JSON_INPUT: "",
GITHUB_OUTPUT: outputPath,
PATH: process.env.PATH,
RELEASE_FAIL_FAST_INPUT: "false",
RELEASE_MODE_INPUT: "both",
RELEASE_PROFILE_INPUT: releaseProfile,
RELEASE_PROVIDER_INPUT: "openai",
RELEASE_QA_DISCORD_LIVE_CI_ENABLED: "false",
RELEASE_QA_SLACK_LIVE_CI_ENABLED: "false",
RELEASE_QA_WHATSAPP_LIVE_CI_ENABLED: "false",
RELEASE_REF_INPUT: "main",
RELEASE_RERUN_GROUP_INPUT: "all",
RELEASE_RUN_MATURITY_SCORECARD_INPUT: "false",
RELEASE_RUN_RELEASE_SOAK_INPUT: "false",
RELEASE_SKIP_PACKAGE_TELEGRAM_E2E_INPUT: skipTelegram,
},
});
return { outputPath, result };
}
function runFullReleaseTargetSummary(rerunGroup: string, skipTelegram: string) {
const step = workflowStep(
workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "resolve_target"),
"Summarize target",
);
const workdir = tempDirs.make("full-release-target-summary-");
const summaryPath = resolve(workdir, "github-summary");
const stepEnv = Object.fromEntries(Object.keys(step.env ?? {}).map((name) => [name, ""]));
const result = spawnSync("bash", ["-c", step.run ?? ""], {
encoding: "utf8",
env: {
...stepEnv,
GITHUB_STEP_SUMMARY: summaryPath,
PATH: process.env.PATH,
RELEASE_PROFILE: "beta",
RERUN_GROUP: rerunGroup,
SKIP_PACKAGE_TELEGRAM_E2E: skipTelegram,
TARGET_REF: "main",
TARGET_SHA: "a".repeat(40),
},
});
const summary = result.status === 0 ? readFileSync(summaryPath, "utf8") : "";
return { result, summary };
}
function shellFunctionSource(source: string, functionName: string): string {
const startMarker = `${functionName}() {`;
const endMarker = "\n}\n";
@@ -368,6 +443,7 @@ if (args[0] === "workflow" && args[1] === "run") {
RERUN_GROUP: "all",
RUN_RELEASE_SOAK: "false",
SCENARIO: "",
SKIP_PACKAGE_TELEGRAM_E2E: "false",
TARGET_CONTEXT_REF: "",
TARGET_REF: "main",
TARGET_SHA: "b".repeat(40),
@@ -3292,6 +3368,66 @@ describe("package artifact reuse", () => {
expect(workflow).toContain("run_package_telegram_e2e:");
});
it.each(["stable", "full"])(
"rejects Package Acceptance Telegram deferral for direct %s release checks",
(releaseProfile) => {
const { result } = runReleaseChecksInputValidation(releaseProfile, "true");
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"skip_package_telegram_e2e is allowed only for release_profile=beta.",
);
},
);
it.each(["stable", "full"])(
"rejects Package Acceptance Telegram deferral for umbrella %s validation",
(releaseProfile) => {
const result = runFullReleaseInputValidation(releaseProfile, "true");
expect(result.status).toBe(1);
expect(result.stderr).toContain(
"skip_package_telegram_e2e is allowed only for release_profile=beta.",
);
},
);
it("allows Package Acceptance Telegram deferral only for beta validation", () => {
const direct = runReleaseChecksInputValidation("beta", "true");
const umbrella = runFullReleaseInputValidation("beta", "true");
expect(direct.result.status, direct.result.stderr).toBe(0);
expect(readFileSync(direct.outputPath, "utf8")).toContain("skip_package_telegram_e2e=true");
expect(umbrella.status, umbrella.stderr).toBe(0);
});
it.each(["stable", "full"])(
"preserves normal %s validation when Telegram deferral is false",
(releaseProfile) => {
const direct = runReleaseChecksInputValidation(releaseProfile, "false");
const umbrella = runFullReleaseInputValidation(releaseProfile, "false");
expect(direct.result.status, direct.result.stderr).toBe(0);
expect(readFileSync(direct.outputPath, "utf8")).toContain("skip_package_telegram_e2e=false");
expect(umbrella.status, umbrella.stderr).toBe(0);
},
);
it("summarizes Telegram deferral only when Package Acceptance is scheduled", () => {
const scheduled = runFullReleaseTargetSummary("release-checks", "true");
const unrelated = runFullReleaseTargetSummary("ci", "true");
expect(scheduled.result.status, scheduled.result.stderr).toBe(0);
expect(scheduled.summary).toContain(
"Package Telegram E2E: deferred by `skip_package_telegram_e2e`",
);
expect(unrelated.result.status, unrelated.result.stderr).toBe(0);
expect(unrelated.summary).toContain("Package Telegram E2E: skipped by rerun group");
expect(unrelated.summary).not.toContain(
"Package Telegram E2E: deferred by `skip_package_telegram_e2e`",
);
});
it("includes package acceptance in release checks", () => {
const workflow = readFileSync(RELEASE_CHECKS_WORKFLOW, "utf8");
const packageAcceptanceWorkflow = parse(readFileSync(PACKAGE_ACCEPTANCE_WORKFLOW, "utf8")) as {
@@ -3303,6 +3439,10 @@ describe("package artifact reuse", () => {
RELEASE_CHECKS_WORKFLOW,
"package_acceptance_release_checks",
);
const releaseChecksTargetSummary = workflowStep(
workflowJob(RELEASE_CHECKS_WORKFLOW, "resolve_target"),
"Summarize validated ref",
);
const dockerAcceptanceJob = workflowJob(PACKAGE_ACCEPTANCE_WORKFLOW, "docker_acceptance");
expect(workflow).toContain("package_acceptance_release_checks:");
@@ -3324,6 +3464,10 @@ describe("package artifact reuse", () => {
package_version: "${{ needs.prepare_release_package.outputs.package_version }}",
suite_profile: "custom",
});
expect(releaseChecksTargetSummary.env).toMatchObject({
SKIP_PACKAGE_TELEGRAM_E2E: "${{ steps.inputs.outputs.skip_package_telegram_e2e }}",
});
expect(releaseChecksTargetSummary.run).toContain("Package Acceptance Telegram E2E deferred:");
expect(dockerAcceptanceJob.with).toMatchObject({
allow_frozen_target_scenario_omissions:
"${{ inputs.allow_frozen_target_scenario_omissions || false }}",
@@ -3381,7 +3525,16 @@ describe("package artifact reuse", () => {
expect(workflow).toContain(
"published_upgrade_survivor_scenarios: ${{ needs.resolve_target.outputs.run_release_soak == 'true' && 'reported-issues' || '' }}",
);
expect(workflow).toContain("telegram_mode: mock-openai");
expect(readWorkflow(RELEASE_CHECKS_WORKFLOW).on?.workflow_dispatch?.inputs).toMatchObject({
skip_package_telegram_e2e: {
default: false,
type: "boolean",
},
});
expect(packageAcceptanceJob.with).toMatchObject({
telegram_mode:
"${{ needs.resolve_target.outputs.skip_package_telegram_e2e == 'true' && 'none' || 'mock-openai' }}",
});
expect(packageAcceptanceJob.with).toMatchObject({
telegram_advisory: "${{ needs.resolve_target.outputs.release_profile == 'beta' }}",
});
@@ -4007,14 +4160,30 @@ describe("package artifact reuse", () => {
it("runs full release children from the trusted workflow ref", () => {
const workflow = readFileSync(FULL_RELEASE_VALIDATION_WORKFLOW, "utf8");
const workflowInputs = readWorkflow(FULL_RELEASE_VALIDATION_WORKFLOW).on?.workflow_dispatch
?.inputs;
const resolveTargetJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "resolve_target");
const evidenceReuseJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "evidence_reuse");
const releaseChecksJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "release_checks");
const npmTelegramJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "npm_telegram");
const performanceJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "performance");
const summaryJob = workflowJob(FULL_RELEASE_VALIDATION_WORKFLOW, "summary");
const targetSummaryStep = workflowStep(resolveTargetJob, "Summarize target");
const evidenceReuseStep = workflowStep(evidenceReuseJob, "Find reusable validation evidence");
const releaseChecksDispatchStep = workflowStep(
releaseChecksJob,
"Dispatch and monitor release checks",
);
const dispatchStep = workflowStep(npmTelegramJob, "Dispatch and monitor npm Telegram E2E");
const verificationStep = workflowStep(summaryJob, "Verify child workflow results");
const manifestStep = workflowStep(summaryJob, "Write release validation manifest");
expect(workflowInputs).toMatchObject({
skip_package_telegram_e2e: {
default: false,
type: "boolean",
},
});
expect(workflow).toContain("CHILD_WORKFLOW_REF: ${{ github.ref_name }}");
expect(workflow).toContain('gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@" 2>&1');
expect(npmTelegramJob.name).toBe("Run package Telegram E2E");
@@ -4035,13 +4204,28 @@ describe("package artifact reuse", () => {
NPM_TELEGRAM_PACKAGE_SPEC: "${{ inputs.npm_telegram_package_spec }}",
NPM_TELEGRAM_PROVIDER_MODE: "${{ inputs.npm_telegram_provider_mode }}",
NPM_TELEGRAM_SCENARIO: "${{ inputs.npm_telegram_scenario }}",
SKIP_PACKAGE_TELEGRAM_E2E: "${{ inputs.skip_package_telegram_e2e }}",
});
expectTextToIncludeAll(evidenceReuseStep.run, [
"npmTelegramPackageSpec: $npmTelegramPackageSpec",
"npmTelegramProviderMode: $npmTelegramProviderMode",
"npmTelegramScenario: $npmTelegramScenario",
"skipPackageTelegramE2e: $skipPackageTelegramE2e",
"allowUnreleasedChangelog: $allowUnreleasedChangelog",
]);
expect(targetSummaryStep.env).toMatchObject({
SKIP_PACKAGE_TELEGRAM_E2E: "${{ inputs.skip_package_telegram_e2e }}",
});
expectTextToIncludeAll(targetSummaryStep.run, [
"Package Acceptance Telegram E2E deferred:",
"Package Telegram E2E: deferred by \\`skip_package_telegram_e2e\\`",
]);
expect(releaseChecksDispatchStep.env).toMatchObject({
SKIP_PACKAGE_TELEGRAM_E2E: "${{ inputs.skip_package_telegram_e2e }}",
});
expect(releaseChecksDispatchStep.run).toContain(
'-f skip_package_telegram_e2e="$SKIP_PACKAGE_TELEGRAM_E2E"',
);
expect(dispatchStep.env).toEqual({
CHILD_WORKFLOW_KIND: "npm-telegram",
CHILD_WORKFLOW_REF: "${{ github.ref_name }}",
@@ -4059,13 +4243,19 @@ describe("package artifact reuse", () => {
NPM_TELEGRAM_PACKAGE_SPEC: "${{ inputs.npm_telegram_package_spec }}",
NPM_TELEGRAM_PROVIDER_MODE: "${{ inputs.npm_telegram_provider_mode }}",
NPM_TELEGRAM_SCENARIO: "${{ inputs.npm_telegram_scenario }}",
SKIP_PACKAGE_TELEGRAM_E2E: "${{ inputs.skip_package_telegram_e2e }}",
});
expectTextToIncludeAll(manifestStep.run, [
"npmTelegramPackageSpec: $npmTelegramPackageSpec",
"npmTelegramProviderMode: $npmTelegramProviderMode",
"npmTelegramScenario: $npmTelegramScenario",
"skipPackageTelegramE2e: $skipPackageTelegramE2e",
"allowUnreleasedChangelog: $allowUnreleasedChangelog",
]);
expect(verificationStep.env).toMatchObject({
SKIP_PACKAGE_TELEGRAM_E2E: "${{ inputs.skip_package_telegram_e2e }}",
});
expect(verificationStep.run).toContain("Package Telegram E2E deferred:");
expectTextToIncludeAll(dispatchStep.run, [
'dispatch_id="full-release-validation-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-npm-telegram"',
'dispatch_output="$(gh workflow run "$workflow" --ref "$CHILD_WORKFLOW_REF" "$@" 2>&1)"',
@@ -4118,6 +4308,7 @@ describe("package artifact reuse", () => {
expectTextToIncludeAll(workflow, [
"Published-package Telegram E2E:",
"Package Telegram E2E: deferred by \\`skip_package_telegram_e2e\\`",
"Package Telegram E2E: OpenClaw Release Checks Package Acceptance",
"Package Telegram E2E: focused rerun requires \\`release_package_spec\\` or \\`npm_telegram_package_spec\\`",
]);

View File

@@ -247,6 +247,7 @@ function rawManifest({
packageAcceptancePackageSpec: "",
provider: "openai",
releasePackageSpec: "",
skipPackageTelegramE2e: "false",
targetContextRef: "",
},
version,
@@ -1316,6 +1317,7 @@ describe("release CI summary child correlation", () => {
const raw = rawManifest({});
raw.childRuns.npmTelegram = "505";
raw.validationInputs.npmTelegramPackageSpec = "openclaw@beta";
raw.validationInputs.skipPackageTelegramE2e = "true";
const manifest = validateParentManifest(raw, {
runAttempt: 2,
runId: "29090000000",