From 2315f88e1a080bdd888465af631ccfd8659fadea Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 13 Mar 2026 14:55:40 +0000 Subject: [PATCH 1/2] fix syncing of new credentials into different projects --- packages/project/src/merge/merge-project.ts | 4 + .../project/src/serialize/to-app-state.ts | 2 +- .../project/test/merge/merge-project.test.ts | 94 ++++++++++++++++--- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/packages/project/src/merge/merge-project.ts b/packages/project/src/merge/merge-project.ts index f1578fdef..20311bee6 100644 --- a/packages/project/src/merge/merge-project.ts +++ b/packages/project/src/merge/merge-project.ts @@ -144,6 +144,10 @@ export function merge( options.mode === SANDBOX_MERGE ? { workflows: finalWorkflows, + credentials: replaceCredentials( + source.credentials, + target.credentials + ), } : { workflows: finalWorkflows, diff --git a/packages/project/src/serialize/to-app-state.ts b/packages/project/src/serialize/to-app-state.ts index 2b7602344..ebf9362d7 100644 --- a/packages/project/src/serialize/to-app-state.ts +++ b/packages/project/src/serialize/to-app-state.ts @@ -13,7 +13,7 @@ type Options = { format?: 'json' | 'yaml' }; const defaultJobProps = { // TODO why does the provisioner throw if these keys are not set? - // Ok, 90% of jobs will have a credenial, but it's still optional right? + // Ok, 90% of jobs will have a credential, but it's still optional right? keychain_credential_id: null, project_credential_id: null, }; diff --git a/packages/project/test/merge/merge-project.test.ts b/packages/project/test/merge/merge-project.test.ts index e495f7e32..f8932490f 100644 --- a/packages/project/test/merge/merge-project.test.ts +++ b/packages/project/test/merge/merge-project.test.ts @@ -39,7 +39,7 @@ const assignUUIDs = (workflow, generator = randomUUID) => ({ }), }); -const createProject = (workflow, id = 'a') => +const createProject = (workflow, id = 'a', extra = {}) => new Project({ id, name: id, @@ -47,6 +47,7 @@ const createProject = (workflow, id = 'a') => openfn: { uuid: randomUUID(), }, + ...extra, }); const createStep = (id, props) => ({ @@ -87,6 +88,69 @@ test('Preserve the name and UUID of the target project', (t) => { t.is(result.openfn.uuid, main.openfn.uuid); }); +// In this test,two projects with the same credential "id" +// but different UUIDs are merged +// This is less like a sandbox merge and more like a project-project merge +// It's important that existing credentials are preserved, but new ones are added +test('Merge new credentials into the target', (t) => { + // create a base workflow + const wf = { + steps: [ + { + id: 'x', + name: 'X', + adaptor: 'common', + expression: 'fn(s => s)', + }, + ], + }; + + // step up two copies with UUIDS + const wf_a = assignUUIDs(wf); + const wf_b = assignUUIDs(wf); + + const main = createProject(wf_a, 'a', { + credentials: [ + { + name: 'a', + owner: 'admin@openfn.org', + uuid: '1234', + }, + ], + }); + const staging = createProject(wf_b, 'b', { + credentials: [ + { + name: 'a', + owner: 'admin@openfn.org', + uuid: '5678', + }, + { + name: 'b', + owner: 'admin@openfn.org', + uuid: '1111', + }, + ], + }); + + // merge staging into main + const result = merge(staging, main); + + t.deepEqual(result.credentials, [ + // Keep the original credential + { + name: 'a', + owner: 'admin@openfn.org', + uuid: '1234', + }, + // Add the new credential BUT without a UUID + { + name: 'b', + owner: 'admin@openfn.org', + }, + ]); +}); + test('replace mode: replace the name and UUID of the target project', (t) => { const wf = { steps: [ @@ -445,11 +509,11 @@ test('merge a new workflow', (t) => { const main = createProject([wf1], 'a'); const staging = createProject([wf1, wf2], 'b'); - t.is(main.workflows.length, 1) - t.is(staging.workflows.length, 2) + t.is(main.workflows.length, 1); + t.is(staging.workflows.length, 2); const result = merge(staging, main); - t.is(result.workflows.length, 2) + t.is(result.workflows.length, 2); }); test('merge a new workflow with onlyUpdated: true', (t) => { @@ -465,11 +529,11 @@ test('merge a new workflow with onlyUpdated: true', (t) => { const main = createProject([wf1], 'a'); const staging = createProject([wf1, wf2], 'b'); - t.is(main.workflows.length, 1) - t.is(staging.workflows.length, 2) + t.is(main.workflows.length, 1); + t.is(staging.workflows.length, 2); const result = merge(staging, main, { onlyUpdated: true }); - t.is(result.workflows.length, 2) + t.is(result.workflows.length, 2); }); test('remove a workflow', (t) => { @@ -484,12 +548,12 @@ test('remove a workflow', (t) => { const main = createProject([wf1, wf2], 'a'); const staging = createProject([wf1], 'b'); - - t.is(main.workflows.length, 2) - t.is(staging.workflows.length, 1) + + t.is(main.workflows.length, 2); + t.is(staging.workflows.length, 1); const result = merge(staging, main); - t.is(result.workflows.length, 1) + t.is(result.workflows.length, 1); }); test('remove a workflow with onlyUpdated: true', (t) => { @@ -504,12 +568,12 @@ test('remove a workflow with onlyUpdated: true', (t) => { const main = createProject([wf1, wf2], 'a'); const staging = createProject([wf1], 'b'); - - t.is(main.workflows.length, 2) - t.is(staging.workflows.length, 1) + + t.is(main.workflows.length, 2); + t.is(staging.workflows.length, 1); const result = merge(staging, main, { onlyUpdated: true }); - t.is(result.workflows.length, 1) + t.is(result.workflows.length, 1); }); test('id match: same workflow in source and target project', (t) => { From d255a78a573272a929cae63bc22c489f70a1d8f4 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 13 Mar 2026 14:56:32 +0000 Subject: [PATCH 2/2] versions --- integration-tests/cli/CHANGELOG.md | 8 ++++++++ integration-tests/cli/package.json | 2 +- packages/cli/CHANGELOG.md | 8 ++++++++ packages/cli/package.json | 2 +- packages/lightning-mock/CHANGELOG.md | 7 +++++++ packages/lightning-mock/package.json | 3 +-- packages/project/CHANGELOG.md | 6 ++++++ packages/project/package.json | 2 +- 8 files changed, 33 insertions(+), 5 deletions(-) diff --git a/integration-tests/cli/CHANGELOG.md b/integration-tests/cli/CHANGELOG.md index 5513eaa95..8c9817d9f 100644 --- a/integration-tests/cli/CHANGELOG.md +++ b/integration-tests/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @openfn/integration-tests-cli +## 1.0.17 + +### Patch Changes + +- Updated dependencies + - @openfn/project@0.14.3 + - @openfn/lightning-mock@2.4.6 + ## 1.0.16 ### Patch Changes diff --git a/integration-tests/cli/package.json b/integration-tests/cli/package.json index 1569cfc3e..9eae012bc 100644 --- a/integration-tests/cli/package.json +++ b/integration-tests/cli/package.json @@ -1,7 +1,7 @@ { "name": "@openfn/integration-tests-cli", "private": true, - "version": "1.0.16", + "version": "1.0.17", "description": "CLI integration tests", "author": "Open Function Group ", "license": "ISC", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index e366ce7dd..f70a2e4c5 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,13 @@ # @openfn/cli +## 1.30.2 + +### Patch Changes + +- Fix an issue where new credentials are not properly merged into sandboxes or projects +- Updated dependencies + - @openfn/project@0.14.3 + ## 1.30.1 ### Patch Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 254c11f90..a81fda5c9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/cli", - "version": "1.30.1", + "version": "1.30.2", "description": "CLI devtools for the OpenFn toolchain", "engines": { "node": ">=18", diff --git a/packages/lightning-mock/CHANGELOG.md b/packages/lightning-mock/CHANGELOG.md index 10b2d65ba..1ab7d3573 100644 --- a/packages/lightning-mock/CHANGELOG.md +++ b/packages/lightning-mock/CHANGELOG.md @@ -1,5 +1,12 @@ # @openfn/lightning-mock +## 2.4.6 + +### Patch Changes + +- Updated dependencies + - @openfn/project@0.14.3 + ## 2.4.5 ### Patch Changes diff --git a/packages/lightning-mock/package.json b/packages/lightning-mock/package.json index acd647801..b43fc5b82 100644 --- a/packages/lightning-mock/package.json +++ b/packages/lightning-mock/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/lightning-mock", - "version": "2.4.5", + "version": "2.4.6", "private": true, "description": "A mock Lightning server", "main": "dist/index.js", @@ -23,7 +23,6 @@ "@openfn/logger": "workspace:*", "@openfn/project": "workspace:*", "@openfn/runtime": "workspace:*", - "@openfn/project": "workspace:*", "@types/koa-logger": "^3.1.5", "@types/ws": "^8.18.1", "fast-safe-stringify": "^2.1.1", diff --git a/packages/project/CHANGELOG.md b/packages/project/CHANGELOG.md index c8d16aa47..a696aef27 100644 --- a/packages/project/CHANGELOG.md +++ b/packages/project/CHANGELOG.md @@ -1,5 +1,11 @@ # @openfn/project +## 0.14.3 + +### Patch Changes + +- Fix an issue where new credentials are not properly merged into sandboxes or projects + ## 0.14.2 ### Patch Changes diff --git a/packages/project/package.json b/packages/project/package.json index d1f2f5641..91298d5ba 100644 --- a/packages/project/package.json +++ b/packages/project/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/project", - "version": "0.14.2", + "version": "0.14.3", "description": "Read, serialize, replicate and sync OpenFn projects", "scripts": { "test": "pnpm ava",