mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-16 12:25:44 +00:00
fix(ci): trust frozen plugin prerelease fixtures (#124446)
* fix(ci): trust frozen plugin prerelease fixtures * fix(ci): apply scoped plugin test omissions
This commit is contained in:
parent
7d05f7c5b9
commit
255ca8d96b
4 changed files with 331 additions and 9 deletions
25
.github/workflows/full-release-validation.yml
vendored
25
.github/workflows/full-release-validation.yml
vendored
|
|
@ -84,6 +84,11 @@ on:
|
|||
- qa-live
|
||||
- npm-telegram
|
||||
- performance
|
||||
plugin_prerelease_node_exclude_patterns_json:
|
||||
description: Exact Plugin Prerelease Node test paths omitted only for frozen-target validation
|
||||
required: false
|
||||
default: "[]"
|
||||
type: string
|
||||
reuse_evidence:
|
||||
description: Reuse matching green product validation for the same target or a changelog-only Release SHA
|
||||
required: false
|
||||
|
|
@ -216,7 +221,9 @@ jobs:
|
|||
RERUN_GROUP: ${{ inputs.rerun_group }}
|
||||
LIVE_SUITE_FILTER: ${{ inputs.live_suite_filter }}
|
||||
CROSS_OS_SUITE_FILTER: ${{ inputs.cross_os_suite_filter }}
|
||||
PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON: ${{ inputs.plugin_prerelease_node_exclude_patterns_json }}
|
||||
run: |
|
||||
plugin_prerelease_node_exclusions="$(jq -c . <<< "$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON")"
|
||||
{
|
||||
echo "## Full release validation"
|
||||
echo
|
||||
|
|
@ -246,6 +253,7 @@ jobs:
|
|||
fi
|
||||
if [[ "$RERUN_GROUP" == "all" || "$RERUN_GROUP" == "plugin-prerelease" ]]; then
|
||||
echo "- Plugin prerelease: \`Plugin Prerelease\` with \`target_ref=${TARGET_SHA}\`"
|
||||
echo "- Plugin prerelease Node exclusions: \`${plugin_prerelease_node_exclusions}\`"
|
||||
else
|
||||
echo "- Plugin prerelease: skipped by rerun group"
|
||||
fi
|
||||
|
|
@ -342,6 +350,7 @@ jobs:
|
|||
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')) }}
|
||||
PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON: ${{ inputs.plugin_prerelease_node_exclude_patterns_json }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
# Lane-selection inputs must match the prior run's manifest exactly;
|
||||
|
|
@ -360,6 +369,7 @@ jobs:
|
|||
--arg npmTelegramScenario "$NPM_TELEGRAM_SCENARIO" \
|
||||
--arg skipPackageTelegramE2e "$SKIP_PACKAGE_TELEGRAM_E2E" \
|
||||
--arg allowUnreleasedChangelog "$ALLOW_UNRELEASED_CHANGELOG" \
|
||||
--arg pluginPrereleaseNodeExcludePatternsJson "$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON" \
|
||||
'{
|
||||
provider: $provider,
|
||||
mode: $mode,
|
||||
|
|
@ -373,7 +383,8 @@ jobs:
|
|||
npmTelegramProviderMode: $npmTelegramProviderMode,
|
||||
npmTelegramScenario: $npmTelegramScenario,
|
||||
skipPackageTelegramE2e: $skipPackageTelegramE2e,
|
||||
allowUnreleasedChangelog: $allowUnreleasedChangelog
|
||||
allowUnreleasedChangelog: $allowUnreleasedChangelog,
|
||||
pluginPrereleaseNodeExcludePatternsJson: $pluginPrereleaseNodeExcludePatternsJson
|
||||
}')"
|
||||
bash workflow/scripts/github/find-reusable-release-validation.sh \
|
||||
--target-sha "$TARGET_SHA" \
|
||||
|
|
@ -848,15 +859,19 @@ jobs:
|
|||
dispatch_and_wait ci.yml "$dispatch_run_name" "${args[@]}"
|
||||
;;
|
||||
plugin-prerelease)
|
||||
plugin_prerelease_node_exclusions="$(
|
||||
jq -c . <<< "$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON"
|
||||
)"
|
||||
{
|
||||
echo "### Plugin prerelease"
|
||||
echo
|
||||
echo "- Target ref: \`${TARGET_REF}\`"
|
||||
echo "- Target SHA: \`${TARGET_SHA}\`"
|
||||
echo "- Frozen-target Node test omissions: \`${plugin_prerelease_node_exclusions}\`"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
dispatch_id="full-release-validation-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-plugin-prerelease"
|
||||
dispatch_run_name="Plugin Prerelease ${dispatch_id}"
|
||||
args=(-f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA" -f full_release_validation=true -f dispatch_id="$dispatch_id")
|
||||
args=(-f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA" -f full_release_validation=true -f dispatch_id="$dispatch_id" -f node_test_exclude_patterns_json="$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON")
|
||||
if [[ -n "${CANDIDATE_ARTIFACT_JSON// }" ]]; then
|
||||
args+=(-f candidate_artifact_json="$CANDIDATE_ARTIFACT_JSON")
|
||||
fi
|
||||
|
|
@ -1006,6 +1021,7 @@ jobs:
|
|||
CHILD_WORKFLOW_REF: ${{ github.ref_name }}
|
||||
PARENT_WORKFLOW_SHA: ${{ github.sha }}
|
||||
CANDIDATE_ARTIFACT_JSON: ${{ needs.prepare_release_candidate.outputs.candidate_artifact_json }}
|
||||
PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON: ${{ inputs.plugin_prerelease_node_exclude_patterns_json }}
|
||||
FAIL_FAST: ${{ inputs.fail_fast }}
|
||||
run: *full_release_child_dispatch
|
||||
|
||||
|
|
@ -1667,6 +1683,7 @@ jobs:
|
|||
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')) }}
|
||||
PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON: ${{ inputs.plugin_prerelease_node_exclude_patterns_json }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
manifest_dir="${RUNNER_TEMP}/full-release-validation"
|
||||
|
|
@ -1743,6 +1760,7 @@ jobs:
|
|||
--arg npmTelegramScenario "$NPM_TELEGRAM_SCENARIO" \
|
||||
--arg skipPackageTelegramE2e "$SKIP_PACKAGE_TELEGRAM_E2E" \
|
||||
--arg allowUnreleasedChangelog "$ALLOW_UNRELEASED_CHANGELOG" \
|
||||
--arg pluginPrereleaseNodeExcludePatternsJson "$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON" \
|
||||
'{
|
||||
version: 3,
|
||||
workflowName: $workflowName,
|
||||
|
|
@ -1770,7 +1788,8 @@ jobs:
|
|||
npmTelegramProviderMode: $npmTelegramProviderMode,
|
||||
npmTelegramScenario: $npmTelegramScenario,
|
||||
skipPackageTelegramE2e: $skipPackageTelegramE2e,
|
||||
allowUnreleasedChangelog: $allowUnreleasedChangelog
|
||||
allowUnreleasedChangelog: $allowUnreleasedChangelog,
|
||||
pluginPrereleaseNodeExcludePatternsJson: $pluginPrereleaseNodeExcludePatternsJson
|
||||
},
|
||||
controls: {
|
||||
stableSoakRequired: ($releaseProfile == "stable" or $releaseProfile == "full"),
|
||||
|
|
|
|||
109
.github/workflows/plugin-prerelease.yml
vendored
109
.github/workflows/plugin-prerelease.yml
vendored
|
|
@ -20,6 +20,11 @@ on:
|
|||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
node_test_exclude_patterns_json:
|
||||
description: Full Release Validation-only exact plugin test paths omitted for a frozen target
|
||||
required: false
|
||||
default: "[]"
|
||||
type: string
|
||||
dispatch_id:
|
||||
description: Optional parent workflow dispatch identifier
|
||||
required: false
|
||||
|
|
@ -53,11 +58,73 @@ jobs:
|
|||
plugin_prerelease_static_matrix: ${{ steps.manifest.outputs.plugin_prerelease_static_matrix }}
|
||||
run_plugin_prerelease_node: ${{ steps.manifest.outputs.run_plugin_prerelease_node }}
|
||||
plugin_prerelease_node_matrix: ${{ steps.manifest.outputs.plugin_prerelease_node_matrix }}
|
||||
node_test_exclude_patterns_json: ${{ steps.node_test_exclusions.outputs.patterns_json }}
|
||||
run_plugin_prerelease_extensions: ${{ steps.manifest.outputs.run_plugin_prerelease_extensions }}
|
||||
plugin_prerelease_extension_matrix: ${{ steps.manifest.outputs.plugin_prerelease_extension_matrix }}
|
||||
run_plugin_prerelease_docker: ${{ steps.manifest.outputs.run_plugin_prerelease_docker }}
|
||||
plugin_prerelease_docker_lanes: ${{ steps.manifest.outputs.plugin_prerelease_docker_lanes }}
|
||||
steps:
|
||||
- name: Validate frozen-target Node exclusions
|
||||
id: node_test_exclusions
|
||||
env:
|
||||
FULL_RELEASE_VALIDATION: ${{ inputs.full_release_validation && 'true' || 'false' }}
|
||||
NODE_TEST_EXCLUDE_PATTERNS_JSON: ${{ inputs.node_test_exclude_patterns_json }}
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node --input-type=module <<'EOF'
|
||||
import { appendFileSync } from "node:fs";
|
||||
|
||||
const rawPatterns = process.env.NODE_TEST_EXCLUDE_PATTERNS_JSON ?? "";
|
||||
let patterns;
|
||||
try {
|
||||
patterns = JSON.parse(rawPatterns);
|
||||
} catch (error) {
|
||||
console.error(`node_test_exclude_patterns_json must be valid JSON: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!Array.isArray(patterns)) {
|
||||
console.error("node_test_exclude_patterns_json must be a JSON array");
|
||||
process.exit(1);
|
||||
}
|
||||
const invalidPattern = patterns.find((pattern) => {
|
||||
if (typeof pattern !== "string" || pattern.length === 0 || pattern !== pattern.trim()) {
|
||||
return true;
|
||||
}
|
||||
const segments = pattern.split("/");
|
||||
return (
|
||||
!pattern.startsWith("src/plugins/") ||
|
||||
!pattern.endsWith(".test.ts") ||
|
||||
pattern.includes("\\") ||
|
||||
pattern.includes("..") ||
|
||||
segments.some((segment) => segment.length === 0 || segment === ".") ||
|
||||
!/^[A-Za-z0-9._/-]+$/u.test(pattern)
|
||||
);
|
||||
});
|
||||
if (invalidPattern !== undefined) {
|
||||
console.error(
|
||||
`Invalid frozen-target Node test omission: ${JSON.stringify(invalidPattern)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (new Set(patterns).size !== patterns.length) {
|
||||
console.error("node_test_exclude_patterns_json must not contain duplicate paths");
|
||||
process.exit(1);
|
||||
}
|
||||
const fullReleaseValidation = process.env.FULL_RELEASE_VALIDATION === "true";
|
||||
if (!fullReleaseValidation && rawPatterns !== "[]") {
|
||||
console.error(
|
||||
"node_test_exclude_patterns_json may differ from [] only when full_release_validation=true",
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
appendFileSync(
|
||||
process.env.GITHUB_OUTPUT,
|
||||
`patterns_json=${JSON.stringify(patterns)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
EOF
|
||||
|
||||
- name: Checkout target
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
|
|
@ -305,6 +372,32 @@ jobs:
|
|||
persist-credentials: false
|
||||
submodules: false
|
||||
|
||||
- name: Checkout trusted npm security inventory
|
||||
if: inputs.full_release_validation
|
||||
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
path: .plugin-prerelease-trusted
|
||||
fetch-depth: 1
|
||||
fetch-tags: false
|
||||
persist-credentials: false
|
||||
sparse-checkout: src/plugins/npm-install-security-scan.release.test.ts
|
||||
sparse-checkout-cone-mode: false
|
||||
submodules: false
|
||||
|
||||
- name: Install trusted npm security inventory
|
||||
if: inputs.full_release_validation
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
trusted_checkout=.plugin-prerelease-trusted
|
||||
trap 'rm -rf -- "$trusted_checkout"' EXIT
|
||||
install -m 0644 \
|
||||
"$trusted_checkout/src/plugins/npm-install-security-scan.release.test.ts" \
|
||||
src/plugins/npm-install-security-scan.release.test.ts
|
||||
rm -rf -- "$trusted_checkout"
|
||||
trap - EXIT
|
||||
|
||||
- name: Setup Node environment
|
||||
uses: ./.github/actions/setup-node-env
|
||||
with:
|
||||
|
|
@ -316,6 +409,7 @@ jobs:
|
|||
- name: Run release-only plugin Node shard
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=8192
|
||||
NODE_TEST_EXCLUDE_PATTERNS_JSON: ${{ needs.preflight.outputs.node_test_exclude_patterns_json }}
|
||||
OPENCLAW_NODE_TEST_CONFIGS_JSON: ${{ toJson(matrix.configs) }}
|
||||
OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON: ${{ toJson(matrix.includePatterns) }}
|
||||
OPENCLAW_VITEST_SHARD_NAME: ${{ matrix.shard_name }}
|
||||
|
|
@ -336,6 +430,16 @@ jobs:
|
|||
const includePatterns = JSON.parse(
|
||||
process.env.OPENCLAW_NODE_TEST_INCLUDE_PATTERNS_JSON ?? "null",
|
||||
);
|
||||
const excludePatterns = JSON.parse(
|
||||
process.env.NODE_TEST_EXCLUDE_PATTERNS_JSON ?? "[]",
|
||||
);
|
||||
if (
|
||||
!Array.isArray(excludePatterns) ||
|
||||
excludePatterns.some((pattern) => typeof pattern !== "string")
|
||||
) {
|
||||
console.error("Invalid frozen-target Node exclusions");
|
||||
process.exit(1);
|
||||
}
|
||||
const childEnv = { ...process.env };
|
||||
if (Array.isArray(includePatterns) && includePatterns.length > 0) {
|
||||
const includeFile = join(
|
||||
|
|
@ -346,7 +450,10 @@ jobs:
|
|||
childEnv.OPENCLAW_VITEST_INCLUDE_FILE = includeFile;
|
||||
}
|
||||
|
||||
const result = spawnSync("pnpm", ["test", "--", ...configs], {
|
||||
const excludeArgs = excludePatterns.map(
|
||||
(pattern) => `--exclude=${pattern.slice("src/plugins/".length)}`,
|
||||
);
|
||||
const result = spawnSync("pnpm", ["test", "--", ...configs, "--", ...excludeArgs], {
|
||||
env: childEnv,
|
||||
stdio: "inherit",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -525,6 +525,7 @@ exit 0
|
|||
PACKAGE_ACCEPTANCE_PACKAGE_SPEC: "",
|
||||
PACKAGE_SPEC: "openclaw@beta",
|
||||
PARENT_WORKFLOW_SHA: parentSha,
|
||||
PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON: "[]",
|
||||
PROVIDER: "openai",
|
||||
PROVIDER_MODE: "mock-openai",
|
||||
RELEASE_PACKAGE_SPEC: "",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// Plugin Prerelease Test Plan tests cover plugin prerelease test plan script behavior.
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { execFileSync, spawnSync } from "node:child_process";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
import { findLaneByName } from "../../scripts/lib/docker-e2e-plan.mts";
|
||||
import { BUNDLED_PLUGIN_INSTALL_UNINSTALL_SHARDS } from "../../scripts/lib/docker-e2e-scenarios.mts";
|
||||
|
|
@ -14,9 +15,11 @@ import {
|
|||
pluginPrereleaseTimeoutComponents,
|
||||
releaseTimeoutForProfile,
|
||||
} from "../helpers/release-workflow-timeouts.js";
|
||||
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
|
||||
|
||||
const CHECKOUT_V6 = "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10";
|
||||
const UPLOAD_ARTIFACT_V7 = "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a";
|
||||
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
||||
|
||||
type WorkflowStep = {
|
||||
env?: Record<string, string>;
|
||||
|
|
@ -63,6 +66,35 @@ function getDockerLane(name: string) {
|
|||
return lane;
|
||||
}
|
||||
|
||||
function runFrozenTargetNodeExclusionValidation(params: {
|
||||
fullReleaseValidation: boolean;
|
||||
patternsJson: string;
|
||||
}) {
|
||||
const workflow = readPluginPrereleaseWorkflow();
|
||||
const validationStep = workflow.jobs.preflight.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Validate frozen-target Node exclusions",
|
||||
);
|
||||
if (!validationStep?.run) {
|
||||
throw new Error("Missing frozen-target Node exclusion validation step");
|
||||
}
|
||||
|
||||
const root = tempDirs.make("openclaw-plugin-prerelease-excludes-");
|
||||
const outputPath = join(root, "github-output");
|
||||
const result = spawnSync("bash", ["-c", validationStep.run], {
|
||||
encoding: "utf8",
|
||||
env: {
|
||||
FULL_RELEASE_VALIDATION: String(params.fullReleaseValidation),
|
||||
GITHUB_OUTPUT: outputPath,
|
||||
NODE_TEST_EXCLUDE_PATTERNS_JSON: params.patternsJson,
|
||||
PATH: process.env.PATH,
|
||||
},
|
||||
});
|
||||
return {
|
||||
...result,
|
||||
output: existsSync(outputPath) ? readFileSync(outputPath, "utf8") : "",
|
||||
};
|
||||
}
|
||||
|
||||
describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
||||
it("covers every pre-release plugin skill surface in the plugin prerelease plan", () => {
|
||||
const plan = assertPluginPrereleaseTestPlanComplete();
|
||||
|
|
@ -276,6 +308,166 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
|||
expect(fixtureServer).toContain("/versions/${fixture.version}/artifact");
|
||||
});
|
||||
|
||||
it("validates frozen-target Node omissions as exact full-release-only plugin test paths", () => {
|
||||
const defaultResult = runFrozenTargetNodeExclusionValidation({
|
||||
fullReleaseValidation: false,
|
||||
patternsJson: "[]",
|
||||
});
|
||||
expect(defaultResult.status, defaultResult.stderr).toBe(0);
|
||||
expect(defaultResult.output).toBe("patterns_json=[]\n");
|
||||
|
||||
const validPatterns = [
|
||||
"src/plugins/example.test.ts",
|
||||
"src/plugins/runtime/example.runtime.test.ts",
|
||||
];
|
||||
const fullReleaseResult = runFrozenTargetNodeExclusionValidation({
|
||||
fullReleaseValidation: true,
|
||||
patternsJson: JSON.stringify(validPatterns),
|
||||
});
|
||||
expect(fullReleaseResult.status, fullReleaseResult.stderr).toBe(0);
|
||||
expect(fullReleaseResult.output).toBe(`patterns_json=${JSON.stringify(validPatterns)}\n`);
|
||||
|
||||
for (const patternsJson of ["", "{}", "null", '"src/plugins/example.test.ts"']) {
|
||||
const result = runFrozenTargetNodeExclusionValidation({
|
||||
fullReleaseValidation: true,
|
||||
patternsJson,
|
||||
});
|
||||
expect(result.status, `${patternsJson}\n${result.stderr}`).not.toBe(0);
|
||||
}
|
||||
|
||||
for (const patternsJson of [JSON.stringify(["src/plugins/example.test.ts"]), " [ ] "]) {
|
||||
const result = runFrozenTargetNodeExclusionValidation({
|
||||
fullReleaseValidation: false,
|
||||
patternsJson,
|
||||
});
|
||||
expect(result.status, `${patternsJson}\n${result.stderr}`).not.toBe(0);
|
||||
}
|
||||
|
||||
for (const patternsJson of [
|
||||
JSON.stringify([""]),
|
||||
JSON.stringify([42]),
|
||||
JSON.stringify(["/src/plugins/example.test.ts"]),
|
||||
JSON.stringify(["C:\\src\\plugins\\example.test.ts"]),
|
||||
JSON.stringify(["../src/plugins/example.test.ts"]),
|
||||
JSON.stringify(["./src/plugins/example.test.ts"]),
|
||||
JSON.stringify(["src/plugins/../example.test.ts"]),
|
||||
JSON.stringify(["src/plugins//example.test.ts"]),
|
||||
JSON.stringify(["src/agents/example.test.ts"]),
|
||||
JSON.stringify(["src/plugins/example.ts"]),
|
||||
JSON.stringify(["src/plugins/*.test.ts"]),
|
||||
JSON.stringify(["src/plugins/example?.test.ts"]),
|
||||
JSON.stringify(["src/plugins/[example].test.ts"]),
|
||||
JSON.stringify(["src/plugins/example.test.ts", "src/plugins/example.test.ts"]),
|
||||
]) {
|
||||
const result = runFrozenTargetNodeExclusionValidation({
|
||||
fullReleaseValidation: true,
|
||||
patternsJson,
|
||||
});
|
||||
expect(result.status, `${patternsJson}\n${result.stderr}`).not.toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps frozen-target fixture ownership and Node omissions explicit in release evidence", () => {
|
||||
const pluginWorkflow = readPluginPrereleaseWorkflow();
|
||||
const pluginSource = readFileSync(".github/workflows/plugin-prerelease.yml", "utf8");
|
||||
const preflight = pluginWorkflow.jobs.preflight;
|
||||
const nodeShard = pluginWorkflow.jobs["plugin-prerelease-node-shard"];
|
||||
const trustedCheckout = nodeShard.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Checkout trusted npm security inventory",
|
||||
);
|
||||
const installInventory = nodeShard.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Install trusted npm security inventory",
|
||||
);
|
||||
const runNodeShard = nodeShard.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Run release-only plugin Node shard",
|
||||
);
|
||||
const releaseWorkflow = readFullReleaseValidationWorkflow();
|
||||
const releaseSource = readFileSync(".github/workflows/full-release-validation.yml", "utf8");
|
||||
const targetSummary = releaseWorkflow.jobs.resolve_target.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Summarize target",
|
||||
);
|
||||
const pluginDispatch = releaseWorkflow.jobs.plugin_prerelease.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Dispatch and monitor plugin prerelease",
|
||||
);
|
||||
const evidenceReuse = releaseWorkflow.jobs.evidence_reuse.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Find reusable validation evidence",
|
||||
);
|
||||
const manifest = releaseWorkflow.jobs.summary.steps.find(
|
||||
(step: WorkflowStep) => step.name === "Write release validation manifest",
|
||||
);
|
||||
|
||||
expect(pluginWorkflow.on.workflow_dispatch.inputs.node_test_exclude_patterns_json).toEqual({
|
||||
default: "[]",
|
||||
description:
|
||||
"Full Release Validation-only exact plugin test paths omitted for a frozen target",
|
||||
required: false,
|
||||
type: "string",
|
||||
});
|
||||
expect(preflight.outputs.node_test_exclude_patterns_json).toBe(
|
||||
"${{ steps.node_test_exclusions.outputs.patterns_json }}",
|
||||
);
|
||||
expect(trustedCheckout).toMatchObject({
|
||||
if: "inputs.full_release_validation",
|
||||
uses: CHECKOUT_V6,
|
||||
with: {
|
||||
"persist-credentials": false,
|
||||
ref: "${{ github.sha }}",
|
||||
path: ".plugin-prerelease-trusted",
|
||||
"sparse-checkout": "src/plugins/npm-install-security-scan.release.test.ts",
|
||||
"sparse-checkout-cone-mode": false,
|
||||
},
|
||||
});
|
||||
expect(installInventory?.run).toContain(
|
||||
'"$trusted_checkout/src/plugins/npm-install-security-scan.release.test.ts"',
|
||||
);
|
||||
expect(installInventory?.run).toContain(
|
||||
"src/plugins/npm-install-security-scan.release.test.ts",
|
||||
);
|
||||
expect(installInventory?.run).toContain('rm -rf -- "$trusted_checkout"');
|
||||
expect(runNodeShard?.env?.NODE_TEST_EXCLUDE_PATTERNS_JSON).toBe(
|
||||
"${{ needs.preflight.outputs.node_test_exclude_patterns_json }}",
|
||||
);
|
||||
expect(runNodeShard?.run).toContain('process.env.NODE_TEST_EXCLUDE_PATTERNS_JSON ?? "[]"');
|
||||
expect(runNodeShard?.run).toContain('pattern.slice("src/plugins/".length)');
|
||||
expect(runNodeShard?.run).toContain("`--exclude=${pattern.slice");
|
||||
expect(runNodeShard?.run).toContain('["test", "--", ...configs, "--", ...excludeArgs]');
|
||||
expect(pluginSource).not.toContain("runtime-llm.runtime.test.ts");
|
||||
|
||||
expect(
|
||||
releaseWorkflow.on.workflow_dispatch.inputs.plugin_prerelease_node_exclude_patterns_json,
|
||||
).toEqual({
|
||||
default: "[]",
|
||||
description:
|
||||
"Exact Plugin Prerelease Node test paths omitted only for frozen-target validation",
|
||||
required: false,
|
||||
type: "string",
|
||||
});
|
||||
expect(targetSummary?.env?.PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON).toBe(
|
||||
"${{ inputs.plugin_prerelease_node_exclude_patterns_json }}",
|
||||
);
|
||||
expect(targetSummary?.run).toContain("- Plugin prerelease Node exclusions:");
|
||||
expect(pluginDispatch?.env?.PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON).toBe(
|
||||
"${{ inputs.plugin_prerelease_node_exclude_patterns_json }}",
|
||||
);
|
||||
expect(pluginDispatch?.run).toContain(
|
||||
'-f node_test_exclude_patterns_json="$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON"',
|
||||
);
|
||||
expect(pluginDispatch?.run).toContain("- Frozen-target Node test omissions:");
|
||||
expect(evidenceReuse?.env?.PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON).toBe(
|
||||
"${{ inputs.plugin_prerelease_node_exclude_patterns_json }}",
|
||||
);
|
||||
expect(evidenceReuse?.run).toContain(
|
||||
"pluginPrereleaseNodeExcludePatternsJson: $pluginPrereleaseNodeExcludePatternsJson",
|
||||
);
|
||||
expect(manifest?.env?.PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON).toBe(
|
||||
"${{ inputs.plugin_prerelease_node_exclude_patterns_json }}",
|
||||
);
|
||||
expect(manifest?.run).toContain(
|
||||
"pluginPrereleaseNodeExcludePatternsJson: $pluginPrereleaseNodeExcludePatternsJson",
|
||||
);
|
||||
expect(releaseSource).not.toContain("runtime-llm.runtime.test.ts");
|
||||
});
|
||||
|
||||
it("wires the full plugin prerelease plan into its release workflow", () => {
|
||||
const workflow = readCiWorkflow();
|
||||
const preflight = workflow.jobs.preflight;
|
||||
|
|
@ -477,7 +669,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
|||
expect(normalCiDispatchCase).toContain('dispatch_and_wait ci.yml "$dispatch_run_name"');
|
||||
expect(normalCiDispatchCase).not.toContain("full_release_validation=true");
|
||||
expect(pluginPrereleaseScript).toContain(
|
||||
'args=(-f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA" -f full_release_validation=true -f dispatch_id="$dispatch_id")',
|
||||
'args=(-f target_ref="$TARGET_SHA" -f expected_sha="$TARGET_SHA" -f full_release_validation=true -f dispatch_id="$dispatch_id" -f node_test_exclude_patterns_json="$PLUGIN_PRERELEASE_NODE_EXCLUDE_PATTERNS_JSON")',
|
||||
);
|
||||
expect(pluginPrereleaseScript).toContain(
|
||||
'args+=(-f candidate_artifact_json="$CANDIDATE_ARTIFACT_JSON")',
|
||||
|
|
@ -500,7 +692,9 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
|||
const pluginNodeShardScript = pluginWorkflow.jobs["plugin-prerelease-node-shard"].steps.find(
|
||||
(step: WorkflowStep) => step.name === "Run release-only plugin Node shard",
|
||||
).run;
|
||||
expect(pluginNodeShardScript).toContain('spawnSync("pnpm", ["test", "--", ...configs]');
|
||||
expect(pluginNodeShardScript).toContain(
|
||||
'spawnSync("pnpm", ["test", "--", ...configs, "--", ...excludeArgs]',
|
||||
);
|
||||
expect(pluginNodeShardScript).not.toContain("scripts/test-projects.mts");
|
||||
expect(pluginWorkflow.on.workflow_dispatch.inputs.target_ref).toEqual({
|
||||
default: "main",
|
||||
|
|
@ -537,6 +731,7 @@ describe("scripts/lib/plugin-prerelease-test-plan.mts", () => {
|
|||
plugin_prerelease_extension_matrix:
|
||||
"${{ steps.manifest.outputs.plugin_prerelease_extension_matrix }}",
|
||||
plugin_prerelease_node_matrix: "${{ steps.manifest.outputs.plugin_prerelease_node_matrix }}",
|
||||
node_test_exclude_patterns_json: "${{ steps.node_test_exclusions.outputs.patterns_json }}",
|
||||
plugin_prerelease_static_matrix:
|
||||
"${{ steps.manifest.outputs.plugin_prerelease_static_matrix }}",
|
||||
run_plugin_prerelease_docker: "${{ steps.manifest.outputs.run_plugin_prerelease_docker }}",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue