@cedarjs/cli 1.0.0-canary.13037 → 1.0.0-canary.13040

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.
@@ -1,9 +1,12 @@
1
+ import { builtinModules } from "node:module";
2
+ import os from "node:os";
1
3
  import path from "node:path";
2
4
  import { ListrEnquirerPromptAdapter } from "@listr2/prompt-adapter-enquirer";
3
5
  import execa from "execa";
4
6
  import fs from "fs-extra";
5
7
  import latestVersion from "latest-version";
6
8
  import { Listr } from "listr2";
9
+ import semver from "semver";
7
10
  import { terminalLink } from "termi-link";
8
11
  import { recordTelemetryAttributes } from "@cedarjs/cli-helpers";
9
12
  import { getConfig } from "@cedarjs/project-config";
@@ -41,6 +44,11 @@ const builder = (yargs) => {
41
44
  describe: "Skip prompts and use defaults",
42
45
  default: false,
43
46
  type: "boolean"
47
+ }).option("force", {
48
+ alias: "f",
49
+ describe: "Force upgrade even if pre-upgrade checks fail",
50
+ default: false,
51
+ type: "boolean"
44
52
  }).epilogue(
45
53
  `Also see the ${terminalLink(
46
54
  "CedarJS CLI Reference for the upgrade command",
@@ -70,15 +78,18 @@ const validateTag = (tag) => {
70
78
  }
71
79
  return tag;
72
80
  };
73
- const handler = async ({ dryRun, tag, verbose, dedupe, yes }) => {
81
+ const handler = async ({ dryRun, tag, verbose, dedupe, yes, force }) => {
74
82
  recordTelemetryAttributes({
75
83
  command: "upgrade",
76
84
  dryRun,
77
85
  tag,
78
86
  verbose,
79
87
  dedupe,
80
- yes
88
+ yes,
89
+ force
81
90
  });
91
+ let preUpgradeMessage = "";
92
+ let preUpgradeError = "";
82
93
  const tasks = new Listr(
83
94
  [
84
95
  {
@@ -117,38 +128,55 @@ const handler = async ({ dryRun, tag, verbose, dedupe, yes }) => {
117
128
  title: "Checking latest version",
118
129
  task: async (ctx) => setLatestVersionToContext(ctx, tag)
119
130
  },
131
+ {
132
+ title: "Running pre-upgrade scripts",
133
+ task: async (ctx, task) => {
134
+ await runPreUpgradeScripts(ctx, task, { verbose, force });
135
+ if (ctx.preUpgradeMessage) {
136
+ preUpgradeMessage = ctx.preUpgradeMessage;
137
+ }
138
+ if (ctx.preUpgradeError) {
139
+ preUpgradeError = ctx.preUpgradeError;
140
+ }
141
+ },
142
+ enabled: (ctx) => !!ctx.versionToUpgradeTo
143
+ },
120
144
  {
121
145
  title: "Updating your CedarJS version",
122
146
  task: (ctx) => updateCedarJSDepsForAllSides(ctx, { dryRun, verbose }),
123
- enabled: (ctx) => !!ctx.versionToUpgradeTo
147
+ enabled: (ctx) => !!ctx.versionToUpgradeTo && !ctx.preUpgradeError
124
148
  },
125
149
  {
126
150
  title: "Updating other packages in your package.json(s)",
127
151
  task: (ctx) => updatePackageVersionsFromTemplate(ctx, { dryRun, verbose }),
128
- enabled: (ctx) => ctx.versionToUpgradeTo?.includes("canary")
152
+ enabled: (ctx) => ctx.versionToUpgradeTo?.includes("canary") && !ctx.preUpgradeError
129
153
  },
130
154
  {
131
155
  title: "Downloading yarn patches",
132
156
  task: (ctx) => downloadYarnPatches(ctx, { dryRun, verbose }),
133
- enabled: (ctx) => ctx.versionToUpgradeTo?.includes("canary")
157
+ enabled: (ctx) => ctx.versionToUpgradeTo?.includes("canary") && !ctx.preUpgradeError
134
158
  },
135
159
  {
136
160
  title: "Removing CLI cache",
137
- task: (ctx) => removeCliCache(ctx, { dryRun, verbose })
161
+ task: (ctx) => removeCliCache(ctx, { dryRun, verbose }),
162
+ enabled: (ctx) => !ctx.preUpgradeError
138
163
  },
139
164
  {
140
165
  title: "Running yarn install",
141
166
  task: (ctx) => yarnInstall(ctx, { dryRun, verbose }),
167
+ enabled: (ctx) => !ctx.preUpgradeError,
142
168
  skip: () => dryRun
143
169
  },
144
170
  {
145
171
  title: "Refreshing the Prisma client",
146
172
  task: (_ctx, task) => refreshPrismaClient(task, { verbose }),
173
+ enabled: (ctx) => !ctx.preUpgradeError,
147
174
  skip: () => dryRun
148
175
  },
149
176
  {
150
177
  title: "De-duplicating dependencies",
151
178
  skip: () => dryRun || !dedupe,
179
+ enabled: (ctx) => !ctx.preUpgradeError,
152
180
  task: (_ctx, task) => dedupeDeps(task, { verbose })
153
181
  },
154
182
  {
@@ -208,6 +236,21 @@ const handler = async ({ dryRun, tag, verbose, dedupe, yes }) => {
208
236
  }
209
237
  );
210
238
  await tasks.run();
239
+ if (preUpgradeError) {
240
+ console.error("");
241
+ console.error(` \u274C ${c.error("Pre-upgrade Error:")}
242
+ `);
243
+ console.error(" " + preUpgradeError.replace(/\n/g, "\n "));
244
+ if (!force) {
245
+ process.exit(1);
246
+ }
247
+ }
248
+ if (preUpgradeMessage) {
249
+ console.log("");
250
+ console.log(` \u{1F4E3} ${c.info("Pre-upgrade Message:")}
251
+ `);
252
+ console.log(" " + preUpgradeMessage.replace(/\n/g, "\n "));
253
+ }
211
254
  };
212
255
  async function yarnInstall({ verbose }) {
213
256
  try {
@@ -459,10 +502,212 @@ const dedupeDeps = async (_task, { verbose }) => {
459
502
  }
460
503
  await yarnInstall({ verbose });
461
504
  };
505
+ async function runPreUpgradeScripts(ctx, task, { verbose, force }) {
506
+ if (!ctx.versionToUpgradeTo) {
507
+ return;
508
+ }
509
+ const version = ctx.versionToUpgradeTo;
510
+ const parsed = semver.parse(version);
511
+ const baseUrl = "https://raw.githubusercontent.com/cedarjs/cedar/main/upgrade-scripts/";
512
+ const manifestUrl = `${baseUrl}manifest.json`;
513
+ let manifest = [];
514
+ try {
515
+ const res = await fetch(manifestUrl);
516
+ if (res.status === 200) {
517
+ manifest = await res.json();
518
+ } else {
519
+ if (verbose) {
520
+ console.log("No upgrade script manifest found.");
521
+ }
522
+ }
523
+ } catch (e) {
524
+ if (verbose) {
525
+ console.log("Failed to fetch upgrade script manifest", e);
526
+ }
527
+ }
528
+ if (!Array.isArray(manifest) || manifest.length === 0) {
529
+ return;
530
+ }
531
+ const checkLevels = [];
532
+ if (parsed) {
533
+ checkLevels.push({
534
+ id: "exact",
535
+ candidates: [`${version}.ts`, `${version}/index.ts`]
536
+ });
537
+ checkLevels.push({
538
+ id: "patch",
539
+ candidates: [
540
+ `${parsed.major}.${parsed.minor}.x.ts`,
541
+ `${parsed.major}.${parsed.minor}.x/index.ts`
542
+ ]
543
+ });
544
+ checkLevels.push({
545
+ id: "minor",
546
+ candidates: [`${parsed.major}.x.ts`, `${parsed.major}.x/index.ts`]
547
+ });
548
+ } else {
549
+ checkLevels.push({
550
+ id: "tag",
551
+ candidates: [`${version}.ts`, `${version}/index.ts`]
552
+ });
553
+ }
554
+ const scriptsToRun = [];
555
+ for (const level of checkLevels) {
556
+ for (const candidate of level.candidates) {
557
+ if (manifest.includes(candidate)) {
558
+ scriptsToRun.push(candidate);
559
+ break;
560
+ }
561
+ }
562
+ }
563
+ if (scriptsToRun.length === 0) {
564
+ if (verbose) {
565
+ console.log(`No upgrade scripts found for ${version}`);
566
+ }
567
+ return;
568
+ }
569
+ ctx.preUpgradeMessage = "";
570
+ ctx.preUpgradeError = "";
571
+ for (const scriptName of scriptsToRun) {
572
+ task.output = `Found upgrade check script: ${scriptName}. Downloading...`;
573
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "cedar-upgrade-"));
574
+ const scriptPath = path.join(tempDir, "script.ts");
575
+ const isDirectoryScript = scriptName.includes("/");
576
+ if (isDirectoryScript) {
577
+ const dirName = scriptName.split("/")[0];
578
+ const githubApiUrl = `https://api.github.com/repos/cedarjs/cedar/contents/upgrade-scripts/${dirName}`;
579
+ try {
580
+ const dirRes = await fetch(githubApiUrl, {
581
+ headers: {
582
+ Accept: "application/vnd.github.v3+json"
583
+ }
584
+ });
585
+ if (dirRes.status !== 200) {
586
+ throw new Error(
587
+ `Failed to fetch directory contents: ${dirRes.statusText}`
588
+ );
589
+ }
590
+ const files = await dirRes.json();
591
+ for (const file of files) {
592
+ if (file.type === "file") {
593
+ task.output = `Downloading ${file.name}...`;
594
+ const fileRes = await fetch(file.download_url);
595
+ if (fileRes.status !== 200) {
596
+ throw new Error(`Failed to download ${file.name}`);
597
+ }
598
+ const fileContent = await fileRes.text();
599
+ const filePath = path.join(tempDir, file.name);
600
+ await fs.writeFile(filePath, fileContent);
601
+ if (file.name === "index.ts") {
602
+ await fs.rename(filePath, scriptPath);
603
+ }
604
+ }
605
+ }
606
+ } catch (e) {
607
+ if (verbose) {
608
+ console.error(e);
609
+ }
610
+ throw new Error(
611
+ `Failed to download upgrade script directory from ${githubApiUrl}`
612
+ );
613
+ }
614
+ } else {
615
+ const scriptUrl = `${baseUrl}${scriptName}`;
616
+ try {
617
+ const res = await fetch(scriptUrl);
618
+ if (res.status !== 200) {
619
+ throw new Error(`Failed to download script: ${res.statusText}`);
620
+ }
621
+ const scriptContent2 = await res.text();
622
+ await fs.writeFile(scriptPath, scriptContent2);
623
+ } catch (e) {
624
+ if (verbose) {
625
+ console.error(e);
626
+ }
627
+ throw new Error(`Failed to download upgrade script from ${scriptUrl}`);
628
+ }
629
+ }
630
+ const scriptContent = await fs.readFile(scriptPath, "utf8");
631
+ const deps = extractDependencies(scriptContent);
632
+ if (deps.length > 0) {
633
+ const depList = deps.join(", ");
634
+ task.output = `Installing dependencies for ${scriptName}: ${depList}...`;
635
+ await fs.writeJson(path.join(tempDir, "package.json"), {
636
+ name: "pre-upgrade-script",
637
+ version: "0.0.0",
638
+ dependencies: {}
639
+ });
640
+ await execa("yarn", ["add", ...deps], { cwd: tempDir });
641
+ }
642
+ task.output = `Running pre-upgrade script: ${scriptName}...`;
643
+ let shouldCleanup = true;
644
+ try {
645
+ const { stdout } = await execa(
646
+ "node",
647
+ ["script.ts", "--verbose", verbose, "--force", force],
648
+ { cwd: tempDir }
649
+ );
650
+ if (stdout) {
651
+ if (ctx.preUpgradeMessage) {
652
+ ctx.preUpgradeMessage += "\n\n";
653
+ }
654
+ ctx.preUpgradeMessage += `
655
+ ${stdout}`;
656
+ }
657
+ } catch (e) {
658
+ const errorOutput = e.stdout || e.stderr || e.message || "";
659
+ const errorMessage = `Pre-upgrade check ${scriptName} failed:
660
+ ${errorOutput}`;
661
+ if (ctx.preUpgradeError) {
662
+ ctx.preUpgradeError += "\n\n";
663
+ }
664
+ ctx.preUpgradeError += errorMessage;
665
+ if (!force) {
666
+ await fs.remove(tempDir);
667
+ shouldCleanup = false;
668
+ return;
669
+ }
670
+ } finally {
671
+ if (shouldCleanup) {
672
+ await fs.remove(tempDir);
673
+ }
674
+ }
675
+ }
676
+ }
677
+ const extractDependencies = (content) => {
678
+ const deps = /* @__PURE__ */ new Map();
679
+ const commentRegex = /\/\/\s*@dependency:\s*(\S+)/g;
680
+ let match;
681
+ while ((match = commentRegex.exec(content)) !== null) {
682
+ const spec = match[1];
683
+ const nameMatch = spec.match(/^(@?[^@\s]+)(?:@.+)?$/);
684
+ if (nameMatch) {
685
+ deps.set(nameMatch[1], spec);
686
+ }
687
+ }
688
+ const importRegex = /(?:import|from)\s*\(?['"]([^'"]+)['"]\)?/g;
689
+ while ((match = importRegex.exec(content)) !== null) {
690
+ let name = match[1];
691
+ if (name.startsWith(".") || name.startsWith("/") || name.startsWith("node:") || builtinModules.includes(name)) {
692
+ continue;
693
+ }
694
+ const parts = name.split("/");
695
+ if (name.startsWith("@") && parts.length >= 2) {
696
+ name = parts.slice(0, 2).join("/");
697
+ } else if (parts.length >= 1) {
698
+ name = parts[0];
699
+ }
700
+ if (!deps.has(name)) {
701
+ deps.set(name, name);
702
+ }
703
+ }
704
+ return Array.from(deps.values());
705
+ };
462
706
  export {
463
707
  builder,
464
708
  command,
465
709
  description,
466
710
  handler,
711
+ runPreUpgradeScripts,
467
712
  validateTag
468
713
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cedarjs/cli",
3
- "version": "1.0.0-canary.13037+c8e9cd2b2",
3
+ "version": "1.0.0-canary.13040+853b364fd",
4
4
  "description": "The CedarJS Command Line",
5
5
  "repository": {
6
6
  "type": "git",
@@ -31,15 +31,15 @@
31
31
  "dependencies": {
32
32
  "@babel/preset-typescript": "7.28.5",
33
33
  "@babel/runtime-corejs3": "7.28.4",
34
- "@cedarjs/api-server": "1.0.0-canary.13037",
35
- "@cedarjs/cli-helpers": "1.0.0-canary.13037",
36
- "@cedarjs/fastify-web": "1.0.0-canary.13037",
37
- "@cedarjs/internal": "1.0.0-canary.13037",
38
- "@cedarjs/prerender": "1.0.0-canary.13037",
39
- "@cedarjs/project-config": "1.0.0-canary.13037",
40
- "@cedarjs/structure": "1.0.0-canary.13037",
41
- "@cedarjs/telemetry": "1.0.0-canary.13037",
42
- "@cedarjs/web-server": "1.0.0-canary.13037",
34
+ "@cedarjs/api-server": "1.0.0-canary.13040",
35
+ "@cedarjs/cli-helpers": "1.0.0-canary.13040",
36
+ "@cedarjs/fastify-web": "1.0.0-canary.13040",
37
+ "@cedarjs/internal": "1.0.0-canary.13040",
38
+ "@cedarjs/prerender": "1.0.0-canary.13040",
39
+ "@cedarjs/project-config": "1.0.0-canary.13040",
40
+ "@cedarjs/structure": "1.0.0-canary.13040",
41
+ "@cedarjs/telemetry": "1.0.0-canary.13040",
42
+ "@cedarjs/web-server": "1.0.0-canary.13040",
43
43
  "@listr2/prompt-adapter-enquirer": "2.0.16",
44
44
  "@opentelemetry/api": "1.8.0",
45
45
  "@opentelemetry/core": "1.22.0",
@@ -94,6 +94,7 @@
94
94
  "@types/archiver": "^6",
95
95
  "memfs": "4.51.1",
96
96
  "node-ssh": "13.2.1",
97
+ "ts-dedent": "2.2.0",
97
98
  "tsx": "4.21.0",
98
99
  "typescript": "5.9.3",
99
100
  "vitest": "3.2.4"
@@ -101,5 +102,5 @@
101
102
  "publishConfig": {
102
103
  "access": "public"
103
104
  },
104
- "gitHead": "c8e9cd2b280fba5e83e4b666d2c2c8adbe04e2c9"
105
+ "gitHead": "853b364fd7336788a4a48b99a6c879b17ac3824b"
105
106
  }