From 58a9919ca1bcae12bcfcf40dcc8620dbb2ee4ddc Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 10:16:24 +0000 Subject: [PATCH 1/8] ensure added and removed projects are properly handlded on merge --- .changeset/angry-sloths-smash.md | 5 ++ packages/project/src/merge/merge-project.ts | 8 +- .../src/util/find-changed-workflows.ts | 15 +++- .../project/test/merge/merge-project.test.ts | 80 +++++++++++++++++++ .../test/util/find-changed-workflows.test.ts | 31 +++++++ 5 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 .changeset/angry-sloths-smash.md diff --git a/.changeset/angry-sloths-smash.md b/.changeset/angry-sloths-smash.md new file mode 100644 index 000000000..38c72a42e --- /dev/null +++ b/.changeset/angry-sloths-smash.md @@ -0,0 +1,5 @@ +--- +'@openfn/project': patch +--- + +Fix an issue where added and removed workflows are ignored by merge diff --git a/packages/project/src/merge/merge-project.ts b/packages/project/src/merge/merge-project.ts index f1578fdef..a2a2675f7 100644 --- a/packages/project/src/merge/merge-project.ts +++ b/packages/project/src/merge/merge-project.ts @@ -166,11 +166,9 @@ export function merge( ), collections: source.collections ?? target.collections, }; - - // with project level props merging, target goes into source because we want to preserve the target props. - return new Project( - baseMerge(target, source, ['collections'], assigns as any) - ); + + // with project level props merging, target goes into source because we want to preserve the target props. + return new Project(baseMerge(target, source, ['collections'], assigns as any)); } export const replaceCredentials = ( diff --git a/packages/project/src/util/find-changed-workflows.ts b/packages/project/src/util/find-changed-workflows.ts index baa61b2f2..99c7c2626 100644 --- a/packages/project/src/util/find-changed-workflows.ts +++ b/packages/project/src/util/find-changed-workflows.ts @@ -1,8 +1,9 @@ import Project from '../Project'; +import type Workflow from '../Workflow'; import { generateHash } from './version'; /** - * For a give Project, identify which workflows have changed + * For a given Project, identify which workflows have changed * Uses forked_from as the base, or history if that's unavailable */ export default (project: Project) => { @@ -23,8 +24,20 @@ export default (project: Project) => { if (hash !== base[wf.id]) { changed.push(wf); } + delete base[wf.id] + } else { + // If a workflow doens't appear in forked_from, we assume it's new + // (and so changed!) + changed.push(wf); } } + // Anything in forked_from that hasn't been handled + // must have been removed (and so changed!) + for(const removedId in base) { + changed.push({ id: removedId, $deleted: true } as unknown as Workflow) + } + + return changed; }; diff --git a/packages/project/test/merge/merge-project.test.ts b/packages/project/test/merge/merge-project.test.ts index 1ed4af421..e495f7e32 100644 --- a/packages/project/test/merge/merge-project.test.ts +++ b/packages/project/test/merge/merge-project.test.ts @@ -432,6 +432,86 @@ test('should merge two projects and preserve edge id', (t) => { t.is(target.getUUID('a-b'), resultEdge.openfn.uuid); }); +test('merge a new workflow', (t) => { + const wf1 = assignUUIDs({ + name: 'wf1', + steps: [], + }); + // Wf2 has no UUIDs + const wf2 = { + name: 'wf2', + steps: [], + }; + + const main = createProject([wf1], 'a'); + const staging = createProject([wf1, wf2], 'b'); + t.is(main.workflows.length, 1) + t.is(staging.workflows.length, 2) + + const result = merge(staging, main); + t.is(result.workflows.length, 2) +}); + +test('merge a new workflow with onlyUpdated: true', (t) => { + const wf1 = assignUUIDs({ + name: 'wf1', + steps: [], + }); + // Wf2 has no UUIDs + const wf2 = { + name: 'wf2', + steps: [], + }; + + const main = createProject([wf1], 'a'); + const staging = createProject([wf1, wf2], 'b'); + 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) +}); + +test('remove a workflow', (t) => { + const wf1 = assignUUIDs({ + name: 'wf1', + steps: [], + }); + const wf2 = assignUUIDs({ + name: 'wf2', + steps: [], + }); + + const main = createProject([wf1, wf2], 'a'); + const staging = createProject([wf1], 'b'); + + t.is(main.workflows.length, 2) + t.is(staging.workflows.length, 1) + + const result = merge(staging, main); + t.is(result.workflows.length, 1) +}); + +test('remove a workflow with onlyUpdated: true', (t) => { + const wf1 = assignUUIDs({ + name: 'wf1', + steps: [], + }); + const wf2 = assignUUIDs({ + name: 'wf2', + steps: [], + }); + + const main = createProject([wf1, wf2], 'a'); + const staging = createProject([wf1], 'b'); + + 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) +}); + test('id match: same workflow in source and target project', (t) => { const source = generateWorkflow('a-b'); const target = generateWorkflow('a-b'); diff --git a/packages/project/test/util/find-changed-workflows.test.ts b/packages/project/test/util/find-changed-workflows.test.ts index c3b144ae9..2ccd969cc 100644 --- a/packages/project/test/util/find-changed-workflows.test.ts +++ b/packages/project/test/util/find-changed-workflows.test.ts @@ -36,6 +36,37 @@ test('should return 1 changed workflows from forked_from', (t) => { t.is(changed[0].id, 'b'); }); +test('should return 1 removed workflow', (t) => { + const project = generateProject('proj', ['@id a a-b', '@id b x-y']); + const [a, b] = project.workflows; + + project.cli.forked_from = { + [a.id]: generateHash(a), + [b.id]: generateHash(b), + }; + + // remove workflow b + project.workflows.pop() + + const changed = findChangedWorkflows(project); + t.is(changed.length, 1); + t.is(changed[0].id, 'b'); +}); + +test('should return 1 added workflow', (t) => { + const project = generateProject('proj', ['@id a a-b', '@id b x-y']); + const [a, b] = project.workflows; + + project.cli.forked_from = { + [a.id]: generateHash(a), + // Do not include b in forked_from - it's new! + }; + + const changed = findChangedWorkflows(project); + t.is(changed.length, 1); + t.is(changed[0].id, 'b'); +}); + test.todo('changed from history'); test.todo('multiple changed workflows'); test.todo('if no base available, assume a change'); From 66e7c13d461bbc3631ef7de42a9fefe02281e849 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 12:00:57 +0000 Subject: [PATCH 2/8] fix sync for when pushing to a different remote --- .changeset/petite-spiders-boil.md | 5 ++ packages/cli/src/projects/deploy.ts | 88 ++++++++++++++++++----------- packages/cli/src/projects/merge.ts | 4 +- 3 files changed, 62 insertions(+), 35 deletions(-) create mode 100644 .changeset/petite-spiders-boil.md diff --git a/.changeset/petite-spiders-boil.md b/.changeset/petite-spiders-boil.md new file mode 100644 index 000000000..03d9c5c8d --- /dev/null +++ b/.changeset/petite-spiders-boil.md @@ -0,0 +1,5 @@ +--- +'@openfn/cli': patch +--- + +Fix deploy when the target project is a different UUID to the local project diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index 8e9754679..af394a02f 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -22,6 +22,8 @@ import type { Provisioner } from '@openfn/lexicon/lightning'; import type { Logger } from '../util/logger'; import type { Opts } from '../options'; +const DEFAULT_ENDPOINT = 'https://app.openfn.org'; + export type DeployOptions = Pick< Opts, | 'apiKey' @@ -63,7 +65,7 @@ const printProjectName = (project: Project) => `${project.id} (${project.openfn?.uuid || ''})`; export const command: yargs.CommandModule = { - command: 'deploy', + command: 'deploy [project]', aliases: 'push', describe: `Deploy the checked out project to a Lightning Instance`, builder: (yargs: yargs.Argv) => @@ -74,7 +76,11 @@ export const command: yargs.CommandModule = { }) .example( 'deploy', - 'Deploy the checkout project to the connected instance' + 'Deploy the checked-out project its connected remoteinstance' + ) + .example( + 'deploy staging', + 'Deploy the checkout-out project to the remote project with alias "staging"' ), handler: ensure('project-deploy', options), }; @@ -97,9 +103,11 @@ export const hasRemoteDiverged = ( if (wf.id in refs) { const forkedVersion = refs[wf.id]; const remoteVersion = remote.getWorkflow(wf.id)?.history.at(-1); - if (!versionsEqual(forkedVersion, remoteVersion!)) { - diverged ??= []; - diverged.push(wf.id); + if (remoteVersion) { + if (!versionsEqual(forkedVersion, remoteVersion!)) { + diverged ??= []; + diverged.push(wf.id); + } } } else { // TODO what if there's no forked from for this workflow? @@ -120,26 +128,34 @@ const syncProjects = async ( config: Required, ws: Workspace, localProject: Project, + trackedProject: Project, // the project we want to update logger: Logger ): Promise => { - logger.info('Attempting to load checked-out project from workspace'); - + // First step, fetch the latest version and write // this may throw! let remoteProject: Project; try { + + + logger.info('Fetching remote target ', printProjectName(trackedProject)); + // TODO should we prefer endpoint over alias? + // maybe if it's explicitly passed? + const endpoint = trackedProject.openfn.endpoint ?? config.endpoint + + // TODO we need to look up the remote based on the alias const { data } = await fetchProject( - config.endpoint, + endpoint, config.apiKey, - localProject.uuid ?? localProject.id, + trackedProject.uuid, logger ); remoteProject = await Project.from('state', data!, { - endpoint: config.endpoint, + endpoint: endpoint, }); - logger.success('Downloaded latest version of project at ', config.endpoint); + logger.success('Downloaded latest version of project at ', endpoint); } catch (e) { console.log(e); throw e; @@ -150,16 +166,18 @@ const syncProjects = async ( // this will actually happen later } - // warn if the remote UUID is different to the local UUID - // This shouldn't happen? - if (!options.force && localProject.uuid !== remoteProject.uuid) { - logger.error(`UUID conflict! + // This is bogus if doing a deploy-merge to a different project + // it's quite legit for local and remote to have different UUIDs + // warn if the remote UUID is different to the local UUID + // This shouldn't happen? + // if (!options.force && localProject.uuid !== remoteProject.uuid) { + // logger.error(`UUID conflict! -Your local project (${localProject.uuid}) has a different UUID to the remote project (${remoteProject.uuid}). + // Your local project (${localProject.uuid}) has a different UUID to the remote project (${remoteProject.uuid}). -Pass --force to override this error and deploy anyway.`); - process.exit(1); - } + // Pass --force to override this error and deploy anyway.`); + // process.exit(1); + // } const locallyChangedWorkflows = await findLocallyChangedWorkflows( ws, @@ -183,7 +201,7 @@ Pass --force to override this error and deploy anyway.`); // Skip divergence testing if the remote has no history in its workflows // (this will only happen on older versions of lightning) // TODO now maybe skip if there's no forked_from - const skipVersionTest = remoteProject.workflows.find( + const skipVersionTest = options.force || remoteProject.workflows.find( (wf) => wf.history.length === 0 ); @@ -222,10 +240,11 @@ Pass --force to override this error and deploy anyway.`); } logger.info('Merging changes into remote project'); - // TODO I would like to log which workflows are being updated const merged = Project.merge(localProject, remoteProject!, { - mode: 'replace', + // If pushing the same project, we use a replace strategy + // Otherwise, use the sandbox strategy to preserve UUIDs + mode: localProject.uuid === remoteProject.uuid ? 'replace' : 'sandbox', force: true, onlyUpdated: true, }); @@ -252,7 +271,15 @@ export async function handler(options: DeployOptions, logger: Logger) { name: options.name, }); + // Track the remote we want to target + // If the used passed a project alias, we need to use that + // Otherwise just sync with the local project + const tracker = ws.get(options.project ?? localProject.uuid); + let endpoint = tracker.openfn.endpoint; + if (options.new) { + endpoint = config.endpoint ?? localProject.openfn.endpoint ?? DEFAULT_ENDPOINT; + // reset all metadata localProject.openfn = { endpoint: config.endpoint, @@ -261,11 +288,11 @@ export async function handler(options: DeployOptions, logger: Logger) { // generate a credential map localProject.credentials = localProject.buildCredentialMap(); - logger.success(`Loaded local project ${printProjectName(localProject)}`); + logger.success(`Loaded checked-out project ${printProjectName(localProject)}`); const merged: Project = options.new ? localProject - : await syncProjects(options, config, ws, localProject, logger); + : await syncProjects(options, config, ws, localProject, tracker, logger); const state = merged.serialize('state', { format: 'json', @@ -279,9 +306,6 @@ export async function handler(options: DeployOptions, logger: Logger) { logger.debug(JSON.stringify(state, null, 2)); logger.debug(); - // TODO not totally sold on endpoint handling right now - config.endpoint ??= localProject.openfn?.endpoint!; - // TODO: I want to report diff HERE, after the merged state and stuff has been built if (options.dryRun) { @@ -294,7 +318,7 @@ export async function handler(options: DeployOptions, logger: Logger) { if (options.confirm) { if ( !(await logger.confirm( - `Ready to deploy changes to ${config.endpoint}?` + `Ready to deploy changes to ${endpoint}?` )) ) { logger.always('Cancelled deployment'); @@ -303,9 +327,9 @@ export async function handler(options: DeployOptions, logger: Logger) { } logger.info('Sending project to app...'); - + console.log(endpoint, config.apiKey) const { data: result } = await deployProject( - config.endpoint, + endpoint, config.apiKey, state, logger @@ -315,7 +339,7 @@ export async function handler(options: DeployOptions, logger: Logger) { 'state', result, { - endpoint: config.endpoint, + endpoint: endpoint, alias, }, merged.config @@ -332,7 +356,7 @@ export async function handler(options: DeployOptions, logger: Logger) { const fullFinalPath = await serialize(finalProject, finalOutputPath); logger.debug('Updated local project at ', fullFinalPath); - logger.success('Updated project at', config.endpoint); + logger.success('Updated project at', endpoint); } } diff --git a/packages/cli/src/projects/merge.ts b/packages/cli/src/projects/merge.ts index af237a646..6b471e39a 100644 --- a/packages/cli/src/projects/merge.ts +++ b/packages/cli/src/projects/merge.ts @@ -50,7 +50,7 @@ const options = [ const command: yargs.CommandModule = { command: 'merge ', describe: - 'Merges the specified project (by UUID, id or alias) into the currently checked out project', + 'Merges the currently checked-out project into the target project, and checks out the result. Does not update the remote project or local project.yaml file', handler: ensure('project-merge', options), builder: (yargs) => build(options, yargs), }; @@ -141,8 +141,6 @@ export const handler = async (options: MergeOptions, logger: Logger) => { logger.info('Checking out merged project to filesystem'); - // TODO support --no-checkout to merge without expanding - // Checkout after merge to expand updated files into filesystem await checkout( { From 8587a85d60a2a93d01c60b883e21e448dabe5524 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 12:01:25 +0000 Subject: [PATCH 3/8] remove comments --- packages/cli/src/projects/deploy.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index af394a02f..e90af284d 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -166,19 +166,6 @@ const syncProjects = async ( // this will actually happen later } - // This is bogus if doing a deploy-merge to a different project - // it's quite legit for local and remote to have different UUIDs - // warn if the remote UUID is different to the local UUID - // This shouldn't happen? - // if (!options.force && localProject.uuid !== remoteProject.uuid) { - // logger.error(`UUID conflict! - - // Your local project (${localProject.uuid}) has a different UUID to the remote project (${remoteProject.uuid}). - - // Pass --force to override this error and deploy anyway.`); - // process.exit(1); - // } - const locallyChangedWorkflows = await findLocallyChangedWorkflows( ws, localProject From e84e221d74cc44e59744bee0af1e34f4bfc2ccc5 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 12:05:27 +0000 Subject: [PATCH 4/8] update changeset --- .changeset/angry-sloths-smash.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.changeset/angry-sloths-smash.md b/.changeset/angry-sloths-smash.md index 38c72a42e..66de39979 100644 --- a/.changeset/angry-sloths-smash.md +++ b/.changeset/angry-sloths-smash.md @@ -1,5 +1,6 @@ --- '@openfn/project': patch +'@openfn/cli': patch --- Fix an issue where added and removed workflows are ignored by merge From 5ad1287aa22cb27dca13527f1be1a8b242727eea Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 13:49:42 +0000 Subject: [PATCH 5/8] formatting --- packages/cli/src/projects/deploy.ts | 27 ++++++++----------- packages/project/src/merge/merge-project.ts | 8 +++--- .../src/util/find-changed-workflows.ts | 7 +++-- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index e90af284d..d9449ee65 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -131,17 +131,14 @@ const syncProjects = async ( trackedProject: Project, // the project we want to update logger: Logger ): Promise => { - // First step, fetch the latest version and write // this may throw! let remoteProject: Project; try { - - logger.info('Fetching remote target ', printProjectName(trackedProject)); // TODO should we prefer endpoint over alias? // maybe if it's explicitly passed? - const endpoint = trackedProject.openfn.endpoint ?? config.endpoint + const endpoint = trackedProject.openfn.endpoint ?? config.endpoint; // TODO we need to look up the remote based on the alias const { data } = await fetchProject( @@ -188,9 +185,9 @@ const syncProjects = async ( // Skip divergence testing if the remote has no history in its workflows // (this will only happen on older versions of lightning) // TODO now maybe skip if there's no forked_from - const skipVersionTest = options.force || remoteProject.workflows.find( - (wf) => wf.history.length === 0 - ); + const skipVersionTest = + options.force || + remoteProject.workflows.find((wf) => wf.history.length === 0); if (skipVersionTest) { logger.warn( @@ -265,7 +262,8 @@ export async function handler(options: DeployOptions, logger: Logger) { let endpoint = tracker.openfn.endpoint; if (options.new) { - endpoint = config.endpoint ?? localProject.openfn.endpoint ?? DEFAULT_ENDPOINT; + endpoint = + config.endpoint ?? localProject.openfn.endpoint ?? DEFAULT_ENDPOINT; // reset all metadata localProject.openfn = { @@ -275,7 +273,9 @@ export async function handler(options: DeployOptions, logger: Logger) { // generate a credential map localProject.credentials = localProject.buildCredentialMap(); - logger.success(`Loaded checked-out project ${printProjectName(localProject)}`); + logger.success( + `Loaded checked-out project ${printProjectName(localProject)}` + ); const merged: Project = options.new ? localProject @@ -303,18 +303,14 @@ export async function handler(options: DeployOptions, logger: Logger) { // The following workflows will be updated if (options.confirm) { - if ( - !(await logger.confirm( - `Ready to deploy changes to ${endpoint}?` - )) - ) { + if (!(await logger.confirm(`Ready to deploy changes to ${endpoint}?`))) { logger.always('Cancelled deployment'); return false; } } logger.info('Sending project to app...'); - console.log(endpoint, config.apiKey) + console.log(endpoint, config.apiKey); const { data: result } = await deployProject( endpoint, config.apiKey, @@ -392,4 +388,3 @@ export const reportDiff = ( return diffs; }; -``; diff --git a/packages/project/src/merge/merge-project.ts b/packages/project/src/merge/merge-project.ts index a2a2675f7..f1578fdef 100644 --- a/packages/project/src/merge/merge-project.ts +++ b/packages/project/src/merge/merge-project.ts @@ -166,9 +166,11 @@ export function merge( ), collections: source.collections ?? target.collections, }; - - // with project level props merging, target goes into source because we want to preserve the target props. - return new Project(baseMerge(target, source, ['collections'], assigns as any)); + + // with project level props merging, target goes into source because we want to preserve the target props. + return new Project( + baseMerge(target, source, ['collections'], assigns as any) + ); } export const replaceCredentials = ( diff --git a/packages/project/src/util/find-changed-workflows.ts b/packages/project/src/util/find-changed-workflows.ts index 99c7c2626..ca22e5ccf 100644 --- a/packages/project/src/util/find-changed-workflows.ts +++ b/packages/project/src/util/find-changed-workflows.ts @@ -24,7 +24,7 @@ export default (project: Project) => { if (hash !== base[wf.id]) { changed.push(wf); } - delete base[wf.id] + delete base[wf.id]; } else { // If a workflow doens't appear in forked_from, we assume it's new // (and so changed!) @@ -34,10 +34,9 @@ export default (project: Project) => { // Anything in forked_from that hasn't been handled // must have been removed (and so changed!) - for(const removedId in base) { - changed.push({ id: removedId, $deleted: true } as unknown as Workflow) + for (const removedId in base) { + changed.push({ id: removedId, $deleted: true } as unknown as Workflow); } - return changed; }; From ccc4996cffb03c3435f575b29f265b88c2e1c578 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 13:57:56 +0000 Subject: [PATCH 6/8] types --- packages/cli/src/projects/deploy.ts | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index d9449ee65..c6f88d982 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -35,6 +35,7 @@ export type DeployOptions = Pick< | 'logJson' | 'confirm' > & { + project?: string; // this is a CLI positional arg, not an option workspace?: string; dryRun?: boolean; new?: boolean; @@ -76,7 +77,7 @@ export const command: yargs.CommandModule = { }) .example( 'deploy', - 'Deploy the checked-out project its connected remoteinstance' + 'Deploy the checked-out project its connected remote instance' ) .example( 'deploy staging', @@ -138,13 +139,13 @@ const syncProjects = async ( logger.info('Fetching remote target ', printProjectName(trackedProject)); // TODO should we prefer endpoint over alias? // maybe if it's explicitly passed? - const endpoint = trackedProject.openfn.endpoint ?? config.endpoint; + const endpoint = trackedProject.openfn?.endpoint ?? config.endpoint; // TODO we need to look up the remote based on the alias const { data } = await fetchProject( endpoint, config.apiKey, - trackedProject.uuid, + trackedProject.uuid!, logger ); @@ -258,12 +259,30 @@ export async function handler(options: DeployOptions, logger: Logger) { // Track the remote we want to target // If the used passed a project alias, we need to use that // Otherwise just sync with the local project - const tracker = ws.get(options.project ?? localProject.uuid); - let endpoint = tracker.openfn.endpoint; + const tracker = ws.get(options.project ?? localProject.uuid!); + + if (!tracker) { + // Is this really an error? Unlikely to happen I thuink + console.log( + `ERROR: Failed to find tracked remote project ${ + options.project ?? localProject.uuid! + } locally` + ); + console.log('To deploy a new project, add --new to the command'); + // TODO can we automate the fetch bit? + // If it's a UUID it should be ok? + console.log( + 'You may need to fetch the project before you can safely deploy' + ); + + throw new Error('Failed to find remote project locally'); + } + + let endpoint: string = tracker.openfn?.endpoint ?? ''; if (options.new) { endpoint = - config.endpoint ?? localProject.openfn.endpoint ?? DEFAULT_ENDPOINT; + config.endpoint ?? localProject.openfn?.endpoint ?? DEFAULT_ENDPOINT; // reset all metadata localProject.openfn = { From 91a213c4800369b5b1b2b432b10b2c7015516b6c Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 13:58:23 +0000 Subject: [PATCH 7/8] versions --- .changeset/angry-sloths-smash.md | 6 ------ .changeset/petite-spiders-boil.md | 5 ----- integration-tests/cli/CHANGELOG.md | 7 +++++++ integration-tests/cli/package.json | 2 +- packages/cli/CHANGELOG.md | 9 +++++++++ packages/cli/package.json | 2 +- packages/project/CHANGELOG.md | 6 ++++++ packages/project/package.json | 2 +- 8 files changed, 25 insertions(+), 14 deletions(-) delete mode 100644 .changeset/angry-sloths-smash.md delete mode 100644 .changeset/petite-spiders-boil.md diff --git a/.changeset/angry-sloths-smash.md b/.changeset/angry-sloths-smash.md deleted file mode 100644 index 66de39979..000000000 --- a/.changeset/angry-sloths-smash.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@openfn/project': patch -'@openfn/cli': patch ---- - -Fix an issue where added and removed workflows are ignored by merge diff --git a/.changeset/petite-spiders-boil.md b/.changeset/petite-spiders-boil.md deleted file mode 100644 index 03d9c5c8d..000000000 --- a/.changeset/petite-spiders-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@openfn/cli': patch ---- - -Fix deploy when the target project is a different UUID to the local project diff --git a/integration-tests/cli/CHANGELOG.md b/integration-tests/cli/CHANGELOG.md index 2037b71e0..f22a475d8 100644 --- a/integration-tests/cli/CHANGELOG.md +++ b/integration-tests/cli/CHANGELOG.md @@ -1,5 +1,12 @@ # @openfn/integration-tests-cli +## 1.0.15 + +### Patch Changes + +- Updated dependencies [58a9919] + - @openfn/project@0.14.1 + ## 1.0.14 ### Patch Changes diff --git a/integration-tests/cli/package.json b/integration-tests/cli/package.json index b60284eaa..6ad5957c3 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.14", + "version": "1.0.15", "description": "CLI integration tests", "author": "Open Function Group ", "license": "ISC", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f42b3540f..6a71eecbf 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,14 @@ # @openfn/cli +## 1.28.1 + +### Patch Changes + +- 58a9919: Fix an issue where added and removed workflows are ignored by merge +- 66e7c13: Fix deploy when the target project is a different UUID to the local project +- Updated dependencies [58a9919] + - @openfn/project@0.14.1 + ## 1.28.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 7fdfd0f05..7863b80b4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/cli", - "version": "1.28.0", + "version": "1.28.1", "description": "CLI devtools for the OpenFn toolchain", "engines": { "node": ">=18", diff --git a/packages/project/CHANGELOG.md b/packages/project/CHANGELOG.md index 009374793..60ddd603a 100644 --- a/packages/project/CHANGELOG.md +++ b/packages/project/CHANGELOG.md @@ -1,5 +1,11 @@ # @openfn/project +## 0.14.1 + +### Patch Changes + +- 58a9919: Fix an issue where added and removed workflows are ignored by merge + ## 0.14.0 ### Minor Changes diff --git a/packages/project/package.json b/packages/project/package.json index 46110dd89..473fbffb3 100644 --- a/packages/project/package.json +++ b/packages/project/package.json @@ -1,6 +1,6 @@ { "name": "@openfn/project", - "version": "0.14.0", + "version": "0.14.1", "description": "Read, serialize, replicate and sync OpenFn projects", "scripts": { "test": "pnpm ava", From 854094f4bc84729e4563100ea999211af8d5a9b5 Mon Sep 17 00:00:00 2001 From: Joe Clark Date: Fri, 20 Feb 2026 14:01:53 +0000 Subject: [PATCH 8/8] self review tidyups --- packages/cli/src/projects/deploy.ts | 5 ++--- packages/cli/src/projects/util.ts | 3 ++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/projects/deploy.ts b/packages/cli/src/projects/deploy.ts index c6f88d982..41a655759 100644 --- a/packages/cli/src/projects/deploy.ts +++ b/packages/cli/src/projects/deploy.ts @@ -22,7 +22,7 @@ import type { Provisioner } from '@openfn/lexicon/lightning'; import type { Logger } from '../util/logger'; import type { Opts } from '../options'; -const DEFAULT_ENDPOINT = 'https://app.openfn.org'; +export const DEFAULT_ENDPOINT = 'https://app.openfn.org'; export type DeployOptions = Pick< Opts, @@ -141,7 +141,6 @@ const syncProjects = async ( // maybe if it's explicitly passed? const endpoint = trackedProject.openfn?.endpoint ?? config.endpoint; - // TODO we need to look up the remote based on the alias const { data } = await fetchProject( endpoint, config.apiKey, @@ -329,7 +328,7 @@ export async function handler(options: DeployOptions, logger: Logger) { } logger.info('Sending project to app...'); - console.log(endpoint, config.apiKey); + const { data: result } = await deployProject( endpoint, config.apiKey, diff --git a/packages/cli/src/projects/util.ts b/packages/cli/src/projects/util.ts index 47061656b..d325ec7ec 100644 --- a/packages/cli/src/projects/util.ts +++ b/packages/cli/src/projects/util.ts @@ -9,6 +9,7 @@ import { CLIError } from '../errors'; import resolvePath from '../util/resolve-path'; import { rimraf } from 'rimraf'; import { versionsEqual, Workspace } from '@openfn/project'; +import { DEFAULT_ENDPOINT } from './deploy'; export type AuthOptions = Pick; @@ -20,7 +21,7 @@ export const loadAppAuthConfig = ( const config: AuthOptions = { apiKey: options.apiKey, - endpoint: options.endpoint ?? 'https://app.openfn.org', + endpoint: options.endpoint ?? DEFAULT_ENDPOINT, }; if (!options.apiKey && OPENFN_API_KEY) {