feat(n8n): add rotating provider access credential

This commit is contained in:
Codex
2026-07-19 10:11:56 +03:00
parent 3c5d8f6cef
commit 0611a88971
10 changed files with 408 additions and 64 deletions
@@ -13,19 +13,30 @@ const engineRoot = resolve(platformRoot, "../NODEDC_ENGINE_INFRA");
const artifactRoot = resolve(process.env.NODEDC_DEPLOY_ARTIFACT_DIR || join(here, "../deploy-artifacts"));
const stageArtifact = resolve(
process.env.NODEDC_N8N_EXTENSION_STAGE_ARTIFACT
|| join(artifactRoot, "nodedc-n8n-private-extension-n8n-nodes-ndc-history-read-20260717-004.tgz"),
|| join(artifactRoot, "nodedc-n8n-private-extension-n8n-nodes-ndc-provider-rotating-access-20260718-006.tgz"),
);
const predecessorArtifact = resolve(
process.env.NODEDC_N8N_EXTENSION_PREDECESSOR_ARTIFACT
|| join(artifactRoot, "nodedc-engine-n8n-private-extension-20260717-005.tgz"),
);
const stageArtifactSha256 = "a78f8250704e4893eab727cdb862b69ce45a84310fbb73ecd3dfd968b72d12ee";
const releaseId = "0.1.3-3354149b5245e39a";
const packageVersion = "0.1.3";
const packageSha256 = "3354149b5245e39a10f6f03a4b43796602d7e57ea1f91dcc6cb6774ead97501d";
const stageArtifactSha256 = "2699e53c482f2d2bec6d13709a7e9b9a0bb3791ad2018c142e9bc4e7bf371e4a";
const predecessorArtifactSha256 = "f0f687ddcc1bc006db56c75e73c8213e99298c31c2d881cde078e5ddc9600172";
const releaseId = "0.1.4-59dc9f7882721d6a";
const packageVersion = "0.1.4";
const packageSha256 = "59dc9f7882721d6a5e2ae84ffa6aa8cd9bc5432f5904d24e70e363573ed2757f";
const n8nVersion = "2.3.2";
const baseImage = "docker.n8n.io/n8nio/n8n:2.3.2";
const architecture = "amd64";
const generatedAt = "2026-07-17T21:00:00.000Z";
const predecessorGitRevision = "96ad46e3c62943f818233481957c62e5088ae35c";
const previouslyIssuedTransitionIds = new Set(["20260715-002", "20260716-003", "20260717-004"]);
const generatedAt = "2026-07-18T21:00:00.000Z";
const previouslyIssuedTransitionIds = new Set([
"20260715-002",
"20260716-003",
"20260717-004",
"20260717-005",
"20260718-006",
"20260718-007",
]);
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}`;
@@ -51,7 +62,9 @@ const expectedCredentialTypes = [
"ndcDataProductWriterApi",
"ndcDataProductReaderApi",
"ndcFoundryBindingApi",
"ndcProviderRotatingAccessApi",
];
const predecessorCredentialTypes = expectedCredentialTypes.slice(0, 3);
const nodeModules = [
["dist/nodes/NdcDataProductPublish/NdcDataProductPublish.node.js", "NdcDataProductPublish"],
["dist/nodes/NdcDataProductRead/NdcDataProductRead.node.js", "NdcDataProductRead"],
@@ -61,12 +74,14 @@ const credentialModules = [
["dist/credentials/NdcDataProductWriterApi.credentials.js", "NdcDataProductWriterApi"],
["dist/credentials/NdcDataProductReaderApi.credentials.js", "NdcDataProductReaderApi"],
["dist/credentials/NdcFoundryBindingApi.credentials.js", "NdcFoundryBindingApi"],
["dist/credentials/NdcProviderRotatingAccessApi.credentials.js", "NdcProviderRotatingAccessApi"],
];
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");
assertSha(await readFile(predecessorArtifact), predecessorArtifactSha256, "predecessor artifact");
assertEngineBaseline(await readFile(join(engineRoot, "docker-compose.yml"), "utf8"));
const work = await mkdtemp(join(tmpdir(), "nodedc-engine-n8n-sealed-"));
@@ -111,10 +126,10 @@ try {
assertExact(privateNodes.map((item) => item.name), expectedNodeTypes, "node types");
assertExact(privateCredentials.map((item) => item.name), expectedCredentialTypes, "credential types");
const predecessorDescriptor = JSON.parse(gitFile(descriptorRel));
const predecessorNodes = JSON.parse(gitFile(nodesCatalogRel));
const predecessorCredentials = JSON.parse(gitFile(credentialsCatalogRel));
const predecessorMeta = JSON.parse(gitFile(metaRel));
const predecessorDescriptor = JSON.parse(predecessorArtifactFile(descriptorRel));
const predecessorNodes = JSON.parse(predecessorArtifactFile(nodesCatalogRel));
const predecessorCredentials = JSON.parse(predecessorArtifactFile(credentialsCatalogRel));
const predecessorMeta = JSON.parse(predecessorArtifactFile(metaRel));
assertPredecessorCatalogs(
predecessorDescriptor,
predecessorNodes,
@@ -148,8 +163,18 @@ try {
packageVersion: predecessorDescriptor.packageVersion,
packageSha256: predecessorDescriptor.packageSha256,
};
const activationDescriptor = descriptor(target, predecessor.releaseId, predecessor.releaseId);
const rollbackDescriptor = descriptor(predecessor, releaseId, releaseId);
const activationDescriptor = descriptor(
target,
predecessor.releaseId,
predecessor.releaseId,
expectedCredentialTypes,
);
const rollbackDescriptor = descriptor(
predecessor,
releaseId,
releaseId,
predecessorCredentialTypes,
);
const override = composeOverride(target);
const rollbackOverride = composeOverride(predecessor);
@@ -204,7 +229,7 @@ try {
await rm(work, { recursive: true, force: true });
}
function descriptor(target, expectedCurrent, rollbackBaseline) {
function descriptor(target, expectedCurrent, rollbackBaseline, credentialTypes) {
return {
schemaVersion: "nodedc.engine-n8n-private-extension-transition/v1",
action: "activate",
@@ -221,7 +246,7 @@ function descriptor(target, expectedCurrent, rollbackBaseline) {
topologyServices: ["n8n"],
expectedCurrent,
expectedNodeTypes,
expectedCredentialTypes,
expectedCredentialTypes: credentialTypes,
rollbackBaseline,
};
}
@@ -327,15 +352,15 @@ function assertPackage(value) {
function assertPredecessorCatalogs(descriptorValue, nodes, credentials, meta) {
if (descriptorValue?.action !== "activate"
|| descriptorValue?.releaseId !== "0.1.2-05e4b38b14b4a019"
|| descriptorValue?.packageVersion !== "0.1.2"
|| descriptorValue?.packageSha256 !== "05e4b38b14b4a019ce1f6eee27b9e320094cb3560903bd68074966b3a1267af5") {
|| descriptorValue?.releaseId !== "0.1.3-3354149b5245e39a"
|| descriptorValue?.packageVersion !== "0.1.3"
|| descriptorValue?.packageSha256 !== "3354149b5245e39a10f6f03a4b43796602d7e57ea1f91dcc6cb6774ead97501d") {
throw new Error("predecessor_descriptor_mismatch");
}
if (!Array.isArray(nodes) || nodes.length !== 437
|| !Array.isArray(credentials) || credentials.length !== 388
|| meta?.n8nVersion !== n8nVersion
|| meta?.source !== "n8n-core+n8n-nodes-ndc@0.1.2"
|| meta?.source !== "n8n-core+n8n-nodes-ndc@0.1.3"
|| meta?.nodeCount !== 437
|| meta?.credentialCount !== 388) {
throw new Error("predecessor_catalog_mismatch");
@@ -346,8 +371,8 @@ function assertPredecessorCatalogs(descriptorValue, nodes, credentials, meta) {
"predecessor node types",
);
assertExact(
credentials.filter((item) => expectedCredentialTypes.includes(String(item?.name || ""))).map((item) => item.name),
expectedCredentialTypes,
credentials.filter((item) => predecessorCredentialTypes.includes(String(item?.name || ""))).map((item) => item.name),
predecessorCredentialTypes,
"predecessor credential types",
);
}
@@ -381,8 +406,17 @@ function assertSha(bytes, expected, label) {
if (actual !== expected) throw new Error(`${label.replaceAll(" ", "_")}_sha256_mismatch:${actual}`);
}
function gitFile(rel) {
return run("git", ["show", `${predecessorGitRevision}:${rel}`], engineRoot).stdout;
function predecessorArtifactFile(rel) {
const script = [
"import pathlib,sys,tarfile",
"archive=pathlib.Path(sys.argv[1])",
"member='payload/'+sys.argv[2]",
"with tarfile.open(archive,'r:gz') as source:",
" extracted=source.extractfile(member)",
" if extracted is None: raise SystemExit('predecessor member missing')",
" sys.stdout.buffer.write(extracted.read())",
].join("\n");
return run("python3", ["-c", script, predecessorArtifact, rel]).stdout;
}
async function writeJson(path, value) {