mimi-seed 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +327 -68
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  } from "./chunk-Q5YGYAFK.js";
5
5
 
6
6
  // src/index.ts
7
- import os3 from "os";
8
- import fs4 from "fs";
9
- import path4 from "path";
7
+ import os4 from "os";
8
+ import fs5 from "fs";
9
+ import path5 from "path";
10
10
  import kleur8 from "kleur";
11
11
  import open from "open";
12
12
 
@@ -853,6 +853,145 @@ async function cmdAuth(args) {
853
853
  // src/deploy.ts
854
854
  import kleur6 from "kleur";
855
855
  import * as readline from "readline";
856
+
857
+ // src/ci-providers.ts
858
+ import fs3 from "fs";
859
+ import path3 from "path";
860
+ import os2 from "os";
861
+ var CI_CONFIG_PATH = path3.join(os2.homedir(), ".mimi-seed", "ci.json");
862
+ function loadCiProviderConfig() {
863
+ try {
864
+ return JSON.parse(fs3.readFileSync(CI_CONFIG_PATH, "utf-8"));
865
+ } catch {
866
+ return null;
867
+ }
868
+ }
869
+ function saveCiProviderConfig(cfg) {
870
+ const dir = path3.dirname(CI_CONFIG_PATH);
871
+ if (!fs3.existsSync(dir)) {
872
+ fs3.mkdirSync(dir, { recursive: true, mode: 448 });
873
+ }
874
+ fs3.writeFileSync(CI_CONFIG_PATH, JSON.stringify(cfg, null, 2));
875
+ if (process.platform !== "win32") {
876
+ fs3.chmodSync(CI_CONFIG_PATH, 384);
877
+ }
878
+ }
879
+ function ghBase(cfg) {
880
+ if (cfg.host) return `${cfg.host.replace(/\/$/, "")}/api/v3`;
881
+ return "https://api.github.com";
882
+ }
883
+ function ghHeaders(token) {
884
+ return {
885
+ Authorization: `Bearer ${token}`,
886
+ Accept: "application/vnd.github+json",
887
+ "X-GitHub-Api-Version": "2022-11-28",
888
+ "Content-Type": "application/json"
889
+ };
890
+ }
891
+ async function ghTriggerWorkflow(cfg, workflow, ref, inputs = {}) {
892
+ const startTime = /* @__PURE__ */ new Date();
893
+ const wfId = /^\d+$/.test(workflow) ? Number(workflow) : workflow;
894
+ const dispatchRes = await fetch(
895
+ `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/workflows/${wfId}/dispatches`,
896
+ {
897
+ method: "POST",
898
+ headers: ghHeaders(cfg.token),
899
+ body: JSON.stringify({ ref, inputs })
900
+ }
901
+ );
902
+ if (!dispatchRes.ok) {
903
+ throw new Error(`GitHub dispatch ${dispatchRes.status}: ${await dispatchRes.text()}`);
904
+ }
905
+ await new Promise((r) => setTimeout(r, 3e3));
906
+ const runsRes = await fetch(
907
+ `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/workflows/${wfId}/runs?per_page=5`,
908
+ { headers: ghHeaders(cfg.token) }
909
+ );
910
+ if (!runsRes.ok) return null;
911
+ const data = await runsRes.json();
912
+ const run = data.workflow_runs.find((r) => new Date(r.created_at) >= startTime) ?? data.workflow_runs[0];
913
+ if (!run) return null;
914
+ return { runId: run.id, url: run.html_url };
915
+ }
916
+ async function ghPollRun(cfg, runId, onTick, timeoutMs = 30 * 60 * 1e3) {
917
+ const start = Date.now();
918
+ let consecutiveErrors = 0;
919
+ while (Date.now() - start < timeoutMs) {
920
+ await new Promise((r) => setTimeout(r, 15e3));
921
+ try {
922
+ const res = await fetch(
923
+ `${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/runs/${runId}`,
924
+ { headers: ghHeaders(cfg.token) }
925
+ );
926
+ if (!res.ok) {
927
+ consecutiveErrors++;
928
+ if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958");
929
+ continue;
930
+ }
931
+ consecutiveErrors = 0;
932
+ const data = await res.json();
933
+ onTick?.(data.status);
934
+ if (data.status === "completed") {
935
+ if (data.conclusion === "success") return "success";
936
+ if (data.conclusion === "cancelled") return "cancelled";
937
+ return "failure";
938
+ }
939
+ } catch {
940
+ consecutiveErrors++;
941
+ if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
942
+ }
943
+ }
944
+ return "timeout";
945
+ }
946
+ function glBase(cfg) {
947
+ return `${cfg.host ?? "https://gitlab.com"}/api/v4`;
948
+ }
949
+ function glProjectId(cfg) {
950
+ return encodeURIComponent(`${cfg.owner}/${cfg.repo}`);
951
+ }
952
+ async function glTriggerPipeline(cfg, ref, variables = {}) {
953
+ const vars = Object.entries(variables).map(([key, value]) => ({ key, value }));
954
+ const body = { ref };
955
+ if (vars.length > 0) body.variables = vars;
956
+ const res = await fetch(`${glBase(cfg)}/projects/${glProjectId(cfg)}/pipeline`, {
957
+ method: "POST",
958
+ headers: { "PRIVATE-TOKEN": cfg.token, "Content-Type": "application/json" },
959
+ body: JSON.stringify(body)
960
+ });
961
+ if (!res.ok) throw new Error(`GitLab trigger ${res.status}: ${await res.text()}`);
962
+ const data = await res.json();
963
+ return { pipelineId: data.id, url: data.web_url };
964
+ }
965
+ async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3) {
966
+ const start = Date.now();
967
+ let consecutiveErrors = 0;
968
+ while (Date.now() - start < timeoutMs) {
969
+ await new Promise((r) => setTimeout(r, 15e3));
970
+ try {
971
+ const res = await fetch(
972
+ `${glBase(cfg)}/projects/${glProjectId(cfg)}/pipelines/${pipelineId}`,
973
+ { headers: { "PRIVATE-TOKEN": cfg.token } }
974
+ );
975
+ if (!res.ok) {
976
+ consecutiveErrors++;
977
+ if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958");
978
+ continue;
979
+ }
980
+ consecutiveErrors = 0;
981
+ const data = await res.json();
982
+ onTick?.(data.status);
983
+ if (data.status === "success") return "success";
984
+ if (data.status === "failed") return "failure";
985
+ if (data.status === "canceled" || data.status === "skipped") return "cancelled";
986
+ } catch {
987
+ consecutiveErrors++;
988
+ if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
989
+ }
990
+ }
991
+ return "timeout";
992
+ }
993
+
994
+ // src/deploy.ts
856
995
  var PHASE_ICON = {
857
996
  init: "\u{1F680}",
858
997
  verify: "\u{1F50D}",
@@ -963,7 +1102,17 @@ async function streamDeploy(webBase, token, body) {
963
1102
  }
964
1103
  }
965
1104
  function parseArgs4(argv) {
966
- const args = { platform: "android", language: "ko-KR", dryRun: false, skipBuild: false, setupJenkins: false };
1105
+ const args = {
1106
+ platform: "android",
1107
+ language: "ko-KR",
1108
+ dryRun: false,
1109
+ skipBuild: false,
1110
+ setupJenkins: false,
1111
+ setupGithub: false,
1112
+ setupGitlab: false,
1113
+ ci: "auto",
1114
+ ref: "main"
1115
+ };
967
1116
  for (let i = 0; i < argv.length; i++) {
968
1117
  if ((argv[i] === "--platform" || argv[i] === "-p") && argv[i + 1]) args.platform = argv[++i];
969
1118
  if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
@@ -973,7 +1122,12 @@ function parseArgs4(argv) {
973
1122
  if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
974
1123
  if (argv[i] === "--dry-run") args.dryRun = true;
975
1124
  if (argv[i] === "--skip-build") args.skipBuild = true;
1125
+ if (argv[i] === "--ci" && argv[i + 1]) args.ci = argv[++i];
1126
+ if (argv[i] === "--workflow" && argv[i + 1]) args.workflow = argv[++i];
1127
+ if (argv[i] === "--ref" && argv[i + 1]) args.ref = argv[++i];
976
1128
  if (argv[i] === "setup-jenkins") args.setupJenkins = true;
1129
+ if (argv[i] === "setup-github") args.setupGithub = true;
1130
+ if (argv[i] === "setup-gitlab") args.setupGitlab = true;
977
1131
  }
978
1132
  return args;
979
1133
  }
@@ -989,6 +1143,78 @@ async function promptJenkinsSetup() {
989
1143
  rl.close();
990
1144
  return { url, user, token, jobAndroid: jobAndroid || void 0, jobIos: jobIos || void 0 };
991
1145
  }
1146
+ async function promptGitProviderSetup(provider) {
1147
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1148
+ const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
1149
+ const isGh = provider === "github";
1150
+ log2(kleur6.bold(isGh ? "GitHub Actions \uC124\uC815" : "GitLab CI \uC124\uC815"));
1151
+ const tokenLabel = isGh ? " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): " : " GitLab Personal Access Token: ";
1152
+ const token = await ask(tokenLabel);
1153
+ const owner = await ask(isGh ? " Owner (org/user): " : " Namespace/group: ");
1154
+ const repo = await ask(" Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ");
1155
+ const hostPrompt = isGh ? " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): " : " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ";
1156
+ const host = await ask(hostPrompt);
1157
+ rl.close();
1158
+ return {
1159
+ provider,
1160
+ token,
1161
+ owner,
1162
+ repo,
1163
+ host: host || void 0
1164
+ };
1165
+ }
1166
+ function resolveCi(ciOption, jenkins, ciProvider) {
1167
+ if (ciOption !== "auto") return ciOption;
1168
+ if (jenkins?.url && jenkins.token) return "jenkins";
1169
+ if (ciProvider) return ciProvider.provider;
1170
+ throw new Error(
1171
+ "CI \uC124\uC815 \uC5C6\uC74C. \uB2E4\uC74C \uC911 \uD558\uB098 \uC2E4\uD589:\n \u2022 mimi-seed deploy setup-jenkins\n \u2022 mimi-seed deploy setup-github\n \u2022 mimi-seed deploy setup-gitlab"
1172
+ );
1173
+ }
1174
+ async function runGitProviderBuild(cfg, args) {
1175
+ let runUrl = "";
1176
+ let runId;
1177
+ if (cfg.provider === "github") {
1178
+ if (!args.workflow) {
1179
+ throw new Error("--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)");
1180
+ }
1181
+ log2(`\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${kleur6.cyan(args.workflow)} @ ${args.ref}`);
1182
+ const inputs = {};
1183
+ if (args.appId) inputs.MIMI_APP_ID = args.appId;
1184
+ inputs.PLATFORM = args.platform;
1185
+ const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
1186
+ if (!result2) {
1187
+ throw new Error("GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.");
1188
+ }
1189
+ runId = result2.runId;
1190
+ runUrl = result2.url;
1191
+ log2(kleur6.dim(` Run ID: ${runId} \u2192 ${runUrl}`));
1192
+ } else {
1193
+ log2(`\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${args.ref}`);
1194
+ const variables = { PLATFORM: args.platform };
1195
+ if (args.appId) variables.MIMI_APP_ID = args.appId;
1196
+ const result2 = await glTriggerPipeline(cfg, args.ref, variables);
1197
+ runId = result2.pipelineId;
1198
+ runUrl = result2.url;
1199
+ log2(kleur6.dim(` Pipeline ID: ${runId} \u2192 ${runUrl}`));
1200
+ }
1201
+ log2(" \uC644\uB8CC \uB300\uAE30 \uC911...");
1202
+ let dots = 0;
1203
+ const onTick = (status) => {
1204
+ dots = (dots + 1) % 4;
1205
+ process.stdout.write(`\r \u23F3 ${status}${".".repeat(dots + 1)} `);
1206
+ };
1207
+ const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
1208
+ process.stdout.write("\n");
1209
+ if (result === "success") {
1210
+ log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${runId} \uC131\uACF5`));
1211
+ return runId;
1212
+ }
1213
+ log2(kleur6.red(`\uBE4C\uB4DC \uC885\uB8CC: ${result}`));
1214
+ log2(kleur6.dim(` ${runUrl}`));
1215
+ log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code <N> --platform ${args.platform}`));
1216
+ process.exit(1);
1217
+ }
992
1218
  async function cmdDeploy(argv) {
993
1219
  const args = parseArgs4(argv);
994
1220
  const cfg = await getEffectiveConfig();
@@ -1002,56 +1228,84 @@ async function cmdDeploy(argv) {
1002
1228
  log2(kleur6.green("\u2705 Jenkins \uC124\uC815 \uC800\uC7A5\uB428"));
1003
1229
  return;
1004
1230
  }
1231
+ if (args.setupGithub) {
1232
+ const ciCfg = await promptGitProviderSetup("github");
1233
+ saveCiProviderConfig(ciCfg);
1234
+ log2(kleur6.green(`\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1235
+ return;
1236
+ }
1237
+ if (args.setupGitlab) {
1238
+ const ciCfg = await promptGitProviderSetup("gitlab");
1239
+ saveCiProviderConfig(ciCfg);
1240
+ log2(kleur6.green(`\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
1241
+ return;
1242
+ }
1005
1243
  log2(kleur6.bold(`mimi-seed deploy \u2014 ${args.platform}`));
1006
1244
  if (args.dryRun) log2(kleur6.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
1007
1245
  log2("");
1008
1246
  let versionCode = args.versionCode;
1009
1247
  if (!args.skipBuild) {
1010
- if (!cfg.jenkins?.url || !cfg.jenkins?.token) {
1011
- log2(kleur6.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
1012
- log2(kleur6.dim(" \uB610\uB294 \uC11C\uBC84 /workspace/integrations\uC5D0\uC11C jenkins \uD504\uB85C\uBC14\uC774\uB354 \uB4F1\uB85D \uD6C4 \uC11C\uBC84\uC0AC\uC774\uB4DC \uD2B8\uB9AC\uAC70 \uAC00\uB2A5."));
1013
- process.exit(1);
1014
- }
1015
- const jenkins = cfg.jenkins;
1016
- const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1017
- if (!jobName) {
1018
- log2(kleur6.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1019
- process.exit(1);
1020
- }
1021
- log2(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur6.cyan(jobName)}`);
1022
- const buildParams = {};
1023
- if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1024
- const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1025
- if (!queueItemId) {
1026
- log2(kleur6.yellow(" \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."));
1248
+ const ciProvider = loadCiProviderConfig();
1249
+ const kind = resolveCi(args.ci, cfg.jenkins, ciProvider);
1250
+ log2(kleur6.dim(` CI: ${kind}`));
1251
+ if (kind === "jenkins") {
1252
+ if (!cfg.jenkins?.url || !cfg.jenkins?.token) {
1253
+ log2(kleur6.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
1254
+ process.exit(1);
1255
+ }
1256
+ const jenkins = cfg.jenkins;
1257
+ const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
1258
+ if (!jobName) {
1259
+ log2(kleur6.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
1260
+ process.exit(1);
1261
+ }
1262
+ log2(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur6.cyan(jobName)}`);
1263
+ const buildParams = {};
1264
+ if (args.appId) buildParams.MIMI_APP_ID = args.appId;
1265
+ const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
1266
+ if (!queueItemId) {
1267
+ log2(kleur6.yellow(" \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."));
1268
+ } else {
1269
+ log2(kleur6.dim(` Queue item: ${queueItemId}`));
1270
+ }
1271
+ let buildNumber = null;
1272
+ if (queueItemId) {
1273
+ log2(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1274
+ for (let i = 0; i < 6; i++) {
1275
+ await new Promise((r) => setTimeout(r, 5e3));
1276
+ buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
1277
+ if (buildNumber) break;
1278
+ }
1279
+ }
1280
+ if (!buildNumber) {
1281
+ log2(kleur6.yellow(" \uBE4C\uB4DC \uBC88\uD638\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. --skip-build + --version-code \uB85C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5."));
1282
+ process.exit(1);
1283
+ }
1284
+ log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1285
+ const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1286
+ if (result !== "SUCCESS") {
1287
+ log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
1288
+ log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
1289
+ log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
1290
+ process.exit(1);
1291
+ }
1292
+ log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1293
+ if (!versionCode) {
1294
+ versionCode = buildNumber;
1295
+ log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
1296
+ }
1027
1297
  } else {
1028
- log2(kleur6.dim(` Queue item: ${queueItemId}`));
1029
- }
1030
- let buildNumber = null;
1031
- if (queueItemId) {
1032
- log2(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
1033
- for (let i = 0; i < 6; i++) {
1034
- await new Promise((r) => setTimeout(r, 5e3));
1035
- buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
1036
- if (buildNumber) break;
1298
+ if (!ciProvider) {
1299
+ log2(kleur6.red(`${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`));
1300
+ process.exit(1);
1301
+ }
1302
+ const buildId = await runGitProviderBuild(ciProvider, args);
1303
+ if (!versionCode) {
1304
+ versionCode = buildId;
1305
+ log2(kleur6.dim(` versionCode = build id (${versionCode})`));
1306
+ log2(kleur6.dim(` \uC8FC\uC758: GitHub run ID / GitLab pipeline ID\uB294 versionCode\uB85C \uC801\uD569\uD558\uC9C0 \uC54A\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`));
1307
+ log2(kleur6.dim(` --version-code <N> \uB85C \uBA85\uC2DC \uAD8C\uC7A5.`));
1037
1308
  }
1038
- }
1039
- if (!buildNumber) {
1040
- log2(kleur6.yellow(" \uBE4C\uB4DC \uBC88\uD638\uB97C \uAC00\uC838\uC624\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. --skip-build + --version-code \uB85C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5."));
1041
- process.exit(1);
1042
- }
1043
- log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
1044
- const result = await pollBuildComplete(jenkins, jobName, buildNumber);
1045
- if (result !== "SUCCESS") {
1046
- log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
1047
- log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
1048
- log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
1049
- process.exit(1);
1050
- }
1051
- log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
1052
- if (!versionCode) {
1053
- versionCode = buildNumber;
1054
- log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
1055
1309
  }
1056
1310
  }
1057
1311
  if (!versionCode) {
@@ -1098,17 +1352,17 @@ async function cmdDeploy(argv) {
1098
1352
 
1099
1353
  // src/mcp-restart.ts
1100
1354
  import { execSync as execSync2 } from "child_process";
1101
- import fs3 from "fs";
1102
- import os2 from "os";
1103
- import path3 from "path";
1355
+ import fs4 from "fs";
1356
+ import os3 from "os";
1357
+ import path4 from "path";
1104
1358
  import kleur7 from "kleur";
1105
1359
  function log3(msg) {
1106
1360
  process.stdout.write(msg + "\n");
1107
1361
  }
1108
1362
  function readClaudeJson() {
1109
- const p = path3.join(os2.homedir(), ".claude.json");
1363
+ const p = path4.join(os3.homedir(), ".claude.json");
1110
1364
  try {
1111
- return JSON.parse(fs3.readFileSync(p, "utf8"));
1365
+ return JSON.parse(fs4.readFileSync(p, "utf8"));
1112
1366
  } catch {
1113
1367
  return {};
1114
1368
  }
@@ -1124,7 +1378,7 @@ function findProcessMarker(cfg) {
1124
1378
  return meaningful.at(-1) ?? null;
1125
1379
  }
1126
1380
  function killByMarker(marker) {
1127
- const isWin = os2.platform() === "win32";
1381
+ const isWin = os3.platform() === "win32";
1128
1382
  if (isWin) {
1129
1383
  const escaped = marker.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
1130
1384
  let pids = [];
@@ -1235,7 +1489,7 @@ async function cmdInit() {
1235
1489
  return;
1236
1490
  }
1237
1491
  log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
1238
- const hostName = os3.hostname().slice(0, 32);
1492
+ const hostName = os4.hostname().slice(0, 32);
1239
1493
  const name = `cli-${hostName}`;
1240
1494
  const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
1241
1495
  const callback = `http://127.0.0.1:${port}/cb`;
@@ -1271,10 +1525,10 @@ async function cmdInit() {
1271
1525
  }
1272
1526
  log4("");
1273
1527
  }
1274
- const claudeDir = path4.join(cwd, ".claude");
1275
- const agentMdPath = path4.join(claudeDir, "mimi-seed.md");
1276
- if (!fs4.existsSync(agentMdPath)) {
1277
- fs4.mkdirSync(claudeDir, { recursive: true });
1528
+ const claudeDir = path5.join(cwd, ".claude");
1529
+ const agentMdPath = path5.join(claudeDir, "mimi-seed.md");
1530
+ if (!fs5.existsSync(agentMdPath)) {
1531
+ fs5.mkdirSync(claudeDir, { recursive: true });
1278
1532
  const appLines = hints.flatMap((h) => {
1279
1533
  const parts = [
1280
1534
  h.name && ` name: ${h.name}`,
@@ -1305,7 +1559,7 @@ async function cmdInit() {
1305
1559
  "- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
1306
1560
  "- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
1307
1561
  ].join("\n");
1308
- fs4.writeFileSync(agentMdPath, agentMd, { mode: 420 });
1562
+ fs5.writeFileSync(agentMdPath, agentMd, { mode: 420 });
1309
1563
  log4(kleur8.dim(` \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
1310
1564
  }
1311
1565
  log4(kleur8.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
@@ -1387,15 +1641,20 @@ ${kleur8.bold("mimi-seed review \uC635\uC158:")}
1387
1641
  --no-interactive CI \uBAA8\uB4DC
1388
1642
 
1389
1643
  ${kleur8.bold("mimi-seed deploy \uC635\uC158:")}
1390
- --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1391
- --app <id> \uC571 ID \uC9C0\uC815
1392
- --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
1393
- --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
1394
- --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
1395
- --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
1396
- --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
1397
- --skip-build Jenkins \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
1398
- setup-jenkins Jenkins \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1644
+ --platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
1645
+ --app <id> \uC571 ID \uC9C0\uC815
1646
+ --version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
1647
+ --from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
1648
+ --to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
1649
+ --language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
1650
+ --dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
1651
+ --skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
1652
+ --ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
1653
+ --workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
1654
+ --ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
1655
+ setup-jenkins Jenkins \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1656
+ setup-github GitHub Actions \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1657
+ setup-gitlab GitLab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
1399
1658
 
1400
1659
  ${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
1401
1660
  MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mimi-seed",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Mimi Seed CLI — Claude Code에서 앱 출시 운영을 관리합니다.",
5
5
  "bin": {
6
6
  "mimi-seed": "dist/index.js"