@mutmutco/cli 4.3.67 → 4.3.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.cjs +285 -88
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -1741,8 +1741,8 @@ var Help = class {
|
|
|
1741
1741
|
* @param {string} str
|
|
1742
1742
|
* @returns {number}
|
|
1743
1743
|
*/
|
|
1744
|
-
displayWidth(
|
|
1745
|
-
return (0, import_node_util.stripVTControlCharacters)(
|
|
1744
|
+
displayWidth(str2) {
|
|
1745
|
+
return (0, import_node_util.stripVTControlCharacters)(str2).length;
|
|
1746
1746
|
}
|
|
1747
1747
|
/**
|
|
1748
1748
|
* Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
|
|
@@ -1750,11 +1750,11 @@ var Help = class {
|
|
|
1750
1750
|
* @param {string} str
|
|
1751
1751
|
* @returns {string}
|
|
1752
1752
|
*/
|
|
1753
|
-
styleTitle(
|
|
1754
|
-
return
|
|
1753
|
+
styleTitle(str2) {
|
|
1754
|
+
return str2;
|
|
1755
1755
|
}
|
|
1756
|
-
styleUsage(
|
|
1757
|
-
return
|
|
1756
|
+
styleUsage(str2) {
|
|
1757
|
+
return str2.split(" ").map((word) => {
|
|
1758
1758
|
if (word === "[options]") return this.styleOptionText(word);
|
|
1759
1759
|
if (word === "[command]") return this.styleSubcommandText(word);
|
|
1760
1760
|
if (word[0] === "[" || word[0] === "<")
|
|
@@ -1762,46 +1762,46 @@ var Help = class {
|
|
|
1762
1762
|
return this.styleCommandText(word);
|
|
1763
1763
|
}).join(" ");
|
|
1764
1764
|
}
|
|
1765
|
-
styleCommandDescription(
|
|
1766
|
-
return this.styleDescriptionText(
|
|
1765
|
+
styleCommandDescription(str2) {
|
|
1766
|
+
return this.styleDescriptionText(str2);
|
|
1767
1767
|
}
|
|
1768
|
-
styleOptionDescription(
|
|
1769
|
-
return this.styleDescriptionText(
|
|
1768
|
+
styleOptionDescription(str2) {
|
|
1769
|
+
return this.styleDescriptionText(str2);
|
|
1770
1770
|
}
|
|
1771
|
-
styleSubcommandDescription(
|
|
1772
|
-
return this.styleDescriptionText(
|
|
1771
|
+
styleSubcommandDescription(str2) {
|
|
1772
|
+
return this.styleDescriptionText(str2);
|
|
1773
1773
|
}
|
|
1774
|
-
styleArgumentDescription(
|
|
1775
|
-
return this.styleDescriptionText(
|
|
1774
|
+
styleArgumentDescription(str2) {
|
|
1775
|
+
return this.styleDescriptionText(str2);
|
|
1776
1776
|
}
|
|
1777
|
-
styleDescriptionText(
|
|
1778
|
-
return
|
|
1777
|
+
styleDescriptionText(str2) {
|
|
1778
|
+
return str2;
|
|
1779
1779
|
}
|
|
1780
|
-
styleOptionTerm(
|
|
1781
|
-
return this.styleOptionText(
|
|
1780
|
+
styleOptionTerm(str2) {
|
|
1781
|
+
return this.styleOptionText(str2);
|
|
1782
1782
|
}
|
|
1783
|
-
styleSubcommandTerm(
|
|
1784
|
-
return
|
|
1783
|
+
styleSubcommandTerm(str2) {
|
|
1784
|
+
return str2.split(" ").map((word) => {
|
|
1785
1785
|
if (word === "[options]") return this.styleOptionText(word);
|
|
1786
1786
|
if (word[0] === "[" || word[0] === "<")
|
|
1787
1787
|
return this.styleArgumentText(word);
|
|
1788
1788
|
return this.styleSubcommandText(word);
|
|
1789
1789
|
}).join(" ");
|
|
1790
1790
|
}
|
|
1791
|
-
styleArgumentTerm(
|
|
1792
|
-
return this.styleArgumentText(
|
|
1791
|
+
styleArgumentTerm(str2) {
|
|
1792
|
+
return this.styleArgumentText(str2);
|
|
1793
1793
|
}
|
|
1794
|
-
styleOptionText(
|
|
1795
|
-
return
|
|
1794
|
+
styleOptionText(str2) {
|
|
1795
|
+
return str2;
|
|
1796
1796
|
}
|
|
1797
|
-
styleArgumentText(
|
|
1798
|
-
return
|
|
1797
|
+
styleArgumentText(str2) {
|
|
1798
|
+
return str2;
|
|
1799
1799
|
}
|
|
1800
|
-
styleSubcommandText(
|
|
1801
|
-
return
|
|
1800
|
+
styleSubcommandText(str2) {
|
|
1801
|
+
return str2;
|
|
1802
1802
|
}
|
|
1803
|
-
styleCommandText(
|
|
1804
|
-
return
|
|
1803
|
+
styleCommandText(str2) {
|
|
1804
|
+
return str2;
|
|
1805
1805
|
}
|
|
1806
1806
|
/**
|
|
1807
1807
|
* Calculate the pad width from the maximum term length.
|
|
@@ -1824,8 +1824,8 @@ var Help = class {
|
|
|
1824
1824
|
* @param {string} str
|
|
1825
1825
|
* @returns {boolean}
|
|
1826
1826
|
*/
|
|
1827
|
-
preformatted(
|
|
1828
|
-
return /\n[^\S\r\n]/.test(
|
|
1827
|
+
preformatted(str2) {
|
|
1828
|
+
return /\n[^\S\r\n]/.test(str2);
|
|
1829
1829
|
}
|
|
1830
1830
|
/**
|
|
1831
1831
|
* Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
|
|
@@ -1871,9 +1871,9 @@ ${itemIndentStr}`);
|
|
|
1871
1871
|
* @param {number} width
|
|
1872
1872
|
* @returns {string}
|
|
1873
1873
|
*/
|
|
1874
|
-
boxWrap(
|
|
1875
|
-
if (width < this.minWidthToWrap) return
|
|
1876
|
-
const rawLines =
|
|
1874
|
+
boxWrap(str2, width) {
|
|
1875
|
+
if (width < this.minWidthToWrap) return str2;
|
|
1876
|
+
const rawLines = str2.split(/\r\n|\n/);
|
|
1877
1877
|
const chunkPattern = /[\s]*[^\s]+/g;
|
|
1878
1878
|
const wrappedLines = [];
|
|
1879
1879
|
rawLines.forEach((line) => {
|
|
@@ -2163,9 +2163,9 @@ var DualOptions = class {
|
|
|
2163
2163
|
return option.negate === (negativeValue === value);
|
|
2164
2164
|
}
|
|
2165
2165
|
};
|
|
2166
|
-
function camelcase(
|
|
2167
|
-
return
|
|
2168
|
-
return
|
|
2166
|
+
function camelcase(str2) {
|
|
2167
|
+
return str2.split("-").reduce((str3, word) => {
|
|
2168
|
+
return str3 + word[0].toUpperCase() + word.slice(1);
|
|
2169
2169
|
});
|
|
2170
2170
|
}
|
|
2171
2171
|
function splitOptionFlags(flags) {
|
|
@@ -2325,14 +2325,14 @@ var Command = class _Command extends import_node_events.EventEmitter {
|
|
|
2325
2325
|
this._showSuggestionAfterError = true;
|
|
2326
2326
|
this._savedState = null;
|
|
2327
2327
|
this._outputConfiguration = {
|
|
2328
|
-
writeOut: (
|
|
2329
|
-
writeErr: (
|
|
2330
|
-
outputError: (
|
|
2328
|
+
writeOut: (str2) => import_node_process.default.stdout.write(str2),
|
|
2329
|
+
writeErr: (str2) => import_node_process.default.stderr.write(str2),
|
|
2330
|
+
outputError: (str2, write) => write(str2),
|
|
2331
2331
|
getOutHelpWidth: () => import_node_process.default.stdout.isTTY ? import_node_process.default.stdout.columns : void 0,
|
|
2332
2332
|
getErrHelpWidth: () => import_node_process.default.stderr.isTTY ? import_node_process.default.stderr.columns : void 0,
|
|
2333
2333
|
getOutHasColors: () => useColor() ?? (import_node_process.default.stdout.isTTY && import_node_process.default.stdout.hasColors?.()),
|
|
2334
2334
|
getErrHasColors: () => useColor() ?? (import_node_process.default.stderr.isTTY && import_node_process.default.stderr.hasColors?.()),
|
|
2335
|
-
stripColor: (
|
|
2335
|
+
stripColor: (str2) => (0, import_node_util2.stripVTControlCharacters)(str2)
|
|
2336
2336
|
};
|
|
2337
2337
|
this._hidden = false;
|
|
2338
2338
|
this._helpOption = void 0;
|
|
@@ -4060,18 +4060,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4060
4060
|
* @param {string} [description]
|
|
4061
4061
|
* @return {(this | string | undefined)} `this` command for chaining, or version string if no arguments
|
|
4062
4062
|
*/
|
|
4063
|
-
version(
|
|
4064
|
-
if (
|
|
4065
|
-
this._version =
|
|
4063
|
+
version(str2, flags, description) {
|
|
4064
|
+
if (str2 === void 0) return this._version;
|
|
4065
|
+
this._version = str2;
|
|
4066
4066
|
flags = flags || "-V, --version";
|
|
4067
4067
|
description = description || "output the version number";
|
|
4068
4068
|
const versionOption = this.createOption(flags, description);
|
|
4069
4069
|
this._versionOptionName = versionOption.attributeName();
|
|
4070
4070
|
this._registerOption(versionOption);
|
|
4071
4071
|
this.on("option:" + versionOption.name(), () => {
|
|
4072
|
-
this._outputConfiguration.writeOut(`${
|
|
4072
|
+
this._outputConfiguration.writeOut(`${str2}
|
|
4073
4073
|
`);
|
|
4074
|
-
this._exit(0, "commander.version",
|
|
4074
|
+
this._exit(0, "commander.version", str2);
|
|
4075
4075
|
});
|
|
4076
4076
|
return this;
|
|
4077
4077
|
}
|
|
@@ -4082,10 +4082,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4082
4082
|
* @param {object} [argsDescription]
|
|
4083
4083
|
* @return {(string|Command)}
|
|
4084
4084
|
*/
|
|
4085
|
-
description(
|
|
4086
|
-
if (
|
|
4085
|
+
description(str2, argsDescription) {
|
|
4086
|
+
if (str2 === void 0 && argsDescription === void 0)
|
|
4087
4087
|
return this._description;
|
|
4088
|
-
this._description =
|
|
4088
|
+
this._description = str2;
|
|
4089
4089
|
if (argsDescription) {
|
|
4090
4090
|
this._argsDescription = argsDescription;
|
|
4091
4091
|
}
|
|
@@ -4097,9 +4097,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4097
4097
|
* @param {string} [str]
|
|
4098
4098
|
* @return {(string|Command)}
|
|
4099
4099
|
*/
|
|
4100
|
-
summary(
|
|
4101
|
-
if (
|
|
4102
|
-
this._summary =
|
|
4100
|
+
summary(str2) {
|
|
4101
|
+
if (str2 === void 0) return this._summary;
|
|
4102
|
+
this._summary = str2;
|
|
4103
4103
|
return this;
|
|
4104
4104
|
}
|
|
4105
4105
|
/**
|
|
@@ -4147,8 +4147,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4147
4147
|
* @param {string} [str]
|
|
4148
4148
|
* @return {(string|Command)}
|
|
4149
4149
|
*/
|
|
4150
|
-
usage(
|
|
4151
|
-
if (
|
|
4150
|
+
usage(str2) {
|
|
4151
|
+
if (str2 === void 0) {
|
|
4152
4152
|
if (this._usage) return this._usage;
|
|
4153
4153
|
const args = this.registeredArguments.map((arg) => {
|
|
4154
4154
|
return humanReadableArgName(arg);
|
|
@@ -4159,7 +4159,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4159
4159
|
this.registeredArguments.length ? args : []
|
|
4160
4160
|
).join(" ");
|
|
4161
4161
|
}
|
|
4162
|
-
this._usage =
|
|
4162
|
+
this._usage = str2;
|
|
4163
4163
|
return this;
|
|
4164
4164
|
}
|
|
4165
4165
|
/**
|
|
@@ -4168,9 +4168,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4168
4168
|
* @param {string} [str]
|
|
4169
4169
|
* @return {(string|Command)}
|
|
4170
4170
|
*/
|
|
4171
|
-
name(
|
|
4172
|
-
if (
|
|
4173
|
-
this._name =
|
|
4171
|
+
name(str2) {
|
|
4172
|
+
if (str2 === void 0) return this._name;
|
|
4173
|
+
this._name = str2;
|
|
4174
4174
|
return this;
|
|
4175
4175
|
}
|
|
4176
4176
|
/**
|
|
@@ -4304,17 +4304,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
4304
4304
|
let hasColors;
|
|
4305
4305
|
let helpWidth;
|
|
4306
4306
|
if (error) {
|
|
4307
|
-
baseWrite = (
|
|
4307
|
+
baseWrite = (str2) => this._outputConfiguration.writeErr(str2);
|
|
4308
4308
|
hasColors = this._outputConfiguration.getErrHasColors();
|
|
4309
4309
|
helpWidth = this._outputConfiguration.getErrHelpWidth();
|
|
4310
4310
|
} else {
|
|
4311
|
-
baseWrite = (
|
|
4311
|
+
baseWrite = (str2) => this._outputConfiguration.writeOut(str2);
|
|
4312
4312
|
hasColors = this._outputConfiguration.getOutHasColors();
|
|
4313
4313
|
helpWidth = this._outputConfiguration.getOutHelpWidth();
|
|
4314
4314
|
}
|
|
4315
|
-
const write = (
|
|
4316
|
-
if (!hasColors)
|
|
4317
|
-
return baseWrite(
|
|
4315
|
+
const write = (str2) => {
|
|
4316
|
+
if (!hasColors) str2 = this._outputConfiguration.stripColor(str2);
|
|
4317
|
+
return baseWrite(str2);
|
|
4318
4318
|
};
|
|
4319
4319
|
return { error, write, hasColors, helpWidth };
|
|
4320
4320
|
}
|
|
@@ -7004,7 +7004,7 @@ init_github_client();
|
|
|
7004
7004
|
|
|
7005
7005
|
// src/project-model.ts
|
|
7006
7006
|
var PROJECT_TYPES = ["web-app", "hub-service", "content", "desktop-app", "desktop-game", "mobile-app", "non-deployable", "cli-tool", "worker"];
|
|
7007
|
-
var DEPLOY_MODELS = ["hub-serverless", "serverless", "tenant-container", "solo-container", "static-cdn", "registry-publish", "content", "none"];
|
|
7007
|
+
var DEPLOY_MODELS = ["hub-serverless", "serverless", "tenant-container", "solo-container", "static-cdn", "registry-publish", "content", "none", "vercel"];
|
|
7008
7008
|
var RELEASE_TRACKS = ["full", "direct", "trunk"];
|
|
7009
7009
|
var REQUIRED_CHECK_BRANCHES = ["development", "rc", "main"];
|
|
7010
7010
|
var PROJECT_TYPE_SET = new Set(PROJECT_TYPES);
|
|
@@ -9209,6 +9209,66 @@ async function secretsCopy(deps, opts) {
|
|
|
9209
9209
|
}
|
|
9210
9210
|
return true;
|
|
9211
9211
|
}
|
|
9212
|
+
function transferKey(leaf, stage) {
|
|
9213
|
+
return stage ? `${stage}/${leaf}` : leaf;
|
|
9214
|
+
}
|
|
9215
|
+
async function secretsTransfer(deps, opts) {
|
|
9216
|
+
const fromRepo = opts.fromRepo.trim();
|
|
9217
|
+
const toRepo = opts.toRepo.trim();
|
|
9218
|
+
if (!fromRepo || !toRepo) {
|
|
9219
|
+
deps.err("secrets transfer: --from-repo and --to-repo are required (owner/repo)");
|
|
9220
|
+
return false;
|
|
9221
|
+
}
|
|
9222
|
+
if (fromRepo.toLowerCase() === toRepo.toLowerCase() && opts.fromStage === opts.toStage) {
|
|
9223
|
+
deps.err("secrets transfer: source and destination are the same coordinate \u2014 use `secrets copy` to move between stages of one project");
|
|
9224
|
+
return false;
|
|
9225
|
+
}
|
|
9226
|
+
const report = await probeCapabilities(deps, fromRepo);
|
|
9227
|
+
if (report?.role !== "master") {
|
|
9228
|
+
deps.err(report === void 0 ? "secrets transfer: could not verify your role (capabilities probe unavailable) \u2014 cross-project transfer is master-only and refuses on an unverified role" : `secrets transfer: master-admin only \u2014 your role on ${fromRepo} is ${report.role}. Cross-project value movement is master-gated; ask the master to run the transfer.`);
|
|
9229
|
+
return false;
|
|
9230
|
+
}
|
|
9231
|
+
const keys = [...new Set(opts.keys.flatMap((k) => k.split(/[,\s]+/)).filter(Boolean))];
|
|
9232
|
+
if (!keys.length) {
|
|
9233
|
+
deps.err("secrets transfer: --keys required (comma- or space-separated allowlist)");
|
|
9234
|
+
return false;
|
|
9235
|
+
}
|
|
9236
|
+
for (const key of keys) {
|
|
9237
|
+
if (!isValidSecretKey(key)) {
|
|
9238
|
+
deps.err(`invalid secret key ${JSON.stringify(key)}`);
|
|
9239
|
+
return false;
|
|
9240
|
+
}
|
|
9241
|
+
if (isSecretCopyBlocked(key)) {
|
|
9242
|
+
deps.err(`secrets transfer: ${key} is not transferable \u2014 generate encryption/stage-distinct keys per project`);
|
|
9243
|
+
return false;
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
for (const key of keys) {
|
|
9247
|
+
const leaf = secretLeafName(key);
|
|
9248
|
+
const srcKey = transferKey(leaf, opts.fromStage);
|
|
9249
|
+
const dstKey = transferKey(leaf, opts.toStage);
|
|
9250
|
+
const srcPath = `${fromRepo} ${srcKey}`;
|
|
9251
|
+
const dstPath = `${toRepo} ${dstKey}`;
|
|
9252
|
+
if (opts.dryRun) {
|
|
9253
|
+
const probe = await fetchSecretValue(deps, srcKey, { repo: fromRepo });
|
|
9254
|
+
if (!probe) {
|
|
9255
|
+
deps.err(`secrets transfer: could not read source ${srcPath}`);
|
|
9256
|
+
return false;
|
|
9257
|
+
}
|
|
9258
|
+
deps.log(`would transfer ${srcPath} \u2192 ${dstPath}`);
|
|
9259
|
+
continue;
|
|
9260
|
+
}
|
|
9261
|
+
const value = await fetchSecretValue(deps, srcKey, { repo: fromRepo });
|
|
9262
|
+
if (!value) {
|
|
9263
|
+
deps.err(`secrets transfer: could not read source ${srcPath}`);
|
|
9264
|
+
return false;
|
|
9265
|
+
}
|
|
9266
|
+
const ok = await putSecret(deps, dstKey, value, { repo: toRepo });
|
|
9267
|
+
if (!ok) return false;
|
|
9268
|
+
deps.log(`transferred ${srcPath} \u2192 ${dstPath}`);
|
|
9269
|
+
}
|
|
9270
|
+
return true;
|
|
9271
|
+
}
|
|
9212
9272
|
var CMD_METACHAR_RE = /[&|^%<>()!"]/;
|
|
9213
9273
|
var CMD_METACHAR_G = /[&|^%<>()!"]/g;
|
|
9214
9274
|
function argvQuote(token, force) {
|
|
@@ -13772,7 +13832,7 @@ function walk(dir, root, out) {
|
|
|
13772
13832
|
return out;
|
|
13773
13833
|
}
|
|
13774
13834
|
function defaultListDocs(root) {
|
|
13775
|
-
const docs = ["docs", "skills/org-guide", "skills/mmi-help"
|
|
13835
|
+
const docs = ["docs", "skills/org-guide", "skills/mmi-help"].flatMap((path2) => {
|
|
13776
13836
|
const directory = (0, import_node_path12.join)(root, path2);
|
|
13777
13837
|
return (0, import_node_fs14.existsSync)(directory) ? walk(directory, root, []) : [];
|
|
13778
13838
|
}).filter(
|
|
@@ -14755,6 +14815,7 @@ function planTrainApplyRepoGuard(applyRepo, cwdRepo, rerun, what = "--apply") {
|
|
|
14755
14815
|
};
|
|
14756
14816
|
}
|
|
14757
14817
|
async function resolveFoldPaths(deps, model) {
|
|
14818
|
+
if (model === "vercel") return [];
|
|
14758
14819
|
const helper = "scripts/release-distribution.mjs";
|
|
14759
14820
|
if (model === "hub-serverless" || model === "registry-publish") {
|
|
14760
14821
|
if ((0, import_node_fs16.existsSync)(helper)) {
|
|
@@ -15801,10 +15862,10 @@ var rollout_plan_default = {
|
|
|
15801
15862
|
note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
|
|
15802
15863
|
},
|
|
15803
15864
|
baseline: {
|
|
15804
|
-
version: "4.3.
|
|
15805
|
-
tag: "v4.3.
|
|
15806
|
-
commit: "
|
|
15807
|
-
npm: "@mutmutco/cli@4.3.
|
|
15865
|
+
version: "4.3.68",
|
|
15866
|
+
tag: "v4.3.68",
|
|
15867
|
+
commit: "e9edc6e353ec",
|
|
15868
|
+
npm: "@mutmutco/cli@4.3.68"
|
|
15808
15869
|
},
|
|
15809
15870
|
exitCriterion: "fleet-n-of-n",
|
|
15810
15871
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15821,14 +15882,14 @@ var rollout_plan_default = {
|
|
|
15821
15882
|
repo: "mutmutco/mmi-hub",
|
|
15822
15883
|
role: "canary",
|
|
15823
15884
|
schedule: "train",
|
|
15824
|
-
v3Target: "v4.3.
|
|
15885
|
+
v3Target: "v4.3.68"
|
|
15825
15886
|
}
|
|
15826
15887
|
],
|
|
15827
15888
|
rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
|
|
15828
15889
|
rollback: {
|
|
15829
15890
|
independent: true,
|
|
15830
|
-
mechanism: "npm dist-tag latest -> 4.3.
|
|
15831
|
-
v3Target: "v4.3.
|
|
15891
|
+
mechanism: "npm dist-tag latest -> 4.3.68 and redeploy the Hub Lambda from tag v4.3.68 (e9edc6e353ec); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15892
|
+
v3Target: "v4.3.68 (@mutmutco/cli@4.3.68, tag commit e9edc6e353ec \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
15832
15893
|
}
|
|
15833
15894
|
},
|
|
15834
15895
|
{
|
|
@@ -18670,6 +18731,56 @@ function isReleaseShaDeployEnumerationCandidate(row, nonDeployEvents) {
|
|
|
18670
18731
|
return true;
|
|
18671
18732
|
}
|
|
18672
18733
|
|
|
18734
|
+
// src/vercel-deployments.ts
|
|
18735
|
+
var TERMINAL = /* @__PURE__ */ new Set(["success", "failure", "error", "inactive"]);
|
|
18736
|
+
function isTerminalVercelState(state) {
|
|
18737
|
+
return TERMINAL.has(state);
|
|
18738
|
+
}
|
|
18739
|
+
function str(v) {
|
|
18740
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
18741
|
+
}
|
|
18742
|
+
async function readVercelDeployment(api, repo, target, environment) {
|
|
18743
|
+
const query = target.sha ? `sha=${encodeURIComponent(target.sha)}` : `ref=${encodeURIComponent(target.ref ?? "")}`;
|
|
18744
|
+
const listed = await api(`repos/${repo}/deployments?${query}&per_page=30`);
|
|
18745
|
+
const rows = Array.isArray(listed) ? listed : [];
|
|
18746
|
+
const prefix = environment.toLowerCase();
|
|
18747
|
+
const hit = rows.find((d) => typeof d.id === "number" && (str(d.environment) ?? "").toLowerCase().startsWith(prefix));
|
|
18748
|
+
if (!hit) return void 0;
|
|
18749
|
+
const statuses = await api(`repos/${repo}/deployments/${hit.id}/statuses?per_page=1`);
|
|
18750
|
+
const latest = Array.isArray(statuses) ? statuses[0] : void 0;
|
|
18751
|
+
const state = str(latest?.state) ?? "none";
|
|
18752
|
+
const url = str(latest?.environment_url) ?? str(latest?.target_url);
|
|
18753
|
+
return {
|
|
18754
|
+
id: hit.id,
|
|
18755
|
+
environment: str(hit.environment) ?? environment,
|
|
18756
|
+
sha: str(hit.sha) ?? target.sha ?? "",
|
|
18757
|
+
ref: str(hit.ref) ?? target.ref ?? "",
|
|
18758
|
+
state,
|
|
18759
|
+
...url ? { url } : {},
|
|
18760
|
+
...str(latest?.log_url) ? { logUrl: str(latest?.log_url) } : {}
|
|
18761
|
+
};
|
|
18762
|
+
}
|
|
18763
|
+
var VERCEL_WATCH_ATTEMPTS = 120;
|
|
18764
|
+
var VERCEL_WATCH_DELAY_MS = 1e4;
|
|
18765
|
+
async function watchVercelProduction(api, repo, sha, sleep2, onTick) {
|
|
18766
|
+
let last;
|
|
18767
|
+
for (let attempt = 0; attempt < VERCEL_WATCH_ATTEMPTS; attempt++) {
|
|
18768
|
+
if (attempt > 0) await sleep2(VERCEL_WATCH_DELAY_MS);
|
|
18769
|
+
last = await readVercelDeployment(api, repo, { sha }, "Production");
|
|
18770
|
+
onTick?.(attempt, last);
|
|
18771
|
+
if (last && isTerminalVercelState(last.state)) return last;
|
|
18772
|
+
}
|
|
18773
|
+
return last;
|
|
18774
|
+
}
|
|
18775
|
+
async function probeVercelUrl(url, fetchImpl = fetch) {
|
|
18776
|
+
try {
|
|
18777
|
+
const res = await fetchImpl(url, { method: "GET", redirect: "follow", signal: AbortSignal.timeout(15e3) });
|
|
18778
|
+
return { ok: res.status < 500, status: res.status };
|
|
18779
|
+
} catch (e) {
|
|
18780
|
+
return { ok: false, error: e instanceof Error ? e.message : String(e) };
|
|
18781
|
+
}
|
|
18782
|
+
}
|
|
18783
|
+
|
|
18673
18784
|
// src/train-apply-deploy.ts
|
|
18674
18785
|
var DISPATCH_ATTEMPTS = 3;
|
|
18675
18786
|
var DISPATCH_RETRY_DELAY_MS = 2e3;
|
|
@@ -18841,7 +18952,45 @@ function prodReleaseTargets(model) {
|
|
|
18841
18952
|
return [];
|
|
18842
18953
|
}
|
|
18843
18954
|
function trainObservesShipment(model) {
|
|
18844
|
-
return isCentralDispatchModel(model) || prodReleaseTargets(model).length > 0;
|
|
18955
|
+
return isCentralDispatchModel(model) || prodReleaseTargets(model).length > 0 || model === "vercel";
|
|
18956
|
+
}
|
|
18957
|
+
async function vercelDeployDispatch(deps, ctx, ref, watch, headSha) {
|
|
18958
|
+
const note = "no dispatch: Vercel deploys main on push (Production) \u2014 observed via the Vercel GitHub app Deployment";
|
|
18959
|
+
if (ref !== "main") return { note: "no dispatch on rc: a vercel repo is direct-track and has no rc stage", deployStatus: "pending" };
|
|
18960
|
+
if (!headSha) return { note, deployStatus: "pending" };
|
|
18961
|
+
const api = async (path2) => JSON.parse(await deps.run("gh", ["api", path2]));
|
|
18962
|
+
const row = (d, conclusion) => ({
|
|
18963
|
+
workflow: "vercel",
|
|
18964
|
+
conclusion,
|
|
18965
|
+
...d?.logUrl ? { runUrl: d.logUrl } : { runUrlNote: "no Vercel Deployment posted for this SHA yet" },
|
|
18966
|
+
event: "push"
|
|
18967
|
+
});
|
|
18968
|
+
const verdict = (d) => d?.state === "success" ? "success" : d && isTerminalVercelState(d.state) ? "failure" : "pending";
|
|
18969
|
+
let read;
|
|
18970
|
+
try {
|
|
18971
|
+
if (!watch) {
|
|
18972
|
+
deps.warn?.("Release: Checking Vercel deployment status");
|
|
18973
|
+
read = await readVercelDeployment(api, ctx.repo, { sha: headSha }, "Production");
|
|
18974
|
+
} else {
|
|
18975
|
+
deps.warn?.("Release: Waiting for Vercel to deploy production");
|
|
18976
|
+
read = await watchVercelProduction(api, ctx.repo, headSha, resolveSleep(deps), (attempt, d) => {
|
|
18977
|
+
if (attempt > 0 && attempt % 6 === 0) deps.warn?.(`Vercel deployment ${d ? d.state : "not posted yet"} \u2014 ${attempt / 6}m elapsed`);
|
|
18978
|
+
});
|
|
18979
|
+
}
|
|
18980
|
+
} catch (e) {
|
|
18981
|
+
return { note: `${note}; the Deployment read FAILED (${describeReadError(e)}) \u2014 verify on Vercel; never re-tag`, workflowRuns: [row(void 0, "failure")], deployStatus: "failure" };
|
|
18982
|
+
}
|
|
18983
|
+
let deployStatus = verdict(read);
|
|
18984
|
+
let detail = read ? `Vercel Production deployment ${read.state}${read.url ? ` at ${read.url}` : ""}` : "no Vercel Deployment posted for this SHA (is the Vercel GitHub app connected?)";
|
|
18985
|
+
if (watch && deployStatus === "success" && read?.url) {
|
|
18986
|
+
const probe = await (deps.probeUrl ?? probeVercelUrl)(read.url);
|
|
18987
|
+
if (probe.ok) detail = `${detail}; URL answered HTTP ${probe.status}`;
|
|
18988
|
+
else {
|
|
18989
|
+
deployStatus = "failure";
|
|
18990
|
+
detail = `${detail}; URL probe FAILED (${probe.error ?? `HTTP ${probe.status}`})`;
|
|
18991
|
+
}
|
|
18992
|
+
}
|
|
18993
|
+
return { note: `${note}; ${detail}`, ...read?.logUrl ? { runUrl: read.logUrl } : {}, workflowRuns: [row(read, deployStatus)], deployStatus };
|
|
18845
18994
|
}
|
|
18846
18995
|
function shipLegsGreen(deploy, publish) {
|
|
18847
18996
|
const green = (s) => s === "complete" || s === "skipped";
|
|
@@ -19023,6 +19172,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
|
|
|
19023
19172
|
deployStatus: aggregateWorkflowRuns(workflowRuns)
|
|
19024
19173
|
};
|
|
19025
19174
|
}
|
|
19175
|
+
if (model === "vercel") return vercelDeployDispatch(deps, ctx, ref, watch, autoRunHeadSha);
|
|
19026
19176
|
return { note: `no manual dispatch: ${model} repo deploys via its own push-triggered workflow`, deployStatus: "pending" };
|
|
19027
19177
|
}
|
|
19028
19178
|
async function readEdgeDomains(deps, slug) {
|
|
@@ -36269,6 +36419,30 @@ function registerSecretsCommands(program3) {
|
|
|
36269
36419
|
});
|
|
36270
36420
|
if (!ok) process.exitCode = 1;
|
|
36271
36421
|
}));
|
|
36422
|
+
secrets.command("transfer").description("(master-only) copy secret VALUES from one project vault to another \u2014 the succession path `copy` cannot serve (#6687). Values never printed, never in argv; encryption/stage-distinct keys blocked; declare-first enforced on the destination").requiredOption("--from-repo <owner/repo>", "source repo whose vault holds the values").requiredOption("--to-repo <owner/repo>", "destination repo receiving the values").requiredOption("--keys <names>", "comma- or space-separated secret names (encryption keys blocked)").option("--from-stage <stage>", "source tier: dev, rc, or main (omit for the stageless canonical)").option("--to-stage <stage>", "destination tier: dev, rc, or main (omit for the stageless canonical)").option("--dry-run", "report the transfers, and verify each source is readable, without writing").action((o) => withSecrets(async (d) => {
|
|
36423
|
+
const stages = ["dev", "rc", "main"];
|
|
36424
|
+
const parseStage = (v, flag) => {
|
|
36425
|
+
if (v === void 0) return void 0;
|
|
36426
|
+
if (!stages.includes(v)) {
|
|
36427
|
+
fail(`secrets transfer: ${flag} must be dev, rc, or main`);
|
|
36428
|
+
return null;
|
|
36429
|
+
}
|
|
36430
|
+
return v;
|
|
36431
|
+
};
|
|
36432
|
+
const fromStage = parseStage(o.fromStage, "--from-stage");
|
|
36433
|
+
if (fromStage === null) return;
|
|
36434
|
+
const toStage = parseStage(o.toStage, "--to-stage");
|
|
36435
|
+
if (toStage === null) return;
|
|
36436
|
+
const ok = await secretsTransfer(d, {
|
|
36437
|
+
fromRepo: o.fromRepo,
|
|
36438
|
+
toRepo: o.toRepo,
|
|
36439
|
+
keys: o.keys.split(","),
|
|
36440
|
+
...fromStage !== void 0 ? { fromStage } : {},
|
|
36441
|
+
...toStage !== void 0 ? { toStage } : {},
|
|
36442
|
+
...o.dryRun !== void 0 ? { dryRun: o.dryRun } : {}
|
|
36443
|
+
});
|
|
36444
|
+
if (!ok) process.exitCode = 1;
|
|
36445
|
+
}));
|
|
36272
36446
|
secrets.command("import-rails-credentials").description("decrypt Rails credentials and import explicit mappings into the vault (values never printed)").requiredOption("--stage <dev|rc|main>", "target vault stage").option("--map <credential.path=ENV_KEY>", "explicit credential-to-env mapping; repeat for each key", collectMap, []).option("--app-dir <path>", "Rails app directory (defaults to cwd)").option("--credentials-file <path>", `encrypted credentials path relative to --app-dir (default: ${DEFAULT_RAILS_CREDENTIALS_FILE})`).option("--master-key-file <path>", `master key path relative to --app-dir (default: ${DEFAULT_RAILS_MASTER_KEY_FILE})`).option("--dry-run", "decrypt and show mapped key names without writing values").option("--remove-files", "delete credentials/master key files after every mapped value imports successfully").option("--repo <owner/repo>", "target repo (defaults to the current repo)").action((o) => withSecrets(async (d) => {
|
|
36273
36447
|
const stages = ["dev", "rc", "main"];
|
|
36274
36448
|
if (!stages.includes(o.stage)) {
|
|
@@ -36699,6 +36873,28 @@ function registerStageCommands(program3) {
|
|
|
36699
36873
|
cfg
|
|
36700
36874
|
};
|
|
36701
36875
|
}
|
|
36876
|
+
async function stagePreview(project2) {
|
|
36877
|
+
if (project2?.deployModel !== "vercel") return null;
|
|
36878
|
+
const branch = await gitOut(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
36879
|
+
const repo = Array.isArray(project2.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : await repoSlug();
|
|
36880
|
+
try {
|
|
36881
|
+
const gh = defaultGitHubClient();
|
|
36882
|
+
const deployment = await readVercelDeployment((path2) => gh.rest("GET", path2), repo, { ref: branch }, "Preview");
|
|
36883
|
+
return { branch, ...deployment ? { deployment } : {} };
|
|
36884
|
+
} catch (e) {
|
|
36885
|
+
return { branch, error: e.message };
|
|
36886
|
+
}
|
|
36887
|
+
}
|
|
36888
|
+
function previewLine(preview) {
|
|
36889
|
+
if (!preview) return "";
|
|
36890
|
+
if (preview.error) return `
|
|
36891
|
+
vercel preview (${preview.branch}): read failed \u2014 ${preview.error}`;
|
|
36892
|
+
const d = preview.deployment;
|
|
36893
|
+
if (!d) return `
|
|
36894
|
+
vercel preview (${preview.branch}): no Preview deployment posted yet \u2014 push the branch, or connect the Vercel GitHub app`;
|
|
36895
|
+
return `
|
|
36896
|
+
vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}`;
|
|
36897
|
+
}
|
|
36702
36898
|
async function fetchStageVaultEnvMerge(project2, cfg) {
|
|
36703
36899
|
if (!cfg.sagaApiUrl || !project2) return void 0;
|
|
36704
36900
|
const names = requiredRuntimeSecretNames("dev", project2.requiredRuntimeSecrets, { includeGoogleOAuth: false });
|
|
@@ -36839,6 +37035,7 @@ function registerStageCommands(program3) {
|
|
|
36839
37035
|
if (o.down && !o.live) return fail("stage: --down applies to --live only (local teardown is `mmi-cli stage stop --apply`)");
|
|
36840
37036
|
if (o.live) return runStageLiveCommand(o);
|
|
36841
37037
|
const { resolution: res, project: project2, cfg: stageCfg } = await resolveStage();
|
|
37038
|
+
const preview = await stagePreview(project2);
|
|
36842
37039
|
if (o.apply) {
|
|
36843
37040
|
if (res.source === "none") return failGraceful(`stage: ${res.gap}`);
|
|
36844
37041
|
const cfg = res.derived.config;
|
|
@@ -36848,7 +37045,7 @@ function registerStageCommands(program3) {
|
|
|
36848
37045
|
const result = await runStage(cfg, { ...stageScopedRunOpts({ timeoutMs: o.timeoutMs }), ...vaultOpts });
|
|
36849
37046
|
const reportUrl = reportedStageUrl(res, result);
|
|
36850
37047
|
const url = reportUrl ? ` \u2014 ${reportUrl}` : "";
|
|
36851
|
-
return printLine(o.json ? JSON.stringify({ ...result, source: res.source, url: reportUrl }) : `mmi-cli stage: ${result.message}${url}`);
|
|
37048
|
+
return printLine(o.json ? JSON.stringify({ ...result, source: res.source, url: reportUrl, ...preview ? { preview } : {} }) : `mmi-cli stage: ${result.message}${url}${previewLine(preview)}`);
|
|
36852
37049
|
} catch (e) {
|
|
36853
37050
|
return failGraceful(`stage: ${e.message}`);
|
|
36854
37051
|
} finally {
|
|
@@ -36856,8 +37053,8 @@ function registerStageCommands(program3) {
|
|
|
36856
37053
|
}
|
|
36857
37054
|
}
|
|
36858
37055
|
const steps = stageStepsFor(res);
|
|
36859
|
-
if (o.json) return console.log(JSON.stringify({ command: "stage", ...stageReceiptFields(res), steps }, null, 2));
|
|
36860
|
-
console.log(renderSteps("mmi-cli stage: dry-run plan", steps));
|
|
37056
|
+
if (o.json) return console.log(JSON.stringify({ command: "stage", ...stageReceiptFields(res), steps, ...preview ? { preview } : {} }, null, 2));
|
|
37057
|
+
console.log(renderSteps("mmi-cli stage: dry-run plan", steps) + previewLine(preview));
|
|
36861
37058
|
});
|
|
36862
37059
|
stage.command("stop").description("stop the previous local stage process recorded in tmp/stage/state.json").option("--json", "machine-readable output").option("--apply", "kill the recorded process tree and remove the state file").action(async () => {
|
|
36863
37060
|
const o = { json: rawFlag("--json"), apply: rawFlag("--apply") };
|
|
@@ -45464,17 +45661,17 @@ function excessArgumentCorrection(extra, argv) {
|
|
|
45464
45661
|
return sibling ? correctedPositionalCommand(sibling, extra[0], argv) : void 0;
|
|
45465
45662
|
}
|
|
45466
45663
|
var pendingHumanParseError;
|
|
45467
|
-
function writeHumanParseError(
|
|
45468
|
-
pendingHumanParseError =
|
|
45664
|
+
function writeHumanParseError(str2) {
|
|
45665
|
+
pendingHumanParseError = str2;
|
|
45469
45666
|
}
|
|
45470
|
-
function envelopeAwareWriteErr(
|
|
45471
|
-
const plain =
|
|
45667
|
+
function envelopeAwareWriteErr(str2) {
|
|
45668
|
+
const plain = str2.replace(/\[[0-9;]*m/g, "");
|
|
45472
45669
|
if (plain.includes(PARSE_HINT_SENTINEL)) {
|
|
45473
45670
|
if (unknownFlagJsonHandled && argvWantsJson3()) {
|
|
45474
45671
|
pendingHumanParseError = void 0;
|
|
45475
45672
|
return;
|
|
45476
45673
|
}
|
|
45477
|
-
const hint =
|
|
45674
|
+
const hint = str2.replace(PARSE_HINT_SENTINEL, resolveParseHint());
|
|
45478
45675
|
if (lastParseErrorKind === "bad-arguments") {
|
|
45479
45676
|
process.stderr.write(hint);
|
|
45480
45677
|
if (pendingHumanParseError) process.stderr.write(pendingHumanParseError);
|
|
@@ -45490,7 +45687,7 @@ function envelopeAwareWriteErr(str) {
|
|
|
45490
45687
|
const badChoice = parseInvalidChoiceError(plain);
|
|
45491
45688
|
if (badChoice) {
|
|
45492
45689
|
if (!argvWantsJson3()) {
|
|
45493
|
-
writeHumanParseError(
|
|
45690
|
+
writeHumanParseError(str2);
|
|
45494
45691
|
return;
|
|
45495
45692
|
}
|
|
45496
45693
|
process.stderr.write(
|
|
@@ -45518,7 +45715,7 @@ function envelopeAwareWriteErr(str) {
|
|
|
45518
45715
|
unknownFlagJsonHandled = true;
|
|
45519
45716
|
return;
|
|
45520
45717
|
}
|
|
45521
|
-
writeHumanParseError(
|
|
45718
|
+
writeHumanParseError(str2.replace(/error: too many arguments[^\n]*/, `error: ${message2}`));
|
|
45522
45719
|
return;
|
|
45523
45720
|
}
|
|
45524
45721
|
const match = /unknown option '([^']+)'/.exec(plain);
|
|
@@ -45544,18 +45741,18 @@ function envelopeAwareWriteErr(str) {
|
|
|
45544
45741
|
return;
|
|
45545
45742
|
}
|
|
45546
45743
|
if (positional) {
|
|
45547
|
-
writeHumanParseError(
|
|
45744
|
+
writeHumanParseError(str2.replace(/unknown option '[^']+'/, unknownTargetFlagMessage(flag, positional)));
|
|
45548
45745
|
return;
|
|
45549
45746
|
}
|
|
45550
|
-
writeHumanParseError(
|
|
45747
|
+
writeHumanParseError(str2);
|
|
45551
45748
|
return;
|
|
45552
45749
|
}
|
|
45553
45750
|
if (unknownFlagJsonHandled && argvWantsJson3()) return;
|
|
45554
45751
|
if (argvWantsJson3()) {
|
|
45555
|
-
process.stderr.write(
|
|
45752
|
+
process.stderr.write(str2);
|
|
45556
45753
|
return;
|
|
45557
45754
|
}
|
|
45558
|
-
writeHumanParseError(
|
|
45755
|
+
writeHumanParseError(str2);
|
|
45559
45756
|
}
|
|
45560
45757
|
var INVOKED_ARGV3 = process.argv.slice(2);
|
|
45561
45758
|
var program2 = new Command();
|
package/package.json
CHANGED