feat: add dev deploy apply plan report
This commit is contained in:
@@ -30,6 +30,21 @@ const requiredValidationCommands = [
|
||||
"node --check scripts/validate-dev-gate-report.mjs",
|
||||
"node scripts/validate-dev-gate-report.mjs"
|
||||
];
|
||||
const requiredDevDeployApplySupports = [
|
||||
"pikasTech/HWLAB#50",
|
||||
"pikasTech/HWLAB#7",
|
||||
"pikasTech/HWLAB#33",
|
||||
"pikasTech/HWLAB#17",
|
||||
"pikasTech/HWLAB#32",
|
||||
"pikasTech/HWLAB#30"
|
||||
];
|
||||
const requiredDevDeployApplyValidationCommands = [
|
||||
"node --check scripts/validate-dev-gate-report.mjs",
|
||||
"node scripts/validate-dev-gate-report.mjs",
|
||||
"node --check scripts/dev-deploy-apply.mjs",
|
||||
"node --check scripts/src/dev-deploy-apply.mjs",
|
||||
"node scripts/dev-deploy-apply.mjs --dry-run --expect-blocked"
|
||||
];
|
||||
const requiredPreflightValidationCommands = [
|
||||
"node --check scripts/dev-gate-preflight.mjs",
|
||||
"node --check scripts/src/dev-gate-preflight.mjs",
|
||||
@@ -435,6 +450,10 @@ async function validateReport(relativePath) {
|
||||
});
|
||||
assertUnique(report.devPreconditions.evidence, `${label}.devPreconditions.evidence`);
|
||||
}
|
||||
|
||||
if (report.taskId === "dev-deploy-apply") {
|
||||
assertDevDeployApplyReport(report, label);
|
||||
}
|
||||
}
|
||||
|
||||
async function assertDocumentSet(documentsValue, label, requiredDocs) {
|
||||
@@ -469,6 +488,145 @@ function assertBlockers(blockers, label) {
|
||||
}
|
||||
}
|
||||
|
||||
function assertCommandList(value, label, { minLength = 1 } = {}) {
|
||||
assertStringArray(value, label, { minLength });
|
||||
assertUnique(value, label);
|
||||
for (const command of value) {
|
||||
assert.ok(!command.includes("--prod"), `${label} must not include PROD command`);
|
||||
assert.ok(!command.includes("--production"), `${label} must not include production command`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertDevDeployApplyReport(report, label) {
|
||||
assert.equal(report.issue, issueFamily.DEV_DEPLOY_APPLY, `${label}.issue`);
|
||||
|
||||
assertStringArray(report.supports, `${label}.supports`, {
|
||||
minLength: requiredDevDeployApplySupports.length
|
||||
});
|
||||
for (const supportedIssue of requiredDevDeployApplySupports) {
|
||||
assert.ok(report.supports.includes(supportedIssue), `${label}.supports missing ${supportedIssue}`);
|
||||
}
|
||||
|
||||
for (const requiredCommand of requiredDevDeployApplyValidationCommands) {
|
||||
assert.ok(
|
||||
report.validationCommands.includes(requiredCommand),
|
||||
`${label}.validationCommands missing ${requiredCommand}`
|
||||
);
|
||||
}
|
||||
|
||||
assertObject(report.devDeployApply, `${label}.devDeployApply`);
|
||||
const plan = report.devDeployApply;
|
||||
for (const field of [
|
||||
"conclusion",
|
||||
"applyBoundary",
|
||||
"target",
|
||||
"artifactPlan",
|
||||
"workloadPlan",
|
||||
"servicePlan",
|
||||
"manualCommands",
|
||||
"rollbackHint",
|
||||
"remainingBlockers"
|
||||
]) {
|
||||
assert.ok(Object.hasOwn(plan, field), `${label}.devDeployApply missing ${field}`);
|
||||
}
|
||||
|
||||
assertObject(plan.conclusion, `${label}.devDeployApply.conclusion`);
|
||||
assert.ok(["blocked", "ready"].includes(plan.conclusion.status), `${label}.devDeployApply.conclusion.status`);
|
||||
assertString(plan.conclusion.reason, `${label}.devDeployApply.conclusion.reason`);
|
||||
assert.equal(plan.conclusion.blockerCount, report.blockers.length, `${label}.devDeployApply.conclusion.blockerCount`);
|
||||
|
||||
assertObject(plan.applyBoundary, `${label}.devDeployApply.applyBoundary`);
|
||||
assert.equal(plan.applyBoundary.defaultNoWrite, plan.mode !== "apply", `${label}.devDeployApply.applyBoundary.defaultNoWrite`);
|
||||
assert.equal(plan.applyBoundary.mutationAttempted, report.devDeployApply.mutationAttempted, `${label}.devDeployApply.applyBoundary.mutationAttempted`);
|
||||
assertStringArray(plan.applyBoundary.applyRequiresFlags, `${label}.devDeployApply.applyBoundary.applyRequiresFlags`, {
|
||||
minLength: 3
|
||||
});
|
||||
for (const flag of ["--apply", "--confirm-dev", "--confirmed-non-production"]) {
|
||||
assert.ok(plan.applyBoundary.applyRequiresFlags.includes(flag), `${label}.devDeployApply.applyBoundary.applyRequiresFlags missing ${flag}`);
|
||||
}
|
||||
assertStringArray(plan.applyBoundary.forbiddenActions, `${label}.devDeployApply.applyBoundary.forbiddenActions`, {
|
||||
minLength: 6
|
||||
});
|
||||
for (const forbiddenAction of ["prod-deploy", "secret-read", "runtime-substitute", "heavy-master-e2e"]) {
|
||||
assert.ok(plan.applyBoundary.forbiddenActions.includes(forbiddenAction), `${label}.devDeployApply.applyBoundary.forbiddenActions missing ${forbiddenAction}`);
|
||||
}
|
||||
assertString(plan.applyBoundary.writeScope, `${label}.devDeployApply.applyBoundary.writeScope`);
|
||||
assertString(plan.applyBoundary.noWriteScope, `${label}.devDeployApply.applyBoundary.noWriteScope`);
|
||||
|
||||
assertObject(plan.target, `${label}.devDeployApply.target`);
|
||||
assert.equal(plan.target.environment, "dev", `${label}.devDeployApply.target.environment`);
|
||||
assert.equal(plan.target.namespace, "hwlab-dev", `${label}.devDeployApply.target.namespace`);
|
||||
assert.equal(plan.target.endpoint, "http://74.48.78.17:6667", `${label}.devDeployApply.target.endpoint`);
|
||||
assert.equal(plan.target.prodDisabled, true, `${label}.devDeployApply.target.prodDisabled`);
|
||||
|
||||
assertObject(plan.artifactPlan, `${label}.devDeployApply.artifactPlan`);
|
||||
for (const field of ["expectedArtifactCommit", "deployCommitId", "catalogCommitId", "sourceCommitId"]) {
|
||||
assertString(plan.artifactPlan[field], `${label}.devDeployApply.artifactPlan.${field}`);
|
||||
}
|
||||
assertStringArray(plan.artifactPlan.serviceCommitIds, `${label}.devDeployApply.artifactPlan.serviceCommitIds`, {
|
||||
minLength: 1
|
||||
});
|
||||
assert.equal(typeof plan.artifactPlan.matchesSourceCommit, "boolean", `${label}.devDeployApply.artifactPlan.matchesSourceCommit`);
|
||||
assert.equal(typeof plan.artifactPlan.published, "boolean", `${label}.devDeployApply.artifactPlan.published`);
|
||||
assert.equal(typeof plan.artifactPlan.registryVerified, "boolean", `${label}.devDeployApply.artifactPlan.registryVerified`);
|
||||
assert.ok(Number.isInteger(plan.artifactPlan.imageCount), `${label}.devDeployApply.artifactPlan.imageCount`);
|
||||
assertStringArray(plan.artifactPlan.unpublishedServices, `${label}.devDeployApply.artifactPlan.unpublishedServices`);
|
||||
|
||||
assertArray(plan.workloadPlan, `${label}.devDeployApply.workloadPlan`);
|
||||
assert.ok(plan.workloadPlan.length >= 1, `${label}.devDeployApply.workloadPlan must not be empty`);
|
||||
assertArray(plan.servicePlan, `${label}.devDeployApply.servicePlan`);
|
||||
assert.ok(plan.servicePlan.length >= 1, `${label}.devDeployApply.servicePlan must not be empty`);
|
||||
for (const [index, workload] of plan.workloadPlan.entries()) {
|
||||
const workloadLabel = `${label}.devDeployApply.workloadPlan[${index}]`;
|
||||
assertObject(workload, workloadLabel);
|
||||
for (const field of ["kind", "name", "namespace", "serviceIds", "containers"]) {
|
||||
assert.ok(Object.hasOwn(workload, field), `${workloadLabel} missing ${field}`);
|
||||
}
|
||||
assert.equal(workload.namespace, "hwlab-dev", `${workloadLabel}.namespace`);
|
||||
assertStringArray(workload.serviceIds, `${workloadLabel}.serviceIds`, { minLength: 1 });
|
||||
assertArray(workload.containers, `${workloadLabel}.containers`);
|
||||
}
|
||||
|
||||
assertObject(plan.manualCommands, `${label}.devDeployApply.manualCommands`);
|
||||
assert.ok(["blocked", "ready"].includes(plan.manualCommands.status), `${label}.devDeployApply.manualCommands.status`);
|
||||
assertCommandList(plan.manualCommands.beforeHumanApproval, `${label}.devDeployApply.manualCommands.beforeHumanApproval`);
|
||||
assertCommandList(plan.manualCommands.afterHumanApproval, `${label}.devDeployApply.manualCommands.afterHumanApproval`, {
|
||||
minLength: 0
|
||||
});
|
||||
assertString(plan.manualCommands.summary, `${label}.devDeployApply.manualCommands.summary`);
|
||||
if (plan.conclusion.status === "blocked") {
|
||||
assert.equal(plan.manualCommands.status, "blocked", `${label}.devDeployApply.manualCommands.status`);
|
||||
assert.equal(plan.manualCommands.afterHumanApproval.length, 0, `${label}.devDeployApply.manualCommands.afterHumanApproval`);
|
||||
}
|
||||
if (plan.conclusion.status === "ready") {
|
||||
assert.equal(plan.manualCommands.status, "ready", `${label}.devDeployApply.manualCommands.status`);
|
||||
assert.ok(
|
||||
plan.manualCommands.afterHumanApproval.includes("node scripts/dev-deploy-apply.mjs --apply --confirm-dev --confirmed-non-production --write-report"),
|
||||
`${label}.devDeployApply.manualCommands.afterHumanApproval missing apply command`
|
||||
);
|
||||
}
|
||||
|
||||
assertObject(plan.rollbackHint, `${label}.devDeployApply.rollbackHint`);
|
||||
assert.equal(plan.rollbackHint.namespace, "hwlab-dev", `${label}.devDeployApply.rollbackHint.namespace`);
|
||||
assertString(plan.rollbackHint.strategy, `${label}.devDeployApply.rollbackHint.strategy`);
|
||||
assertCommandList(plan.rollbackHint.captureBeforeApply, `${label}.devDeployApply.rollbackHint.captureBeforeApply`);
|
||||
assertCommandList(plan.rollbackHint.deploymentRollbackCommands, `${label}.devDeployApply.rollbackHint.deploymentRollbackCommands`);
|
||||
assertCommandList(plan.rollbackHint.jobCleanupCommands, `${label}.devDeployApply.rollbackHint.jobCleanupCommands`);
|
||||
assertCommandList(plan.rollbackHint.postRollbackChecks, `${label}.devDeployApply.rollbackHint.postRollbackChecks`);
|
||||
|
||||
assertArray(plan.remainingBlockers, `${label}.devDeployApply.remainingBlockers`);
|
||||
assert.equal(plan.remainingBlockers.length, report.blockers.length, `${label}.devDeployApply.remainingBlockers.length`);
|
||||
for (const [index, blocker] of plan.remainingBlockers.entries()) {
|
||||
const blockerLabel = `${label}.devDeployApply.remainingBlockers[${index}]`;
|
||||
assertObject(blocker, blockerLabel);
|
||||
for (const field of ["type", "scope", "status", "summary", "unblockHint"]) {
|
||||
assert.ok(Object.hasOwn(blocker, field), `${blockerLabel} missing ${field}`);
|
||||
}
|
||||
assert.ok(blockerTypes.has(blocker.type), `${blockerLabel}.type must be known`);
|
||||
assertString(blocker.unblockHint, `${blockerLabel}.unblockHint`);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateDevEdgeReport(report, label) {
|
||||
for (const field of [
|
||||
"$schema",
|
||||
|
||||
Reference in New Issue
Block a user