@openfn/cli 1.27.0 → 1.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1255,31 +1255,65 @@ var command2 = {
1255
1255
  var version_default = command2;
1256
1256
 
1257
1257
  // src/projects/merge.ts
1258
- import Project3, { Workspace as Workspace5 } from "@openfn/project";
1258
+ import Project4, { Workspace as Workspace6 } from "@openfn/project";
1259
1259
 
1260
1260
  // src/projects/checkout.ts
1261
- import Project2, { Workspace as Workspace4 } from "@openfn/project";
1261
+ import Project3, { Workspace as Workspace5 } from "@openfn/project";
1262
1262
  import { rimraf as rimraf2 } from "rimraf";
1263
1263
 
1264
1264
  // src/projects/util.ts
1265
1265
  import { rimraf } from "rimraf";
1266
- import { versionsEqual } from "@openfn/project";
1266
+ import { versionsEqual as versionsEqual2 } from "@openfn/project";
1267
1267
 
1268
- // src/projects/checkout.ts
1269
- var options11 = [log, workspace, clean, force];
1268
+ // src/projects/deploy.ts
1269
+ import Project2, { versionsEqual, Workspace as Workspace3 } from "@openfn/project";
1270
+ import c2 from "chalk";
1271
+ var options11 = [
1272
+ env2,
1273
+ workspace,
1274
+ dryRun2,
1275
+ newProject,
1276
+ name,
1277
+ alias,
1278
+ apiKey,
1279
+ endpoint,
1280
+ log,
1281
+ logJson,
1282
+ snapshots,
1283
+ force,
1284
+ confirm
1285
+ ];
1270
1286
  var command3 = {
1287
+ command: "deploy [project]",
1288
+ aliases: "push",
1289
+ describe: `Deploy the checked out project to a Lightning Instance`,
1290
+ builder: (yargs2) => build(options11, yargs2).positional("project", {
1291
+ describe: "The UUID, local id or local alias of the project to deploy to"
1292
+ }).example(
1293
+ "deploy",
1294
+ "Deploy the checked-out project its connected remote instance"
1295
+ ).example(
1296
+ "deploy staging",
1297
+ 'Deploy the checkout-out project to the remote project with alias "staging"'
1298
+ ),
1299
+ handler: ensure("project-deploy", options11)
1300
+ };
1301
+
1302
+ // src/projects/checkout.ts
1303
+ var options12 = [log, workspace, clean, force];
1304
+ var command4 = {
1271
1305
  command: "checkout <project>",
1272
1306
  describe: "Switch to a different OpenFn project in the same workspace",
1273
- handler: ensure("project-checkout", options11),
1274
- builder: (yargs2) => build(options11, yargs2).positional("project", {
1307
+ handler: ensure("project-checkout", options12),
1308
+ builder: (yargs2) => build(options12, yargs2).positional("project", {
1275
1309
  describe: "The id, alias or UUID of the project to checkout",
1276
1310
  demandOption: true
1277
1311
  })
1278
1312
  };
1279
- var checkout_default = command3;
1313
+ var checkout_default = command4;
1280
1314
 
1281
1315
  // src/projects/merge.ts
1282
- var options12 = [
1316
+ var options13 = [
1283
1317
  removeUnmapped,
1284
1318
  workflowMappings,
1285
1319
  workspace,
@@ -1304,17 +1338,17 @@ var options12 = [
1304
1338
  description: "Force a merge even when workflows are incompatible"
1305
1339
  })
1306
1340
  ];
1307
- var command4 = {
1341
+ var command5 = {
1308
1342
  command: "merge <project>",
1309
- describe: "Merges the specified project (by UUID, id or alias) into the currently checked out project",
1310
- handler: ensure("project-merge", options12),
1311
- builder: (yargs2) => build(options12, yargs2)
1343
+ describe: "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",
1344
+ handler: ensure("project-merge", options13),
1345
+ builder: (yargs2) => build(options13, yargs2)
1312
1346
  };
1313
- var merge_default = command4;
1347
+ var merge_default = command5;
1314
1348
 
1315
1349
  // src/projects/fetch.ts
1316
- import Project4, { Workspace as Workspace6 } from "@openfn/project";
1317
- var options13 = [
1350
+ import Project5, { Workspace as Workspace7 } from "@openfn/project";
1351
+ var options14 = [
1318
1352
  alias,
1319
1353
  apiKey,
1320
1354
  endpoint,
@@ -1330,22 +1364,22 @@ var options13 = [
1330
1364
  workspace,
1331
1365
  format
1332
1366
  ];
1333
- var command5 = {
1367
+ var command6 = {
1334
1368
  command: "fetch [project]",
1335
1369
  describe: `Download the latest version of a project from a lightning server (does not expand the project, use checkout)`,
1336
- builder: (yargs2) => build(options13, yargs2).positional("project", {
1370
+ builder: (yargs2) => build(options14, yargs2).positional("project", {
1337
1371
  describe: "The id, alias or UUID of the project to fetch. If not set, will default to the active project"
1338
1372
  }).example(
1339
1373
  "fetch 57862287-23e6-4650-8d79-e1dd88b24b1c",
1340
1374
  "Fetch an updated copy of a the above spec and state from a Lightning Instance"
1341
1375
  ),
1342
- handler: ensure("project-fetch", options13)
1376
+ handler: ensure("project-fetch", options14)
1343
1377
  };
1344
- var fetch_default = command5;
1378
+ var fetch_default = command6;
1345
1379
 
1346
1380
  // src/projects/pull.ts
1347
- import { Workspace as Workspace7 } from "@openfn/project";
1348
- var options14 = [
1381
+ import { Workspace as Workspace8 } from "@openfn/project";
1382
+ var options15 = [
1349
1383
  alias,
1350
1384
  env2,
1351
1385
  workspace,
@@ -1360,47 +1394,16 @@ var options14 = [
1360
1394
  path2,
1361
1395
  force
1362
1396
  ];
1363
- var command6 = {
1397
+ var command7 = {
1364
1398
  command: "pull [project]",
1365
1399
  describe: `Pull a project from a Lightning Instance and expand to the file system (ie fetch + checkout)`,
1366
- builder: (yargs2) => build(options14, yargs2).positional("project", {
1400
+ builder: (yargs2) => build(options15, yargs2).positional("project", {
1367
1401
  describe: "The UUID, local id or local alias of the project to pull"
1368
1402
  }).example(
1369
1403
  "pull 57862287-23e6-4650-8d79-e1dd88b24b1c",
1370
1404
  "Pull project with a UUID from a lightning instance"
1371
1405
  ),
1372
- handler: ensure("project-pull", options14)
1373
- };
1374
-
1375
- // src/projects/deploy.ts
1376
- import Project5, { versionsEqual as versionsEqual2, Workspace as Workspace8 } from "@openfn/project";
1377
- import c2 from "chalk";
1378
- var options15 = [
1379
- env2,
1380
- workspace,
1381
- dryRun2,
1382
- newProject,
1383
- name,
1384
- alias,
1385
- apiKey,
1386
- endpoint,
1387
- log,
1388
- logJson,
1389
- snapshots,
1390
- force,
1391
- confirm
1392
- ];
1393
- var command7 = {
1394
- command: "deploy",
1395
- aliases: "push",
1396
- describe: `Deploy the checked out project to a Lightning Instance`,
1397
- builder: (yargs2) => build(options15, yargs2).positional("project", {
1398
- describe: "The UUID, local id or local alias of the project to deploy to"
1399
- }).example(
1400
- "deploy",
1401
- "Deploy the checkout project to the connected instance"
1402
- ),
1403
- handler: ensure("project-deploy", options15)
1406
+ handler: ensure("project-pull", options15)
1404
1407
  };
1405
1408
 
1406
1409
  // src/projects/command.ts
@@ -1410,7 +1413,7 @@ var projectsCommand = {
1410
1413
  describe: "Sync and manage an OpenFn project",
1411
1414
  handler: () => {
1412
1415
  },
1413
- builder: (yargs2) => yargs2.command(command6).command(command7).command(list_default).command(version_default).command(merge_default).command(checkout_default).command(fetch_default).example("project", "list all projects in the workspace").example("project list", "list all projects in the workspace").example(
1416
+ builder: (yargs2) => yargs2.command(command7).command(command3).command(list_default).command(version_default).command(merge_default).command(checkout_default).command(fetch_default).example("project", "list all projects in the workspace").example("project list", "list all projects in the workspace").example(
1414
1417
  "project checkout staging",
1415
1418
  "Checkout the project with id staging"
1416
1419
  ).example(
@@ -2072,7 +2072,7 @@ var loadAppAuthConfig = (options8, logger) => {
2072
2072
  const { OPENFN_API_KEY, OPENFN_ENDPOINT } = process.env;
2073
2073
  const config2 = {
2074
2074
  apiKey: options8.apiKey,
2075
- endpoint: options8.endpoint ?? "https://app.openfn.org"
2075
+ endpoint: options8.endpoint ?? DEFAULT_ENDPOINT
2076
2076
  };
2077
2077
  if (!options8.apiKey && OPENFN_API_KEY) {
2078
2078
  logger.info("Using OPENFN_API_KEY environment variable");
@@ -2304,6 +2304,7 @@ var override = (command8, yargs) => {
2304
2304
  };
2305
2305
 
2306
2306
  // src/projects/deploy.ts
2307
+ var DEFAULT_ENDPOINT = "https://app.openfn.org";
2307
2308
  var options = [
2308
2309
  env,
2309
2310
  workspace,
@@ -2321,14 +2322,17 @@ var options = [
2321
2322
  ];
2322
2323
  var printProjectName = (project) => `${project.id} (${project.openfn?.uuid || "<no UUID>"})`;
2323
2324
  var command = {
2324
- command: "deploy",
2325
+ command: "deploy [project]",
2325
2326
  aliases: "push",
2326
2327
  describe: `Deploy the checked out project to a Lightning Instance`,
2327
2328
  builder: (yargs) => build(options, yargs).positional("project", {
2328
2329
  describe: "The UUID, local id or local alias of the project to deploy to"
2329
2330
  }).example(
2330
2331
  "deploy",
2331
- "Deploy the checkout project to the connected instance"
2332
+ "Deploy the checked-out project its connected remote instance"
2333
+ ).example(
2334
+ "deploy staging",
2335
+ 'Deploy the checkout-out project to the remote project with alias "staging"'
2332
2336
  ),
2333
2337
  handler: ensure("project-deploy", options)
2334
2338
  };
@@ -2340,41 +2344,36 @@ var hasRemoteDiverged = (local, remote, workflows = []) => {
2340
2344
  if (wf.id in refs) {
2341
2345
  const forkedVersion = refs[wf.id];
2342
2346
  const remoteVersion = remote.getWorkflow(wf.id)?.history.at(-1);
2343
- if (!versionsEqual2(forkedVersion, remoteVersion)) {
2344
- diverged ??= [];
2345
- diverged.push(wf.id);
2347
+ if (remoteVersion) {
2348
+ if (!versionsEqual2(forkedVersion, remoteVersion)) {
2349
+ diverged ??= [];
2350
+ diverged.push(wf.id);
2351
+ }
2346
2352
  }
2347
2353
  } else {
2348
2354
  }
2349
2355
  }
2350
2356
  return diverged;
2351
2357
  };
2352
- var syncProjects = async (options8, config2, ws, localProject, logger) => {
2353
- logger.info("Attempting to load checked-out project from workspace");
2358
+ var syncProjects = async (options8, config2, ws, localProject, trackedProject, logger) => {
2354
2359
  let remoteProject;
2355
2360
  try {
2361
+ logger.info("Fetching remote target ", printProjectName(trackedProject));
2362
+ const endpoint2 = trackedProject.openfn?.endpoint ?? config2.endpoint;
2356
2363
  const { data } = await fetchProject(
2357
- config2.endpoint,
2364
+ endpoint2,
2358
2365
  config2.apiKey,
2359
- localProject.uuid ?? localProject.id,
2366
+ trackedProject.uuid,
2360
2367
  logger
2361
2368
  );
2362
2369
  remoteProject = await Project.from("state", data, {
2363
- endpoint: config2.endpoint
2370
+ endpoint: endpoint2
2364
2371
  });
2365
- logger.success("Downloaded latest version of project at ", config2.endpoint);
2372
+ logger.success("Downloaded latest version of project at ", endpoint2);
2366
2373
  } catch (e) {
2367
2374
  console.log(e);
2368
2375
  throw e;
2369
2376
  }
2370
- if (!options8.force && localProject.uuid !== remoteProject.uuid) {
2371
- logger.error(`UUID conflict!
2372
-
2373
- Your local project (${localProject.uuid}) has a different UUID to the remote project (${remoteProject.uuid}).
2374
-
2375
- Pass --force to override this error and deploy anyway.`);
2376
- process.exit(1);
2377
- }
2378
2377
  const locallyChangedWorkflows = await findLocallyChangedWorkflows(
2379
2378
  ws,
2380
2379
  localProject
@@ -2389,9 +2388,7 @@ Pass --force to override this error and deploy anyway.`);
2389
2388
  logger.success("Nothing to deploy");
2390
2389
  process.exit(0);
2391
2390
  }
2392
- const skipVersionTest = remoteProject.workflows.find(
2393
- (wf) => wf.history.length === 0
2394
- );
2391
+ const skipVersionTest = options8.force || remoteProject.workflows.find((wf) => wf.history.length === 0);
2395
2392
  if (skipVersionTest) {
2396
2393
  logger.warn(
2397
2394
  "Skipping compatibility check as no local version history detected"
@@ -2427,7 +2424,9 @@ Pass --force to override this error and deploy anyway.`);
2427
2424
  }
2428
2425
  logger.info("Merging changes into remote project");
2429
2426
  const merged = Project.merge(localProject, remoteProject, {
2430
- mode: "replace",
2427
+ // If pushing the same project, we use a replace strategy
2428
+ // Otherwise, use the sandbox strategy to preserve UUIDs
2429
+ mode: localProject.uuid === remoteProject.uuid ? "replace" : "sandbox",
2431
2430
  force: true,
2432
2431
  onlyUpdated: true
2433
2432
  });
@@ -2446,34 +2445,47 @@ async function handler(options8, logger) {
2446
2445
  alias: alias2,
2447
2446
  name: options8.name
2448
2447
  });
2448
+ const tracker = ws.get(options8.project ?? localProject.uuid);
2449
+ if (!tracker) {
2450
+ console.log(
2451
+ `ERROR: Failed to find tracked remote project ${options8.project ?? localProject.uuid} locally`
2452
+ );
2453
+ console.log("To deploy a new project, add --new to the command");
2454
+ console.log(
2455
+ "You may need to fetch the project before you can safely deploy"
2456
+ );
2457
+ throw new Error("Failed to find remote project locally");
2458
+ }
2459
+ let endpoint2 = tracker.openfn?.endpoint ?? "";
2449
2460
  if (options8.new) {
2461
+ endpoint2 = config2.endpoint ?? localProject.openfn?.endpoint ?? DEFAULT_ENDPOINT;
2450
2462
  localProject.openfn = {
2451
2463
  endpoint: config2.endpoint
2452
2464
  };
2453
2465
  }
2454
- logger.success(`Loaded local project ${printProjectName(localProject)}`);
2455
- const merged = options8.new ? localProject : await syncProjects(options8, config2, ws, localProject, logger);
2466
+ localProject.credentials = localProject.buildCredentialMap();
2467
+ logger.success(
2468
+ `Loaded checked-out project ${printProjectName(localProject)}`
2469
+ );
2470
+ const merged = options8.new ? localProject : await syncProjects(options8, config2, ws, localProject, tracker, logger);
2456
2471
  const state = merged.serialize("state", {
2457
2472
  format: "json"
2458
2473
  });
2459
2474
  logger.debug("Provisioner state ready to upload:");
2460
2475
  logger.debug(JSON.stringify(state, null, 2));
2461
2476
  logger.debug();
2462
- config2.endpoint ??= localProject.openfn?.endpoint;
2463
2477
  if (options8.dryRun) {
2464
2478
  logger.always("dryRun option set: skipping upload step");
2465
2479
  } else {
2466
2480
  if (options8.confirm) {
2467
- if (!await logger.confirm(
2468
- `Ready to deploy changes to ${config2.endpoint}?`
2469
- )) {
2481
+ if (!await logger.confirm(`Ready to deploy changes to ${endpoint2}?`)) {
2470
2482
  logger.always("Cancelled deployment");
2471
2483
  return false;
2472
2484
  }
2473
2485
  }
2474
2486
  logger.info("Sending project to app...");
2475
2487
  const { data: result } = await deployProject(
2476
- config2.endpoint,
2488
+ endpoint2,
2477
2489
  config2.apiKey,
2478
2490
  state,
2479
2491
  logger
@@ -2482,7 +2494,7 @@ async function handler(options8, logger) {
2482
2494
  "state",
2483
2495
  result,
2484
2496
  {
2485
- endpoint: config2.endpoint,
2497
+ endpoint: endpoint2,
2486
2498
  alias: alias2
2487
2499
  },
2488
2500
  merged.config
@@ -2496,7 +2508,7 @@ async function handler(options8, logger) {
2496
2508
  const finalOutputPath = getSerializePath(localProject, options8.workspace);
2497
2509
  const fullFinalPath = await serialize(finalProject, finalOutputPath);
2498
2510
  logger.debug("Updated local project at ", fullFinalPath);
2499
- logger.success("Updated project at", config2.endpoint);
2511
+ logger.success("Updated project at", endpoint2);
2500
2512
  }
2501
2513
  }
2502
2514
  var reportDiff = (local, remote, locallyChangedWorkflows, logger) => {
@@ -3591,7 +3603,7 @@ var options7 = [
3591
3603
  ];
3592
3604
  var command7 = {
3593
3605
  command: "merge <project>",
3594
- describe: "Merges the specified project (by UUID, id or alias) into the currently checked out project",
3606
+ describe: "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",
3595
3607
  handler: ensure("project-merge", options7),
3596
3608
  builder: (yargs) => build(options7, yargs)
3597
3609
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/cli",
3
- "version": "1.27.0",
3
+ "version": "1.28.1",
4
4
  "description": "CLI devtools for the OpenFn toolchain",
5
5
  "engines": {
6
6
  "node": ">=18",
@@ -52,11 +52,11 @@
52
52
  "yargs": "^17.7.2",
53
53
  "@openfn/compiler": "1.2.2",
54
54
  "@openfn/deploy": "0.11.5",
55
- "@openfn/lexicon": "^1.4.1",
56
55
  "@openfn/describe-package": "0.1.5",
57
56
  "@openfn/logger": "1.1.1",
58
- "@openfn/project": "^0.13.1",
59
- "@openfn/runtime": "1.8.4"
57
+ "@openfn/project": "^0.14.1",
58
+ "@openfn/runtime": "1.8.4",
59
+ "@openfn/lexicon": "^1.4.1"
60
60
  },
61
61
  "files": [
62
62
  "dist",