mimi-seed 0.19.14 → 0.19.18
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.
|
@@ -538,17 +538,17 @@ var CREDENTIALS = [
|
|
|
538
538
|
obtain: {
|
|
539
539
|
ko: [
|
|
540
540
|
"Instagram \uACFC **\uBCC4\uAC1C \uACC4\uC815\xB7\uBCC4\uAC1C \uD1A0\uD070**\uC774\uB2E4 (Threads Graph API).",
|
|
541
|
-
"
|
|
542
|
-
"\
|
|
543
|
-
"\
|
|
544
|
-
"
|
|
541
|
+
"\uAE30\uBCF8 \uD750\uB984: `mimi-seed auth threads`\uB97C \uC2E4\uD589\uD558\uBA74 HTTPS \uBE0C\uB85C\uCEE4\uAC00 \uC5EC\uB294 \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C Threads \uB85C\uADF8\uC778\xB7\uAD8C\uD55C \uC2B9\uC778\uC744 \uC644\uB8CC\uD55C\uB2E4.",
|
|
542
|
+
"\uBE0C\uB85C\uCEE4 \uC6B4\uC601\uC790\uB294 Threads App ID/secret\uACFC \uC571\uC5D0 \uB4F1\uB85D\uD55C HTTPS callback URL\uC744 \uC124\uC815\xB7\uBC30\uD3EC\uD574\uC57C \uD55C\uB2E4 (SDK\uAC00 \uBE0C\uB85C\uCEE4\uB97C \uBC30\uD3EC\uD558\uC9C0\uB294 \uC54A\uB294\uB2E4).",
|
|
543
|
+
"\uD1A0\uD070\uC744 \uC9C1\uC811 \uBD99\uC5EC \uB123\uB294 `--manual-token`\uC740 \uACE0\uAE09/\uD5E4\uB4DC\uB9AC\uC2A4 \uD750\uB984\uC774\uB2E4. \uC800\uC7A5 \uC804\uC5D0 Threads \uACC4\uC815\uC744 \uAC80\uC99D\uD55C\uB2E4.",
|
|
544
|
+
"`--profile <id>`\uB85C \uC774\uB984 \uC788\uB294 \uACC4\uC815\uC744 \uC800\uC7A5\uD560 \uC218 \uC788\uC73C\uBA70, OAuth \uC751\uB2F5\uC758 \uC2E4\uC81C \uB9CC\uB8CC \uC2DC\uAC01\uC744 \uB85C\uCEEC \uD504\uB85C\uD544\uC5D0 \uAE30\uB85D\uD55C\uB2E4."
|
|
545
545
|
],
|
|
546
546
|
en: [
|
|
547
547
|
"A **separate account and token** from Instagram (Threads Graph API).",
|
|
548
|
-
"
|
|
549
|
-
"
|
|
550
|
-
"
|
|
551
|
-
"
|
|
548
|
+
"Default: run `mimi-seed auth threads` and finish Threads login and consent in the browser opened by the HTTPS broker.",
|
|
549
|
+
"The broker operator must deploy it with the Threads App ID/secret and the HTTPS callback URL registered in the app (the SDK does not deploy the broker).",
|
|
550
|
+
"Pasting a token with `--manual-token` is the advanced/headless path; the Threads account is verified before saving.",
|
|
551
|
+
"Use `--profile <id>` for a named account; the actual expiry returned by OAuth is saved in the local profile."
|
|
552
552
|
]
|
|
553
553
|
},
|
|
554
554
|
docsAnchor: "threads",
|
|
@@ -1231,6 +1231,7 @@ async function hasAnyProjectSignal(cwd) {
|
|
|
1231
1231
|
// src/deploy.ts
|
|
1232
1232
|
import kleur from "kleur";
|
|
1233
1233
|
import * as readline from "readline";
|
|
1234
|
+
import { randomUUID } from "crypto";
|
|
1234
1235
|
|
|
1235
1236
|
// src/config.ts
|
|
1236
1237
|
import fs6 from "fs/promises";
|
|
@@ -1272,10 +1273,234 @@ async function getEffectiveConfig() {
|
|
|
1272
1273
|
return readConfig();
|
|
1273
1274
|
}
|
|
1274
1275
|
|
|
1275
|
-
// src/
|
|
1276
|
-
|
|
1276
|
+
// src/deploy-stream.ts
|
|
1277
|
+
var DeployStreamError = class extends Error {
|
|
1278
|
+
constructor(reason, message = "") {
|
|
1279
|
+
super(message);
|
|
1280
|
+
this.reason = reason;
|
|
1281
|
+
}
|
|
1282
|
+
reason;
|
|
1283
|
+
};
|
|
1284
|
+
async function consumeDeployStream(reader, onEvent) {
|
|
1285
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
1286
|
+
let line = "";
|
|
1287
|
+
let data = [];
|
|
1288
|
+
let skipLf = false;
|
|
1289
|
+
let done = false;
|
|
1290
|
+
let jobId;
|
|
1291
|
+
function dispatch() {
|
|
1292
|
+
if (data.length === 0) return;
|
|
1293
|
+
const payload = data.join("\n");
|
|
1294
|
+
data = [];
|
|
1295
|
+
let value;
|
|
1296
|
+
try {
|
|
1297
|
+
value = JSON.parse(payload);
|
|
1298
|
+
} catch {
|
|
1299
|
+
throw new DeployStreamError("malformed");
|
|
1300
|
+
}
|
|
1301
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
1302
|
+
throw new DeployStreamError("malformed");
|
|
1303
|
+
}
|
|
1304
|
+
const event = value;
|
|
1305
|
+
if (typeof event.phase !== "string" || !event.phase || typeof event.message !== "string" || !["running", "done", "failed", "skipped"].includes(String(event.status)) || event.jobId !== void 0 && (typeof event.jobId !== "string" || !event.jobId)) {
|
|
1306
|
+
throw new DeployStreamError("malformed");
|
|
1307
|
+
}
|
|
1308
|
+
const parsed = event;
|
|
1309
|
+
if (parsed.jobId) jobId = parsed.jobId;
|
|
1310
|
+
onEvent(parsed);
|
|
1311
|
+
if (parsed.status === "failed") throw new DeployStreamError("failed", parsed.message);
|
|
1312
|
+
if (parsed.phase === "done" && parsed.status === "done") done = true;
|
|
1313
|
+
}
|
|
1314
|
+
function consumeLine() {
|
|
1315
|
+
if (!line) {
|
|
1316
|
+
dispatch();
|
|
1317
|
+
} else if (!line.startsWith(":")) {
|
|
1318
|
+
const colon = line.indexOf(":");
|
|
1319
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
1320
|
+
const raw = colon < 0 ? "" : line.slice(colon + 1);
|
|
1321
|
+
if (field === "data") data.push(raw.startsWith(" ") ? raw.slice(1) : raw);
|
|
1322
|
+
}
|
|
1323
|
+
line = "";
|
|
1324
|
+
}
|
|
1325
|
+
function consumeText(text) {
|
|
1326
|
+
for (const char of text) {
|
|
1327
|
+
if (skipLf) {
|
|
1328
|
+
skipLf = false;
|
|
1329
|
+
if (char === "\n") continue;
|
|
1330
|
+
}
|
|
1331
|
+
if (char === "\r") {
|
|
1332
|
+
consumeLine();
|
|
1333
|
+
skipLf = true;
|
|
1334
|
+
} else if (char === "\n") {
|
|
1335
|
+
consumeLine();
|
|
1336
|
+
} else {
|
|
1337
|
+
line += char;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
try {
|
|
1342
|
+
while (true) {
|
|
1343
|
+
const { done: eof, value } = await reader.read();
|
|
1344
|
+
if (eof) break;
|
|
1345
|
+
try {
|
|
1346
|
+
consumeText(decoder.decode(value, { stream: true }));
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
if (error instanceof TypeError) throw new DeployStreamError("malformed");
|
|
1349
|
+
throw error;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
try {
|
|
1353
|
+
consumeText(decoder.decode());
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
if (error instanceof TypeError) throw new DeployStreamError("malformed");
|
|
1356
|
+
throw error;
|
|
1357
|
+
}
|
|
1358
|
+
} finally {
|
|
1359
|
+
reader.releaseLock();
|
|
1360
|
+
}
|
|
1361
|
+
if (line) consumeLine();
|
|
1362
|
+
dispatch();
|
|
1363
|
+
if (!done) throw new DeployStreamError("incomplete");
|
|
1364
|
+
return jobId;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// src/project-link.ts
|
|
1368
|
+
import fs7 from "fs/promises";
|
|
1277
1369
|
import path8 from "path";
|
|
1370
|
+
var PROJECT_LINK_FILENAME = ".mimi-seed-link.json";
|
|
1278
1371
|
var M = catalog({
|
|
1372
|
+
lookupFailed: (status) => `\uD504\uB85C\uC81D\uD2B8 \uC571 \uC5F0\uACB0 \uC870\uD68C \uC2E4\uD328 (${status}). --app <id>\uB85C \uC9C0\uC815\uD558\uC138\uC694.`,
|
|
1373
|
+
invalidResponse: "\uD504\uB85C\uC81D\uD2B8 \uC571 \uC5F0\uACB0 \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
1374
|
+
mismatch: "\uC800\uC7A5\uB41C \uD504\uB85C\uC81D\uD2B8 \uC571 \uC5F0\uACB0\uC774 \uD604\uC7AC \uACC4\uC815\xB7\uC571\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uC5F0\uACB0 \uD30C\uC77C\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
1375
|
+
invalidFile: "\uD504\uB85C\uC81D\uD2B8 \uC571 \uC5F0\uACB0 \uD30C\uC77C\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."
|
|
1376
|
+
}, {
|
|
1377
|
+
lookupFailed: (status) => `Project app lookup failed (${status}). Specify --app <id>.`,
|
|
1378
|
+
invalidResponse: "Invalid project app lookup response.",
|
|
1379
|
+
mismatch: "Saved project app link differs from the current account or app. Check the link file.",
|
|
1380
|
+
invalidFile: "Invalid project app link file."
|
|
1381
|
+
});
|
|
1382
|
+
function base(value) {
|
|
1383
|
+
return value.replace(/\/+$/, "");
|
|
1384
|
+
}
|
|
1385
|
+
function isLink(value) {
|
|
1386
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1387
|
+
const link = value;
|
|
1388
|
+
return link.schema === 1 && typeof link.webBase === "string" && !!link.webBase && typeof link.appId === "string" && !!link.appId && (link.packageName === void 0 || typeof link.packageName === "string") && (link.bundleId === void 0 || typeof link.bundleId === "string");
|
|
1389
|
+
}
|
|
1390
|
+
async function findProjectLink(cwd) {
|
|
1391
|
+
let dir = path8.resolve(cwd);
|
|
1392
|
+
for (let depth = 0; depth <= 8; depth++) {
|
|
1393
|
+
const file = path8.join(dir, PROJECT_LINK_FILENAME);
|
|
1394
|
+
try {
|
|
1395
|
+
const parsed = JSON.parse(await fs7.readFile(file, "utf8"));
|
|
1396
|
+
if (!isLink(parsed)) throw new Error(M().invalidFile);
|
|
1397
|
+
return parsed;
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
if (error.code !== "ENOENT") throw error;
|
|
1400
|
+
}
|
|
1401
|
+
const parent = path8.dirname(dir);
|
|
1402
|
+
if (parent === dir) break;
|
|
1403
|
+
dir = parent;
|
|
1404
|
+
}
|
|
1405
|
+
return null;
|
|
1406
|
+
}
|
|
1407
|
+
function resolveLinkedAppId(link, webBase, explicitAppId) {
|
|
1408
|
+
if (!link) return explicitAppId;
|
|
1409
|
+
if (base(link.webBase) !== base(webBase) || explicitAppId && explicitAppId !== link.appId) {
|
|
1410
|
+
throw new Error(M().mismatch);
|
|
1411
|
+
}
|
|
1412
|
+
return link.appId;
|
|
1413
|
+
}
|
|
1414
|
+
function validateProjectIdentity(link, hints) {
|
|
1415
|
+
if (!link || hints.length === 0) return;
|
|
1416
|
+
if (!hints.every((hint) => (!hint.packageName || hint.packageName === link.packageName) && (!hint.bundleId || hint.bundleId === link.bundleId))) {
|
|
1417
|
+
throw new Error(M().mismatch);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
async function linkProject(cwd, webBase, token, hint) {
|
|
1421
|
+
const res = await fetch(new URL("/api/deploy/link", webBase), {
|
|
1422
|
+
method: "POST",
|
|
1423
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1424
|
+
body: JSON.stringify({ packageName: hint.packageName, bundleId: hint.bundleId }),
|
|
1425
|
+
signal: AbortSignal.timeout(3e4)
|
|
1426
|
+
});
|
|
1427
|
+
if (!res.ok) throw new Error(M().lookupFailed(res.status));
|
|
1428
|
+
const value = await res.json();
|
|
1429
|
+
const app = value && typeof value === "object" && "app" in value ? value.app : null;
|
|
1430
|
+
if (!app || typeof app !== "object" || !("id" in app) || typeof app.id !== "string" || !app.id || hint.packageName && (!("packageName" in app) || app.packageName !== hint.packageName) || hint.bundleId && (!("bundleId" in app) || app.bundleId !== hint.bundleId)) {
|
|
1431
|
+
throw new Error(M().invalidResponse);
|
|
1432
|
+
}
|
|
1433
|
+
const link = {
|
|
1434
|
+
schema: 1,
|
|
1435
|
+
webBase: base(webBase),
|
|
1436
|
+
appId: app.id,
|
|
1437
|
+
...hint.packageName ? { packageName: hint.packageName } : {},
|
|
1438
|
+
...hint.bundleId ? { bundleId: hint.bundleId } : {}
|
|
1439
|
+
};
|
|
1440
|
+
const existing = await findProjectLink(cwd);
|
|
1441
|
+
if (existing && (existing.appId !== link.appId || base(existing.webBase) !== link.webBase || existing.packageName !== link.packageName || existing.bundleId !== link.bundleId)) {
|
|
1442
|
+
throw new Error(M().mismatch);
|
|
1443
|
+
}
|
|
1444
|
+
if (!existing) {
|
|
1445
|
+
await fs7.writeFile(path8.join(cwd, PROJECT_LINK_FILENAME), `${JSON.stringify(link, null, 2)}
|
|
1446
|
+
`, { mode: 384, flag: "wx" });
|
|
1447
|
+
}
|
|
1448
|
+
const ignorePath = path8.join(cwd, ".gitignore");
|
|
1449
|
+
const ignore = await fs7.readFile(ignorePath, "utf8").catch((error) => {
|
|
1450
|
+
if (error.code !== "ENOENT") throw error;
|
|
1451
|
+
return "";
|
|
1452
|
+
});
|
|
1453
|
+
if (!ignore.split(/\r?\n/).includes(PROJECT_LINK_FILENAME)) {
|
|
1454
|
+
await fs7.appendFile(ignorePath, `${ignore && !ignore.endsWith("\n") ? "\n" : ""}${PROJECT_LINK_FILENAME}
|
|
1455
|
+
`);
|
|
1456
|
+
}
|
|
1457
|
+
return link;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
// src/deploy-runs.ts
|
|
1461
|
+
function parseRun(value) {
|
|
1462
|
+
if (!value || typeof value !== "object") throw new Error(M2().invalidResponse);
|
|
1463
|
+
const run = value;
|
|
1464
|
+
const jobId = run.jobId ?? run.id;
|
|
1465
|
+
if (typeof jobId !== "string" || !jobId || typeof run.appId !== "string" || !run.appId || run.platform !== "android" && run.platform !== "ios" || typeof run.status !== "string" || run.versionCode != null && (!Number.isSafeInteger(run.versionCode) || run.versionCode < 1) || run.jenkinsBuildNumber != null && (!Number.isSafeInteger(run.jenkinsBuildNumber) || run.jenkinsBuildNumber < 1) || run.fromRef != null && typeof run.fromRef !== "string" || run.toRef != null && typeof run.toRef !== "string") {
|
|
1466
|
+
throw new Error(M2().invalidResponse);
|
|
1467
|
+
}
|
|
1468
|
+
return { ...run, jobId };
|
|
1469
|
+
}
|
|
1470
|
+
async function updateDeployRun(webBase, token, request) {
|
|
1471
|
+
const res = await fetch(new URL("/api/deploy/runs", webBase), {
|
|
1472
|
+
method: "POST",
|
|
1473
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1474
|
+
body: JSON.stringify(request),
|
|
1475
|
+
signal: AbortSignal.timeout(3e4)
|
|
1476
|
+
});
|
|
1477
|
+
if (!res.ok) throw new Error(M2().updateFailed(res.status));
|
|
1478
|
+
return parseRun(await res.json());
|
|
1479
|
+
}
|
|
1480
|
+
async function getDeployRun(webBase, token, runId) {
|
|
1481
|
+
const url = new URL("/api/deploy", webBase);
|
|
1482
|
+
url.searchParams.set("jobId", runId);
|
|
1483
|
+
const res = await fetch(url, {
|
|
1484
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
1485
|
+
signal: AbortSignal.timeout(3e4)
|
|
1486
|
+
});
|
|
1487
|
+
if (!res.ok) throw new Error(M2().lookupFailed(res.status));
|
|
1488
|
+
return parseRun(await res.json());
|
|
1489
|
+
}
|
|
1490
|
+
var M2 = catalog({
|
|
1491
|
+
invalidResponse: "\uBC30\uD3EC \uAE30\uB85D \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
1492
|
+
updateFailed: (status) => `\uBC30\uD3EC \uAE30\uB85D \uAC31\uC2E0 \uC2E4\uD328 (${status}). \uC6F9 \uCF58\uC194\uC5D0\uC11C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.`,
|
|
1493
|
+
lookupFailed: (status) => `\uBC30\uD3EC \uAE30\uB85D \uC870\uD68C \uC2E4\uD328 (${status}). \uC6F9 \uCF58\uC194\uC5D0\uC11C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.`
|
|
1494
|
+
}, {
|
|
1495
|
+
invalidResponse: "Invalid deployment run response.",
|
|
1496
|
+
updateFailed: (status) => `Deployment record update failed (${status}). Check its status in the web console.`,
|
|
1497
|
+
lookupFailed: (status) => `Deployment record lookup failed (${status}). Check its status in the web console.`
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
// src/jenkins-project.ts
|
|
1501
|
+
import fs8 from "fs";
|
|
1502
|
+
import path9 from "path";
|
|
1503
|
+
var M3 = catalog({
|
|
1279
1504
|
invalidManifest: "\uD504\uB85C\uC81D\uD2B8 .mimi-seed.json\uC740 \uC62C\uBC14\uB978 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
|
|
1280
1505
|
controllerMismatch: "\uD504\uB85C\uC81D\uD2B8 Jenkins URL\uACFC \uB85C\uCEEC\uC5D0\uC11C \uC778\uC99D\uB41C \uCEE8\uD2B8\uB864\uB7EC\uAC00 \uB2E4\uB985\uB2C8\uB2E4.",
|
|
1281
1506
|
invalidJob: ".mimi-seed.json\uC758 services.jenkins.jobAndroid/jobIos\uC5D0 \uC62C\uBC14\uB978 \uC7A1 \uACBD\uB85C\uB97C \uC124\uC815\uD558\uC138\uC694.",
|
|
@@ -1288,26 +1513,26 @@ var M = catalog({
|
|
|
1288
1513
|
});
|
|
1289
1514
|
function resolveProjectJenkins(cfg, platform, cwd = process.cwd()) {
|
|
1290
1515
|
const field = platform === "android" ? "jobAndroid" : "jobIos";
|
|
1291
|
-
let dir =
|
|
1516
|
+
let dir = path9.resolve(cwd);
|
|
1292
1517
|
for (let depth = 0; depth <= 8; depth++) {
|
|
1293
|
-
const file =
|
|
1294
|
-
if (
|
|
1518
|
+
const file = path9.join(dir, MANIFEST_FILENAME);
|
|
1519
|
+
if (fs8.existsSync(file)) {
|
|
1295
1520
|
let manifest;
|
|
1296
1521
|
try {
|
|
1297
|
-
manifest = JSON.parse(
|
|
1522
|
+
manifest = JSON.parse(fs8.readFileSync(file, "utf8"));
|
|
1298
1523
|
} catch {
|
|
1299
|
-
throw new Error(
|
|
1524
|
+
throw new Error(M3().invalidManifest);
|
|
1300
1525
|
}
|
|
1301
1526
|
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
1302
|
-
throw new Error(
|
|
1527
|
+
throw new Error(M3().invalidManifest);
|
|
1303
1528
|
}
|
|
1304
1529
|
const service = manifest.services?.jenkins;
|
|
1305
1530
|
if (service?.url !== void 0 && (typeof service.url !== "string" || service.url.replace(/\/+$/, "") !== cfg.url.replace(/\/+$/, ""))) {
|
|
1306
|
-
throw new Error(
|
|
1531
|
+
throw new Error(M3().controllerMismatch);
|
|
1307
1532
|
}
|
|
1308
1533
|
return { job: validateJob(service?.[field]), source: file };
|
|
1309
1534
|
}
|
|
1310
|
-
const parent =
|
|
1535
|
+
const parent = path9.dirname(dir);
|
|
1311
1536
|
if (parent === dir) break;
|
|
1312
1537
|
dir = parent;
|
|
1313
1538
|
}
|
|
@@ -1315,7 +1540,7 @@ function resolveProjectJenkins(cfg, platform, cwd = process.cwd()) {
|
|
|
1315
1540
|
}
|
|
1316
1541
|
function validateJob(job) {
|
|
1317
1542
|
if (typeof job !== "string" || !job || job.split("/").some((part) => !part || part === "." || part === ".." || /[\\?#%]/.test(part) || [...part].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127))) {
|
|
1318
|
-
throw new Error(
|
|
1543
|
+
throw new Error(M3().invalidJob);
|
|
1319
1544
|
}
|
|
1320
1545
|
return job;
|
|
1321
1546
|
}
|
|
@@ -1324,7 +1549,7 @@ function jenkinsJobPath(job) {
|
|
|
1324
1549
|
}
|
|
1325
1550
|
function jenkinsBuildParameters(platform, ref, appId) {
|
|
1326
1551
|
if (!ref || /^[a-f0-9]{7,40}$/i.test(ref) || ref.startsWith("-") || /[\s~^:?*[\\]|\.\.|@\{/.test(ref)) {
|
|
1327
|
-
throw new Error(
|
|
1552
|
+
throw new Error(M3().invalidRef);
|
|
1328
1553
|
}
|
|
1329
1554
|
return {
|
|
1330
1555
|
BUILD_TARGET: platform,
|
|
@@ -1340,11 +1565,11 @@ function jenkinsBuildParameters(platform, ref, appId) {
|
|
|
1340
1565
|
}
|
|
1341
1566
|
|
|
1342
1567
|
// src/ci-providers.ts
|
|
1343
|
-
import
|
|
1344
|
-
import
|
|
1568
|
+
import fs9 from "fs";
|
|
1569
|
+
import path10 from "path";
|
|
1345
1570
|
import os5 from "os";
|
|
1346
|
-
var CI_CONFIG_PATH =
|
|
1347
|
-
var
|
|
1571
|
+
var CI_CONFIG_PATH = path10.join(os5.homedir(), ".mimi-seed", "ci.json");
|
|
1572
|
+
var M4 = catalog(
|
|
1348
1573
|
{
|
|
1349
1574
|
badToken: (provider, status) => `${provider} ${status} \u2014 \uD1A0\uD070\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544`,
|
|
1350
1575
|
noScope: (scopes) => `\uD1A0\uD070\uC5D0 \`workflow\` \uC2A4\uCF54\uD504\uAC00 \uC5C6\uC5B4 (\uD604\uC7AC: ${scopes || "\uC5C6\uC74C"}). \uC6CC\uD06C\uD50C\uB85C \uC2E4\uD589\uC774 403 \uC73C\uB85C \uB9C9\uD78C\uB2E4.`,
|
|
@@ -1361,7 +1586,7 @@ var M2 = catalog(
|
|
|
1361
1586
|
);
|
|
1362
1587
|
function loadCiProviderConfig() {
|
|
1363
1588
|
try {
|
|
1364
|
-
const cfg = JSON.parse(
|
|
1589
|
+
const cfg = JSON.parse(fs9.readFileSync(CI_CONFIG_PATH, "utf-8"));
|
|
1365
1590
|
const host = normalizeHost(cfg.host);
|
|
1366
1591
|
return host ? { ...cfg, host } : { ...cfg, host: void 0 };
|
|
1367
1592
|
} catch {
|
|
@@ -1375,15 +1600,15 @@ function normalizeHost(host) {
|
|
|
1375
1600
|
return withScheme.replace(/\/+$/, "");
|
|
1376
1601
|
}
|
|
1377
1602
|
function saveCiProviderConfig(cfg) {
|
|
1378
|
-
const dir =
|
|
1379
|
-
if (!
|
|
1380
|
-
|
|
1603
|
+
const dir = path10.dirname(CI_CONFIG_PATH);
|
|
1604
|
+
if (!fs9.existsSync(dir)) {
|
|
1605
|
+
fs9.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
1381
1606
|
}
|
|
1382
1607
|
const normalized = { ...cfg, host: normalizeHost(cfg.host) };
|
|
1383
1608
|
if (!normalized.host) delete normalized.host;
|
|
1384
|
-
|
|
1609
|
+
fs9.writeFileSync(CI_CONFIG_PATH, JSON.stringify(normalized, null, 2));
|
|
1385
1610
|
if (process.platform !== "win32") {
|
|
1386
|
-
|
|
1611
|
+
fs9.chmodSync(CI_CONFIG_PATH, 384);
|
|
1387
1612
|
}
|
|
1388
1613
|
}
|
|
1389
1614
|
async function verifyCiToken(cfg) {
|
|
@@ -1394,18 +1619,18 @@ async function verifyCiToken(cfg) {
|
|
|
1394
1619
|
headers: { Authorization: `Bearer ${probe.token}`, Accept: "application/vnd.github+json" }
|
|
1395
1620
|
});
|
|
1396
1621
|
if (!res2.ok) {
|
|
1397
|
-
return { ok: false, reason:
|
|
1622
|
+
return { ok: false, reason: M4().badToken("GitHub", res2.status) };
|
|
1398
1623
|
}
|
|
1399
1624
|
const user2 = await res2.json();
|
|
1400
1625
|
const scopes = res2.headers.get("x-oauth-scopes");
|
|
1401
1626
|
if (scopes !== null && !scopes.split(/,\s*/).filter(Boolean).includes("workflow")) {
|
|
1402
|
-
return { ok: false, reason:
|
|
1627
|
+
return { ok: false, reason: M4().noScope(scopes) };
|
|
1403
1628
|
}
|
|
1404
1629
|
return { ok: true, login: user2.login };
|
|
1405
1630
|
}
|
|
1406
1631
|
const res = await fetch(`${glBase(probe)}/user`, { headers: { "PRIVATE-TOKEN": probe.token } });
|
|
1407
1632
|
if (!res.ok) {
|
|
1408
|
-
return { ok: false, reason:
|
|
1633
|
+
return { ok: false, reason: M4().badToken("GitLab", res.status) };
|
|
1409
1634
|
}
|
|
1410
1635
|
const user = await res.json();
|
|
1411
1636
|
return { ok: true, login: user.username };
|
|
@@ -1463,12 +1688,12 @@ async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3, interval
|
|
|
1463
1688
|
);
|
|
1464
1689
|
} catch {
|
|
1465
1690
|
consecutiveErrors++;
|
|
1466
|
-
if (consecutiveErrors >= 3) throw new Error(
|
|
1691
|
+
if (consecutiveErrors >= 3) throw new Error(M4().pollFailed("GitHub"));
|
|
1467
1692
|
continue;
|
|
1468
1693
|
}
|
|
1469
1694
|
if (!res.ok) {
|
|
1470
1695
|
consecutiveErrors++;
|
|
1471
|
-
if (consecutiveErrors >= 3) throw new Error(
|
|
1696
|
+
if (consecutiveErrors >= 3) throw new Error(M4().pollFailedHttp("GitHub", res.status));
|
|
1472
1697
|
continue;
|
|
1473
1698
|
}
|
|
1474
1699
|
consecutiveErrors = 0;
|
|
@@ -1514,12 +1739,12 @@ async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3
|
|
|
1514
1739
|
);
|
|
1515
1740
|
} catch {
|
|
1516
1741
|
consecutiveErrors++;
|
|
1517
|
-
if (consecutiveErrors >= 3) throw new Error(
|
|
1742
|
+
if (consecutiveErrors >= 3) throw new Error(M4().pollFailed("GitLab"));
|
|
1518
1743
|
continue;
|
|
1519
1744
|
}
|
|
1520
1745
|
if (!res.ok) {
|
|
1521
1746
|
consecutiveErrors++;
|
|
1522
|
-
if (consecutiveErrors >= 3) throw new Error(
|
|
1747
|
+
if (consecutiveErrors >= 3) throw new Error(M4().pollFailedHttp("GitLab", res.status));
|
|
1523
1748
|
continue;
|
|
1524
1749
|
}
|
|
1525
1750
|
consecutiveErrors = 0;
|
|
@@ -1545,7 +1770,7 @@ var PHASE_ICON = {
|
|
|
1545
1770
|
function log(msg) {
|
|
1546
1771
|
process.stdout.write(msg + "\n");
|
|
1547
1772
|
}
|
|
1548
|
-
var
|
|
1773
|
+
var M5 = catalog(
|
|
1549
1774
|
{
|
|
1550
1775
|
missingValue: (option) => `${option} \uB4A4\uC5D0 \uAC12\uC744 \uC785\uB825\uD558\uC138\uC694.`,
|
|
1551
1776
|
unknownOption: (option) => `\uC54C \uC218 \uC5C6\uB294 deploy \uC635\uC158: ${option}`,
|
|
@@ -1564,6 +1789,15 @@ var M3 = catalog(
|
|
|
1564
1789
|
// 서버 스트림
|
|
1565
1790
|
serverDeployFailed: (status, body) => `\uC11C\uBC84 \uBC30\uD3EC \uC2E4\uD328 ${status}: ${body}`,
|
|
1566
1791
|
noSseStream: "SSE \uC2A4\uD2B8\uB9BC \uC5C6\uC74C",
|
|
1792
|
+
malformedSse: "\uC11C\uBC84 \uBC30\uD3EC \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC6F9 \uCF58\uC194\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
1793
|
+
incompleteSse: "\uC11C\uBC84 \uBC30\uD3EC \uC751\uB2F5\uC774 \uC644\uB8CC \uC804\uC5D0 \uB04A\uACBC\uC2B5\uB2C8\uB2E4. \uC6F9 \uCF58\uC194\uC5D0\uC11C \uBC30\uD3EC \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.",
|
|
1794
|
+
deploymentLink: (url) => ` \uC6F9 \uBC30\uD3EC \uAE30\uB85D: ${url}`,
|
|
1795
|
+
runId: (id) => ` \uBC30\uD3EC ID: ${id}`,
|
|
1796
|
+
prepared: (id) => `\uBC30\uD3EC \uC900\uBE44 \uC644\uB8CC. \uC81C\uCD9C\uD558\uB824\uBA74 mimi-seed deploy --resume ${id} --yes`,
|
|
1797
|
+
invalidResume: "--resume\uC740 --skip-build, --prepare-only, --dry-run\uACFC \uD568\uAED8 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
1798
|
+
resumeNotReady: "\uC900\uBE44 \uC644\uB8CC \uC0C1\uD0DC\uC758 \uBC30\uD3EC\uB9CC \uC7AC\uAC1C\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
1799
|
+
runMismatch: "\uAE30\uC874 \uBC30\uD3EC \uAE30\uB85D\uC758 \uC571 \uB610\uB294 \uD50C\uB7AB\uD3FC\uC774 \uD604\uC7AC \uC694\uCCAD\uACFC \uB2E4\uB985\uB2C8\uB2E4.",
|
|
1800
|
+
ciProviderMismatch: "\uC120\uD0DD\uD55C --ci\uC640 \uC800\uC7A5\uB41C CI \uC81C\uACF5\uC790\uAC00 \uB2E4\uB985\uB2C8\uB2E4. CI \uC124\uC815\uC744 \uD655\uC778\uD558\uC138\uC694.",
|
|
1567
1801
|
// CI provider 설정 프롬프트
|
|
1568
1802
|
githubSetupTitle: "GitHub Actions \uC124\uC815",
|
|
1569
1803
|
gitlabSetupTitle: "GitLab CI \uC124\uC815",
|
|
@@ -1599,7 +1833,7 @@ var M3 = catalog(
|
|
|
1599
1833
|
noQueueItem: " \u26A0 Queue item ID\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uBE4C\uB4DC\uB294 \uC2DC\uC791\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
1600
1834
|
queueItem: (id) => ` Queue item: ${id}`,
|
|
1601
1835
|
waitingForBuildNumber: " \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...",
|
|
1602
|
-
noBuildNumber: " \uBE4C\uB4DC \uBC88\uD638\uB97C \
|
|
1836
|
+
noBuildNumber: " \uBE4C\uB4DC \uBC88\uD638\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC6F9 \uBC30\uD3EC \uAE30\uB85D\uACFC Jenkins \uC2E4\uD589 \uACB0\uACFC\uB97C \uD655\uC778\uD55C \uB4A4 \uC870\uCE58\uD558\uC138\uC694.",
|
|
1603
1837
|
buildStarted: (n) => ` \uBE4C\uB4DC #${n} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`,
|
|
1604
1838
|
buildFailed: (result) => `\uBE4C\uB4DC \uC2E4\uD328: ${result}`,
|
|
1605
1839
|
jenkinsLink: (url) => ` Jenkins: ${url}`,
|
|
@@ -1619,7 +1853,7 @@ var M3 = catalog(
|
|
|
1619
1853
|
confirmPrompt: "\uACC4\uC18D \uC9C4\uD589\uD560\uAE4C\uC694? [y/N]: ",
|
|
1620
1854
|
confirmCancelled: "\uCDE8\uC18C\uB428. (--yes \uB85C \uD655\uC778 \uC0DD\uB7B5 \uAC00\uB2A5)",
|
|
1621
1855
|
pipelineStarting: "\u{1F4E1} \uC11C\uBC84 \uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC2DC\uC791...",
|
|
1622
|
-
done: "\uC644\uB8CC.
|
|
1856
|
+
done: "\uBC30\uD3EC \uD30C\uC774\uD504\uB77C\uC778 \uC644\uB8CC. \uC6F9 \uCF58\uC194\uC5D0\uC11C \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694."
|
|
1623
1857
|
},
|
|
1624
1858
|
{
|
|
1625
1859
|
missingValue: (option) => `Missing value: ${option}`,
|
|
@@ -1639,6 +1873,15 @@ var M3 = catalog(
|
|
|
1639
1873
|
// Server stream
|
|
1640
1874
|
serverDeployFailed: (status, body) => `Server deploy failed ${status}: ${body}`,
|
|
1641
1875
|
noSseStream: "No SSE stream",
|
|
1876
|
+
malformedSse: "Invalid server deploy response. Check the deployment in the web console.",
|
|
1877
|
+
incompleteSse: "The server deploy response ended before completion. Check the deployment in the web console.",
|
|
1878
|
+
deploymentLink: (url) => ` Web deployment record: ${url}`,
|
|
1879
|
+
runId: (id) => ` Deployment ID: ${id}`,
|
|
1880
|
+
prepared: (id) => `Deployment ready. Submit with mimi-seed deploy --resume ${id} --yes`,
|
|
1881
|
+
invalidResume: "--resume cannot be combined with --skip-build, --prepare-only, or --dry-run.",
|
|
1882
|
+
resumeNotReady: "Only a ready deployment can be resumed.",
|
|
1883
|
+
runMismatch: "The saved deployment app or platform differs from this request.",
|
|
1884
|
+
ciProviderMismatch: "Selected --ci differs from the configured CI provider. Check the CI configuration.",
|
|
1642
1885
|
// CI provider setup prompts
|
|
1643
1886
|
githubSetupTitle: "GitHub Actions setup",
|
|
1644
1887
|
gitlabSetupTitle: "GitLab CI setup",
|
|
@@ -1674,7 +1917,7 @@ var M3 = catalog(
|
|
|
1674
1917
|
noQueueItem: " \u26A0 Could not read the queue item ID. The build may have started anyway.",
|
|
1675
1918
|
queueItem: (id) => ` Queue item: ${id}`,
|
|
1676
1919
|
waitingForBuildNumber: " Waiting for the build number...",
|
|
1677
|
-
noBuildNumber: " Could not
|
|
1920
|
+
noBuildNumber: " Could not confirm the build number. Check the web deployment record and Jenkins outcome before taking action.",
|
|
1678
1921
|
buildStarted: (n) => ` Build #${n} started. Waiting for it to finish...`,
|
|
1679
1922
|
buildFailed: (result) => `Build failed: ${result}`,
|
|
1680
1923
|
jenkinsLink: (url) => ` Jenkins: ${url}`,
|
|
@@ -1694,7 +1937,7 @@ var M3 = catalog(
|
|
|
1694
1937
|
confirmPrompt: "Continue? [y/N]: ",
|
|
1695
1938
|
confirmCancelled: "Cancelled. (use --yes to skip this confirmation)",
|
|
1696
1939
|
pipelineStarting: "\u{1F4E1} Starting the server deploy pipeline...",
|
|
1697
|
-
done: "
|
|
1940
|
+
done: "Deploy pipeline complete. Check its status in the web console."
|
|
1698
1941
|
}
|
|
1699
1942
|
);
|
|
1700
1943
|
function jenkinsHeaders(cfg) {
|
|
@@ -1712,7 +1955,7 @@ async function triggerBuild(cfg, jobName, params) {
|
|
|
1712
1955
|
body: qs
|
|
1713
1956
|
});
|
|
1714
1957
|
if (!res.ok) {
|
|
1715
|
-
throw new Error(
|
|
1958
|
+
throw new Error(M5().jenkinsTriggerFailed(res.status, await res.text()));
|
|
1716
1959
|
}
|
|
1717
1960
|
const location2 = res.headers.get("Location") ?? "";
|
|
1718
1961
|
const match = location2.match(/\/queue\/item\/(\d+)\//);
|
|
@@ -1728,7 +1971,7 @@ async function getQueueBuildNumber(cfg, queueItemId) {
|
|
|
1728
1971
|
async function getBuildStatus(cfg, jobName, buildNumber) {
|
|
1729
1972
|
const url = `${cfg.url}/${jenkinsJobPath(jobName)}/${buildNumber}/api/json`;
|
|
1730
1973
|
const res = await fetch(url, { headers: jenkinsHeaders(cfg), redirect: "error", signal: AbortSignal.timeout(3e4) });
|
|
1731
|
-
if (!res.ok) throw new Error(
|
|
1974
|
+
if (!res.ok) throw new Error(M5().buildStatusFailed(res.status));
|
|
1732
1975
|
const data = await res.json();
|
|
1733
1976
|
return {
|
|
1734
1977
|
building: data.building ?? true,
|
|
@@ -1749,20 +1992,20 @@ async function pollBuildComplete(cfg, jobName, buildNumber, timeoutMs = 30 * 60
|
|
|
1749
1992
|
consecutiveErrors = 0;
|
|
1750
1993
|
} catch {
|
|
1751
1994
|
consecutiveErrors++;
|
|
1752
|
-
if (consecutiveErrors >= 3) throw new Error(
|
|
1753
|
-
process.stdout.write(`\r \u26A0 ${
|
|
1995
|
+
if (consecutiveErrors >= 3) throw new Error(M5().jenkinsConnErrorFatal);
|
|
1996
|
+
process.stdout.write(`\r \u26A0 ${M5().jenkinsConnErrorRetry(consecutiveErrors)} `);
|
|
1754
1997
|
continue;
|
|
1755
1998
|
}
|
|
1756
1999
|
dots = (dots + 1) % 4;
|
|
1757
|
-
process.stdout.write(`\r \u23F3 ${
|
|
2000
|
+
process.stdout.write(`\r \u23F3 ${M5().buildRunning}${".".repeat(dots + 1)} `);
|
|
1758
2001
|
if (!status.building) {
|
|
1759
2002
|
process.stdout.write("\n");
|
|
1760
2003
|
return status.result ?? "FAILURE";
|
|
1761
2004
|
}
|
|
1762
2005
|
}
|
|
1763
|
-
throw new Error(
|
|
2006
|
+
throw new Error(M5().buildTimeout);
|
|
1764
2007
|
}
|
|
1765
|
-
async function streamDeploy(webBase, token, body) {
|
|
2008
|
+
async function streamDeploy(webBase, token, appId, body, linkAlreadyPrinted = false) {
|
|
1766
2009
|
const res = await fetch(`${webBase}/api/deploy`, {
|
|
1767
2010
|
method: "POST",
|
|
1768
2011
|
headers: {
|
|
@@ -1773,41 +2016,41 @@ async function streamDeploy(webBase, token, body) {
|
|
|
1773
2016
|
});
|
|
1774
2017
|
if (!res.ok) {
|
|
1775
2018
|
const text = await res.text().catch(() => "");
|
|
1776
|
-
throw new Error(
|
|
2019
|
+
throw new Error(M5().serverDeployFailed(res.status, text.slice(0, 200)));
|
|
1777
2020
|
}
|
|
1778
2021
|
const reader = res.body?.getReader();
|
|
1779
|
-
if (!reader) throw new Error(
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
for (const line of lines) {
|
|
1789
|
-
if (!line.startsWith("data: ")) continue;
|
|
1790
|
-
try {
|
|
1791
|
-
const event = JSON.parse(line.slice(6));
|
|
1792
|
-
const icon = PHASE_ICON[event.phase] ?? "\u25B8";
|
|
1793
|
-
const color = event.status === "done" ? kleur.green : event.status === "failed" ? kleur.red : event.status === "skipped" ? kleur.yellow : kleur.dim;
|
|
1794
|
-
log(` ${icon} ${color(event.message)}`);
|
|
1795
|
-
if (event.phase === "error" || event.phase === "verify" && event.status === "failed" || event.phase === "promote" && event.status === "failed") {
|
|
1796
|
-
process.exit(1);
|
|
1797
|
-
}
|
|
1798
|
-
} catch {
|
|
2022
|
+
if (!reader) throw new Error(M5().noSseStream);
|
|
2023
|
+
let linkPrinted = linkAlreadyPrinted;
|
|
2024
|
+
try {
|
|
2025
|
+
await consumeDeployStream(reader, (event) => {
|
|
2026
|
+
if (event.jobId && !linkPrinted) {
|
|
2027
|
+
const link = new URL(`/apps/${encodeURIComponent(appId)}`, webBase);
|
|
2028
|
+
link.searchParams.set("deployment", event.jobId);
|
|
2029
|
+
log(kleur.dim(M5().deploymentLink(link.toString())));
|
|
2030
|
+
linkPrinted = true;
|
|
1799
2031
|
}
|
|
2032
|
+
const icon = PHASE_ICON[event.phase] ?? "\u25B8";
|
|
2033
|
+
const color = event.status === "done" ? kleur.green : event.status === "failed" ? kleur.red : event.status === "skipped" ? kleur.yellow : kleur.dim;
|
|
2034
|
+
log(` ${icon} ${color(event.message)}`);
|
|
2035
|
+
});
|
|
2036
|
+
} catch (error) {
|
|
2037
|
+
if (error instanceof DeployStreamError) {
|
|
2038
|
+
if (error.reason === "malformed") throw new Error(M5().malformedSse, { cause: error });
|
|
2039
|
+
if (error.reason === "incomplete") throw new Error(M5().incompleteSse, { cause: error });
|
|
1800
2040
|
}
|
|
2041
|
+
throw error;
|
|
1801
2042
|
}
|
|
1802
2043
|
}
|
|
1803
2044
|
var ANDROID_VERSION_CODE_MAX = 21e8;
|
|
1804
2045
|
function parseArgs(argv) {
|
|
1805
2046
|
const args = {
|
|
1806
2047
|
platform: "android",
|
|
2048
|
+
platformExplicit: false,
|
|
1807
2049
|
language: "ko-KR",
|
|
1808
2050
|
dryRun: false,
|
|
1809
2051
|
yes: false,
|
|
1810
2052
|
skipBuild: false,
|
|
2053
|
+
prepareOnly: false,
|
|
1811
2054
|
setupJenkins: false,
|
|
1812
2055
|
setupGithub: false,
|
|
1813
2056
|
setupGitlab: false,
|
|
@@ -1818,13 +2061,14 @@ function parseArgs(argv) {
|
|
|
1818
2061
|
const option = argv[i];
|
|
1819
2062
|
const value = () => {
|
|
1820
2063
|
const next = argv[++i];
|
|
1821
|
-
if (!next?.trim() || next.startsWith("--")) throw new Error(
|
|
2064
|
+
if (!next?.trim() || next.startsWith("--")) throw new Error(M5().missingValue(option));
|
|
1822
2065
|
return next;
|
|
1823
2066
|
};
|
|
1824
2067
|
switch (option) {
|
|
1825
2068
|
case "--platform":
|
|
1826
2069
|
case "-p":
|
|
1827
2070
|
args.platform = value();
|
|
2071
|
+
args.platformExplicit = true;
|
|
1828
2072
|
break;
|
|
1829
2073
|
case "--app":
|
|
1830
2074
|
args.appId = value();
|
|
@@ -1851,6 +2095,12 @@ function parseArgs(argv) {
|
|
|
1851
2095
|
case "--skip-build":
|
|
1852
2096
|
args.skipBuild = true;
|
|
1853
2097
|
break;
|
|
2098
|
+
case "--prepare-only":
|
|
2099
|
+
args.prepareOnly = true;
|
|
2100
|
+
break;
|
|
2101
|
+
case "--resume":
|
|
2102
|
+
args.resume = value();
|
|
2103
|
+
break;
|
|
1854
2104
|
case "--ci":
|
|
1855
2105
|
args.ci = value();
|
|
1856
2106
|
break;
|
|
@@ -1870,23 +2120,24 @@ function parseArgs(argv) {
|
|
|
1870
2120
|
args.setupGitlab = true;
|
|
1871
2121
|
break;
|
|
1872
2122
|
default:
|
|
1873
|
-
throw new Error(
|
|
2123
|
+
throw new Error(M5().unknownOption(option));
|
|
1874
2124
|
}
|
|
1875
2125
|
}
|
|
1876
|
-
if (!["android", "ios"].includes(args.platform)) throw new Error(
|
|
1877
|
-
if (!["auto", "jenkins", "github", "gitlab"].includes(args.ci)) throw new Error(
|
|
2126
|
+
if (!["android", "ios"].includes(args.platform)) throw new Error(M5().invalidPlatform);
|
|
2127
|
+
if (!["auto", "jenkins", "github", "gitlab"].includes(args.ci)) throw new Error(M5().invalidCi);
|
|
1878
2128
|
if (args.versionCode !== void 0 && (!Number.isSafeInteger(args.versionCode) || args.versionCode < 1 || args.versionCode > ANDROID_VERSION_CODE_MAX)) {
|
|
1879
|
-
throw new Error(
|
|
2129
|
+
throw new Error(M5().invalidVersion);
|
|
1880
2130
|
}
|
|
1881
|
-
if ([args.setupJenkins, args.setupGithub, args.setupGitlab].filter(Boolean).length > 1) throw new Error(
|
|
1882
|
-
if (args.dryRun && (args.setupJenkins || args.setupGithub || args.setupGitlab)) throw new Error(
|
|
2131
|
+
if ([args.setupJenkins, args.setupGithub, args.setupGitlab].filter(Boolean).length > 1) throw new Error(M5().multipleSetup);
|
|
2132
|
+
if (args.dryRun && (args.setupJenkins || args.setupGithub || args.setupGitlab)) throw new Error(M5().drySetup);
|
|
2133
|
+
if (args.resume && (args.skipBuild || args.prepareOnly || args.dryRun)) throw new Error(M5().invalidResume);
|
|
1883
2134
|
return args;
|
|
1884
2135
|
}
|
|
1885
2136
|
async function promptGitProviderSetup(provider) {
|
|
1886
2137
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1887
2138
|
const ask2 = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
1888
2139
|
const isGh = provider === "github";
|
|
1889
|
-
const m =
|
|
2140
|
+
const m = M5();
|
|
1890
2141
|
log(kleur.bold(isGh ? m.githubSetupTitle : m.gitlabSetupTitle));
|
|
1891
2142
|
const tokenLabel = isGh ? m.githubTokenPrompt : m.gitlabTokenPrompt;
|
|
1892
2143
|
const token = await ask2(tokenLabel);
|
|
@@ -1907,36 +2158,36 @@ function resolveCi(ciOption, jenkins, ciProvider) {
|
|
|
1907
2158
|
if (ciOption !== "auto") return ciOption;
|
|
1908
2159
|
if (jenkins?.url && jenkins.token) return "jenkins";
|
|
1909
2160
|
if (ciProvider) return ciProvider.provider;
|
|
1910
|
-
throw new Error(
|
|
2161
|
+
throw new Error(M5().noCiConfig);
|
|
1911
2162
|
}
|
|
1912
|
-
async function runGitProviderBuild(cfg, args) {
|
|
2163
|
+
async function runGitProviderBuild(cfg, args, appId) {
|
|
1913
2164
|
let runUrl;
|
|
1914
2165
|
let runId;
|
|
1915
2166
|
if (cfg.provider === "github") {
|
|
1916
2167
|
if (!args.workflow) {
|
|
1917
|
-
throw new Error(
|
|
2168
|
+
throw new Error(M5().workflowRequired);
|
|
1918
2169
|
}
|
|
1919
|
-
log(
|
|
2170
|
+
log(M5().ghTrigger(kleur.cyan(args.workflow), args.ref));
|
|
1920
2171
|
const inputs = {};
|
|
1921
|
-
|
|
2172
|
+
inputs.MIMI_APP_ID = appId;
|
|
1922
2173
|
inputs.PLATFORM = args.platform;
|
|
1923
2174
|
const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
|
|
1924
2175
|
if (!result2) {
|
|
1925
|
-
throw new Error(
|
|
2176
|
+
throw new Error(M5().ghRunIdFailed);
|
|
1926
2177
|
}
|
|
1927
2178
|
runId = result2.runId;
|
|
1928
2179
|
runUrl = result2.url;
|
|
1929
|
-
log(kleur.dim(
|
|
2180
|
+
log(kleur.dim(M5().ghRunId(runId, runUrl)));
|
|
1930
2181
|
} else {
|
|
1931
|
-
log(
|
|
2182
|
+
log(M5().glTrigger(args.ref));
|
|
1932
2183
|
const variables = { PLATFORM: args.platform };
|
|
1933
|
-
|
|
2184
|
+
variables.MIMI_APP_ID = appId;
|
|
1934
2185
|
const result2 = await glTriggerPipeline(cfg, args.ref, variables);
|
|
1935
2186
|
runId = result2.pipelineId;
|
|
1936
2187
|
runUrl = result2.url;
|
|
1937
|
-
log(kleur.dim(
|
|
2188
|
+
log(kleur.dim(M5().glPipelineId(runId, runUrl)));
|
|
1938
2189
|
}
|
|
1939
|
-
log(
|
|
2190
|
+
log(M5().waitingForCompletion);
|
|
1940
2191
|
let dots = 0;
|
|
1941
2192
|
const onTick = (status) => {
|
|
1942
2193
|
dots = (dots + 1) % 4;
|
|
@@ -1944,23 +2195,21 @@ async function runGitProviderBuild(cfg, args) {
|
|
|
1944
2195
|
};
|
|
1945
2196
|
const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
|
|
1946
2197
|
process.stdout.write("\n");
|
|
1947
|
-
if (result === "success")
|
|
1948
|
-
|
|
1949
|
-
|
|
2198
|
+
if (result === "success") log(kleur.green(M5().buildSucceeded(runId)));
|
|
2199
|
+
else {
|
|
2200
|
+
log(kleur.red(M5().buildEnded(result)));
|
|
2201
|
+
log(kleur.dim(` ${runUrl}`));
|
|
1950
2202
|
}
|
|
1951
|
-
|
|
1952
|
-
log(kleur.dim(` ${runUrl}`));
|
|
1953
|
-
log(kleur.dim(M3().alreadyBuiltHint("<N>", args.platform)));
|
|
1954
|
-
process.exit(1);
|
|
2203
|
+
return { runId, result };
|
|
1955
2204
|
}
|
|
1956
2205
|
async function cmdDeploy(argv) {
|
|
1957
2206
|
const args = parseArgs(argv);
|
|
1958
2207
|
if (args.dryRun) {
|
|
1959
2208
|
const jenkins = loadJenkinsConfig() ?? void 0;
|
|
1960
2209
|
const ci = args.skipBuild ? null : resolveCi(args.ci, jenkins, loadCiProviderConfig());
|
|
1961
|
-
if (ci === "jenkins" && !jenkins) throw new Error(
|
|
2210
|
+
if (ci === "jenkins" && !jenkins) throw new Error(M5().noJenkinsConfig);
|
|
1962
2211
|
const plan = ci === "jenkins" && jenkins ? { ...resolveProjectJenkins(jenkins, args.platform), parameters: jenkinsBuildParameters(args.platform, args.ref, args.appId) } : void 0;
|
|
1963
|
-
log(
|
|
2212
|
+
log(M5().dryRunNotice);
|
|
1964
2213
|
log(JSON.stringify({
|
|
1965
2214
|
platform: args.platform,
|
|
1966
2215
|
appId: args.appId ?? null,
|
|
@@ -1974,7 +2223,7 @@ async function cmdDeploy(argv) {
|
|
|
1974
2223
|
}
|
|
1975
2224
|
const cfg = await getEffectiveConfig();
|
|
1976
2225
|
if (!cfg) {
|
|
1977
|
-
log(kleur.red(
|
|
2226
|
+
log(kleur.red(M5().noAccount));
|
|
1978
2227
|
process.exit(1);
|
|
1979
2228
|
}
|
|
1980
2229
|
if (args.setupJenkins) {
|
|
@@ -1985,47 +2234,104 @@ async function cmdDeploy(argv) {
|
|
|
1985
2234
|
if (args.setupGithub) {
|
|
1986
2235
|
const ciCfg = await promptGitProviderSetup("github");
|
|
1987
2236
|
saveCiProviderConfig(ciCfg);
|
|
1988
|
-
log(kleur.green(
|
|
2237
|
+
log(kleur.green(M5().githubSaved));
|
|
1989
2238
|
return;
|
|
1990
2239
|
}
|
|
1991
2240
|
if (args.setupGitlab) {
|
|
1992
2241
|
const ciCfg = await promptGitProviderSetup("gitlab");
|
|
1993
2242
|
saveCiProviderConfig(ciCfg);
|
|
1994
|
-
log(kleur.green(
|
|
2243
|
+
log(kleur.green(M5().gitlabSaved));
|
|
1995
2244
|
return;
|
|
1996
2245
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2246
|
+
const resumed = args.resume ? await getDeployRun(cfg.webBase, cfg.token, args.resume) : null;
|
|
2247
|
+
const projectLink = await findProjectLink(process.cwd());
|
|
2248
|
+
const linkedAppId = resolveLinkedAppId(projectLink, cfg.webBase, args.appId);
|
|
2249
|
+
if (projectLink) validateProjectIdentity(projectLink, await detectHints(process.cwd()));
|
|
2250
|
+
const appId = linkedAppId ?? resumed?.appId;
|
|
2251
|
+
if (!appId) throw new Error(M5().noAppId);
|
|
2252
|
+
if (!args.yes) throw new Error(M5().explicitApproval);
|
|
2253
|
+
if (resumed) {
|
|
2254
|
+
if (resumed.status !== "ready") throw new Error(M5().resumeNotReady);
|
|
2255
|
+
if (resumed.jobId !== args.resume || resumed.appId !== appId || args.platformExplicit && resumed.platform !== args.platform || args.versionCode && args.versionCode !== resumed.versionCode) throw new Error(M5().runMismatch);
|
|
2256
|
+
args.platform = resumed.platform;
|
|
2257
|
+
if (!resumed.versionCode) throw new Error(M5().versionCodeUnknown);
|
|
2258
|
+
} else if (!args.versionCode) throw new Error(M5().versionCodeUnknown);
|
|
2259
|
+
log(kleur.bold(M5().title(args.platform)));
|
|
2260
|
+
if (args.dryRun) log(kleur.yellow(M5().dryRunNotice));
|
|
2002
2261
|
log("");
|
|
2003
|
-
const versionCode = args.versionCode;
|
|
2262
|
+
const versionCode = args.versionCode ?? resumed?.versionCode ?? void 0;
|
|
2004
2263
|
let deployBuildNumber;
|
|
2005
|
-
|
|
2006
|
-
|
|
2264
|
+
let ciProvider = null;
|
|
2265
|
+
let jenkinsCfg;
|
|
2266
|
+
let jenkinsJobName;
|
|
2267
|
+
let jenkinsParams;
|
|
2268
|
+
let kind = null;
|
|
2269
|
+
if (!resumed && !args.skipBuild) {
|
|
2007
2270
|
migrateLegacyJenkins();
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2271
|
+
ciProvider = loadCiProviderConfig();
|
|
2272
|
+
jenkinsCfg = loadJenkinsConfig() ?? void 0;
|
|
2273
|
+
kind = resolveCi(args.ci, jenkinsCfg, ciProvider);
|
|
2274
|
+
if (!["jenkins", "github", "gitlab"].includes(kind)) throw new Error(M5().invalidCi);
|
|
2275
|
+
if (kind === "jenkins") {
|
|
2276
|
+
if (!jenkinsCfg?.url || !jenkinsCfg?.token) throw new Error(M5().noJenkinsConfig);
|
|
2277
|
+
jenkinsJobName = resolveProjectJenkins(jenkinsCfg, args.platform).job;
|
|
2278
|
+
jenkinsParams = jenkinsBuildParameters(args.platform, args.ref, appId);
|
|
2279
|
+
} else if (!ciProvider) {
|
|
2280
|
+
throw new Error(M5().noProviderConfig(kind));
|
|
2281
|
+
} else if (ciProvider.provider !== kind) {
|
|
2282
|
+
throw new Error(M5().ciProviderMismatch);
|
|
2283
|
+
} else if (kind === "github" && !args.workflow) {
|
|
2284
|
+
throw new Error(M5().workflowRequired);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
const runId = resumed?.jobId ?? `run_${randomUUID()}`;
|
|
2288
|
+
const runPayload = { runId, appId, platform: args.platform, fromRef: args.fromRef, toRef: args.toRef };
|
|
2289
|
+
const failRun = async (reason) => {
|
|
2290
|
+
await updateDeployRun(cfg.webBase, cfg.token, { action: "failed", ...runPayload, reason });
|
|
2291
|
+
};
|
|
2292
|
+
if (!resumed) {
|
|
2293
|
+
const started = await updateDeployRun(cfg.webBase, cfg.token, {
|
|
2294
|
+
action: "start",
|
|
2295
|
+
...runPayload,
|
|
2296
|
+
...versionCode ? { versionCode } : {}
|
|
2297
|
+
});
|
|
2298
|
+
if (started.jobId !== runId || started.appId !== appId || started.platform !== args.platform) throw new Error(M5().runMismatch);
|
|
2299
|
+
}
|
|
2300
|
+
log(kleur.dim(M5().runId(runId)));
|
|
2301
|
+
const recordLink = new URL(`/apps/${encodeURIComponent(appId)}`, cfg.webBase);
|
|
2302
|
+
recordLink.searchParams.set("deployment", runId);
|
|
2303
|
+
log(kleur.dim(M5().deploymentLink(recordLink.toString())));
|
|
2304
|
+
if (resumed) {
|
|
2305
|
+
await streamDeploy(cfg.webBase, cfg.token, appId, {
|
|
2306
|
+
jobId: runId,
|
|
2307
|
+
appId,
|
|
2308
|
+
platform: args.platform,
|
|
2309
|
+
versionCode,
|
|
2310
|
+
...resumed.jenkinsBuildNumber ? { buildNumber: resumed.jenkinsBuildNumber } : {},
|
|
2311
|
+
fromRef: resumed.fromRef ?? void 0,
|
|
2312
|
+
toRef: resumed.toRef ?? void 0,
|
|
2313
|
+
language: args.language,
|
|
2314
|
+
dryRun: false
|
|
2315
|
+
}, true);
|
|
2316
|
+
log(kleur.bold(M5().done));
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
if (!args.skipBuild) {
|
|
2320
|
+
await updateDeployRun(cfg.webBase, cfg.token, { action: "building", ...runPayload });
|
|
2321
|
+
log(kleur.dim(M5().ciLine(kind)));
|
|
2011
2322
|
if (kind === "jenkins") {
|
|
2012
|
-
if (!jenkinsCfg?.url || !jenkinsCfg?.token) {
|
|
2013
|
-
log(kleur.yellow(M3().noJenkinsConfig));
|
|
2014
|
-
process.exit(1);
|
|
2015
|
-
}
|
|
2016
2323
|
const jenkins = jenkinsCfg;
|
|
2017
|
-
const
|
|
2018
|
-
log(
|
|
2019
|
-
const
|
|
2020
|
-
const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
|
|
2324
|
+
const jobName = jenkinsJobName;
|
|
2325
|
+
log(M5().jenkinsTrigger(kleur.cyan(jobName)));
|
|
2326
|
+
const queueItemId = await triggerBuild(jenkins, jobName, jenkinsParams);
|
|
2021
2327
|
if (!queueItemId) {
|
|
2022
|
-
log(kleur.yellow(
|
|
2328
|
+
log(kleur.yellow(M5().noQueueItem));
|
|
2023
2329
|
} else {
|
|
2024
|
-
log(kleur.dim(
|
|
2330
|
+
log(kleur.dim(M5().queueItem(queueItemId)));
|
|
2025
2331
|
}
|
|
2026
2332
|
let buildNumber = null;
|
|
2027
2333
|
if (queueItemId) {
|
|
2028
|
-
log(
|
|
2334
|
+
log(M5().waitingForBuildNumber);
|
|
2029
2335
|
for (let i = 0; i < 6; i++) {
|
|
2030
2336
|
await new Promise((r) => setTimeout(r, 5e3));
|
|
2031
2337
|
buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
|
|
@@ -2033,46 +2339,61 @@ async function cmdDeploy(argv) {
|
|
|
2033
2339
|
}
|
|
2034
2340
|
}
|
|
2035
2341
|
if (!buildNumber) {
|
|
2036
|
-
log(kleur.yellow(
|
|
2037
|
-
|
|
2342
|
+
log(kleur.yellow(M5().noBuildNumber));
|
|
2343
|
+
throw new Error(M5().noBuildNumber);
|
|
2038
2344
|
}
|
|
2039
|
-
log(
|
|
2345
|
+
log(M5().buildStarted(buildNumber));
|
|
2346
|
+
await updateDeployRun(cfg.webBase, cfg.token, { action: "building", ...runPayload, buildNumber });
|
|
2040
2347
|
const result = await pollBuildComplete(jenkins, jobName, buildNumber);
|
|
2041
2348
|
if (result !== "SUCCESS") {
|
|
2042
|
-
log(kleur.red(
|
|
2043
|
-
log(kleur.dim(
|
|
2044
|
-
log(kleur.dim(
|
|
2045
|
-
|
|
2349
|
+
log(kleur.red(M5().buildFailed(result)));
|
|
2350
|
+
log(kleur.dim(M5().jenkinsLink(`${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`)));
|
|
2351
|
+
log(kleur.dim(M5().alreadyBuiltHint("<N>", args.platform)));
|
|
2352
|
+
await failRun("build_failed");
|
|
2353
|
+
throw new Error(M5().buildFailed(result));
|
|
2046
2354
|
}
|
|
2047
|
-
log(kleur.green(
|
|
2355
|
+
log(kleur.green(M5().buildSucceeded(buildNumber)));
|
|
2048
2356
|
deployBuildNumber = buildNumber;
|
|
2049
2357
|
if (!versionCode) {
|
|
2050
|
-
|
|
2358
|
+
await failRun("metadata_missing");
|
|
2359
|
+
throw new Error(M5().versionCodeUnsuitable("Jenkins", buildNumber) + "\n" + M5().recommendationNext);
|
|
2051
2360
|
}
|
|
2052
2361
|
} else {
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2362
|
+
const build = await runGitProviderBuild(ciProvider, args, appId);
|
|
2363
|
+
if (build.result !== "success") {
|
|
2364
|
+
if (build.result === "failure" || build.result === "cancelled") await failRun("build_failed");
|
|
2365
|
+
throw new Error(M5().buildEnded(build.result));
|
|
2056
2366
|
}
|
|
2057
|
-
const buildId = await runGitProviderBuild(ciProvider, args);
|
|
2058
2367
|
if (!versionCode) {
|
|
2059
|
-
log(kleur.red(
|
|
2060
|
-
log(kleur.dim(
|
|
2061
|
-
log(kleur.dim(
|
|
2062
|
-
log(kleur.dim(
|
|
2063
|
-
|
|
2368
|
+
log(kleur.red(M5().versionCodeUnsuitable(kind, build.runId)));
|
|
2369
|
+
log(kleur.dim(M5().recommendation));
|
|
2370
|
+
log(kleur.dim(M5().recommendationCi));
|
|
2371
|
+
log(kleur.dim(M5().recommendationNext));
|
|
2372
|
+
await failRun("metadata_missing");
|
|
2373
|
+
throw new Error(M5().versionCodeUnknown);
|
|
2064
2374
|
}
|
|
2065
2375
|
}
|
|
2066
2376
|
}
|
|
2067
2377
|
if (!versionCode) {
|
|
2068
|
-
log(kleur.red(
|
|
2069
|
-
|
|
2378
|
+
log(kleur.red(M5().versionCodeUnknown));
|
|
2379
|
+
await failRun("metadata_missing");
|
|
2380
|
+
throw new Error(M5().versionCodeUnknown);
|
|
2381
|
+
}
|
|
2382
|
+
await updateDeployRun(cfg.webBase, cfg.token, {
|
|
2383
|
+
action: "ready",
|
|
2384
|
+
...runPayload,
|
|
2385
|
+
versionCode,
|
|
2386
|
+
...deployBuildNumber ? { buildNumber: deployBuildNumber } : {}
|
|
2387
|
+
});
|
|
2388
|
+
if (args.prepareOnly) {
|
|
2389
|
+
log(kleur.green(M5().prepared(runId)));
|
|
2390
|
+
return;
|
|
2070
2391
|
}
|
|
2071
|
-
const appId = args.appId;
|
|
2072
2392
|
log("");
|
|
2073
|
-
log(
|
|
2393
|
+
log(M5().pipelineStarting);
|
|
2074
2394
|
log("");
|
|
2075
|
-
await streamDeploy(cfg.webBase, cfg.token, {
|
|
2395
|
+
await streamDeploy(cfg.webBase, cfg.token, appId, {
|
|
2396
|
+
jobId: runId,
|
|
2076
2397
|
appId,
|
|
2077
2398
|
platform: args.platform,
|
|
2078
2399
|
versionCode,
|
|
@@ -2081,24 +2402,23 @@ async function cmdDeploy(argv) {
|
|
|
2081
2402
|
toRef: args.toRef,
|
|
2082
2403
|
language: args.language,
|
|
2083
2404
|
dryRun: args.dryRun
|
|
2084
|
-
});
|
|
2405
|
+
}, true);
|
|
2085
2406
|
log("");
|
|
2086
|
-
log(kleur.bold(
|
|
2087
|
-
log(kleur.dim(" https://play.google.com/console/developers"));
|
|
2407
|
+
log(kleur.bold(M5().done));
|
|
2088
2408
|
}
|
|
2089
2409
|
|
|
2090
2410
|
// src/telemetry.ts
|
|
2091
|
-
import
|
|
2411
|
+
import fs10 from "fs";
|
|
2092
2412
|
import os6 from "os";
|
|
2093
|
-
import
|
|
2094
|
-
import { createHmac, randomUUID } from "crypto";
|
|
2413
|
+
import path11 from "path";
|
|
2414
|
+
import { createHmac, randomUUID as randomUUID2 } from "crypto";
|
|
2095
2415
|
|
|
2096
2416
|
// package.json
|
|
2097
|
-
var version = "0.19.
|
|
2417
|
+
var version = "0.19.18";
|
|
2098
2418
|
|
|
2099
2419
|
// src/telemetry.ts
|
|
2100
2420
|
var ENDPOINT = "https://mimi-seed.pryzm.gg/api/sdk-usage";
|
|
2101
|
-
var
|
|
2421
|
+
var M6 = catalog(
|
|
2102
2422
|
{
|
|
2103
2423
|
notice: "\uC120\uD0DD\uC801 \uC0AC\uC6A9\uB7C9 \uCE21\uC815\uC740 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4. \uC548\uB0B4: mimi-seed telemetry --help\n",
|
|
2104
2424
|
enabled: "\uC0AC\uC6A9\uB7C9 \uCE21\uC815 \uCF1C\uC9D0. \uC784\uC758 \uC124\uCE58 ID\xB7\uD504\uB85C\uC81D\uD2B8 \uD574\uC2DC\xB7\uBC84\uC804\xB7OS\xB7\uACB0\uACFC \uCF54\uB4DC\xB7\uC2E4\uD589\uC2DC\uAC04\uC744 \uC804\uC1A1\uD569\uB2C8\uB2E4. \uB044\uAE30: mimi-seed telemetry off\n",
|
|
@@ -2112,46 +2432,46 @@ var M4 = catalog(
|
|
|
2112
2432
|
invalid: "Usage: mimi-seed telemetry on|off|status\n"
|
|
2113
2433
|
}
|
|
2114
2434
|
);
|
|
2115
|
-
var location = () =>
|
|
2435
|
+
var location = () => path11.join(os6.homedir(), ".mimi-seed", "telemetry.json");
|
|
2116
2436
|
function readConsent() {
|
|
2117
2437
|
try {
|
|
2118
|
-
return JSON.parse(
|
|
2438
|
+
return JSON.parse(fs10.readFileSync(location(), "utf8"));
|
|
2119
2439
|
} catch {
|
|
2120
2440
|
return { enabled: false };
|
|
2121
2441
|
}
|
|
2122
2442
|
}
|
|
2123
2443
|
function writeConsent(value) {
|
|
2124
2444
|
const file = location();
|
|
2125
|
-
|
|
2126
|
-
const temp = `${file}.${
|
|
2127
|
-
|
|
2128
|
-
|
|
2445
|
+
fs10.mkdirSync(path11.dirname(file), { recursive: true, mode: 448 });
|
|
2446
|
+
const temp = `${file}.${randomUUID2()}.tmp`;
|
|
2447
|
+
fs10.writeFileSync(temp, JSON.stringify(value), { mode: 384 });
|
|
2448
|
+
fs10.renameSync(temp, file);
|
|
2129
2449
|
}
|
|
2130
2450
|
function telemetryEnabled() {
|
|
2131
2451
|
if (process.env.MIMI_SEED_TELEMETRY !== void 0) return process.env.MIMI_SEED_TELEMETRY === "1";
|
|
2132
2452
|
return readConsent().enabled === true;
|
|
2133
2453
|
}
|
|
2134
2454
|
function telemetryNotice() {
|
|
2135
|
-
return telemetryEnabled() ? "" :
|
|
2455
|
+
return telemetryEnabled() ? "" : M6().notice;
|
|
2136
2456
|
}
|
|
2137
2457
|
function cmdTelemetry(argv) {
|
|
2138
2458
|
const action = argv[0] ?? "status";
|
|
2139
2459
|
if (argv.length > 1 || !["on", "off", "status"].includes(action)) {
|
|
2140
|
-
process.stderr.write(
|
|
2460
|
+
process.stderr.write(M6().invalid);
|
|
2141
2461
|
process.exitCode = 2;
|
|
2142
2462
|
return;
|
|
2143
2463
|
}
|
|
2144
2464
|
if (action === "on") {
|
|
2145
2465
|
const consent = readConsent();
|
|
2146
|
-
writeConsent({ enabled: true, installationId: consent.installationId ??
|
|
2466
|
+
writeConsent({ enabled: true, installationId: consent.installationId ?? randomUUID2(), salt: consent.salt ?? randomUUID2() });
|
|
2147
2467
|
}
|
|
2148
2468
|
if (action === "off") writeConsent({ enabled: false });
|
|
2149
|
-
process.stdout.write(telemetryEnabled() ?
|
|
2469
|
+
process.stdout.write(telemetryEnabled() ? M6().enabled : M6().disabled);
|
|
2150
2470
|
}
|
|
2151
2471
|
function frameworkAt(root) {
|
|
2152
|
-
if (
|
|
2472
|
+
if (fs10.existsSync(path11.join(root, "ProjectSettings", "ProjectSettings.asset"))) return "unity";
|
|
2153
2473
|
try {
|
|
2154
|
-
const pkg = JSON.parse(
|
|
2474
|
+
const pkg = JSON.parse(fs10.readFileSync(path11.join(root, "package.json"), "utf8"));
|
|
2155
2475
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2156
2476
|
if (deps.expo) return "expo";
|
|
2157
2477
|
if (deps["react-native"]) return "react-native";
|
|
@@ -2164,20 +2484,20 @@ function usageRun(command, projectPath) {
|
|
|
2164
2484
|
};
|
|
2165
2485
|
try {
|
|
2166
2486
|
const consent = readConsent();
|
|
2167
|
-
const installationId = consent.installationId ??
|
|
2168
|
-
const salt = consent.salt ??
|
|
2487
|
+
const installationId = consent.installationId ?? randomUUID2();
|
|
2488
|
+
const salt = consent.salt ?? randomUUID2();
|
|
2169
2489
|
if (!consent.installationId || !consent.salt) writeConsent({ enabled: consent.enabled, installationId, salt });
|
|
2170
|
-
let normalized =
|
|
2490
|
+
let normalized = path11.resolve(projectPath);
|
|
2171
2491
|
try {
|
|
2172
|
-
normalized =
|
|
2492
|
+
normalized = fs10.realpathSync(normalized);
|
|
2173
2493
|
} catch {
|
|
2174
2494
|
}
|
|
2175
2495
|
if (process.platform === "win32") normalized = normalized.toLowerCase();
|
|
2176
|
-
const
|
|
2496
|
+
const base2 = {
|
|
2177
2497
|
schema: 1,
|
|
2178
2498
|
installationId,
|
|
2179
2499
|
projectId: createHmac("sha256", salt).update(normalized).digest("hex"),
|
|
2180
|
-
runId:
|
|
2500
|
+
runId: randomUUID2(),
|
|
2181
2501
|
version,
|
|
2182
2502
|
os: process.platform,
|
|
2183
2503
|
ci: Boolean(process.env.CI),
|
|
@@ -2188,7 +2508,7 @@ function usageRun(command, projectPath) {
|
|
|
2188
2508
|
return async (outcome, report) => {
|
|
2189
2509
|
if (!telemetryEnabled()) return;
|
|
2190
2510
|
const payload = {
|
|
2191
|
-
...
|
|
2511
|
+
...base2,
|
|
2192
2512
|
outcome,
|
|
2193
2513
|
durationMs: Math.min(864e5, Math.max(0, Date.now() - started)),
|
|
2194
2514
|
platforms: report?.platforms ?? [],
|
|
@@ -2475,6 +2795,7 @@ export {
|
|
|
2475
2795
|
usageRun,
|
|
2476
2796
|
MCP_PKG,
|
|
2477
2797
|
runMcpBin,
|
|
2798
|
+
linkProject,
|
|
2478
2799
|
cmdDeploy,
|
|
2479
2800
|
parseSetupArgs,
|
|
2480
2801
|
resolveMode,
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
hasAnyProjectSignal,
|
|
18
18
|
isLang,
|
|
19
19
|
isSatisfied,
|
|
20
|
+
linkProject,
|
|
20
21
|
manifestServiceEntries,
|
|
21
22
|
migrateLegacyJenkins,
|
|
22
23
|
resolveLang,
|
|
@@ -27,7 +28,7 @@ import {
|
|
|
27
28
|
usageRun,
|
|
28
29
|
writeConfig,
|
|
29
30
|
writeSettings
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-L3OYPC6L.js";
|
|
31
32
|
|
|
32
33
|
// src/index.ts
|
|
33
34
|
import os3 from "os";
|
|
@@ -2073,9 +2074,9 @@ ${kleur5.bold("\uBE4C\uB4DC / \uB9C8\uCF00\uD305:")}
|
|
|
2073
2074
|
${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
|
|
2074
2075
|
${kleur5.cyan("mimi-seed auth facebook")} Facebook \uD398\uC774\uC9C0
|
|
2075
2076
|
${kleur5.cyan("mimi-seed auth instagram")} Instagram
|
|
2076
|
-
${kleur5.cyan("mimi-seed auth threads")} Threads
|
|
2077
|
+
${kleur5.cyan("mimi-seed auth threads")} Threads \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778
|
|
2077
2078
|
${kleur5.cyan("mimi-seed auth tiktok")} TikTok Business Organic API
|
|
2078
|
-
${kleur5.dim(" Instagram/Threads: --profile <id>\uB85C \uD2B9\uC815 \uC18C\uC15C \uD504\uB85C\uD544 \uC800\uC7A5/\uAC31\uC2E0")}
|
|
2079
|
+
${kleur5.dim(" Instagram/Threads: --profile <id>\uB85C \uD2B9\uC815 \uC18C\uC15C \uD504\uB85C\uD544 \uC800\uC7A5/\uAC31\uC2E0. Instagram/Threads \uC218\uB3D9 \uD1A0\uD070 \uC785\uB825\uC740 --manual-token")}
|
|
2079
2080
|
|
|
2080
2081
|
${kleur5.bold("\uC804\uCCB4 \uC0C1\uD0DC:")}
|
|
2081
2082
|
${kleur5.cyan("mimi-seed auth status --all")} \uBAA8\uB4E0 \uC790\uACA9\uC99D\uBA85 \uBCF4\uC720 \uC5EC\uBD80 \uD55C\uB208\uC5D0
|
|
@@ -2113,9 +2114,9 @@ ${kleur5.bold("Build / marketing:")}
|
|
|
2113
2114
|
${kleur5.cyan("mimi-seed auth googleads")} Google Ads (google-ads.json)
|
|
2114
2115
|
${kleur5.cyan("mimi-seed auth facebook")} Facebook Page
|
|
2115
2116
|
${kleur5.cyan("mimi-seed auth instagram")} Instagram
|
|
2116
|
-
${kleur5.cyan("mimi-seed auth threads")} Threads
|
|
2117
|
+
${kleur5.cyan("mimi-seed auth threads")} Threads (browser login)
|
|
2117
2118
|
${kleur5.cyan("mimi-seed auth tiktok")} TikTok Business Organic API
|
|
2118
|
-
${kleur5.dim(" Instagram/Threads: use --profile <id> to save or refresh a named social profile")}
|
|
2119
|
+
${kleur5.dim(" Instagram/Threads: use --profile <id> to save or refresh a named social profile. Instagram/Threads manual tokens: --manual-token")}
|
|
2119
2120
|
|
|
2120
2121
|
${kleur5.bold("Everything at a glance:")}
|
|
2121
2122
|
${kleur5.cyan("mimi-seed auth status --all")} which credentials you have
|
|
@@ -2169,7 +2170,7 @@ async function cmdAuth(args) {
|
|
|
2169
2170
|
if (sub === "threads") return void exitWith(await runMcpBin("mimi-seed-social-auth", ["threads", ...rest]));
|
|
2170
2171
|
if (sub === "tiktok") return void exitWith(await runMcpBin("mimi-seed-tiktok-business-auth", rest));
|
|
2171
2172
|
if (sub === "ci") {
|
|
2172
|
-
const { cmdSetup: cmdSetup2 } = await import("./setup-
|
|
2173
|
+
const { cmdSetup: cmdSetup2 } = await import("./setup-ICXOGALT.js");
|
|
2173
2174
|
await cmdSetup2(["--only", "github,gitlab", "--reconnect", "github,gitlab"]);
|
|
2174
2175
|
return;
|
|
2175
2176
|
}
|
|
@@ -2744,6 +2745,8 @@ ${kleur9.dim("setup \uB9C8\uBC95\uC0AC\uAC00 \uCCAB \uC2E4\uD589 \uB54C \uBB3C\u
|
|
|
2744
2745
|
--dry-run \uB85C\uCEEC \uACC4\uD68D\uB9CC \uCD9C\uB825 (\uB124\uD2B8\uC6CC\uD06C\xB7\uBE4C\uB4DC\xB7\uC4F0\uAE30 \uC5C6\uC74C)
|
|
2745
2746
|
--yes, -y CI \uC2E4\uD589\xB7\uC2A4\uD1A0\uC5B4 \uBC30\uD3EC \uBA85\uC2DC\uC801 \uC2B9\uC778 (\uC2E4\uD589 \uD544\uC218)
|
|
2746
2747
|
--skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
|
|
2748
|
+
--prepare-only \uBE4C\uB4DC\xB7\uBC84\uC804 \uD655\uC778 \uD6C4 \uC6F9 \uAE30\uB85D\uC744 \uC900\uBE44 \uC0C1\uD0DC\uB85C \uB450\uACE0 \uC81C\uCD9C \uC911\uB2E8
|
|
2749
|
+
--resume <runId> \uC900\uBE44\uB41C \uBC30\uD3EC\uB97C CI \uBE4C\uB4DC \uC5C6\uC774 \uC774\uC5B4\uC11C \uC81C\uCD9C
|
|
2747
2750
|
--ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
|
|
2748
2751
|
--workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
|
|
2749
2752
|
--ref <branch|tag> CI \uC18C\uC2A4 (\uAE30\uBCF8: main, Jenkins\uB294 \uBE0C\uB79C\uCE58\uB9CC)
|
|
@@ -2915,6 +2918,8 @@ Options:
|
|
|
2915
2918
|
--dry-run print a local plan (no network, builds or writes)
|
|
2916
2919
|
--yes, -y explicitly approve CI and store release (required)
|
|
2917
2920
|
--skip-build skip the CI build (--version-code required)
|
|
2921
|
+
--prepare-only stop after build/version verification with a ready web record
|
|
2922
|
+
--resume <runId> submit a ready deployment without rebuilding
|
|
2918
2923
|
--ci jenkins|github|gitlab force the CI provider (default: auto)
|
|
2919
2924
|
--workflow <file> GitHub workflow file (e.g. deploy.yml)
|
|
2920
2925
|
--ref <branch|tag> CI source (default: main; Jenkins: branches only)
|
|
@@ -2993,6 +2998,13 @@ async function cmdInit(args) {
|
|
|
2993
2998
|
log4(kleur9.red(M9().syncFailed(result.text)));
|
|
2994
2999
|
} else {
|
|
2995
3000
|
for (const line of result.text.split("\n")) log4(" " + line);
|
|
3001
|
+
if (hints.length === 1 && (hints[0].packageName || hints[0].bundleId)) {
|
|
3002
|
+
try {
|
|
3003
|
+
await linkProject(cwd, cfg2.webBase, cfg2.token, hints[0]);
|
|
3004
|
+
} catch (error) {
|
|
3005
|
+
log4(kleur9.yellow(String(error)));
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
2996
3008
|
}
|
|
2997
3009
|
}
|
|
2998
3010
|
const manifest2 = await ensureReleaseManifest(cwd);
|
|
@@ -3034,6 +3046,13 @@ async function cmdInit(args) {
|
|
|
3034
3046
|
log4(kleur9.red(M9().syncFailed(result.text)));
|
|
3035
3047
|
} else {
|
|
3036
3048
|
for (const line of result.text.split("\n")) log4(" " + line);
|
|
3049
|
+
if (hints.length === 1 && (hints[0].packageName || hints[0].bundleId)) {
|
|
3050
|
+
try {
|
|
3051
|
+
await linkProject(cwd, cfg.webBase, cfg.token, hints[0]);
|
|
3052
|
+
} catch (error) {
|
|
3053
|
+
log4(kleur9.yellow(String(error)));
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3037
3056
|
}
|
|
3038
3057
|
log4("");
|
|
3039
3058
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mimi-seed",
|
|
3
3
|
"keywords": ["release-doctor", "expo", "react-native", "unity", "google-play", "app-store", "release-readiness", "mcp", "cli"],
|
|
4
|
-
"version": "0.19.
|
|
4
|
+
"version": "0.19.18",
|
|
5
5
|
"description": "Release Doctor: check Expo, React Native and Unity release risks before connecting app stores.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"mimi-seed": "dist/index.js"
|