From f1e8d7147db37d35a1a6dbd14a266d56d32d6c12 Mon Sep 17 00:00:00 2001 From: Frank Midigo Date: Fri, 30 Oct 2026 11:05:57 +0300 Subject: [PATCH] Keep target collection ids when deploying to a different project Sandbox merges took the local collections as-is, so every cross-project deploy sent freshly minted ids for collections the target already had, and Lightning rejected the update with a 500. Match them to the target's collections by name, as same-project deploys already do. Fixes #1606 --- .changeset/keep-collection-ids.md | 6 ++++++ packages/project/src/merge/merge-project.ts | 2 +- .../project/test/merge/merge-project.test.ts | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 .changeset/keep-collection-ids.md diff --git a/.changeset/keep-collection-ids.md b/.changeset/keep-collection-ids.md new file mode 100644 index 000000000..4990cbe62 --- /dev/null +++ b/.changeset/keep-collection-ids.md @@ -0,0 +1,6 @@ +--- +'@openfn/project': patch +'@openfn/cli': patch +--- + +Fix deploying to a different project failing once that project has collections diff --git a/packages/project/src/merge/merge-project.ts b/packages/project/src/merge/merge-project.ts index 566049bf4..8e1642990 100644 --- a/packages/project/src/merge/merge-project.ts +++ b/packages/project/src/merge/merge-project.ts @@ -170,7 +170,7 @@ export function merge( source.credentials, target.credentials ), - collections: source.collections, + collections: mergeCollections(source.collections, target.collections), channels: mergeChannels(source.channels, target.channels), } : { diff --git a/packages/project/test/merge/merge-project.test.ts b/packages/project/test/merge/merge-project.test.ts index 8671afeb1..766494f6a 100644 --- a/packages/project/test/merge/merge-project.test.ts +++ b/packages/project/test/merge/merge-project.test.ts @@ -384,7 +384,7 @@ test('replace mode: undefined local collections leave the target collections alo t.deepEqual(result.collections, targetCollections); }); -test('sandbox mode: source collections fully replace target collections', (t) => { +test('sandbox mode: source collections replace target collections, keeping target uuid on a name match', (t) => { const wf = { steps: [ { id: 'x', name: 'X', adaptor: 'common', expression: 'fn(s => s)' }, @@ -395,17 +395,25 @@ test('sandbox mode: source collections fully replace target collections', (t) => const targetCollections = [ { uuid: 'remote-uuid-1', name: 'target-collection' }, + { uuid: 'remote-uuid-2', name: 'shared-collection' }, + ]; + // the source's own uuids belong to a different project + const sourceCollections = [ + { name: 'source-collection' }, + { uuid: 'source-uuid', name: 'shared-collection' }, ]; - const sourceCollections = [{ name: 'source-collection' }]; const target = createProject(wf_a, 'a', { collections: targetCollections }); const source = createProject(wf_b, 'b', { collections: sourceCollections }); const result = merge(source, target, { mode: SANDBOX_MERGE }); - // b's collections are wholly replaced by a's - sandbox mode does a raw - // swap here, unlike replace mode's name-matching mergeCollections - t.deepEqual(result.collections, sourceCollections); + // a shared collection must keep the target's uuid, or the deploy tries to + // create a duplicate and Lightning rejects it + t.deepEqual(result.collections, [ + { name: 'source-collection', uuid: undefined }, + { name: 'shared-collection', uuid: 'remote-uuid-2' }, + ]); }); test('sandbox mode: target channels are preserved when source has none', (t) => {