feat(deploy): extend canon for managed EDP and Engine grants
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
import { createHash } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { cp, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { cp, lstat, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { createRequire, Module } from "node:module";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
@@ -24,8 +24,10 @@ const n8nVersion = "2.3.2";
|
||||
const baseImage = "docker.n8n.io/n8nio/n8n:2.3.2";
|
||||
const architecture = "amd64";
|
||||
const generatedAt = "2026-07-15T21:51:41.000Z";
|
||||
const activationId = "engine-n8n-private-extension-20260716-003";
|
||||
const rollbackId = "engine-n8n-private-extension-rollback-20260716-003";
|
||||
const previouslyIssuedTransitionIds = new Set(["20260715-002", "20260716-003"]);
|
||||
const transitionId = readTransitionId(process.argv.slice(2), process.env.NODEDC_N8N_TRANSITION_ID);
|
||||
const activationId = `engine-n8n-private-extension-${transitionId}`;
|
||||
const rollbackId = `engine-n8n-private-extension-rollback-${transitionId}`;
|
||||
|
||||
const transitionRoot = "nodedc-source/services/n8n/private-extensions";
|
||||
const descriptorRel = `${transitionRoot}/ndc-activation.json`;
|
||||
@@ -62,6 +64,8 @@ const credentialModules = [
|
||||
];
|
||||
|
||||
await mkdir(artifactRoot, { recursive: true });
|
||||
await assertArtifactTargetFresh(join(artifactRoot, `nodedc-${activationId}.tgz`));
|
||||
await assertArtifactTargetFresh(join(artifactRoot, `nodedc-${rollbackId}.tgz`));
|
||||
assertSha(await readFile(stageArtifact), stageArtifactSha256, "staging artifact");
|
||||
assertEngineBaseline(await readFile(join(engineRoot, "docker-compose.yml"), "utf8"));
|
||||
|
||||
@@ -125,13 +129,15 @@ try {
|
||||
const rollbackDescriptor = descriptor("rollback-inactive", [], [], releaseId);
|
||||
const override = composeOverride();
|
||||
|
||||
await writeJson(join(engineRoot, nodesCatalogRel), activeNodes);
|
||||
await writeJson(join(engineRoot, credentialsCatalogRel), activeCredentials);
|
||||
await writeJson(join(engineRoot, metaRel), activeMeta);
|
||||
await writeJson(join(engineRoot, descriptorRel), activationDescriptor);
|
||||
await writeFile(join(engineRoot, overrideRel), override, "utf8");
|
||||
await cp(join(packageRoot, "dist/icons/ndc.svg"), join(engineRoot, iconRel), { force: true });
|
||||
await cp(join(packageRoot, "dist/icons/ndc.dark.svg"), join(engineRoot, darkIconRel), { force: true });
|
||||
const generatedRoot = join(work, "generated-engine-payload");
|
||||
await writeJson(join(generatedRoot, nodesCatalogRel), activeNodes);
|
||||
await writeJson(join(generatedRoot, credentialsCatalogRel), activeCredentials);
|
||||
await writeJson(join(generatedRoot, metaRel), activeMeta);
|
||||
await writeJson(join(generatedRoot, descriptorRel), activationDescriptor);
|
||||
await writeFile(join(generatedRoot, overrideRel), override, "utf8");
|
||||
await mkdir(join(generatedRoot, iconRoot), { recursive: true });
|
||||
await cp(join(packageRoot, "dist/icons/ndc.svg"), join(generatedRoot, iconRel), { force: false });
|
||||
await cp(join(packageRoot, "dist/icons/ndc.dark.svg"), join(generatedRoot, darkIconRel), { force: false });
|
||||
|
||||
const activationEntries = [
|
||||
descriptorRel,
|
||||
@@ -144,7 +150,7 @@ try {
|
||||
];
|
||||
const activationArtifact = await buildArtifact(work, activationId, activationEntries, async (payload) => {
|
||||
for (const rel of activationEntries) {
|
||||
await cp(join(engineRoot, rel), join(payload, rel), { recursive: true, force: false });
|
||||
await cp(join(generatedRoot, rel), join(payload, rel), { recursive: true, force: false });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -159,6 +165,7 @@ try {
|
||||
|
||||
console.log(JSON.stringify({
|
||||
ok: true,
|
||||
transitionId,
|
||||
releaseId,
|
||||
packageSha256,
|
||||
nodeTypes: expectedNodeTypes,
|
||||
@@ -192,6 +199,42 @@ function descriptor(action, nodeTypes, credentialTypes, expectedCurrent) {
|
||||
};
|
||||
}
|
||||
|
||||
function readTransitionId(args, environmentValue) {
|
||||
if (args.length > 1) throw new Error("transition_id_argument_count_invalid");
|
||||
const argumentValue = args[0] || "";
|
||||
const envValue = String(environmentValue || "").trim();
|
||||
if (argumentValue && envValue && argumentValue !== envValue) {
|
||||
throw new Error("transition_id_sources_conflict");
|
||||
}
|
||||
const value = argumentValue || envValue;
|
||||
if (!value) throw new Error("transition_id_required");
|
||||
const match = /^(\d{4})(\d{2})(\d{2})-([0-9]{3})$/.exec(value);
|
||||
if (!match || match[4] === "000") throw new Error("transition_id_invalid");
|
||||
const year = Number(match[1]);
|
||||
const month = Number(match[2]);
|
||||
const day = Number(match[3]);
|
||||
const parsed = new Date(Date.UTC(year, month - 1, day));
|
||||
if (parsed.getUTCFullYear() !== year
|
||||
|| parsed.getUTCMonth() !== month - 1
|
||||
|| parsed.getUTCDate() !== day) {
|
||||
throw new Error("transition_id_invalid");
|
||||
}
|
||||
if (previouslyIssuedTransitionIds.has(value)) {
|
||||
throw new Error("transition_id_already_issued");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
async function assertArtifactTargetFresh(path) {
|
||||
try {
|
||||
await lstat(path);
|
||||
} catch (error) {
|
||||
if (error?.code === "ENOENT") return;
|
||||
throw error;
|
||||
}
|
||||
throw new Error("transition_artifact_already_exists");
|
||||
}
|
||||
|
||||
function composeOverride() {
|
||||
const health = "const http=require('http');const req=http.get('http://127.0.0.1:5678/healthz/readiness',r=>{r.resume();process.exit(r.statusCode===200?0:1)});req.on('error',()=>process.exit(1));req.setTimeout(4000,()=>{req.destroy();process.exit(1)});";
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user