@cellajs/cli 0.5.0 → 0.6.0
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/README.md +96 -96
- package/dist/config.d.ts +15 -11
- package/dist/config.js +4 -2
- package/dist/config.js.map +1 -1
- package/dist/index.js +188 -143
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -57,7 +57,7 @@ import { z as z2 } from "zod";
|
|
|
57
57
|
|
|
58
58
|
// src/config/types.ts
|
|
59
59
|
import { z } from "zod";
|
|
60
|
-
var
|
|
60
|
+
var PACKAGE_JSON_KEYS = [
|
|
61
61
|
"dependencies",
|
|
62
62
|
"devDependencies",
|
|
63
63
|
"peerDependencies",
|
|
@@ -75,7 +75,9 @@ var cellaConfigSchema = z.object({
|
|
|
75
75
|
upstreamBranch: z.string().min(1).optional(),
|
|
76
76
|
upstreamTrack: z.enum(["release", "branch"]).optional(),
|
|
77
77
|
releaseBase: z.string().min(1).optional(),
|
|
78
|
-
|
|
78
|
+
packageJsonKeys: z.array(z.enum(PACKAGE_JSON_KEYS)).optional(),
|
|
79
|
+
mergePackageJson: z.boolean().optional(),
|
|
80
|
+
packageJsonSync: z.array(z.enum(PACKAGE_JSON_KEYS)).optional(),
|
|
79
81
|
syncWithPackages: z.boolean().optional(),
|
|
80
82
|
fileLinkMode: z.enum(["commit", "file"]).optional()
|
|
81
83
|
}).strict(),
|
|
@@ -156,13 +158,18 @@ function resolveAppModuleFolders(repoPath) {
|
|
|
156
158
|
// src/utils/config.ts
|
|
157
159
|
var DEFAULT_UPSTREAM_REMOTE = "cella-upstream";
|
|
158
160
|
var DEFAULT_BRANCH = "main";
|
|
159
|
-
var
|
|
160
|
-
var
|
|
161
|
+
var UPGRADE_BRANCH_PREFIX = "cella/upgrade";
|
|
162
|
+
var TEMPORARY_BRANCH_PREFIXES = [UPGRADE_BRANCH_PREFIX, "cella/sync"];
|
|
163
|
+
var DEFAULT_PACKAGE_JSON_KEYS = ["dependencies", "devDependencies"];
|
|
164
|
+
var RENAMED_SETTINGS = { packageJsonSync: "packageJsonKeys", syncWithPackages: "mergePackageJson" };
|
|
165
|
+
function renamedSettingWarnings(config) {
|
|
166
|
+
return Object.entries(RENAMED_SETTINGS).filter(([old]) => old in config.settings).map(([old, current]) => `settings.${old} is renamed to ${current}: rename it in ${CONFIG_FILE}`);
|
|
167
|
+
}
|
|
161
168
|
function isUpstreamRepo(repoPath) {
|
|
162
169
|
return JSON.parse(readFileSync(join2(repoPath, "package.json"), "utf8")).name === "cella";
|
|
163
170
|
}
|
|
164
171
|
function isTemporarySyncBranch(branch) {
|
|
165
|
-
return branch.startsWith(`${
|
|
172
|
+
return TEMPORARY_BRANCH_PREFIXES.some((prefix) => branch.startsWith(`${prefix}/`));
|
|
166
173
|
}
|
|
167
174
|
function resolveReleaseBase(settings) {
|
|
168
175
|
return settings.releaseBase ?? DEFAULT_BRANCH;
|
|
@@ -171,7 +178,7 @@ function buildTemporarySyncBranch() {
|
|
|
171
178
|
const now = /* @__PURE__ */ new Date();
|
|
172
179
|
const pad2 = (n) => String(n).padStart(2, "0");
|
|
173
180
|
const stamp = `${now.getFullYear()}${pad2(now.getMonth() + 1)}${pad2(now.getDate())}-${pad2(now.getHours())}${pad2(now.getMinutes())}`;
|
|
174
|
-
return `${
|
|
181
|
+
return `${UPGRADE_BRANCH_PREFIX}/${stamp}`;
|
|
175
182
|
}
|
|
176
183
|
function resolveUpstream(settings) {
|
|
177
184
|
const track = settings.upstreamTrack ?? "release";
|
|
@@ -201,6 +208,8 @@ async function loadConfig(forkPath) {
|
|
|
201
208
|
${z2.prettifyError(result.error)}`);
|
|
202
209
|
}
|
|
203
210
|
const config = result.data;
|
|
211
|
+
config.settings.packageJsonKeys ??= config.settings.packageJsonSync;
|
|
212
|
+
config.settings.mergePackageJson ??= config.settings.syncWithPackages;
|
|
204
213
|
const appModuleFolders = resolveAppModuleFolders(realForkPath);
|
|
205
214
|
if (appModuleFolders.length > 0) {
|
|
206
215
|
const existing = config.overrides?.ignored ?? [];
|
|
@@ -219,10 +228,10 @@ import process5 from "process";
|
|
|
219
228
|
// package.json
|
|
220
229
|
var package_default = {
|
|
221
230
|
name: "@cellajs/cli",
|
|
222
|
-
version: "0.
|
|
231
|
+
version: "0.6.0",
|
|
223
232
|
private: false,
|
|
224
233
|
license: "MIT",
|
|
225
|
-
description: "CLI for managing your monorepo and
|
|
234
|
+
description: "CLI for managing your monorepo and upgrading it with upstream Cella.",
|
|
226
235
|
repository: {
|
|
227
236
|
type: "git",
|
|
228
237
|
url: "https://github.com/cellajs/cella-cli"
|
|
@@ -746,16 +755,25 @@ async function cleanupEmptyParentDirs(cwd, relativePath) {
|
|
|
746
755
|
} catch {
|
|
747
756
|
}
|
|
748
757
|
}
|
|
758
|
+
var LAST_UPGRADE_REF = "refs/cella/last-upgrade";
|
|
759
|
+
var LEGACY_LAST_UPGRADE_REF = "refs/cella/last-sync";
|
|
749
760
|
async function storeLastSyncRef(cwd, upstreamHash) {
|
|
750
|
-
await git(["update-ref",
|
|
761
|
+
await git(["update-ref", LAST_UPGRADE_REF, upstreamHash], cwd);
|
|
751
762
|
const head = await revParse(cwd, "HEAD");
|
|
752
|
-
if (head) await git(["update-ref",
|
|
763
|
+
if (head) await git(["update-ref", `${LAST_UPGRADE_REF}-head`, head], cwd);
|
|
753
764
|
}
|
|
754
765
|
async function stagePath(cwd, path) {
|
|
755
766
|
await git(["add", "--", path], cwd, { ignoreErrors: true });
|
|
756
767
|
}
|
|
768
|
+
async function readStoredSyncRefs(cwd) {
|
|
769
|
+
for (const name of [LAST_UPGRADE_REF, LEGACY_LAST_UPGRADE_REF]) {
|
|
770
|
+
const ref = await revParse(cwd, name);
|
|
771
|
+
if (ref) return { ref, head: await revParse(cwd, `${name}-head`) };
|
|
772
|
+
}
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
757
775
|
async function getStoredSyncRef(cwd) {
|
|
758
|
-
return
|
|
776
|
+
return (await readStoredSyncRefs(cwd))?.ref ?? null;
|
|
759
777
|
}
|
|
760
778
|
async function readManifestAtRef(cwd, ref) {
|
|
761
779
|
const raw = await git(["show", `${ref}:${MANIFEST_FILE}`], cwd, { ignoreErrors: true });
|
|
@@ -804,11 +822,11 @@ async function unshallowRepository(cwd) {
|
|
|
804
822
|
${error.message}` : "";
|
|
805
823
|
throw new Error(
|
|
806
824
|
`this is a shallow clone and its full history could not be restored automatically.
|
|
807
|
-
|
|
825
|
+
An upgrade needs the complete history to find the common ancestor with upstream. Restore it with:
|
|
808
826
|
|
|
809
827
|
git fetch --unshallow
|
|
810
828
|
|
|
811
|
-
then re-run
|
|
829
|
+
then re-run the upgrade.${detail}`
|
|
812
830
|
);
|
|
813
831
|
}
|
|
814
832
|
}
|
|
@@ -834,20 +852,20 @@ async function ensureSyncBase(cwd, headRef, upstreamRef) {
|
|
|
834
852
|
}
|
|
835
853
|
if (!baseSha) {
|
|
836
854
|
throw new Error(
|
|
837
|
-
`no common ancestor between the fork and '${upstreamRef}', and no
|
|
855
|
+
`no common ancestor between the fork and '${upstreamRef}', and no upgrade base could be determined.
|
|
838
856
|
|
|
839
857
|
This fork has unrelated history with upstream (typical for a create-cella scaffold, or after
|
|
840
858
|
upstream squashed its history) and no recorded base commit exists after fetching
|
|
841
|
-
upstream. Seed it manually with the upstream commit the fork was created from, then re-run
|
|
859
|
+
upstream. Seed it manually with the upstream commit the fork was created from, then re-run the upgrade:
|
|
842
860
|
|
|
843
|
-
git update-ref
|
|
861
|
+
git update-ref ${LAST_UPGRADE_REF} <upstream-sha>
|
|
844
862
|
`
|
|
845
863
|
);
|
|
846
864
|
}
|
|
847
865
|
const root = await getRootCommit(cwd, headRef);
|
|
848
|
-
if (!root) throw new Error("could not determine the fork's root commit to bootstrap
|
|
866
|
+
if (!root) throw new Error("could not determine the fork's root commit to bootstrap upgrade ancestry");
|
|
849
867
|
await git(["replace", "-f", "--graft", root, baseSha], cwd);
|
|
850
|
-
await git(["update-ref",
|
|
868
|
+
await git(["update-ref", LAST_UPGRADE_REF, baseSha], cwd);
|
|
851
869
|
}
|
|
852
870
|
async function isAncestor(cwd, ancestor, descendant) {
|
|
853
871
|
try {
|
|
@@ -858,14 +876,13 @@ async function isAncestor(cwd, ancestor, descendant) {
|
|
|
858
876
|
}
|
|
859
877
|
}
|
|
860
878
|
async function getLegacySyncRef(cwd, headRef) {
|
|
861
|
-
const
|
|
862
|
-
if (!
|
|
863
|
-
|
|
864
|
-
if (storedHead) {
|
|
879
|
+
const stored = await readStoredSyncRefs(cwd);
|
|
880
|
+
if (!stored) return null;
|
|
881
|
+
if (stored.head) {
|
|
865
882
|
const currentHead = await revParse(cwd, headRef);
|
|
866
|
-
if (currentHead && currentHead ===
|
|
883
|
+
if (currentHead && currentHead === stored.head) return null;
|
|
867
884
|
}
|
|
868
|
-
return
|
|
885
|
+
return stored.ref;
|
|
869
886
|
}
|
|
870
887
|
async function getEffectiveMergeBase(cwd, headRef, upstreamRef) {
|
|
871
888
|
const gitBase = await getMergeBase(cwd, headRef, upstreamRef);
|
|
@@ -1044,7 +1061,7 @@ ${colors_default.dim(forkPath)}`;
|
|
|
1044
1061
|
}
|
|
1045
1062
|
function formatUpstreamStep(upstream) {
|
|
1046
1063
|
const { label, sha, baseSha, pinned, commitCount, commits, githubUrl } = upstream;
|
|
1047
|
-
const since = commitCount === 0 ? "no new commits since the last
|
|
1064
|
+
const since = commitCount === 0 ? "no new commits since the last upgrade" : `${plural(commitCount, "new commit")} since the last upgrade at ${baseSha}`;
|
|
1048
1065
|
return [
|
|
1049
1066
|
`${label} @ ${sha}${colors_default.dim(`${pinned ? " \xB7 --ref" : ""} \xB7 ${since}`)}`,
|
|
1050
1067
|
...formatUpstreamCommits(commitCount, commits, githubUrl)
|
|
@@ -1111,7 +1128,7 @@ function formatMergeInProgressDetail(conflictCount, autoMergedFiles, linkOptions
|
|
|
1111
1128
|
return lines.join("\n");
|
|
1112
1129
|
}
|
|
1113
1130
|
var statusConfig = {
|
|
1114
|
-
behind: { icon: colors_default.cyan("\u2193"), label: "behind", color: colors_default.cyan, description: "upstream changed, will
|
|
1131
|
+
behind: { icon: colors_default.cyan("\u2193"), label: "behind", color: colors_default.cyan, description: "upstream changed, will update" },
|
|
1115
1132
|
diverged: { icon: colors_default.magenta("\u21C5"), label: "diverged", color: colors_default.magenta, description: "both changed, will merge" },
|
|
1116
1133
|
drifted: { icon: colors_default.yellow("!"), label: "drifted", color: colors_default.yellow, description: "fork changed (at risk)" },
|
|
1117
1134
|
ahead: { icon: colors_default.blue("\u2191"), label: "ahead", color: colors_default.blue, description: "fork changed (protected)" },
|
|
@@ -1162,7 +1179,7 @@ function findProtectedBehindUpstream(result) {
|
|
|
1162
1179
|
const ignoredOnly = new Set((result.ignoredUpstreamChanges ?? []).flatMap((group) => group.paths));
|
|
1163
1180
|
return [...findProtectedBehind(result.files), ...result.files.filter((file) => ignoredOnly.has(file.path))];
|
|
1164
1181
|
}
|
|
1165
|
-
var PROTECTED_BEHIND_HINT = "pinned/ignored files where upstream also changed since the last
|
|
1182
|
+
var PROTECTED_BEHIND_HINT = "pinned/ignored files where upstream also changed since the last upgrade; the fork side wins on conflict, so review what upstream changed and adopt what you need.";
|
|
1166
1183
|
function protectionIcon(file) {
|
|
1167
1184
|
return file.isIgnored ? statusConfig.ignored.icon : statusConfig.pinned.icon;
|
|
1168
1185
|
}
|
|
@@ -1178,13 +1195,13 @@ function printSyncComplete(result, options = {}) {
|
|
|
1178
1195
|
const counts = `${plural(updated, "file")} updated, ${merged} auto-merged, ${plural(conflicts, "conflict")}`;
|
|
1179
1196
|
console.info();
|
|
1180
1197
|
if (options.stagedBranch) {
|
|
1181
|
-
console.info(`${checkMark}
|
|
1198
|
+
console.info(`${checkMark} upgrade merge staged on '${options.stagedBranch}'`);
|
|
1182
1199
|
console.info(colors_default.dim(` ${counts}. committing next...`));
|
|
1183
1200
|
} else if (conflicts > 0) {
|
|
1184
|
-
console.info(`${warningMark}
|
|
1201
|
+
console.info(`${warningMark} upgrade merge staged with conflicts`);
|
|
1185
1202
|
console.info(colors_default.dim(` ${counts}`));
|
|
1186
1203
|
} else {
|
|
1187
|
-
console.info(`${checkMark}
|
|
1204
|
+
console.info(`${checkMark} upgrade merge complete`);
|
|
1188
1205
|
console.info(colors_default.dim(` ${counts}`));
|
|
1189
1206
|
}
|
|
1190
1207
|
printProtectedConflicts(result);
|
|
@@ -1224,31 +1241,33 @@ function printIgnoredUpstreamChanges(result) {
|
|
|
1224
1241
|
if (group.deleted > 0) counts.push(`${group.deleted} deleted`);
|
|
1225
1242
|
console.info(` ${statusConfig.ignored.icon} ${group.entry} ${colors_default.dim(`\xB7 ${counts.join(", ")}`)}`);
|
|
1226
1243
|
}
|
|
1227
|
-
console.info(
|
|
1244
|
+
console.info(
|
|
1245
|
+
colors_default.dim(" the fork left these untouched and ignored paths never merge: review and adopt what you need.")
|
|
1246
|
+
);
|
|
1228
1247
|
}
|
|
1229
1248
|
function printUpstreamOverrideChanges(result) {
|
|
1230
1249
|
const report = result.upstreamOverrides;
|
|
1231
1250
|
if (!report) return;
|
|
1232
1251
|
console.info();
|
|
1233
1252
|
if (report.kind === "unreadable") {
|
|
1234
|
-
const side = report.side === "base" ? "the last
|
|
1253
|
+
const side = report.side === "base" ? "the last upgrade" : "the incoming ref";
|
|
1235
1254
|
console.info(
|
|
1236
|
-
colors_default.dim(`upstream
|
|
1255
|
+
colors_default.dim(`upstream CLI config not compared: upstream ${CONFIG_FILE} is missing or unreadable at ${side}`)
|
|
1237
1256
|
);
|
|
1238
1257
|
return;
|
|
1239
1258
|
}
|
|
1240
1259
|
const lines = [];
|
|
1241
|
-
for (const list of ["pinned", "ignored", "
|
|
1260
|
+
for (const list of ["pinned", "ignored", "packageJsonKeys"]) {
|
|
1242
1261
|
for (const entry of report[list].added) lines.push(` ${colors_default.green("+")} ${list}: ${entry}`);
|
|
1243
1262
|
for (const entry of report[list].removed) {
|
|
1244
1263
|
lines.push(` ${colors_default.red("\u2212")} ${list}: ${entry}${colors_default.dim(" \xB7 dropped upstream, still in your config")}`);
|
|
1245
1264
|
}
|
|
1246
1265
|
}
|
|
1247
1266
|
console.info(
|
|
1248
|
-
`${warningMark} ${colors_default.yellow(`upstream changed its
|
|
1267
|
+
`${warningMark} ${colors_default.yellow(`upstream changed its CLI config \xB7 ${plural(lines.length, "entry", "entries")} to review`)}`
|
|
1249
1268
|
);
|
|
1250
1269
|
for (const line of lines) console.info(line);
|
|
1251
|
-
console.info(colors_default.dim(` ${CONFIG_FILE} never
|
|
1270
|
+
console.info(colors_default.dim(` ${CONFIG_FILE} never merges: add or drop these by hand where they fit your app.`));
|
|
1252
1271
|
}
|
|
1253
1272
|
function findMaskingPins(files) {
|
|
1254
1273
|
return files.filter(
|
|
@@ -1327,7 +1346,8 @@ function parseAlignment(id, source, fileNames) {
|
|
|
1327
1346
|
id,
|
|
1328
1347
|
title: title || id,
|
|
1329
1348
|
summary,
|
|
1330
|
-
|
|
1349
|
+
// `syncBreaking` is the field's name in alignments up to cella 0.27
|
|
1350
|
+
breaking: (meta.get("breaking") ?? meta.get("syncBreaking")) === "true",
|
|
1331
1351
|
clientCacheBump: meta.get("clientCacheBump") === "true",
|
|
1332
1352
|
caughtBy,
|
|
1333
1353
|
generate: meta.get("generate") === "true",
|
|
@@ -1532,7 +1552,7 @@ var forkOption = ["--fork <name>", "pre-select a fork by name (skips the fork se
|
|
|
1532
1552
|
var serviceDefinitions = [
|
|
1533
1553
|
{
|
|
1534
1554
|
name: "analyze",
|
|
1535
|
-
description: "dry run to see what would change on
|
|
1555
|
+
description: "dry run to see what would change on upgrade",
|
|
1536
1556
|
options: [
|
|
1537
1557
|
["--list", "non-interactive output for tooling (one file per line)"],
|
|
1538
1558
|
jsonOption,
|
|
@@ -1545,12 +1565,13 @@ var serviceDefinitions = [
|
|
|
1545
1565
|
includeInMenu: (context) => !context.isUpstreamRepo
|
|
1546
1566
|
},
|
|
1547
1567
|
{
|
|
1548
|
-
name: "
|
|
1549
|
-
description: "merge upstream changes onto a fresh branch,
|
|
1568
|
+
name: "upgrade",
|
|
1569
|
+
description: "merge upstream changes onto a fresh branch, merge package.json and open a squash-merge PR",
|
|
1570
|
+
aliases: ["sync"],
|
|
1550
1571
|
options: [
|
|
1551
1572
|
trackOption,
|
|
1552
1573
|
refOption,
|
|
1553
|
-
["--keep-config", "merge with your
|
|
1574
|
+
["--keep-config", "merge with your CLI config as it stands when upstream changed its own"],
|
|
1554
1575
|
unpinnedOption
|
|
1555
1576
|
],
|
|
1556
1577
|
includeInMenu: (context) => !context.isUpstreamRepo
|
|
@@ -1579,10 +1600,10 @@ var serviceDefinitions = [
|
|
|
1579
1600
|
},
|
|
1580
1601
|
{
|
|
1581
1602
|
name: "forks",
|
|
1582
|
-
description: "run
|
|
1603
|
+
description: "run the upgrade inside local fork repositories",
|
|
1583
1604
|
options: [
|
|
1584
1605
|
forkOption,
|
|
1585
|
-
["--keep-config", "merge with the fork's
|
|
1606
|
+
["--keep-config", "merge with the fork's CLI config as it stands when upstream changed its own"]
|
|
1586
1607
|
],
|
|
1587
1608
|
includeInMenu: (context) => context.hasForks
|
|
1588
1609
|
},
|
|
@@ -1621,17 +1642,21 @@ function buildServiceChoices(context) {
|
|
|
1621
1642
|
];
|
|
1622
1643
|
}
|
|
1623
1644
|
function addServiceCommand(program, definition, setSelection) {
|
|
1624
|
-
const
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1645
|
+
for (const name of [definition.name, ...definition.aliases ?? []]) {
|
|
1646
|
+
const renamedFrom = name === definition.name ? void 0 : name;
|
|
1647
|
+
const command = program.command(name, { hidden: !!renamedFrom }).description(definition.description);
|
|
1648
|
+
for (const option of definition.options ?? []) command.option(...option);
|
|
1649
|
+
if (definition.operands) command.argument(...definition.operands);
|
|
1650
|
+
command.action((...args) => {
|
|
1651
|
+
const operands = definition.operands ? args[0] : [];
|
|
1652
|
+
const opts = definition.operands ? args[1] : args[0];
|
|
1653
|
+
setSelection({
|
|
1654
|
+
service: definition.name,
|
|
1655
|
+
renamedFrom,
|
|
1656
|
+
options: readOptions(opts, operands)
|
|
1657
|
+
});
|
|
1633
1658
|
});
|
|
1634
|
-
}
|
|
1659
|
+
}
|
|
1635
1660
|
}
|
|
1636
1661
|
function buildProgram(setSelection) {
|
|
1637
1662
|
const program = new Command("cella").version(VERSION, "-v, --version", "output the current version").usage("[service] [options]").helpOption("-h, --help", "display help for a command").showHelpAfterError().addHelpText(
|
|
@@ -1641,8 +1666,8 @@ function buildProgram(setSelection) {
|
|
|
1641
1666
|
"Examples:",
|
|
1642
1667
|
" $ cella analyze",
|
|
1643
1668
|
" $ cella analyze --json --scope risk",
|
|
1644
|
-
" $ cella
|
|
1645
|
-
" $ cella
|
|
1669
|
+
" $ cella upgrade --track branch",
|
|
1670
|
+
" $ cella upgrade --ref 4f7d87c",
|
|
1646
1671
|
" $ cella align --run 20261001T2116-tailwind-class-conventions -- rewrite frontend/src",
|
|
1647
1672
|
" $ cella align --mark 20261002T0614-config-switch",
|
|
1648
1673
|
" $ cella audit --list",
|
|
@@ -1689,7 +1714,13 @@ async function parseCli(userConfig, forkPath) {
|
|
|
1689
1714
|
if (selection.options.json || selection.options.md || machineList) setJsonMode(true);
|
|
1690
1715
|
printHeader();
|
|
1691
1716
|
const { branchRef } = resolveUpstream(userConfig.settings);
|
|
1692
|
-
const warnings =
|
|
1717
|
+
const warnings = [
|
|
1718
|
+
...renamedSettingWarnings(userConfig),
|
|
1719
|
+
...await validateOverrides(userConfig, forkPath, branchRef)
|
|
1720
|
+
];
|
|
1721
|
+
if (selection.renamedFrom) {
|
|
1722
|
+
warnings.unshift(`\`cella ${selection.renamedFrom}\` is renamed to \`cella ${selection.service}\``);
|
|
1723
|
+
}
|
|
1693
1724
|
for (const warning of warnings) console.info(`${warningMark} ${warning}`);
|
|
1694
1725
|
if (warnings.length > 0) console.info();
|
|
1695
1726
|
if (!selection.service) {
|
|
@@ -1778,7 +1809,7 @@ function cliVersionMismatch(running, range) {
|
|
|
1778
1809
|
const current = parseComparableVersion(running);
|
|
1779
1810
|
if (!required || !current || compareVersions(current, required) >= 0) return null;
|
|
1780
1811
|
return [
|
|
1781
|
-
`upstream needs ${CLI_PACKAGE} ${range}, this is ${running}: an older CLI misses fixes to the
|
|
1812
|
+
`upstream needs ${CLI_PACKAGE} ${range}, this is ${running}: an older CLI misses fixes to the upgrade itself.`,
|
|
1782
1813
|
` update it, commit, then rerun: pnpm add -D -w '${CLI_PACKAGE}@${range}'`
|
|
1783
1814
|
].join("\n");
|
|
1784
1815
|
}
|
|
@@ -1832,12 +1863,13 @@ function readStringEntries(node) {
|
|
|
1832
1863
|
}
|
|
1833
1864
|
return entries;
|
|
1834
1865
|
}
|
|
1835
|
-
function
|
|
1866
|
+
function readPackageJsonKeys(config, sourceFile) {
|
|
1836
1867
|
const settings = readProperty(config, "settings", sourceFile);
|
|
1837
|
-
if (settings === void 0) return [...
|
|
1868
|
+
if (settings === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
|
|
1838
1869
|
if (!settings || !Node.isObjectLiteralExpression(settings)) return null;
|
|
1839
|
-
const
|
|
1840
|
-
|
|
1870
|
+
const current = readProperty(settings, "packageJsonKeys", sourceFile);
|
|
1871
|
+
const keys = current === void 0 ? readProperty(settings, "packageJsonSync", sourceFile) : current;
|
|
1872
|
+
if (keys === void 0) return [...DEFAULT_PACKAGE_JSON_KEYS];
|
|
1841
1873
|
return keys ? readStringEntries(keys) : null;
|
|
1842
1874
|
}
|
|
1843
1875
|
function parseOverrideLists(source) {
|
|
@@ -1849,7 +1881,7 @@ function parseOverrideLists(source) {
|
|
|
1849
1881
|
const lists = {
|
|
1850
1882
|
pinned: [],
|
|
1851
1883
|
ignored: [],
|
|
1852
|
-
|
|
1884
|
+
packageJsonKeys: readPackageJsonKeys(config, sourceFile)
|
|
1853
1885
|
};
|
|
1854
1886
|
const overrides = readProperty(config, "overrides", sourceFile);
|
|
1855
1887
|
if (overrides === void 0) return lists;
|
|
@@ -1877,14 +1909,14 @@ function diffOverrideLists(base, incoming, fork) {
|
|
|
1877
1909
|
};
|
|
1878
1910
|
};
|
|
1879
1911
|
const diffKeys = () => {
|
|
1880
|
-
const [baseKeys, incomingKeys, forkKeys] = [base.
|
|
1912
|
+
const [baseKeys, incomingKeys, forkKeys] = [base.packageJsonKeys, incoming.packageJsonKeys, fork.packageJsonKeys];
|
|
1881
1913
|
if (!baseKeys || !incomingKeys || !forkKeys) return { added: [], removed: [] };
|
|
1882
1914
|
return {
|
|
1883
1915
|
added: [...new Set(incomingKeys)].filter((key) => !baseKeys.includes(key) && !forkKeys.includes(key)),
|
|
1884
1916
|
removed: [...new Set(baseKeys)].filter((key) => !incomingKeys.includes(key) && forkKeys.includes(key))
|
|
1885
1917
|
};
|
|
1886
1918
|
};
|
|
1887
|
-
return { pinned: diff("pinned"), ignored: diff("ignored"),
|
|
1919
|
+
return { pinned: diff("pinned"), ignored: diff("ignored"), packageJsonKeys: diffKeys() };
|
|
1888
1920
|
}
|
|
1889
1921
|
async function compareUpstreamOverrides(repoPath, mergeBaseRef, incomingRef, forkConfig) {
|
|
1890
1922
|
const [baseSource, incomingSource] = await Promise.all(
|
|
@@ -1898,7 +1930,7 @@ async function compareUpstreamOverrides(repoPath, mergeBaseRef, incomingRef, for
|
|
|
1898
1930
|
const fork = {
|
|
1899
1931
|
pinned: forkConfig.overrides?.pinned ?? [],
|
|
1900
1932
|
ignored: forkConfig.overrides?.ignored ?? [],
|
|
1901
|
-
|
|
1933
|
+
packageJsonKeys: forkConfig.settings.packageJsonKeys ?? DEFAULT_PACKAGE_JSON_KEYS
|
|
1902
1934
|
};
|
|
1903
1935
|
const changes = diffOverrideLists(base, incoming, fork);
|
|
1904
1936
|
const count3 = Object.values(changes).reduce((n, list) => n + list.added.length + list.removed.length, 0);
|
|
@@ -2094,7 +2126,7 @@ var UpstreamConfigChangedError = class extends Error {
|
|
|
2094
2126
|
constructor(report) {
|
|
2095
2127
|
super(
|
|
2096
2128
|
[
|
|
2097
|
-
"upstream changed its
|
|
2129
|
+
"upstream changed its CLI config and yours does not follow it: stopped before the merge, nothing changed.",
|
|
2098
2130
|
` add or drop the entries that fit your app in ${CONFIG_FILE}, commit, then rerun.`,
|
|
2099
2131
|
" to merge with your config as it stands, rerun with --keep-config."
|
|
2100
2132
|
].join("\n")
|
|
@@ -2254,7 +2286,7 @@ async function resolvePinnedRef(forkPath, config, remoteName, branchRef, ref) {
|
|
|
2254
2286
|
}
|
|
2255
2287
|
if (!await isPublishedUpstream(forkPath, remoteName, branchRef, resolved.sha)) {
|
|
2256
2288
|
throw new Error(
|
|
2257
|
-
`--ref '${ref}' (${resolved.sha.slice(0, 7)}) is not on ${branchRef} or in an upstream release.
|
|
2289
|
+
`--ref '${ref}' (${resolved.sha.slice(0, 7)}) is not on ${branchRef} or in an upstream release. Upgrade only to commits upstream has published there.`
|
|
2258
2290
|
);
|
|
2259
2291
|
}
|
|
2260
2292
|
return resolved.release ? { ref: resolved.release.ref, releaseTag: resolved.release.tag } : { ref: resolved.sha };
|
|
@@ -2310,9 +2342,9 @@ async function prepareUpstream(config, onProgress, onStep) {
|
|
|
2310
2342
|
getShortSha(forkPath, upstreamCommit.hash),
|
|
2311
2343
|
getShortSha(forkPath, mergeBase)
|
|
2312
2344
|
]);
|
|
2313
|
-
const hint2 = config.ref ? "Pick a newer --ref." : releaseTag ? `Nothing to
|
|
2345
|
+
const hint2 = config.ref ? "Pick a newer --ref." : releaseTag ? `Nothing to upgrade until a release past ${syncShort}; to follow the tip instead, run with --track branch.` : `Nothing to upgrade until '${branchRef}' moves past ${syncShort}.`;
|
|
2314
2346
|
throw new BehindSyncPointError(
|
|
2315
|
-
`upstream ${upstreamLabel} (${upstreamShort2}) is behind the last
|
|
2347
|
+
`upstream ${upstreamLabel} (${upstreamShort2}) is behind the last upgrade at ${syncShort}: the fork already has it, and merging it would revert the upstream changes after it.
|
|
2316
2348
|
${hint2}`
|
|
2317
2349
|
);
|
|
2318
2350
|
}
|
|
@@ -2422,7 +2454,7 @@ async function runSyncMerge(config, ctx, onProgress, onStep) {
|
|
|
2422
2454
|
commit: upstreamCommit.hash,
|
|
2423
2455
|
release: releaseTag ?? null,
|
|
2424
2456
|
url: upstreamGitHubUrl ? releaseTag ? `${upstreamGitHubUrl}/releases/tag/${releaseTag}` : `${upstreamGitHubUrl}/commit/${upstreamCommit.hash}` : void 0,
|
|
2425
|
-
|
|
2457
|
+
upgradedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2426
2458
|
}
|
|
2427
2459
|
};
|
|
2428
2460
|
const recordSyncPoint = async () => {
|
|
@@ -2441,10 +2473,10 @@ async function runSyncMerge(config, ctx, onProgress, onStep) {
|
|
|
2441
2473
|
} else if (totalResolved > 0) {
|
|
2442
2474
|
const label = synced > 0 ? `${synced} files from upstream` : "upstream changes";
|
|
2443
2475
|
await recordSyncPoint();
|
|
2444
|
-
onStep?.("
|
|
2476
|
+
onStep?.("upgraded", `${label} (staged, commit to finish)`);
|
|
2445
2477
|
} else {
|
|
2446
2478
|
await mergeAbort(forkPath);
|
|
2447
|
-
onStep?.("up to date", "no upstream changes to
|
|
2479
|
+
onStep?.("up to date", "no upstream changes to merge");
|
|
2448
2480
|
}
|
|
2449
2481
|
return {
|
|
2450
2482
|
success: remainingConflicts.length === 0,
|
|
@@ -2529,7 +2561,7 @@ var STEP_ROWS_MAX = 5;
|
|
|
2529
2561
|
function alignmentTags(alignment) {
|
|
2530
2562
|
return [
|
|
2531
2563
|
alignment.codemod ? "codemod" : "manual",
|
|
2532
|
-
alignment.
|
|
2564
|
+
alignment.breaking ? "breaking" : "not breaking",
|
|
2533
2565
|
alignment.caughtBy ? `caught by ${alignment.caughtBy}` : null
|
|
2534
2566
|
].filter(Boolean).join(" \xB7 ");
|
|
2535
2567
|
}
|
|
@@ -2538,7 +2570,7 @@ async function alignmentsStep(config, view) {
|
|
|
2538
2570
|
const open = view.open.filter((id) => !arriving.includes(id));
|
|
2539
2571
|
const arrived = (view.arrived ?? []).filter((id) => open.includes(id));
|
|
2540
2572
|
if (arriving.length === 0 && open.length === 0) return void 0;
|
|
2541
|
-
const line = arriving.length > 0 ? `${plural(arriving.length, "alignment")} ${arriving.length === 1 ? "arrives" : "arrive"} with this
|
|
2573
|
+
const line = arriving.length > 0 ? `${plural(arriving.length, "alignment")} ${arriving.length === 1 ? "arrives" : "arrive"} with this upgrade${open.length > 0 ? colors_default.dim(` \xB7 ${open.length} open`) : ""}` : `${plural(open.length, "alignment")} open${arrived.length > 0 ? colors_default.dim(` \xB7 ${arrived.length} arrived with this upgrade`) : ""}`;
|
|
2542
2574
|
const githubUrl = getGitHubBaseUrl(config.settings.upstreamUrl) ?? void 0;
|
|
2543
2575
|
const ids = [...arriving, ...open];
|
|
2544
2576
|
const details = [];
|
|
@@ -2549,7 +2581,7 @@ async function alignmentsStep(config, view) {
|
|
|
2549
2581
|
if (!process7.stdout.isTTY) details.push(colors_default.dim(` id ${id}`));
|
|
2550
2582
|
}
|
|
2551
2583
|
if (ids.length > STEP_ROWS_MAX) details.push(colors_default.dim(`+ ${ids.length - STEP_ROWS_MAX} more`));
|
|
2552
|
-
details.push(`${arriving.length > 0 ? colors_default.dim("after the
|
|
2584
|
+
details.push(`${arriving.length > 0 ? colors_default.dim("after the upgrade: ") : ""}pnpm cella align`);
|
|
2553
2585
|
return { label: "align", line, details };
|
|
2554
2586
|
}
|
|
2555
2587
|
async function readForkView(config, ref) {
|
|
@@ -2592,7 +2624,7 @@ function grepSummary(alignment) {
|
|
|
2592
2624
|
function printAlignment(index, alignment) {
|
|
2593
2625
|
const tags = [
|
|
2594
2626
|
alignment.codemod ? "codemod" : "manual",
|
|
2595
|
-
alignment.
|
|
2627
|
+
alignment.breaking ? "breaking" : null,
|
|
2596
2628
|
alignment.clientCacheBump ? "cache-bump" : null,
|
|
2597
2629
|
alignment.caughtBy ? `caught by: ${alignment.caughtBy}` : null,
|
|
2598
2630
|
alignment.generate ? "generate" : null
|
|
@@ -2634,7 +2666,7 @@ async function showAlignment(config, ref, pending, id) {
|
|
|
2634
2666
|
alignment,
|
|
2635
2667
|
pending.includes(id)
|
|
2636
2668
|
);
|
|
2637
|
-
if (readRef !== ref) console.info(colors_default.dim(`not at the
|
|
2669
|
+
if (readRef !== ref) console.info(colors_default.dim(`not at the last upgrade ${ref.slice(0, 7)}: read at ${upstreamBranch}`));
|
|
2638
2670
|
if (described.url) console.info(colors_default.dim(described.url));
|
|
2639
2671
|
console.info(source);
|
|
2640
2672
|
console.info();
|
|
@@ -2731,7 +2763,7 @@ async function runAlign(config) {
|
|
|
2731
2763
|
const { forkPath } = config;
|
|
2732
2764
|
const ref = await resolveAlignmentsRef(config);
|
|
2733
2765
|
if (!ref) {
|
|
2734
|
-
console.info(colors_default.dim("no
|
|
2766
|
+
console.info(colors_default.dim("no upgrade recorded yet: alignments start with the first `pnpm cella upgrade`."));
|
|
2735
2767
|
return;
|
|
2736
2768
|
}
|
|
2737
2769
|
const pending = await readPending(forkPath);
|
|
@@ -2746,7 +2778,7 @@ async function runAlign(config) {
|
|
|
2746
2778
|
id: ids[i],
|
|
2747
2779
|
title: `${ids[i]} (no longer upstream)`,
|
|
2748
2780
|
summary: "",
|
|
2749
|
-
|
|
2781
|
+
breaking: false,
|
|
2750
2782
|
clientCacheBump: false,
|
|
2751
2783
|
caughtBy: null,
|
|
2752
2784
|
generate: false,
|
|
@@ -3167,30 +3199,30 @@ function reviewSections(result, command) {
|
|
|
3167
3199
|
const [base, upstream] = result.upstreamDiffRange?.split("..") ?? [];
|
|
3168
3200
|
if (!base || !upstream) return [];
|
|
3169
3201
|
const sinceSync = `${base}..${upstream}`;
|
|
3170
|
-
const sections = command === "
|
|
3202
|
+
const sections = command === "upgrade" ? [
|
|
3171
3203
|
{
|
|
3172
3204
|
title: "protected files kept the fork version",
|
|
3173
|
-
hint: "What upstream changed in these files since the last
|
|
3205
|
+
hint: "What upstream changed in these files since the last upgrade. The fork kept its own version, so none of it arrived: adopt what you need.",
|
|
3174
3206
|
range: sinceSync,
|
|
3175
3207
|
paths: result.protectedConflicts ?? []
|
|
3176
3208
|
},
|
|
3177
3209
|
{
|
|
3178
3210
|
title: "ignored paths changed upstream",
|
|
3179
|
-
hint: "What upstream changed under ignored paths since the last
|
|
3211
|
+
hint: "What upstream changed under ignored paths since the last upgrade. The fork left these files untouched, and ignored paths never merge.",
|
|
3180
3212
|
range: sinceSync,
|
|
3181
3213
|
paths: (result.ignoredUpstreamChanges ?? []).flatMap((group) => group.paths)
|
|
3182
3214
|
}
|
|
3183
3215
|
] : [
|
|
3184
3216
|
{
|
|
3185
3217
|
title: "protected but behind upstream",
|
|
3186
|
-
hint: "What upstream changed in these files since the last
|
|
3218
|
+
hint: "What upstream changed in these files since the last upgrade. They are pinned or ignored, so the upgrade keeps the fork version and none of it arrives: adopt what you need.",
|
|
3187
3219
|
range: sinceSync,
|
|
3188
3220
|
paths: findProtectedBehindUpstream(result).map((file) => file.path)
|
|
3189
3221
|
}
|
|
3190
3222
|
];
|
|
3191
3223
|
sections.push({
|
|
3192
3224
|
title: "pinned files that match the previous upstream",
|
|
3193
|
-
hint: "What upstream changed in these files since the last
|
|
3225
|
+
hint: "What upstream changed in these files since the last upgrade. The fork copy still equals the previous upstream, so the pin only drops these changes: unpin to take them.",
|
|
3194
3226
|
range: sinceSync,
|
|
3195
3227
|
paths: findMaskingPins(result.files).map((file) => file.path)
|
|
3196
3228
|
});
|
|
@@ -3198,13 +3230,13 @@ function reviewSections(result, command) {
|
|
|
3198
3230
|
sections.push(
|
|
3199
3231
|
{
|
|
3200
3232
|
title: "protected in fork, upstream lines absent",
|
|
3201
|
-
hint: "The fork's version against upstream's, both as they are now: added lines are upstream content the fork does not have, dropped by an earlier
|
|
3233
|
+
hint: "The fork's version against upstream's, both as they are now: added lines are upstream content the fork does not have, dropped by an earlier upgrade or removed on purpose.",
|
|
3202
3234
|
range: `HEAD..${upstream}`,
|
|
3203
3235
|
paths: filesWithStatus(result.files, "ahead").filter((file) => (file.upstreamLinesAbsent ?? 0) > 0).map((file) => file.path)
|
|
3204
3236
|
},
|
|
3205
3237
|
{
|
|
3206
3238
|
title: "drifted from upstream",
|
|
3207
|
-
hint: "What the fork changed in these files since the last
|
|
3239
|
+
hint: "What the fork changed in these files since the last upgrade. They are not pinned or ignored, so upstream changes still merge into them.",
|
|
3208
3240
|
range: `${base}..HEAD`,
|
|
3209
3241
|
paths: filesWithStatus(result.files, "drifted").map((file) => file.path)
|
|
3210
3242
|
}
|
|
@@ -3253,7 +3285,7 @@ async function writeReviewPage(forkPath, sections, meta, limits = PAGE_LIMITS) {
|
|
|
3253
3285
|
const shown = pageSections.filter((section) => section.files.length > 0 || section.omitted.length > 0);
|
|
3254
3286
|
const page = await writeDiffPage(
|
|
3255
3287
|
shown,
|
|
3256
|
-
{ title: `${meta.command} review`, note: `${basename3(forkPath)} \xB7 last
|
|
3288
|
+
{ title: `${meta.command} review`, note: `${basename3(forkPath)} \xB7 last upgrade ${base} \u2192 upstream ${upstream}` },
|
|
3257
3289
|
forkPath,
|
|
3258
3290
|
`${meta.command}-review`
|
|
3259
3291
|
);
|
|
@@ -3273,21 +3305,21 @@ async function reportReviewPage(forkPath, result, command) {
|
|
|
3273
3305
|
spinnerSuccess();
|
|
3274
3306
|
}
|
|
3275
3307
|
if (!page) {
|
|
3276
|
-
if (command === "
|
|
3308
|
+
if (command === "upgrade") removeSyncReviewPage(forkPath);
|
|
3277
3309
|
return void 0;
|
|
3278
3310
|
}
|
|
3279
|
-
const opened = command === "
|
|
3311
|
+
const opened = command === "upgrade" && process9.stdout.isTTY === true && openInBrowser(page.path);
|
|
3280
3312
|
return { ...page, command, opened };
|
|
3281
3313
|
}
|
|
3282
3314
|
function removeSyncReviewPage(forkPath) {
|
|
3283
3315
|
try {
|
|
3284
|
-
rmSync(diffPagePath(forkPath, "
|
|
3316
|
+
rmSync(diffPagePath(forkPath, "upgrade-review"), { force: true });
|
|
3285
3317
|
} catch {
|
|
3286
3318
|
}
|
|
3287
3319
|
}
|
|
3288
3320
|
function existingSyncReviewPage(forkPath) {
|
|
3289
3321
|
try {
|
|
3290
|
-
const path = diffPagePath(forkPath, "
|
|
3322
|
+
const path = diffPagePath(forkPath, "upgrade-review");
|
|
3291
3323
|
return existsSync7(path) ? path : void 0;
|
|
3292
3324
|
} catch {
|
|
3293
3325
|
return void 0;
|
|
@@ -3296,9 +3328,9 @@ function existingSyncReviewPage(forkPath) {
|
|
|
3296
3328
|
function reviewStep(page) {
|
|
3297
3329
|
const path = typeof page === "string" ? page : page.path;
|
|
3298
3330
|
const url = pathToFileURL(path).href;
|
|
3299
|
-
let detail = colors_default.dim("upstream changes the
|
|
3331
|
+
let detail = colors_default.dim("upstream changes the upgrade did not bring in, from the merge run");
|
|
3300
3332
|
if (typeof page !== "string") {
|
|
3301
|
-
const what = page.command === "
|
|
3333
|
+
const what = page.command === "upgrade" ? `${plural(page.diffs, "diff")} of upstream changes that did not arrive` : `${plural(page.diffs, "diff")} in ${plural(page.sections, "section")}`;
|
|
3302
3334
|
const notes = [
|
|
3303
3335
|
page.listed > 0 ? `${page.listed} listed without a diff` : "",
|
|
3304
3336
|
page.opened ? "opened in the browser" : ""
|
|
@@ -3340,8 +3372,8 @@ async function runAnalyze(config) {
|
|
|
3340
3372
|
upstreamChangedLines: f.upstreamChangedLines ?? null,
|
|
3341
3373
|
upstreamLinesAbsent: f.upstreamLinesAbsent ?? null,
|
|
3342
3374
|
upstreamOnly: f.upstreamOnly ?? false,
|
|
3343
|
-
// Only on the
|
|
3344
|
-
upstreamOverrides: overrides && f.path === CONFIG_FILE ? { pinned: overrides.pinned, ignored: overrides.ignored,
|
|
3375
|
+
// Only on the CLI config: upstream override entries and packageJsonKeys the fork config does not follow
|
|
3376
|
+
upstreamOverrides: overrides && f.path === CONFIG_FILE ? { pinned: overrides.pinned, ignored: overrides.ignored, packageJsonKeys: overrides.packageJsonKeys } : null
|
|
3345
3377
|
}));
|
|
3346
3378
|
writeStdout(JSON.stringify(out, null, 2));
|
|
3347
3379
|
return result;
|
|
@@ -3389,11 +3421,11 @@ function syncStep(result) {
|
|
|
3389
3421
|
renamed > 0 ? `${renamed} rename` : ""
|
|
3390
3422
|
].filter(Boolean);
|
|
3391
3423
|
return {
|
|
3392
|
-
label: "
|
|
3393
|
-
line: "pnpm cella
|
|
3424
|
+
label: "upgrade",
|
|
3425
|
+
line: "pnpm cella upgrade",
|
|
3394
3426
|
details: [
|
|
3395
3427
|
colors_default.dim(
|
|
3396
|
-
`commits on a fresh branch, a rerun opens the PR \xB7 ${changes.length > 0 ? changes.join(", ") : "no
|
|
3428
|
+
`commits on a fresh branch, a rerun opens the PR \xB7 ${changes.length > 0 ? changes.join(", ") : "no file changes"}`
|
|
3397
3429
|
)
|
|
3398
3430
|
]
|
|
3399
3431
|
};
|
|
@@ -5162,7 +5194,7 @@ import { resolve as resolve6 } from "path";
|
|
|
5162
5194
|
import process10 from "process";
|
|
5163
5195
|
import { Separator as Separator2, select as select5 } from "@inquirer/prompts";
|
|
5164
5196
|
|
|
5165
|
-
// src/services/
|
|
5197
|
+
// src/services/upgrade.ts
|
|
5166
5198
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
5167
5199
|
import { select as select4 } from "@inquirer/prompts";
|
|
5168
5200
|
|
|
@@ -5176,7 +5208,7 @@ function runGh(cwd, args) {
|
|
|
5176
5208
|
const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
|
|
5177
5209
|
return { ok: result.status === 0, output };
|
|
5178
5210
|
}
|
|
5179
|
-
function listOpenSyncPrs(cwd,
|
|
5211
|
+
function listOpenSyncPrs(cwd, prefixes) {
|
|
5180
5212
|
const { ok, output } = runGh(cwd, [
|
|
5181
5213
|
"pr",
|
|
5182
5214
|
"list",
|
|
@@ -5194,7 +5226,7 @@ function listOpenSyncPrs(cwd, prefix) {
|
|
|
5194
5226
|
} catch {
|
|
5195
5227
|
return [];
|
|
5196
5228
|
}
|
|
5197
|
-
return parsed.filter((pr) => pr.headRefName.startsWith(`${prefix}/`)).sort((a, b) => b.number - a.number);
|
|
5229
|
+
return parsed.filter((pr) => prefixes.some((prefix) => pr.headRefName.startsWith(`${prefix}/`))).sort((a, b) => b.number - a.number);
|
|
5198
5230
|
}
|
|
5199
5231
|
function createPr(cwd, repoSlug, pr) {
|
|
5200
5232
|
const repo = repoSlug ?? "{owner}/{repo}";
|
|
@@ -5564,8 +5596,8 @@ async function syncPackageJson(config, baseRef, usage, relativePath, keysToSync)
|
|
|
5564
5596
|
return { updated, changes, kept };
|
|
5565
5597
|
}
|
|
5566
5598
|
async function runPackages(config, options = {}) {
|
|
5567
|
-
createSpinner("
|
|
5568
|
-
const keysToSync = config.settings.
|
|
5599
|
+
createSpinner("merging package.json files...");
|
|
5600
|
+
const keysToSync = config.settings.packageJsonKeys || DEFAULT_PACKAGE_JSON_KEYS;
|
|
5569
5601
|
const conflictedSet = new Set(options.conflictedFiles ?? []);
|
|
5570
5602
|
const baseRef = await resolveMergeBase(config.forkPath, config.upstreamRef);
|
|
5571
5603
|
const usage = baseRef ? await loadForkUsage(config.forkPath, config.upstreamRef, baseRef) : null;
|
|
@@ -5578,12 +5610,15 @@ async function runPackages(config, options = {}) {
|
|
|
5578
5610
|
skipped.push(pkgRelPath);
|
|
5579
5611
|
continue;
|
|
5580
5612
|
}
|
|
5581
|
-
spinnerText(`
|
|
5613
|
+
spinnerText(`merging ${location || "root"}/package.json...`);
|
|
5582
5614
|
const { updated, changes, kept: kept2 } = await syncPackageJson(config, baseRef, usage, location, keysToSync);
|
|
5583
5615
|
if (updated || kept2.length > 0) reports.push({ path: pkgRelPath, changes, kept: kept2 });
|
|
5584
5616
|
}
|
|
5585
5617
|
const changedCount = reports.filter(({ changes }) => changes.length > 0).length;
|
|
5586
|
-
spinnerSuccess(
|
|
5618
|
+
spinnerSuccess(
|
|
5619
|
+
"package.json merge complete",
|
|
5620
|
+
`${plural(changedCount, "package.json was", "package.jsons were")} changed`
|
|
5621
|
+
);
|
|
5587
5622
|
for (const { path, changes } of reports) {
|
|
5588
5623
|
if (changes.length === 0) continue;
|
|
5589
5624
|
console.info(` ${path}`);
|
|
@@ -5593,7 +5628,7 @@ async function runPackages(config, options = {}) {
|
|
|
5593
5628
|
const them = kept.length === 1 ? "it" : "them";
|
|
5594
5629
|
printWarnList(`kept ${plural(kept.length, "entry", "entries")} upstream dropped, the fork still uses ${them}:`, kept);
|
|
5595
5630
|
printWarnList(
|
|
5596
|
-
`skipped ${skipped.length} conflicted package.json file(s): resolve the conflicts, commit the merge, then rerun \`pnpm cella
|
|
5631
|
+
`skipped ${skipped.length} conflicted package.json file(s): resolve the conflicts, commit the merge, then rerun \`pnpm cella upgrade\`:`,
|
|
5597
5632
|
skipped
|
|
5598
5633
|
);
|
|
5599
5634
|
}
|
|
@@ -5604,7 +5639,7 @@ function printWarnList(header, entries) {
|
|
|
5604
5639
|
for (const entry of entries) console.info(` ${colors_default.dim("\u2192")} ${entry}`);
|
|
5605
5640
|
}
|
|
5606
5641
|
|
|
5607
|
-
// src/services/
|
|
5642
|
+
// src/services/upgrade.ts
|
|
5608
5643
|
async function resolveCycleStart(forkPath, base, currentBranch) {
|
|
5609
5644
|
if (!await branchExists(forkPath, base)) {
|
|
5610
5645
|
await fetchRemote(forkPath, "origin").catch(() => {
|
|
@@ -5620,7 +5655,7 @@ async function resolveCycleStart(forkPath, base, currentBranch) {
|
|
|
5620
5655
|
if (ahead > 0 && behind > 0) {
|
|
5621
5656
|
throw new Error(
|
|
5622
5657
|
`'${base}' has diverged from '${upstream}' (${ahead} ahead, ${behind} behind).
|
|
5623
|
-
Reconcile '${base}' with '${upstream}' first (e.g. rebase or reset it), then re-run
|
|
5658
|
+
Reconcile '${base}' with '${upstream}' first (e.g. rebase or reset it), then re-run the upgrade.`
|
|
5624
5659
|
);
|
|
5625
5660
|
}
|
|
5626
5661
|
if (behind > 0) {
|
|
@@ -5649,7 +5684,7 @@ async function setupTemporarySyncBranch(config) {
|
|
|
5649
5684
|
console.info(colors_default.dim(`updating '${base}'...`));
|
|
5650
5685
|
const startPoint = await resolveCycleStart(forkPath, base, startBranch);
|
|
5651
5686
|
const temporaryBranch = buildTemporarySyncBranch();
|
|
5652
|
-
console.info(colors_default.dim(`creating temporary
|
|
5687
|
+
console.info(colors_default.dim(`creating temporary upgrade branch '${temporaryBranch}' from '${startPoint}'...`));
|
|
5653
5688
|
console.info();
|
|
5654
5689
|
await createBranchFrom(forkPath, temporaryBranch, startPoint);
|
|
5655
5690
|
return { temporaryBranch, base, startBranch, startCommit };
|
|
@@ -5673,7 +5708,7 @@ async function returnToBase(forkPath, base) {
|
|
|
5673
5708
|
return `back on '${base}'`;
|
|
5674
5709
|
}
|
|
5675
5710
|
async function runSync(config, options) {
|
|
5676
|
-
createSpinner("starting
|
|
5711
|
+
createSpinner("starting upgrade...");
|
|
5677
5712
|
let result;
|
|
5678
5713
|
try {
|
|
5679
5714
|
result = await runEngineWithSpinner(config, true);
|
|
@@ -5688,7 +5723,7 @@ async function runSync(config, options) {
|
|
|
5688
5723
|
if (result.success) {
|
|
5689
5724
|
spinnerSuccess();
|
|
5690
5725
|
} else {
|
|
5691
|
-
spinnerFail("
|
|
5726
|
+
spinnerFail("upgrade merge completed with conflicts");
|
|
5692
5727
|
}
|
|
5693
5728
|
printEngineReports(result, "merge summary");
|
|
5694
5729
|
if (config.unpinned) {
|
|
@@ -5698,7 +5733,7 @@ async function runSync(config, options) {
|
|
|
5698
5733
|
const staged = result.files.some((file) => SYNC_APPLIED_STATUSES.includes(file.status));
|
|
5699
5734
|
const stagedBranch = options?.stagedBranch && result.conflicts.length === 0 && staged ? options.stagedBranch : void 0;
|
|
5700
5735
|
printSyncComplete(result, { stagedBranch });
|
|
5701
|
-
const reviewPage = await reportReviewPage(config.forkPath, result, "
|
|
5736
|
+
const reviewPage = await reportReviewPage(config.forkPath, result, "upgrade");
|
|
5702
5737
|
return { ...result, reviewPage };
|
|
5703
5738
|
}
|
|
5704
5739
|
var CONFLICTS_LISTED = 10;
|
|
@@ -5725,9 +5760,9 @@ async function conflictSteps(config, conflicts, run) {
|
|
|
5725
5760
|
},
|
|
5726
5761
|
{
|
|
5727
5762
|
label: "finish",
|
|
5728
|
-
line: "pnpm cella
|
|
5763
|
+
line: "pnpm cella upgrade",
|
|
5729
5764
|
details: [
|
|
5730
|
-
colors_default.dim("commits the
|
|
5765
|
+
colors_default.dim("commits the upgrade and stops for drift triage"),
|
|
5731
5766
|
colors_default.dim("let the rerun commit: a manual `git commit` records a merge commit that bloats the PR")
|
|
5732
5767
|
]
|
|
5733
5768
|
},
|
|
@@ -5736,13 +5771,13 @@ async function conflictSteps(config, conflicts, run) {
|
|
|
5736
5771
|
];
|
|
5737
5772
|
return steps.filter((step) => !!step);
|
|
5738
5773
|
}
|
|
5739
|
-
var
|
|
5774
|
+
var UPGRADE_PR_TITLE = "chore: upgrade cella";
|
|
5740
5775
|
async function buildSyncCommitMessage(forkPath, upstreamRef) {
|
|
5741
5776
|
const shortSha = await getShortSha(forkPath, upstreamRef).catch(() => "");
|
|
5742
|
-
if (!shortSha) return
|
|
5777
|
+
if (!shortSha) return UPGRADE_PR_TITLE;
|
|
5743
5778
|
const manifest = await readSyncManifest(forkPath);
|
|
5744
5779
|
const version = manifest?.upstream.release ?? await readPackageVersionAtRef(forkPath, upstreamRef).catch(() => null);
|
|
5745
|
-
return version ? `${
|
|
5780
|
+
return version ? `${UPGRADE_PR_TITLE} to v${version.replace(/^v/, "")} (${shortSha})` : `${UPGRADE_PR_TITLE} to ${shortSha}`;
|
|
5746
5781
|
}
|
|
5747
5782
|
function qualifyPrRefs(subject, repoSlug) {
|
|
5748
5783
|
if (!repoSlug) return subject;
|
|
@@ -5755,10 +5790,14 @@ function buildSyncPrBody(input) {
|
|
|
5755
5790
|
const commitRef = (sha) => githubUrl ? `[\`${short(sha)}\`](${githubUrl}/commit/${sha})` : `\`${short(sha)}\``;
|
|
5756
5791
|
const upstreamName = githubUrl ? `[${repoSlug}](${githubUrl})` : "cella";
|
|
5757
5792
|
const versionSuffix = version ? ` (v${version.replace(/^v/, "")})` : "";
|
|
5758
|
-
const lines = [`
|
|
5793
|
+
const lines = [`Upgrades ${upstreamName} to ${commitRef(toSha)}${versionSuffix}.`];
|
|
5759
5794
|
if (totalCount > 0 && fromSha) {
|
|
5760
5795
|
const compare = githubUrl ? ` ([compare](${githubUrl}/compare/${short(fromSha)}...${short(toSha)}))` : "";
|
|
5761
|
-
lines.push(
|
|
5796
|
+
lines.push(
|
|
5797
|
+
"",
|
|
5798
|
+
`**${totalCount} upstream commit${totalCount === 1 ? "" : "s"} since the last upgrade**${compare}:`,
|
|
5799
|
+
""
|
|
5800
|
+
);
|
|
5762
5801
|
if (totalCount > commits.length) lines.push(`- \u2026${totalCount - commits.length} earlier commit(s) not shown`);
|
|
5763
5802
|
for (const commit of commits) {
|
|
5764
5803
|
lines.push(`- ${commitRef(commit.hash)} ${qualifyPrRefs(commit.message, repoSlug)}`);
|
|
@@ -5813,7 +5852,7 @@ async function readPrBodyAlignments(forkPath, toSha, repoSlug) {
|
|
|
5813
5852
|
);
|
|
5814
5853
|
return { total: ids.length, open };
|
|
5815
5854
|
}
|
|
5816
|
-
function printPrCreateStep(branch, base, title =
|
|
5855
|
+
function printPrCreateStep(branch, base, title = UPGRADE_PR_TITLE) {
|
|
5817
5856
|
console.info(colors_default.dim(` gh pr create --base ${base} --head ${branch} --title "${title}" --fill`));
|
|
5818
5857
|
}
|
|
5819
5858
|
async function flattenSyncBranch(forkPath, branch, base) {
|
|
@@ -5837,7 +5876,7 @@ async function shipSyncBranch(config, branch) {
|
|
|
5837
5876
|
let prUrl;
|
|
5838
5877
|
let prOpened = false;
|
|
5839
5878
|
const upstreamCommit = (await readSyncManifest(forkPath))?.upstream.commit;
|
|
5840
|
-
const prTitle = upstreamCommit ? await buildSyncCommitMessage(forkPath, upstreamCommit) :
|
|
5879
|
+
const prTitle = upstreamCommit ? await buildSyncCommitMessage(forkPath, upstreamCommit) : UPGRADE_PR_TITLE;
|
|
5841
5880
|
console.info(colors_default.dim(`pushing '${branch}' to origin...`));
|
|
5842
5881
|
try {
|
|
5843
5882
|
await pushBranch(forkPath, "origin", branch, { forceWithLease: flattened });
|
|
@@ -5866,17 +5905,17 @@ async function shipSyncBranch(config, branch) {
|
|
|
5866
5905
|
const position = await returnToBase(forkPath, base);
|
|
5867
5906
|
console.info();
|
|
5868
5907
|
if (prUrl) {
|
|
5869
|
-
console.info(`${checkMark}
|
|
5908
|
+
console.info(`${checkMark} upgrade pull request ${prOpened ? "opened" : "ready"}`);
|
|
5870
5909
|
console.info(colors_default.dim(` ${prUrl} \xB7 branch pushed, ${position}`));
|
|
5871
5910
|
} else {
|
|
5872
|
-
console.info(`${checkMark}
|
|
5911
|
+
console.info(`${checkMark} upgrade branch pushed`);
|
|
5873
5912
|
console.info(colors_default.dim(` '${branch}' is on origin, ${position}`));
|
|
5874
5913
|
}
|
|
5875
5914
|
const steps = [
|
|
5876
5915
|
prUrl ? {
|
|
5877
5916
|
label: "merge",
|
|
5878
5917
|
line: hyperlink(colors_default.cyan(prUrl), prUrl),
|
|
5879
|
-
details: [colors_default.dim(`squash-merge it into '${base}'; the next
|
|
5918
|
+
details: [colors_default.dim(`squash-merge it into '${base}'; the next upgrade offers to while it is open`)]
|
|
5880
5919
|
} : void 0,
|
|
5881
5920
|
await syncAlignmentsStep(config)
|
|
5882
5921
|
];
|
|
@@ -5916,7 +5955,7 @@ async function runSyncCycle(config) {
|
|
|
5916
5955
|
}
|
|
5917
5956
|
throw error;
|
|
5918
5957
|
}
|
|
5919
|
-
if (config.settings.
|
|
5958
|
+
if (config.settings.mergePackageJson !== false) {
|
|
5920
5959
|
await runPackages(config, { conflictedFiles: result.conflicts });
|
|
5921
5960
|
}
|
|
5922
5961
|
if (result.conflicts.length > 0) return { status: "conflicts", branch, result };
|
|
@@ -5941,7 +5980,11 @@ async function commitSyncMerge(config, branch, run) {
|
|
|
5941
5980
|
printStep("checks", "`pnpm install` or `pnpm check` failed", warningMark);
|
|
5942
5981
|
printNextSteps([
|
|
5943
5982
|
{ label: "fix", line: "what failed above", details: [colors_default.dim(`the merge stays staged on '${branch}'`)] },
|
|
5944
|
-
{
|
|
5983
|
+
{
|
|
5984
|
+
label: "finish",
|
|
5985
|
+
line: "pnpm cella upgrade",
|
|
5986
|
+
details: [colors_default.dim("commits the upgrade and stops for drift triage")]
|
|
5987
|
+
}
|
|
5945
5988
|
]);
|
|
5946
5989
|
return;
|
|
5947
5990
|
}
|
|
@@ -5959,7 +6002,7 @@ ${colors_default.dim(`on '${branch}', not pushed`)}`);
|
|
|
5959
6002
|
details: [colors_default.dim("drift triage: commit any follow-ups on this branch")]
|
|
5960
6003
|
},
|
|
5961
6004
|
await syncAlignmentsStep(config, run),
|
|
5962
|
-
{ label: "ship", line: "pnpm cella
|
|
6005
|
+
{ label: "ship", line: "pnpm cella upgrade", details: [colors_default.dim("pushes the branch and opens the squash-merge PR")] }
|
|
5963
6006
|
];
|
|
5964
6007
|
printNextSteps(steps.filter((step) => !!step));
|
|
5965
6008
|
}
|
|
@@ -5972,7 +6015,7 @@ async function mergeOpenSyncPrs(config, open) {
|
|
|
5972
6015
|
const merged = mergePrSquash(forkPath, newest.number, { deleteBranch: true });
|
|
5973
6016
|
if (!merged.ok) {
|
|
5974
6017
|
console.info();
|
|
5975
|
-
console.info(colors_default.yellow(`could not merge #${newest.number}: resolve it first, then re-run \`pnpm cella
|
|
6018
|
+
console.info(colors_default.yellow(`could not merge #${newest.number}: resolve it first, then re-run \`pnpm cella upgrade\`:`));
|
|
5976
6019
|
if (merged.output) console.info(colors_default.dim(merged.output.replace(/^/gm, " ")));
|
|
5977
6020
|
console.info(colors_default.dim(` ${newest.url}`));
|
|
5978
6021
|
return "cancel";
|
|
@@ -5989,25 +6032,27 @@ async function mergeOpenSyncPrs(config, open) {
|
|
|
5989
6032
|
async function guardAgainstOpenSyncPr(config) {
|
|
5990
6033
|
const { forkPath } = config;
|
|
5991
6034
|
if (!ghAvailable() || !process.stdout.isTTY) return "continue";
|
|
5992
|
-
const open = listOpenSyncPrs(forkPath,
|
|
6035
|
+
const open = listOpenSyncPrs(forkPath, TEMPORARY_BRANCH_PREFIXES);
|
|
5993
6036
|
if (open.length === 0) return "continue";
|
|
5994
6037
|
const many = open.length > 1;
|
|
5995
6038
|
console.info();
|
|
5996
6039
|
console.info(
|
|
5997
|
-
`${warningMark} ${colors_default.yellow(`${many ? `${open.length} earlier
|
|
6040
|
+
`${warningMark} ${colors_default.yellow(`${many ? `${open.length} earlier upgrade PRs are` : "an earlier upgrade PR is"} still open and unmerged:`)}`
|
|
5998
6041
|
);
|
|
5999
6042
|
for (const pr of open) {
|
|
6000
6043
|
console.info(colors_default.dim(` #${pr.number} ${pr.title}`));
|
|
6001
6044
|
console.info(colors_default.dim(` ${pr.url}`));
|
|
6002
6045
|
}
|
|
6003
|
-
console.info(
|
|
6046
|
+
console.info(
|
|
6047
|
+
colors_default.dim(` starting a new upgrade now produces a PR that re-includes ${many ? "these" : "those"} changes.`)
|
|
6048
|
+
);
|
|
6004
6049
|
console.info();
|
|
6005
6050
|
const choice = await select4({
|
|
6006
6051
|
message: "how do you want to proceed?",
|
|
6007
6052
|
choices: [
|
|
6008
6053
|
{
|
|
6009
6054
|
value: "merge",
|
|
6010
|
-
name: `merge ${many ? "them" : `#${open[0].number}`} first, then
|
|
6055
|
+
name: `merge ${many ? "them" : `#${open[0].number}`} first, then upgrade ${colors_default.dim("(recommended)")}`
|
|
6011
6056
|
},
|
|
6012
6057
|
{ value: "continue", name: `continue anyway ${colors_default.dim("(new PR will re-include the unmerged changes)")}` },
|
|
6013
6058
|
{ value: "cancel", name: colors_default.red("cancel") }
|
|
@@ -6018,7 +6063,7 @@ async function guardAgainstOpenSyncPr(config) {
|
|
|
6018
6063
|
if (choice === "continue") return "continue";
|
|
6019
6064
|
return mergeOpenSyncPrs(config, open);
|
|
6020
6065
|
}
|
|
6021
|
-
async function
|
|
6066
|
+
async function runUpgradeCommand(config) {
|
|
6022
6067
|
const { forkPath } = config;
|
|
6023
6068
|
const currentBranch = await getCurrentBranch(forkPath);
|
|
6024
6069
|
const onSyncBranch = isTemporarySyncBranch(currentBranch);
|
|
@@ -6031,7 +6076,7 @@ async function runSyncCommand(config) {
|
|
|
6031
6076
|
console.info();
|
|
6032
6077
|
console.info(
|
|
6033
6078
|
colors_default.yellow(
|
|
6034
|
-
`
|
|
6079
|
+
`upgrade branch '${currentBranch}' has uncommitted changes.
|
|
6035
6080
|
commit them first (\`git commit --amend --no-edit\` or a new commit).`
|
|
6036
6081
|
)
|
|
6037
6082
|
);
|
|
@@ -6041,17 +6086,17 @@ commit them first (\`git commit --amend --no-edit\` or a new commit).`
|
|
|
6041
6086
|
return;
|
|
6042
6087
|
}
|
|
6043
6088
|
if (!await isClean(forkPath)) {
|
|
6044
|
-
throw new Error("working directory has uncommitted changes. please commit or stash before
|
|
6089
|
+
throw new Error("working directory has uncommitted changes. please commit or stash before upgrading.");
|
|
6045
6090
|
}
|
|
6046
6091
|
if (await guardAgainstOpenSyncPr(config) === "cancel") return;
|
|
6047
6092
|
const outcome = await runSyncCycle(config);
|
|
6048
6093
|
if (outcome.status === "noop") {
|
|
6049
6094
|
console.info();
|
|
6050
6095
|
if (outcome.behind) {
|
|
6051
|
-
console.info(colors_default.green("nothing to
|
|
6096
|
+
console.info(colors_default.green("nothing to upgrade: the last upgrade already went past this upstream point."));
|
|
6052
6097
|
for (const line of outcome.behind.split("\n")) console.info(colors_default.dim(` ${line}`));
|
|
6053
6098
|
} else {
|
|
6054
|
-
console.info(colors_default.green("already up to date with upstream, nothing to
|
|
6099
|
+
console.info(colors_default.green("already up to date with upstream, nothing to upgrade."));
|
|
6055
6100
|
}
|
|
6056
6101
|
const align = await syncAlignmentsStep(config, outcome.result);
|
|
6057
6102
|
if (align) printNextSteps([align]);
|
|
@@ -6089,7 +6134,7 @@ function formatForkChoice(name, status) {
|
|
|
6089
6134
|
syncPart = colors_default.dim(`${status.lastSync.date}`);
|
|
6090
6135
|
if (msg) syncPart += colors_default.dim(` '${msg}'`);
|
|
6091
6136
|
} else {
|
|
6092
|
-
syncPart = colors_default.dim("never
|
|
6137
|
+
syncPart = colors_default.dim("never upgraded");
|
|
6093
6138
|
}
|
|
6094
6139
|
const dirtyPart = status.dirty > 0 ? colors_default.yellow(`${status.dirty} uncommitted`) : "";
|
|
6095
6140
|
const parts = [name, colors_default.dim(`[${status.branch}]`), syncPart];
|
|
@@ -6121,15 +6166,15 @@ async function buildForkChoices(forks, basePath) {
|
|
|
6121
6166
|
}
|
|
6122
6167
|
async function syncFork(config, fork, forkPath) {
|
|
6123
6168
|
console.info();
|
|
6124
|
-
console.info(colors_default.cyan(`
|
|
6169
|
+
console.info(colors_default.cyan(`upgrading ${fork.name}...`));
|
|
6125
6170
|
console.info(colors_default.dim(`path: ${forkPath}`));
|
|
6126
6171
|
console.info();
|
|
6127
6172
|
const forkConfig = await loadConfig(forkPath);
|
|
6128
|
-
await
|
|
6173
|
+
await runUpgradeCommand({
|
|
6129
6174
|
...forkConfig,
|
|
6130
6175
|
forkPath,
|
|
6131
6176
|
upstreamRef: resolveUpstream(forkConfig.settings).branchRef,
|
|
6132
|
-
service: "
|
|
6177
|
+
service: "upgrade",
|
|
6133
6178
|
list: false,
|
|
6134
6179
|
json: false,
|
|
6135
6180
|
verbose: false,
|
|
@@ -6156,7 +6201,7 @@ async function runForks(config) {
|
|
|
6156
6201
|
const choices = await buildForkChoices(forks, forkBasePath);
|
|
6157
6202
|
const forkChoices = [...choices, new Separator2(MENU_DIVIDER), { value: "_exit", name: colors_default.dim("exit") }];
|
|
6158
6203
|
const selectedPath = await select5({
|
|
6159
|
-
message: "select fork to
|
|
6204
|
+
message: "select fork to upgrade:",
|
|
6160
6205
|
choices: forkChoices,
|
|
6161
6206
|
loop: false
|
|
6162
6207
|
});
|
|
@@ -6620,7 +6665,7 @@ function preflight(forkPath) {
|
|
|
6620
6665
|
}
|
|
6621
6666
|
var services = {
|
|
6622
6667
|
analyze: runAnalyze,
|
|
6623
|
-
|
|
6668
|
+
upgrade: runUpgradeCommand,
|
|
6624
6669
|
align: runAlign,
|
|
6625
6670
|
audit: runAudit,
|
|
6626
6671
|
forks: runForks,
|