feat(deploy): register n8n publisher replace v2
This commit is contained in:
parent
92d292ce87
commit
9bebd2f504
|
|
@ -103,7 +103,7 @@ not build an image. A fresh transition id is mandatory and previously issued
|
|||
ids are rejected:
|
||||
|
||||
```bash
|
||||
node infra/deploy-runner/build-engine-n8n-private-extension-artifact.mjs 20260717-005
|
||||
node infra/deploy-runner/build-engine-n8n-private-extension-artifact.mjs 20260721-005
|
||||
```
|
||||
|
||||
The builder emits a narrowly scoped Compose override plus a strict transition
|
||||
|
|
@ -113,7 +113,7 @@ same immutable image ID, and extracts the package into the root-owned,
|
|||
read-only Engine release tree:
|
||||
|
||||
```text
|
||||
/volume2/nodedc-demo/n8n-private-extensions/releases/n8n-nodes-ndc/0.1.3-3354149b5245e39a/package
|
||||
/volume2/nodedc-demo/n8n-private-extensions/releases/n8n-nodes-ndc/0.1.5-3c8ae53f010d7c88/package
|
||||
```
|
||||
|
||||
The override sets `N8N_USER_FOLDER=/home/node`, which is required because the
|
||||
|
|
|
|||
|
|
@ -13,22 +13,22 @@ 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-provider-rotating-access-20260718-006.tgz"),
|
||||
|| join(artifactRoot, "nodedc-n8n-private-extension-n8n-nodes-ndc-geozone-replace-20260720-017.tgz"),
|
||||
);
|
||||
const predecessorArtifact = resolve(
|
||||
process.env.NODEDC_N8N_EXTENSION_PREDECESSOR_ARTIFACT
|
||||
|| join(artifactRoot, "nodedc-engine-n8n-private-extension-20260717-005.tgz"),
|
||||
|| join(artifactRoot, "nodedc-engine-n8n-private-extension-20260718-008.tgz"),
|
||||
);
|
||||
|
||||
const stageArtifactSha256 = "2699e53c482f2d2bec6d13709a7e9b9a0bb3791ad2018c142e9bc4e7bf371e4a";
|
||||
const predecessorArtifactSha256 = "f0f687ddcc1bc006db56c75e73c8213e99298c31c2d881cde078e5ddc9600172";
|
||||
const releaseId = "0.1.4-59dc9f7882721d6a";
|
||||
const packageVersion = "0.1.4";
|
||||
const packageSha256 = "59dc9f7882721d6a5e2ae84ffa6aa8cd9bc5432f5904d24e70e363573ed2757f";
|
||||
const stageArtifactSha256 = "0c680d2436917eb6f2416c5c855f2e01dd59ed50bedb1e36456a7405f3f595af";
|
||||
const predecessorArtifactSha256 = "5ef46404dfd7753b4ffd14c5e9b9666f96d2c9875c8fe470714cdea1c6decdce";
|
||||
const releaseId = "0.1.5-3c8ae53f010d7c88";
|
||||
const packageVersion = "0.1.5";
|
||||
const packageSha256 = "3c8ae53f010d7c88b6e90cb8fc4929e7d0880089ad8c3a9edb876ce034786743";
|
||||
const n8nVersion = "2.3.2";
|
||||
const baseImage = "docker.n8n.io/n8nio/n8n:2.3.2";
|
||||
const architecture = "amd64";
|
||||
const generatedAt = "2026-07-18T21:00:00.000Z";
|
||||
const generatedAt = "2026-07-21T10:30:00.000Z";
|
||||
const previouslyIssuedTransitionIds = new Set([
|
||||
"20260715-002",
|
||||
"20260716-003",
|
||||
|
|
@ -36,6 +36,7 @@ const previouslyIssuedTransitionIds = new Set([
|
|||
"20260717-005",
|
||||
"20260718-006",
|
||||
"20260718-007",
|
||||
"20260718-008",
|
||||
]);
|
||||
const transitionId = readTransitionId(process.argv.slice(2), process.env.NODEDC_N8N_TRANSITION_ID);
|
||||
const activationId = `engine-n8n-private-extension-${transitionId}`;
|
||||
|
|
@ -64,7 +65,7 @@ const expectedCredentialTypes = [
|
|||
"ndcFoundryBindingApi",
|
||||
"ndcProviderRotatingAccessApi",
|
||||
];
|
||||
const predecessorCredentialTypes = expectedCredentialTypes.slice(0, 3);
|
||||
const predecessorCredentialTypes = [...expectedCredentialTypes];
|
||||
const nodeModules = [
|
||||
["dist/nodes/NdcDataProductPublish/NdcDataProductPublish.node.js", "NdcDataProductPublish"],
|
||||
["dist/nodes/NdcDataProductRead/NdcDataProductRead.node.js", "NdcDataProductRead"],
|
||||
|
|
@ -125,6 +126,7 @@ try {
|
|||
});
|
||||
assertExact(privateNodes.map((item) => item.name), expectedNodeTypes, "node types");
|
||||
assertExact(privateCredentials.map((item) => item.name), expectedCredentialTypes, "credential types");
|
||||
assertPublishNodeV2(privateNodes[0]);
|
||||
|
||||
const predecessorDescriptor = JSON.parse(predecessorArtifactFile(descriptorRel));
|
||||
const predecessorNodes = JSON.parse(predecessorArtifactFile(nodesCatalogRel));
|
||||
|
|
@ -352,17 +354,17 @@ function assertPackage(value) {
|
|||
|
||||
function assertPredecessorCatalogs(descriptorValue, nodes, credentials, meta) {
|
||||
if (descriptorValue?.action !== "activate"
|
||||
|| descriptorValue?.releaseId !== "0.1.3-3354149b5245e39a"
|
||||
|| descriptorValue?.packageVersion !== "0.1.3"
|
||||
|| descriptorValue?.packageSha256 !== "3354149b5245e39a10f6f03a4b43796602d7e57ea1f91dcc6cb6774ead97501d") {
|
||||
|| descriptorValue?.releaseId !== "0.1.4-59dc9f7882721d6a"
|
||||
|| descriptorValue?.packageVersion !== "0.1.4"
|
||||
|| descriptorValue?.packageSha256 !== "59dc9f7882721d6a5e2ae84ffa6aa8cd9bc5432f5904d24e70e363573ed2757f") {
|
||||
throw new Error("predecessor_descriptor_mismatch");
|
||||
}
|
||||
if (!Array.isArray(nodes) || nodes.length !== 437
|
||||
|| !Array.isArray(credentials) || credentials.length !== 388
|
||||
|| !Array.isArray(credentials) || credentials.length !== 389
|
||||
|| meta?.n8nVersion !== n8nVersion
|
||||
|| meta?.source !== "n8n-core+n8n-nodes-ndc@0.1.3"
|
||||
|| meta?.source !== "n8n-core+n8n-nodes-ndc@0.1.4"
|
||||
|| meta?.nodeCount !== 437
|
||||
|| meta?.credentialCount !== 388) {
|
||||
|| meta?.credentialCount !== 389) {
|
||||
throw new Error("predecessor_catalog_mismatch");
|
||||
}
|
||||
assertExact(
|
||||
|
|
@ -377,6 +379,18 @@ function assertPredecessorCatalogs(descriptorValue, nodes, credentials, meta) {
|
|||
);
|
||||
}
|
||||
|
||||
function assertPublishNodeV2(node) {
|
||||
if (JSON.stringify(node?.version) !== JSON.stringify([1, 2])) {
|
||||
throw new Error("publish_node_versions_mismatch");
|
||||
}
|
||||
const property = node?.properties?.find((item) => item?.name === "publishMode");
|
||||
const values = property?.options?.map((item) => item?.value);
|
||||
if (JSON.stringify(values) !== JSON.stringify(["upsert", "replace"])
|
||||
|| !property?.displayOptions?.show?.["@version"]?.includes(2)) {
|
||||
throw new Error("publish_node_replace_contract_missing");
|
||||
}
|
||||
}
|
||||
|
||||
function assertBaselineCatalogs(nodes, credentials) {
|
||||
if (!Array.isArray(nodes) || nodes.length !== 434 || nodes.some((item) => String(item?.name || "").startsWith("n8n-nodes-ndc."))) {
|
||||
throw new Error("baseline_node_catalog_mismatch");
|
||||
|
|
|
|||
|
|
@ -286,6 +286,11 @@ ENGINE_N8N_RELEASE_CATALOG_JSON_SHA256 = {
|
|||
"credentials": "af8ada839070a4c24b9b10981a751c2b2db651dced1392333d7a4ef13de4564e",
|
||||
"meta": "8fadbc594c5d14b1d53ab69b677f00823949410b9a37f4e43ecc321e89d68983",
|
||||
},
|
||||
"0.1.5-3c8ae53f010d7c88": {
|
||||
"nodes": "fb2ef4d38b9c071fae117a27381714658a1a19334b82fc79d64d13f27555fe5a",
|
||||
"credentials": "af8ada839070a4c24b9b10981a751c2b2db651dced1392333d7a4ef13de4564e",
|
||||
"meta": "b324592c38d518af8320d8540c7061e4684056ae88a676cf5e50c6eef03c0dd7",
|
||||
},
|
||||
}
|
||||
ENGINE_N8N_INACTIVE_NODES_CATALOG_JSON_SHA256 = "b70d9d8130d498c55de46a5d0758c844f1242b70803457b2291ab3a9de8056f2"
|
||||
ENGINE_N8N_INACTIVE_CREDENTIALS_CATALOG_JSON_SHA256 = "680e9f52aac791efbd38e3bd99bd51ef5cded9756d867897c6c2755850e87b50"
|
||||
|
|
@ -331,6 +336,7 @@ ENGINE_N8N_CREDENTIAL_TYPES_BY_RELEASE = {
|
|||
"0.1.2-05e4b38b14b4a019": ENGINE_N8N_CREDENTIAL_TYPES[:3],
|
||||
"0.1.3-3354149b5245e39a": ENGINE_N8N_CREDENTIAL_TYPES[:3],
|
||||
"0.1.4-59dc9f7882721d6a": ENGINE_N8N_CREDENTIAL_TYPES,
|
||||
"0.1.5-3c8ae53f010d7c88": ENGINE_N8N_CREDENTIAL_TYPES,
|
||||
}
|
||||
ENGINE_CREDENTIAL_SINK_ARTIFACT_ENTRIES = (
|
||||
"nodedc-source/server/credentialPolicies/ndcPrivateNode.js",
|
||||
|
|
|
|||
|
|
@ -23,17 +23,17 @@ BUILDER_PATH = SCRIPT_DIR / "build-engine-n8n-private-extension-artifact.mjs"
|
|||
STAGE_ARTIFACT = (
|
||||
PLATFORM_ROOT
|
||||
/ "infra/deploy-artifacts"
|
||||
/ "nodedc-n8n-private-extension-n8n-nodes-ndc-provider-rotating-access-20260718-006.tgz"
|
||||
/ "nodedc-n8n-private-extension-n8n-nodes-ndc-geozone-replace-20260720-017.tgz"
|
||||
)
|
||||
PREDECESSOR_ARTIFACT = (
|
||||
PLATFORM_ROOT
|
||||
/ "infra/deploy-artifacts"
|
||||
/ "nodedc-engine-n8n-private-extension-20260717-005.tgz"
|
||||
/ "nodedc-engine-n8n-private-extension-20260718-008.tgz"
|
||||
)
|
||||
TRANSITION_ID = "20260718-008"
|
||||
SEALED_RELEASE_ID = "0.1.4-59dc9f7882721d6a"
|
||||
SEALED_PACKAGE_SHA256 = "59dc9f7882721d6a5e2ae84ffa6aa8cd9bc5432f5904d24e70e363573ed2757f"
|
||||
PREDECESSOR_RELEASE_ID = "0.1.3-3354149b5245e39a"
|
||||
TRANSITION_ID = "20260721-005"
|
||||
SEALED_RELEASE_ID = "0.1.5-3c8ae53f010d7c88"
|
||||
SEALED_PACKAGE_SHA256 = "3c8ae53f010d7c88b6e90cb8fc4929e7d0880089ad8c3a9edb876ce034786743"
|
||||
PREDECESSOR_RELEASE_ID = "0.1.4-59dc9f7882721d6a"
|
||||
BUILDER_ENGINE_PATHS = (
|
||||
"nodedc-source/server/assets/n8n/schema/v2.3.2/nodes.catalog.json",
|
||||
"nodedc-source/server/assets/n8n/schema/v2.3.2/credentials.catalog.json",
|
||||
|
|
@ -54,7 +54,7 @@ EXPECTED_CREDENTIALS = [
|
|||
"ndcFoundryBindingApi",
|
||||
"ndcProviderRotatingAccessApi",
|
||||
]
|
||||
PREDECESSOR_CREDENTIALS = EXPECTED_CREDENTIALS[:3]
|
||||
PREDECESSOR_CREDENTIALS = EXPECTED_CREDENTIALS
|
||||
PRIVATE_EXTENSION_CANON_FUNCTION_SHA256 = {
|
||||
"expected_engine_n8n_compose_override": "8ee93f3e7c407f5557c711119236449a440d73a7fcac1bbfa50a09e6a13c1cff",
|
||||
"engine_n8n_package_loader_probe_script": "2b3b3c96fad6e5e48ebea74426b21bc7eea6b6fe0e786b537da3366d18aedd9b",
|
||||
|
|
@ -176,7 +176,8 @@ class EngineN8nPrivateExtensionTest(unittest.TestCase):
|
|||
cases = (
|
||||
([], "transition_id_required"),
|
||||
(["20260718-007"], "transition_id_already_issued"),
|
||||
(["engine-n8n-private-extension-20260718-008"], "transition_id_invalid"),
|
||||
(["20260718-008"], "transition_id_already_issued"),
|
||||
(["engine-n8n-private-extension-20260721-005"], "transition_id_invalid"),
|
||||
(["20260230-004"], "transition_id_invalid"),
|
||||
(["20260717-000"], "transition_id_invalid"),
|
||||
([TRANSITION_ID, "unexpected-second-id"], "transition_id_argument_count_invalid"),
|
||||
|
|
@ -245,6 +246,11 @@ class EngineN8nPrivateExtensionTest(unittest.TestCase):
|
|||
self.assertEqual([item["name"] for item in private_credentials], EXPECTED_CREDENTIALS)
|
||||
self.assertTrue(all("usableAsTool" not in item for item in private_nodes))
|
||||
self.assertEqual((len(nodes), len(credentials)), (437, 389))
|
||||
publisher = next(item for item in private_nodes if item["name"].endswith(".ndcDataProductPublish"))
|
||||
self.assertEqual(publisher["version"], [1, 2])
|
||||
publish_mode = next(item for item in publisher["properties"] if item["name"] == "publishMode")
|
||||
self.assertEqual([item["value"] for item in publish_mode["options"]], ["upsert", "replace"])
|
||||
self.assertEqual(publish_mode["displayOptions"]["show"]["@version"], [2])
|
||||
|
||||
def test_rollback_catalog_restores_verified_predecessor_release(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
|
|
@ -267,7 +273,7 @@ class EngineN8nPrivateExtensionTest(unittest.TestCase):
|
|||
[item.get("name") for item in credentials if item.get("name") in PREDECESSOR_CREDENTIALS],
|
||||
PREDECESSOR_CREDENTIALS,
|
||||
)
|
||||
self.assertEqual((len(nodes), len(credentials)), (437, 388))
|
||||
self.assertEqual((len(nodes), len(credentials)), (437, 389))
|
||||
|
||||
def test_compose_override_is_runner_derived_and_offline(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
|
|
@ -282,7 +288,7 @@ class EngineN8nPrivateExtensionTest(unittest.TestCase):
|
|||
self.assertEqual(override, RUNNER.expected_engine_n8n_compose_override(descriptor))
|
||||
self.assertEqual(
|
||||
hashlib.sha256(override.encode("utf-8")).hexdigest(),
|
||||
"d6d1249e93b5bed5e697715b6b773c7efa4ac234f12eab57d868407e6bb5390e",
|
||||
"304377edf7d0e184fa4156475df74b7a807a47e4472c8d8e13102639eb9e9d0b",
|
||||
)
|
||||
self.assertIn("pull_policy: never", override)
|
||||
self.assertIn("N8N_USER_FOLDER: /home/node", override)
|
||||
|
|
@ -574,7 +580,7 @@ class EngineN8nPrivateExtensionTest(unittest.TestCase):
|
|||
|
||||
def test_sealed_release_exact_set_includes_implicit_directories(self):
|
||||
release_relative = Path(
|
||||
"payload/releases/n8n-nodes-ndc/0.1.4-59dc9f7882721d6a"
|
||||
"payload/releases/n8n-nodes-ndc/0.1.5-3c8ae53f010d7c88"
|
||||
)
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
work = Path(directory)
|
||||
|
|
|
|||
Loading…
Reference in New Issue