@cedarjs/cli 4.2.1-next.0 → 4.2.1-next.258

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.
Files changed (153) hide show
  1. package/dist/cfw.js +8 -6
  2. package/dist/commands/build/buildHandler.js +43 -42
  3. package/dist/commands/build/buildPackagesTask.js +6 -6
  4. package/dist/commands/build.js +7 -1
  5. package/dist/commands/deploy/baremetal/SshExecutor.js +13 -4
  6. package/dist/commands/deploy/baremetal/baremetalHandler.js +117 -65
  7. package/dist/commands/deploy/baremetal.js +24 -3
  8. package/dist/commands/deploy/flightcontrolHandler.js +16 -14
  9. package/dist/commands/deploy/helpers/deployHandler.js +10 -5
  10. package/dist/commands/deploy/packing/nft.js +1 -2
  11. package/dist/commands/deploy/render.js +1 -1
  12. package/dist/commands/deploy/renderHandler.js +22 -12
  13. package/dist/commands/deploy/serverlessHandler.js +67 -25
  14. package/dist/commands/destroy/component/component.js +2 -3
  15. package/dist/commands/destroy/directive/directive.js +1 -3
  16. package/dist/commands/destroy/directive/directiveHandler.js +2 -4
  17. package/dist/commands/destroy/function/function.js +1 -3
  18. package/dist/commands/destroy/handlerHelpers.js +16 -16
  19. package/dist/commands/destroy/helpers.js +8 -2
  20. package/dist/commands/destroy/layout/layout.js +1 -2
  21. package/dist/commands/destroy/page/pageHandler.js +1 -1
  22. package/dist/commands/destroy/scaffold/scaffoldHandler.js +28 -9
  23. package/dist/commands/destroy/sdl/sdlHandler.js +1 -1
  24. package/dist/commands/dev/devHandler.js +25 -25
  25. package/dist/commands/dev.js +3 -0
  26. package/dist/commands/experimental/live-queries/liveQueriesHandler.js +110 -4
  27. package/dist/commands/experimental/setupInngest.js +1 -1
  28. package/dist/commands/experimental/setupInngestHandler.js +12 -11
  29. package/dist/commands/experimental/setupOpentelemetry.js +1 -1
  30. package/dist/commands/experimental/setupOpentelemetryHandler.js +11 -7
  31. package/dist/commands/experimental/setupReactCompiler.js +1 -1
  32. package/dist/commands/experimental/setupReactCompilerHandler.js +45 -30
  33. package/dist/commands/experimental/setupRscHandler.js +14 -11
  34. package/dist/commands/experimental/setupStreamingSsr.js +1 -1
  35. package/dist/commands/experimental/setupStreamingSsrHandler.js +46 -31
  36. package/dist/commands/experimental/templates/rsc/Document.tsx.template +1 -1
  37. package/dist/commands/experimental/templates/rsc/entry.client.tsx.template +8 -8
  38. package/dist/commands/experimental/templates/streamingSsr/Document.tsx.template +1 -1
  39. package/dist/commands/experimental/templates/streamingSsr/entry.client.tsx.template +6 -6
  40. package/dist/commands/experimental/util.js +3 -2
  41. package/dist/commands/generate/cell/cellHandler.js +22 -21
  42. package/dist/commands/generate/component/componentHandler.js +14 -16
  43. package/dist/commands/generate/dataMigration/dataMigration.js +24 -13
  44. package/dist/commands/generate/dbAuth/dbAuth.js +1 -0
  45. package/dist/commands/generate/dbAuth/dbAuthHandler.js +40 -30
  46. package/dist/commands/generate/directive/directiveHandler.js +12 -14
  47. package/dist/commands/generate/function/function.js +1 -0
  48. package/dist/commands/generate/function/functionHandler.js +12 -17
  49. package/dist/commands/generate/helpers.js +17 -17
  50. package/dist/commands/generate/job/job.js +1 -0
  51. package/dist/commands/generate/job/jobHandler.js +15 -17
  52. package/dist/commands/generate/layout/layoutHandler.js +13 -16
  53. package/dist/commands/generate/model/model.js +1 -0
  54. package/dist/commands/generate/model/modelHandler.js +9 -3
  55. package/dist/commands/generate/ogImage/ogImage.js +1 -0
  56. package/dist/commands/generate/ogImage/ogImageHandler.js +16 -9
  57. package/dist/commands/generate/package/packageHandler.js +32 -49
  58. package/dist/commands/generate/page/pageHandler.js +25 -22
  59. package/dist/commands/generate/realtime/realtime.js +1 -1
  60. package/dist/commands/generate/realtime/realtimeHandler.js +46 -20
  61. package/dist/commands/generate/scaffold/scaffold.js +1 -0
  62. package/dist/commands/generate/scaffold/scaffoldHandler.js +35 -27
  63. package/dist/commands/generate/script/script.js +1 -0
  64. package/dist/commands/generate/script/templates/tsconfig.json.template +14 -0
  65. package/dist/commands/generate/sdl/sdl.js +4 -3
  66. package/dist/commands/generate/sdl/sdlHandler.js +38 -18
  67. package/dist/commands/generate/secret/secret.js +19 -19
  68. package/dist/commands/generate/service/service.js +5 -5
  69. package/dist/commands/generate/service/serviceHandler.js +59 -22
  70. package/dist/commands/generate/yargsCommandHelpers.js +6 -1
  71. package/dist/commands/generate/yargsHandlerHelpers.js +18 -10
  72. package/dist/commands/generate.js +3 -10
  73. package/dist/commands/jobsHandler.js +3 -2
  74. package/dist/commands/prismaHandler.js +10 -9
  75. package/dist/commands/serve.js +84 -137
  76. package/dist/commands/serveApiHandler.js +5 -5
  77. package/dist/commands/serveBothHandler.js +10 -7
  78. package/dist/commands/serveWebHandler.js +2 -2
  79. package/dist/commands/setup/auth/auth.js +12 -6
  80. package/dist/commands/setup/cache/cacheHandler.js +11 -6
  81. package/dist/commands/setup/deploy/deploy.js +4 -3
  82. package/dist/commands/setup/deploy/helpers/helpers.js +5 -1
  83. package/dist/commands/setup/deploy/helpers/index.js +157 -5
  84. package/dist/commands/setup/deploy/providers/baremetalHandler.js +26 -4
  85. package/dist/commands/setup/deploy/providers/coherenceHandler.js +53 -25
  86. package/dist/commands/setup/deploy/providers/flightcontrolHandler.js +33 -23
  87. package/dist/commands/setup/deploy/providers/netlify.js +6 -0
  88. package/dist/commands/setup/deploy/providers/netlifyHandler.js +112 -11
  89. package/dist/commands/setup/deploy/providers/renderHandler.js +10 -5
  90. package/dist/commands/setup/deploy/providers/serverlessHandler.js +5 -3
  91. package/dist/commands/setup/deploy/providers/universal-deploy.js +11 -0
  92. package/dist/commands/setup/deploy/providers/universalDeployHandler.js +188 -0
  93. package/dist/commands/setup/deploy/providers/vercel.js +6 -0
  94. package/dist/commands/setup/deploy/providers/vercelHandler.js +113 -12
  95. package/dist/commands/setup/deploy/templates/baremetal.js +3 -2
  96. package/dist/commands/setup/deploy/templates/flightcontrol.js +60 -49
  97. package/dist/commands/setup/deploy/templates/netlify.js +2 -1
  98. package/dist/commands/setup/deploy/templates/netlifyUD.js +31 -0
  99. package/dist/commands/setup/docker/docker.js +1 -1
  100. package/dist/commands/setup/docker/dockerHandler.js +72 -37
  101. package/dist/commands/setup/docker/templates/Dockerfile.npm +119 -0
  102. package/dist/commands/setup/docker/templates/Dockerfile.pnpm +112 -0
  103. package/dist/commands/setup/docker/templates/{Dockerfile → Dockerfile.yarn} +1 -1
  104. package/dist/commands/setup/docker/templates/docker-compose.dev.yml +2 -2
  105. package/dist/commands/setup/generator/generatorHandler.js +4 -3
  106. package/dist/commands/setup/graphql/features/fragments/fragmentsHandler.js +2 -2
  107. package/dist/commands/setup/graphql/features/trustedDocuments/trustedDocumentsHandler.js +2 -2
  108. package/dist/commands/setup/i18n/i18nHandler.js +18 -15
  109. package/dist/commands/setup/jobs/jobsHandler.js +10 -5
  110. package/dist/commands/setup/mailer/mailerHandler.js +11 -9
  111. package/dist/commands/setup/package/packageHandler.js +14 -10
  112. package/dist/commands/setup/realtime/realtimeHandler.js +22 -20
  113. package/dist/commands/setup/server-file/serverFileHandler.js +16 -8
  114. package/dist/commands/setup/tsconfig/tsconfigHandler.js +5 -3
  115. package/dist/commands/setup/ui/helpers/helpers.js +5 -1
  116. package/dist/commands/setup/ui/libraries/chakra-uiHandler.js +11 -5
  117. package/dist/commands/setup/ui/libraries/mantineHandler.js +21 -13
  118. package/dist/commands/setup/ui/libraries/tailwindcssHandler.js +38 -26
  119. package/dist/commands/setup/uploads/uploadsHandler.js +7 -4
  120. package/dist/commands/setup.js +1 -2
  121. package/dist/commands/test/testHandler.js +2 -1
  122. package/dist/commands/test/testHandlerEsm.js +2 -2
  123. package/dist/commands/type-checkHandler.js +4 -6
  124. package/dist/commands/upgrade/upgradeHandler.js +2 -4
  125. package/dist/index.js +10 -4
  126. package/dist/lib/configureStorybook.js +2 -2
  127. package/dist/lib/exec.js +36 -24
  128. package/dist/lib/exit.js +19 -4
  129. package/dist/lib/extendFile.js +22 -12
  130. package/dist/lib/index.js +119 -85
  131. package/dist/lib/merge/algorithms.js +4 -1
  132. package/dist/lib/merge/index.js +76 -41
  133. package/dist/lib/merge/semanticIdentity.js +18 -6
  134. package/dist/lib/merge/strategy.js +86 -36
  135. package/dist/lib/packages.js +23 -13
  136. package/dist/lib/plugin.js +12 -5
  137. package/dist/lib/pluralHelpers.js +1 -1
  138. package/dist/lib/project.js +3 -1
  139. package/dist/lib/rollback.js +7 -7
  140. package/dist/lib/schemaHelpers.js +12 -12
  141. package/dist/lib/test.js +4 -0
  142. package/dist/lib/updateCheck.js +6 -4
  143. package/dist/middleware/checkNodeVersion.js +5 -0
  144. package/dist/plugin.js +18 -12
  145. package/dist/telemetry/exporter.js +12 -5
  146. package/dist/telemetry/index.js +4 -3
  147. package/dist/telemetry/resource.js +9 -5
  148. package/dist/telemetry/send.js +14 -10
  149. package/package.json +15 -12
  150. package/dist/commands/setup/vite/templates/vite.config.ts.template +0 -19
  151. package/dist/commands/setup/vite/vite.js +0 -38
  152. package/dist/commands/setup/vite/viteHandler.js +0 -82
  153. /package/dist/{index.d.js → global.d.js} +0 -0
package/dist/cfw.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import Configstore from "configstore";
5
- import execa from "execa";
6
5
  import { terminalLink } from "termi-link";
6
+ import { runScriptSync } from "@cedarjs/cli-helpers/packageManager/exec";
7
7
  import { getConfigPath } from "@cedarjs/project-config";
8
8
  const config = new Configstore("@cedarjs/cli");
9
9
  const CFW_PATH = process.env.CFW_PATH || config.get("CFW_PATH");
@@ -30,14 +30,16 @@ const helpCommands = ["help", "--help"];
30
30
  if (!command.length || command.some((cmd) => helpCommands.includes(cmd))) {
31
31
  command = ["run"];
32
32
  }
33
+ const script = command[0];
34
+ const args = command.slice(1);
33
35
  try {
34
- execa.sync("yarn", [...command], {
36
+ runScriptSync(script, args, {
35
37
  stdio: "inherit",
36
38
  cwd: absCfwPath,
37
- env: {
38
- CEDAR_CWD: projectPath
39
- }
39
+ // @ts-expect-error - testUtils.d.ts augments ProcessEnv with
40
+ // CEDAR_DISABLE_TELEMETRY
41
+ env: { CEDAR_CWD: projectPath }
40
42
  });
41
- } catch (e) {
43
+ } catch {
42
44
  console.log();
43
45
  }
@@ -19,10 +19,8 @@ import { generate } from "@cedarjs/internal/dist/generate/generate";
19
19
  import { generateGqlormArtifacts } from "@cedarjs/internal/dist/generate/gqlormSchema";
20
20
  import { loadAndValidateSdls } from "@cedarjs/internal/dist/validateSchema";
21
21
  import { detectPrerenderRoutes } from "@cedarjs/prerender/detection";
22
- import {} from "@cedarjs/project-config";
23
22
  import { timedTelemetry } from "@cedarjs/telemetry";
24
23
  import { buildCedarApp } from "@cedarjs/vite/build";
25
- import { buildUDApiServer } from "@cedarjs/vite/buildUDApiServer";
26
24
  import { generatePrismaCommand } from "../../lib/generatePrismaClient.js";
27
25
  import { getPaths, getConfig } from "../../lib/index.js";
28
26
  import { buildPackagesTask } from "./buildPackagesTask.js";
@@ -80,14 +78,16 @@ const handler = async ({
80
78
  verbose = false,
81
79
  prisma = true,
82
80
  prerender = true,
83
- ud = false
81
+ ud = false,
82
+ apiRootPath
84
83
  }) => {
85
84
  recordTelemetryAttributes({
86
85
  command: "build",
87
86
  workspace: JSON.stringify(workspace),
88
87
  verbose,
89
88
  prisma,
90
- prerender
89
+ prerender,
90
+ apiRootPath
91
91
  });
92
92
  const cedarPaths = getPaths();
93
93
  const cedarConfig = getConfig();
@@ -97,12 +97,9 @@ const handler = async ({
97
97
  const prismaSchemaExists = fs.existsSync(cedarPaths.api.prismaConfig);
98
98
  const prerenderRoutes = prerender && workspace.includes("web") ? detectPrerenderRoutes() : [];
99
99
  const shouldGeneratePrismaClient = prisma && prismaSchemaExists && (workspace.includes("api") || prerenderRoutes.length > 0);
100
- const packageJsonPath = path.join(cedarPaths.base, "package.json");
101
- const packageJson = JSON.parse(
102
- fs.readFileSync(packageJsonPath, "utf8")
100
+ const nonApiWebWorkspaces = workspace.filter(
101
+ (w) => w !== "api" && w !== "web"
103
102
  );
104
- const packageJsonWorkspaces = packageJson.workspaces;
105
- const nonApiWebWorkspaces = Array.isArray(packageJsonWorkspaces) && packageJsonWorkspaces.length > 2 ? workspace.filter((w) => w !== "api" && w !== "web") : [];
106
103
  const gqlFeaturesTaskTitle = `Generating types needed for ${[
107
104
  useFragments && "GraphQL Fragments",
108
105
  useTrustedDocuments && "Trusted Documents"
@@ -230,7 +227,8 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
230
227
  {
231
228
  stdio: verbose ? "inherit" : "pipe",
232
229
  shell: true,
233
- // `cwd` is needed for yarn to find the cedar-vite-build binary
230
+ // `cwd` is needed for the package manager (e.g. yarn) to find the
231
+ // cedar-vite-build binary
234
232
  // It won't change process.cwd for anything else here, in this
235
233
  // process
236
234
  cwd: cedarPaths.web.base
@@ -255,7 +253,7 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
255
253
  const originalCwd = process.cwd();
256
254
  process.chdir(cedarPaths.web.base);
257
255
  try {
258
- await buildCedarApp({ verbose, workspace });
256
+ await buildCedarApp({ verbose, workspace, ud: true });
259
257
  } finally {
260
258
  process.chdir(originalCwd);
261
259
  }
@@ -268,40 +266,43 @@ Run ` + c.info(formatCedarCommand(["build"])) + " (without specifying a workspac
268
266
  );
269
267
  }
270
268
  }
271
- },
272
- ud && workspace.includes("api") && {
273
- title: "Bundling API server entry (Universal Deploy)...",
274
- task: async () => {
275
- await buildUDApiServer({ verbose });
276
- }
277
- }
278
- ].filter((t) => Boolean(t));
279
- const triggerPrerender = async () => {
280
- console.log("Starting prerendering...");
281
- if (prerenderRoutes.length === 0) {
282
- console.log(
283
- `You have not marked any routes to "prerender" in your ${terminalLink(
284
- "Routes",
285
- "file://" + cedarPaths.web.routes
286
- )}.`
287
- );
288
- return;
289
269
  }
290
- await runBin("cedar", ["prerender"], {
291
- stdio: "inherit",
292
- cwd: cedarPaths.web.base
270
+ ];
271
+ if (apiRootPath !== void 0) {
272
+ process.env.CEDAR_API_ROOT_PATH = apiRootPath;
273
+ }
274
+ try {
275
+ await timedTelemetry(process.argv, { type: "build" }, async () => {
276
+ const listrTasks = tasks.filter((t) => Boolean(t));
277
+ const jobs = new Listr(listrTasks, {
278
+ renderer: verbose ? "verbose" : void 0
279
+ });
280
+ await jobs.run();
281
+ if (workspace.includes("web") && prerender && prismaSchemaExists) {
282
+ await triggerPrerender(prerenderRoutes);
283
+ }
293
284
  });
294
- };
295
- const jobs = new Listr(tasks, {
296
- renderer: verbose ? "verbose" : void 0
297
- });
298
- await timedTelemetry(process.argv, { type: "build" }, async () => {
299
- await jobs.run();
300
- if (workspace.includes("web") && prerender && prismaSchemaExists) {
301
- await triggerPrerender();
302
- }
303
- });
285
+ } finally {
286
+ delete process.env.CEDAR_API_ROOT_PATH;
287
+ }
304
288
  };
289
+ async function triggerPrerender(prerenderRoutes) {
290
+ const cedarPaths = getPaths();
291
+ console.log("Starting prerendering...");
292
+ if (prerenderRoutes.length === 0) {
293
+ console.log(
294
+ `You have not marked any routes to "prerender" in your ${terminalLink(
295
+ "Routes",
296
+ "file://" + cedarPaths.web.routes
297
+ )}.`
298
+ );
299
+ return;
300
+ }
301
+ await runBin("cedar", ["prerender"], {
302
+ stdio: "inherit",
303
+ cwd: cedarPaths.web.base
304
+ });
305
+ }
305
306
  export {
306
307
  handler
307
308
  };
@@ -10,7 +10,7 @@ async function buildPackagesTask(task, nonApiWebWorkspaces) {
10
10
  const workspacePaths = nonApiWebWorkspaces.some((w) => w === "packages/*") ? await Array.fromAsync(fs.promises.glob(globPattern)) : nonApiWebWorkspaces.map((w) => {
11
11
  const workspacePath = path.join(
12
12
  cedarPaths.packages,
13
- w.split("/").at(-1)
13
+ w.split("/").at(-1) ?? ""
14
14
  );
15
15
  if (!fs.existsSync(workspacePath)) {
16
16
  return "";
@@ -30,15 +30,15 @@ async function buildPackagesTask(task, nonApiWebWorkspaces) {
30
30
  try {
31
31
  await runScript("build", [], { cwd: workspacePath });
32
32
  } catch (e) {
33
+ const message = e instanceof Error ? e.message : String(e);
34
+ const stderr = e instanceof Error && "stderr" in e && typeof e.stderr === "string" ? e.stderr : void 0;
33
35
  errorTelemetry(
34
36
  process.argv,
35
- `Error building package "${name}": ${e.message}`
37
+ `Error building package "${name}": ${message}`
36
38
  );
37
- throw new Error(
38
- `Building "${name}" failed
39
+ throw new Error(`Building "${name}" failed
39
40
 
40
- ${e.stderr || e.message}`
41
- );
41
+ ${stderr || message}`);
42
42
  }
43
43
  }
44
44
  };
@@ -28,7 +28,10 @@ const builder = (yargs) => {
28
28
  }).option("ud", {
29
29
  type: "boolean",
30
30
  default: false,
31
- description: "Build the Universal Deploy server entry (api/dist/ud/index.js)."
31
+ description: "Build the Universal Deploy server entry (api/dist/ud/)."
32
+ }).option("apiRootPath", {
33
+ type: "string",
34
+ description: 'Root path where API functions are served. Overrides the apiRootPath option on cedarUniversalDeployPlugin() in your vite config. Defaults to "/".'
32
35
  }).middleware(() => {
33
36
  const check = checkNodeVersion();
34
37
  if (check.ok) {
@@ -43,6 +46,9 @@ const builder = (yargs) => {
43
46
  if (!Array.isArray(workspacesArg)) {
44
47
  return "Workspace must be an array";
45
48
  }
49
+ if (argv.apiRootPath && !argv.ud) {
50
+ return c.error("Error:") + " --apiRootPath requires --ud.\n Use --ud to build the Universal Deploy server entry.";
51
+ }
46
52
  const filtered = workspacesArg.filter(
47
53
  (item) => item !== "api" && item !== "web" && item !== "packages/*"
48
54
  );
@@ -1,5 +1,15 @@
1
1
  import { NodeSSH } from "node-ssh";
2
+ class ExecError extends Error {
3
+ exitCode;
4
+ constructor(message, exitCode) {
5
+ super(message);
6
+ this.name = "ExecError";
7
+ this.exitCode = exitCode;
8
+ }
9
+ }
2
10
  class SshExecutor {
11
+ ssh;
12
+ verbose;
3
13
  constructor(verbose) {
4
14
  this.ssh = new NodeSSH();
5
15
  this.verbose = verbose;
@@ -20,12 +30,11 @@ class SshExecutor {
20
30
  cwd: path
21
31
  });
22
32
  if (result.code !== 0) {
23
- const error = new Error(
33
+ throw new ExecError(
24
34
  `Error while running command \`${sshCommand}\` in ${path}
25
- ` + result.stderr
35
+ ` + result.stderr,
36
+ result.code
26
37
  );
27
- error.exitCode = result.code;
28
- throw error;
29
38
  }
30
39
  return result;
31
40
  }
@@ -1,13 +1,15 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import boxen from "boxen";
4
+ import execa from "execa";
4
5
  import { Listr } from "listr2";
5
6
  import * as toml from "smol-toml";
6
7
  import { env as envInterpolation } from "string-env-interpolation";
7
8
  import { titleCase } from "title-case";
8
- import { colors as c, recordTelemetryAttributes } from "@cedarjs/cli-helpers";
9
+ import { colors as c } from "@cedarjs/cli-helpers";
10
+ import { formatCedarCommand } from "@cedarjs/cli-helpers/packageManager/display";
11
+ import { getPackageManager } from "@cedarjs/project-config/packageManager";
9
12
  import { getPaths } from "../../../lib/index.js";
10
- import { SshExecutor } from "./SshExecutor.js";
11
13
  const CONFIG_FILENAME = "deploy.toml";
12
14
  const SYMLINK_FLAGS = "-nsf";
13
15
  const CURRENT_RELEASE_SYMLINK_NAME = "current";
@@ -15,7 +17,7 @@ const LIFECYCLE_HOOKS = ["before", "after"];
15
17
  const DEFAULT_SERVER_CONFIG = {
16
18
  port: 22,
17
19
  branch: "main",
18
- packageManagerCommand: "yarn",
20
+ packageManagerCommand: getPackageManager(),
19
21
  monitorCommand: "pm2",
20
22
  sides: ["api", "web"],
21
23
  keepReleases: 5,
@@ -30,7 +32,7 @@ const throwMissingConfig = (name) => {
30
32
  const verifyConfig = (config, yargs) => {
31
33
  if (!yargs.environment) {
32
34
  throw new Error(
33
- "Must specify an environment to deploy to, ex: `yarn cedar deploy baremetal production`"
35
+ `Must specify an environment to deploy to, ex: \`${formatCedarCommand(["deploy", "baremetal", "production"])}\``
34
36
  );
35
37
  }
36
38
  if (!config[yargs.environment]) {
@@ -48,20 +50,20 @@ const verifyServerConfig = (config) => {
48
50
  if (!config.repo) {
49
51
  throwMissingConfig("repo");
50
52
  }
51
- if (!/^\d+$/.test(config.freeSpaceRequired)) {
53
+ if (!/^\d+$/.test(String(config.freeSpaceRequired))) {
52
54
  throw new Error('"freeSpaceRequired" must be an integer >= 0');
53
55
  }
54
56
  return true;
55
57
  };
56
- const symlinkCurrentCommand = async (dir, ssh, path2) => {
57
- return await ssh.exec(path2, "ln", [
58
+ const symlinkCurrentCommand = async (dir, ssh, deployPath) => {
59
+ return await ssh.exec(deployPath, "ln", [
58
60
  SYMLINK_FLAGS,
59
61
  dir,
60
62
  CURRENT_RELEASE_SYMLINK_NAME
61
63
  ]);
62
64
  };
63
- const restartProcessCommand = async (processName, ssh, serverConfig, path2) => {
64
- return await ssh.exec(path2, serverConfig.monitorCommand, [
65
+ const restartProcessCommand = async (processName, ssh, serverConfig, deployPath) => {
66
+ return await ssh.exec(deployPath, serverConfig.monitorCommand, [
65
67
  "restart",
66
68
  processName
67
69
  ]);
@@ -104,7 +106,7 @@ const maintenanceTasks = (status, ssh, serverConfig) => {
104
106
  }
105
107
  } else if (status === "down") {
106
108
  tasks.push({
107
- title: `Starting ${serverConfig.processNames.join(", ")} processes...`,
109
+ title: `Starting ${serverConfig.processNames?.join(", ")} processes...`,
108
110
  task: async () => {
109
111
  await ssh.exec(serverConfig.path, serverConfig.monitorCommand, [
110
112
  "start",
@@ -131,7 +133,7 @@ const maintenanceTasks = (status, ssh, serverConfig) => {
131
133
  };
132
134
  const rollbackTasks = (count, ssh, serverConfig) => {
133
135
  let rollbackCount = 1;
134
- if (parseInt(count) === count) {
136
+ if (parseInt(String(count)) === count) {
135
137
  rollbackCount = count;
136
138
  }
137
139
  const tasks = [
@@ -140,7 +142,7 @@ const rollbackTasks = (count, ssh, serverConfig) => {
140
142
  task: async () => {
141
143
  const currentLink = (await ssh.exec(serverConfig.path, "readlink", ["-f", "current"])).stdout.split("/").pop();
142
144
  const dirs = (await ssh.exec(serverConfig.path, "ls", ["-t"])).stdout.split("\n").filter((dirs2) => !dirs2.match(/current/));
143
- const deployedIndex = dirs.indexOf(currentLink);
145
+ const deployedIndex = dirs.indexOf(currentLink ?? "");
144
146
  const rollbackIndex = deployedIndex + rollbackCount;
145
147
  if (dirs[rollbackIndex]) {
146
148
  console.info("Setting symlink");
@@ -151,7 +153,7 @@ const rollbackTasks = (count, ssh, serverConfig) => {
151
153
  );
152
154
  } else {
153
155
  throw new Error(
154
- `Cannot rollback ${rollbackCount} release(s): ${dirs.length - dirs.indexOf(currentLink) - 1} previous release(s) available`
156
+ `Cannot rollback ${rollbackCount} release(s): ${dirs.length - dirs.indexOf(currentLink ?? "") - 1} previous release(s) available`
155
157
  );
156
158
  }
157
159
  }
@@ -189,19 +191,30 @@ const lifecycleTask = (lifecycle, task, skip, { serverLifecycle, ssh, cmdPath })
189
191
  return tasks;
190
192
  }
191
193
  };
192
- const commandWithLifecycleEvents = ({ name, config, skip, command }) => {
194
+ const commandWithLifecycleEvents = ({
195
+ name,
196
+ config,
197
+ skip,
198
+ command
199
+ }) => {
193
200
  const tasks = [];
194
201
  tasks.push(lifecycleTask("before", name, skip, config));
195
202
  tasks.push({ ...command, skip: () => skip });
196
203
  tasks.push(lifecycleTask("after", name, skip, config));
197
- return tasks.flat().filter((t) => t);
204
+ return tasks.flat().filter((t) => Boolean(t));
198
205
  };
199
206
  const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
200
207
  const cmdPath = pathJoin(serverConfig.path, yargs.releaseDir);
201
- const config = { yargs, ssh, serverConfig, serverLifecycle, cmdPath };
208
+ const config = {
209
+ yargs,
210
+ ssh,
211
+ serverConfig,
212
+ serverLifecycle,
213
+ cmdPath
214
+ };
202
215
  const tasks = [];
203
216
  tasks.push(
204
- commandWithLifecycleEvents({
217
+ ...commandWithLifecycleEvents({
205
218
  name: "df",
206
219
  config: { ...config, cmdPath: serverConfig.path },
207
220
  skip: !yargs.df || serverConfig.freeSpaceRequired === 0 || serverConfig.freeSpaceRequired === "0",
@@ -228,7 +241,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
228
241
  }
229
242
  task.output = `Available disk space: ${dfMb}MB`;
230
243
  const freeSpaceRequired = parseInt(
231
- serverConfig.freeSpaceRequired ?? 2048,
244
+ String(serverConfig.freeSpaceRequired ?? 2048),
232
245
  10
233
246
  );
234
247
  if (dfMb < freeSpaceRequired) {
@@ -241,7 +254,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
241
254
  })
242
255
  );
243
256
  tasks.push(
244
- commandWithLifecycleEvents({
257
+ ...commandWithLifecycleEvents({
245
258
  name: "update",
246
259
  config: { ...config, cmdPath: serverConfig.path },
247
260
  skip: !yargs.update,
@@ -260,7 +273,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
260
273
  })
261
274
  );
262
275
  tasks.push(
263
- commandWithLifecycleEvents({
276
+ ...commandWithLifecycleEvents({
264
277
  name: "symlinkEnv",
265
278
  config,
266
279
  skip: !yargs.update,
@@ -273,7 +286,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
273
286
  })
274
287
  );
275
288
  tasks.push(
276
- commandWithLifecycleEvents({
289
+ ...commandWithLifecycleEvents({
277
290
  name: "install",
278
291
  config,
279
292
  skip: !yargs.install,
@@ -288,7 +301,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
288
301
  })
289
302
  );
290
303
  tasks.push(
291
- commandWithLifecycleEvents({
304
+ ...commandWithLifecycleEvents({
292
305
  name: "migrate",
293
306
  config,
294
307
  skip: !yargs.migrate || serverConfig?.migrate === false,
@@ -296,17 +309,20 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
296
309
  title: `DB Migrations...`,
297
310
  task: async () => {
298
311
  await ssh.exec(cmdPath, serverConfig.packageManagerCommand, [
312
+ "exec",
299
313
  "cedar",
300
314
  "prisma",
301
315
  "migrate",
302
316
  "deploy"
303
317
  ]);
304
318
  await ssh.exec(cmdPath, serverConfig.packageManagerCommand, [
319
+ "exec",
305
320
  "cedar",
306
321
  "prisma",
307
322
  "generate"
308
323
  ]);
309
324
  await ssh.exec(cmdPath, serverConfig.packageManagerCommand, [
325
+ "exec",
310
326
  "cedar",
311
327
  "dataMigrate",
312
328
  "up"
@@ -317,7 +333,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
317
333
  );
318
334
  for (const side of serverConfig.sides) {
319
335
  tasks.push(
320
- commandWithLifecycleEvents({
336
+ ...commandWithLifecycleEvents({
321
337
  name: "build",
322
338
  config,
323
339
  skip: !yargs.build,
@@ -325,6 +341,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
325
341
  title: `Building ${side}...`,
326
342
  task: async () => {
327
343
  await ssh.exec(cmdPath, serverConfig.packageManagerCommand, [
344
+ "exec",
328
345
  "cedar",
329
346
  "build",
330
347
  side
@@ -335,7 +352,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
335
352
  );
336
353
  }
337
354
  tasks.push(
338
- commandWithLifecycleEvents({
355
+ ...commandWithLifecycleEvents({
339
356
  name: "symlinkCurrent",
340
357
  config,
341
358
  skip: !yargs.update,
@@ -352,7 +369,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
352
369
  for (const processName of serverConfig.processNames) {
353
370
  if (yargs.firstRun) {
354
371
  tasks.push(
355
- commandWithLifecycleEvents({
372
+ ...commandWithLifecycleEvents({
356
373
  name: "restart",
357
374
  config,
358
375
  skip: !yargs.restart,
@@ -380,7 +397,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
380
397
  });
381
398
  } else {
382
399
  tasks.push(
383
- commandWithLifecycleEvents({
400
+ ...commandWithLifecycleEvents({
384
401
  name: "restart",
385
402
  config,
386
403
  skip: !yargs.restart,
@@ -401,7 +418,7 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
401
418
  }
402
419
  }
403
420
  tasks.push(
404
- commandWithLifecycleEvents({
421
+ ...commandWithLifecycleEvents({
405
422
  name: "cleanup",
406
423
  config: { ...config, cmdPath: serverConfig.path },
407
424
  skip: !yargs.cleanup,
@@ -417,14 +434,15 @@ const deployTasks = (yargs, ssh, serverConfig, serverLifecycle) => {
417
434
  }
418
435
  })
419
436
  );
420
- return tasks.flat().filter((e) => e);
437
+ return tasks;
421
438
  };
422
439
  const mergeLifecycleEvents = (lifecycle, other) => {
423
- let lifecycleCopy = JSON.parse(JSON.stringify(lifecycle));
440
+ const lifecycleCopy = JSON.parse(JSON.stringify(lifecycle));
424
441
  for (const hook of LIFECYCLE_HOOKS) {
425
- for (const key in other[hook]) {
442
+ const otherHook = other[hook] ?? {};
443
+ for (const key in otherHook) {
426
444
  lifecycleCopy[hook][key] = (lifecycleCopy[hook][key] || []).concat(
427
- other[hook][key]
445
+ otherHook[key]
428
446
  );
429
447
  }
430
448
  }
@@ -433,26 +451,26 @@ const mergeLifecycleEvents = (lifecycle, other) => {
433
451
  const parseConfig = (yargs, rawConfigToml) => {
434
452
  const configToml = envInterpolation(rawConfigToml);
435
453
  const config = toml.parse(configToml);
436
- let envConfig;
437
- const emptyLifecycle = {};
454
+ const emptyLifecycle = { before: {}, after: {} };
438
455
  verifyConfig(config, yargs);
439
- for (const hook of LIFECYCLE_HOOKS) {
440
- emptyLifecycle[hook] = {};
441
- }
442
456
  let envLifecycle = mergeLifecycleEvents(emptyLifecycle, config);
443
- envConfig = config[yargs.environment];
457
+ const envConfig = config[yargs.environment];
444
458
  envLifecycle = mergeLifecycleEvents(envLifecycle, envConfig);
445
459
  return { envConfig, envLifecycle };
446
460
  };
447
461
  const commands = (yargs, ssh) => {
448
462
  const deployConfig = fs.readFileSync(pathJoin(getPaths().base, CONFIG_FILENAME)).toString();
449
- let { envConfig, envLifecycle } = parseConfig(yargs, deployConfig);
450
- let servers = [];
463
+ const { envConfig, envLifecycle } = parseConfig(yargs, deployConfig);
464
+ const servers = [];
451
465
  let tasks = [];
452
- for (const config of envConfig.servers) {
466
+ const serverList = envConfig.servers ?? [];
467
+ for (const config of serverList) {
453
468
  const serverConfig = serverConfigWithDefaults(config, yargs);
454
469
  verifyServerConfig(serverConfig);
455
- const serverLifecycle = mergeLifecycleEvents(envLifecycle, serverConfig);
470
+ const serverLifecycle = mergeLifecycleEvents(
471
+ envLifecycle,
472
+ serverConfig
473
+ );
456
474
  tasks.push({
457
475
  title: "Connecting...",
458
476
  task: () => ssh.connect({
@@ -461,9 +479,10 @@ const commands = (yargs, ssh) => {
461
479
  username: serverConfig.username,
462
480
  password: serverConfig.password,
463
481
  privateKey: serverConfig.privateKey,
482
+ // @ts-expect-error - node-ssh Config doesn't expose privateKeyPath but it is supported at runtime
464
483
  privateKeyPath: serverConfig.privateKeyPath,
465
484
  passphrase: serverConfig.passphrase,
466
- agent: serverConfig.agentForward && process.env.SSH_AUTH_SOCK,
485
+ agent: serverConfig.agentForward ? process.env.SSH_AUTH_SOCK : void 0,
467
486
  agentForward: serverConfig.agentForward
468
487
  })
469
488
  });
@@ -482,56 +501,88 @@ const commands = (yargs, ssh) => {
482
501
  title: "Disconnecting...",
483
502
  task: () => ssh.dispose()
484
503
  });
504
+ const tasksCopy = [...tasks];
485
505
  servers.push({
486
506
  title: serverConfig.host,
487
507
  task: () => {
488
- return new Listr(tasks);
508
+ return new Listr(tasksCopy);
489
509
  }
490
510
  });
511
+ tasks = [];
491
512
  }
492
513
  return servers;
493
514
  };
515
+ const warnIfUnpushedCommits = async () => {
516
+ try {
517
+ const { stdout } = await execa("git", ["log", "@{u}..", "--oneline"], {
518
+ cwd: getPaths().base
519
+ });
520
+ const unpushedCommits = stdout.trim();
521
+ if (!unpushedCommits) {
522
+ return;
523
+ }
524
+ console.warn(
525
+ c.warning("\nWarning: You have local commits that have not been pushed:")
526
+ );
527
+ console.warn(
528
+ unpushedCommits.split("\n").map((line) => ` ${line}`).join("\n")
529
+ );
530
+ console.warn(
531
+ c.warning(
532
+ "The server will pull from the remote, so these commits will not be deployed.\n"
533
+ )
534
+ );
535
+ const { default: prompts } = await import("prompts");
536
+ const { confirmed } = await prompts({
537
+ type: "confirm",
538
+ name: "confirmed",
539
+ message: "Deploy anyway?",
540
+ initial: false
541
+ });
542
+ if (!confirmed) {
543
+ console.log("Aborting deploy. Push your commits and try again.");
544
+ process.exit(1);
545
+ }
546
+ } catch (e) {
547
+ console.error(
548
+ c.error("\nCould not check for unpushed commits before deploying.")
549
+ );
550
+ throw e;
551
+ }
552
+ };
494
553
  const handler = async (yargs) => {
495
- recordTelemetryAttributes({
496
- command: "deploy baremetal",
497
- firstRun: yargs.firstRun,
498
- df: yargs.df,
499
- update: yargs.update,
500
- install: yargs.install,
501
- migrate: yargs.migrate,
502
- build: yargs.build,
503
- restart: yargs.restart,
504
- cleanup: yargs.cleanup,
505
- maintenance: yargs.maintenance,
506
- rollback: yargs.rollback,
507
- verbose: yargs.verbose
508
- });
554
+ const { SshExecutor } = await import("./SshExecutor.js");
509
555
  const tomlPath = path.join(getPaths().base, "deploy.toml");
510
556
  const ecosystemPath = path.join(getPaths().base, "ecosystem.config.js");
511
557
  if (!fs.existsSync(tomlPath) || !fs.existsSync(ecosystemPath)) {
512
558
  console.error(
513
- c.error("\nError: Baremetal deploy has not been properly setup.\n") + "Please run `yarn cedar setup deploy baremetal` before deploying"
559
+ c.error("\nError: Baremetal deploy has not been properly setup.\n") + `Please run \`${formatCedarCommand(["setup", "deploy", "baremetal"])}\` before deploying`
514
560
  );
515
561
  process.exit(1);
516
562
  }
517
- const ssh = new SshExecutor(yargs.verbose);
563
+ if (yargs.gitCheck) {
564
+ await warnIfUnpushedCommits();
565
+ }
566
+ const ssh = new SshExecutor(yargs.verbose ?? false);
518
567
  try {
519
- const tasks = new Listr(commands(yargs, ssh), {
568
+ const listrTasks = new Listr(commands(yargs, ssh), {
520
569
  concurrent: true,
521
570
  exitOnError: true,
522
- renderer: yargs.verbose && "verbose"
571
+ renderer: yargs.verbose ? "verbose" : void 0
523
572
  });
524
- await tasks.run();
573
+ await listrTasks.run();
525
574
  } catch (e) {
526
575
  console.error(c.error("\nDeploy failed:"));
576
+ const errMessage = e instanceof Error && "stderr" in e ? e.stderr ?? e.message : e instanceof Error ? e.message : String(e);
577
+ const exitCode = e instanceof Error && "exitCode" in e ? e.exitCode ?? 1 : 1;
527
578
  console.error(
528
- boxen(e.stderr || e.message, {
579
+ boxen(errMessage, {
529
580
  padding: { top: 0, bottom: 0, right: 1, left: 1 },
530
581
  margin: 0,
531
582
  borderColor: "red"
532
583
  })
533
584
  );
534
- process.exit(e?.exitCode || 1);
585
+ process.exit(exitCode);
535
586
  }
536
587
  };
537
588
  export {
@@ -547,5 +598,6 @@ export {
547
598
  serverConfigWithDefaults,
548
599
  throwMissingConfig,
549
600
  verifyConfig,
550
- verifyServerConfig
601
+ verifyServerConfig,
602
+ warnIfUnpushedCommits
551
603
  };