mimi-seed 0.2.5 → 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.
- package/dist/index.js +359 -61
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-Q5YGYAFK.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
|
-
import
|
|
7
|
+
import os4 from "os";
|
|
8
|
+
import fs5 from "fs";
|
|
9
|
+
import path5 from "path";
|
|
8
10
|
import kleur8 from "kleur";
|
|
9
11
|
import open from "open";
|
|
10
12
|
|
|
@@ -851,6 +853,145 @@ async function cmdAuth(args) {
|
|
|
851
853
|
// src/deploy.ts
|
|
852
854
|
import kleur6 from "kleur";
|
|
853
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
|
|
854
995
|
var PHASE_ICON = {
|
|
855
996
|
init: "\u{1F680}",
|
|
856
997
|
verify: "\u{1F50D}",
|
|
@@ -961,7 +1102,17 @@ async function streamDeploy(webBase, token, body) {
|
|
|
961
1102
|
}
|
|
962
1103
|
}
|
|
963
1104
|
function parseArgs4(argv) {
|
|
964
|
-
const args = {
|
|
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
|
+
};
|
|
965
1116
|
for (let i = 0; i < argv.length; i++) {
|
|
966
1117
|
if ((argv[i] === "--platform" || argv[i] === "-p") && argv[i + 1]) args.platform = argv[++i];
|
|
967
1118
|
if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
|
|
@@ -971,7 +1122,12 @@ function parseArgs4(argv) {
|
|
|
971
1122
|
if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
|
|
972
1123
|
if (argv[i] === "--dry-run") args.dryRun = true;
|
|
973
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];
|
|
974
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;
|
|
975
1131
|
}
|
|
976
1132
|
return args;
|
|
977
1133
|
}
|
|
@@ -987,6 +1143,78 @@ async function promptJenkinsSetup() {
|
|
|
987
1143
|
rl.close();
|
|
988
1144
|
return { url, user, token, jobAndroid: jobAndroid || void 0, jobIos: jobIos || void 0 };
|
|
989
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
|
+
}
|
|
990
1218
|
async function cmdDeploy(argv) {
|
|
991
1219
|
const args = parseArgs4(argv);
|
|
992
1220
|
const cfg = await getEffectiveConfig();
|
|
@@ -1000,56 +1228,84 @@ async function cmdDeploy(argv) {
|
|
|
1000
1228
|
log2(kleur6.green("\u2705 Jenkins \uC124\uC815 \uC800\uC7A5\uB428"));
|
|
1001
1229
|
return;
|
|
1002
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
|
+
}
|
|
1003
1243
|
log2(kleur6.bold(`mimi-seed deploy \u2014 ${args.platform}`));
|
|
1004
1244
|
if (args.dryRun) log2(kleur6.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
|
|
1005
1245
|
log2("");
|
|
1006
1246
|
let versionCode = args.versionCode;
|
|
1007
1247
|
if (!args.skipBuild) {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
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
|
+
}
|
|
1025
1297
|
} else {
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
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.`));
|
|
1035
1308
|
}
|
|
1036
|
-
}
|
|
1037
|
-
if (!buildNumber) {
|
|
1038
|
-
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."));
|
|
1039
|
-
process.exit(1);
|
|
1040
|
-
}
|
|
1041
|
-
log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
|
|
1042
|
-
const result = await pollBuildComplete(jenkins, jobName, buildNumber);
|
|
1043
|
-
if (result !== "SUCCESS") {
|
|
1044
|
-
log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
|
|
1045
|
-
log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
|
|
1046
|
-
log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
|
|
1047
|
-
process.exit(1);
|
|
1048
|
-
}
|
|
1049
|
-
log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
|
|
1050
|
-
if (!versionCode) {
|
|
1051
|
-
versionCode = buildNumber;
|
|
1052
|
-
log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
|
|
1053
1309
|
}
|
|
1054
1310
|
}
|
|
1055
1311
|
if (!versionCode) {
|
|
@@ -1096,17 +1352,17 @@ async function cmdDeploy(argv) {
|
|
|
1096
1352
|
|
|
1097
1353
|
// src/mcp-restart.ts
|
|
1098
1354
|
import { execSync as execSync2 } from "child_process";
|
|
1099
|
-
import
|
|
1100
|
-
import
|
|
1101
|
-
import
|
|
1355
|
+
import fs4 from "fs";
|
|
1356
|
+
import os3 from "os";
|
|
1357
|
+
import path4 from "path";
|
|
1102
1358
|
import kleur7 from "kleur";
|
|
1103
1359
|
function log3(msg) {
|
|
1104
1360
|
process.stdout.write(msg + "\n");
|
|
1105
1361
|
}
|
|
1106
1362
|
function readClaudeJson() {
|
|
1107
|
-
const p =
|
|
1363
|
+
const p = path4.join(os3.homedir(), ".claude.json");
|
|
1108
1364
|
try {
|
|
1109
|
-
return JSON.parse(
|
|
1365
|
+
return JSON.parse(fs4.readFileSync(p, "utf8"));
|
|
1110
1366
|
} catch {
|
|
1111
1367
|
return {};
|
|
1112
1368
|
}
|
|
@@ -1122,7 +1378,7 @@ function findProcessMarker(cfg) {
|
|
|
1122
1378
|
return meaningful.at(-1) ?? null;
|
|
1123
1379
|
}
|
|
1124
1380
|
function killByMarker(marker) {
|
|
1125
|
-
const isWin =
|
|
1381
|
+
const isWin = os3.platform() === "win32";
|
|
1126
1382
|
if (isWin) {
|
|
1127
1383
|
const escaped = marker.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1128
1384
|
let pids = [];
|
|
@@ -1233,7 +1489,7 @@ async function cmdInit() {
|
|
|
1233
1489
|
return;
|
|
1234
1490
|
}
|
|
1235
1491
|
log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
|
|
1236
|
-
const hostName =
|
|
1492
|
+
const hostName = os4.hostname().slice(0, 32);
|
|
1237
1493
|
const name = `cli-${hostName}`;
|
|
1238
1494
|
const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
|
|
1239
1495
|
const callback = `http://127.0.0.1:${port}/cb`;
|
|
@@ -1269,6 +1525,43 @@ async function cmdInit() {
|
|
|
1269
1525
|
}
|
|
1270
1526
|
log4("");
|
|
1271
1527
|
}
|
|
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 });
|
|
1532
|
+
const appLines = hints.flatMap((h) => {
|
|
1533
|
+
const parts = [
|
|
1534
|
+
h.name && ` name: ${h.name}`,
|
|
1535
|
+
h.packageName && ` packageName: ${h.packageName}`,
|
|
1536
|
+
h.bundleId && ` bundleId: ${h.bundleId}`
|
|
1537
|
+
].filter(Boolean);
|
|
1538
|
+
return parts;
|
|
1539
|
+
});
|
|
1540
|
+
const agentMd = [
|
|
1541
|
+
"# Mimi Seed Agent",
|
|
1542
|
+
"",
|
|
1543
|
+
"Mimi Seed MCP\uAC00 \uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
1544
|
+
"Google Play \xB7 App Store \xB7 Firebase \xB7 AdMob\uC744 \uB3C4\uAD6C\uB85C \uC9C1\uC811 \uC81C\uC5B4\uD569\uB2C8\uB2E4.",
|
|
1545
|
+
"",
|
|
1546
|
+
"## \uCD9C\uC2DC \uC694\uCCAD \uCC98\uB9AC \uC21C\uC11C",
|
|
1547
|
+
"",
|
|
1548
|
+
"1. \uD56D\uC0C1 `playstore_check_submission_risks` / `appstore_check_submission_risks` \uB85C \uBE14\uB85C\uCEE4 \uBA3C\uC800 \uD655\uC778",
|
|
1549
|
+
"2. \uB9B4\uB9AC\uC988 \uB178\uD2B8: `generate_release_notes_from_commits` \u2192 \uC0AC\uC6A9\uC790 \uD655\uC778 \uD6C4 \uC801\uC6A9",
|
|
1550
|
+
"3. \uC2A4\uD1A0\uC5B4 **\uC4F0\uAE30** \uC791\uC5C5(submit, apply, reply)\uC740 \uBC18\uB4DC\uC2DC \uC0AC\uC6A9\uC790 \uBA85\uC2DC \uB3D9\uC758 \uD6C4 \uC2E4\uD589",
|
|
1551
|
+
"4. \uC644\uB8CC \uD6C4 \uACB0\uACFC \uC694\uC57D \uC81C\uACF5",
|
|
1552
|
+
"",
|
|
1553
|
+
"## \uC571 \uC815\uBCF4",
|
|
1554
|
+
...appLines.length > 0 ? appLines : [" (mimi-seed status \uB85C \uD655\uC778)"],
|
|
1555
|
+
"",
|
|
1556
|
+
"## \uC2AC\uB798\uC2DC \uCEE4\uB9E8\uB4DC",
|
|
1557
|
+
"",
|
|
1558
|
+
"- `/mimi-seed:deploy` \u2014 \uC804\uCCB4 \uCD9C\uC2DC \uD30C\uC774\uD504\uB77C\uC778",
|
|
1559
|
+
"- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
|
|
1560
|
+
"- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
|
|
1561
|
+
].join("\n");
|
|
1562
|
+
fs5.writeFileSync(agentMdPath, agentMd, { mode: 420 });
|
|
1563
|
+
log4(kleur8.dim(` \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
|
|
1564
|
+
}
|
|
1272
1565
|
log4(kleur8.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
|
|
1273
1566
|
log4("");
|
|
1274
1567
|
log4("Claude Code\uC5D0\uC11C \uC774\uB807\uAC8C \uBB3C\uC5B4\uBCF4\uC138\uC694:");
|
|
@@ -1348,15 +1641,20 @@ ${kleur8.bold("mimi-seed review \uC635\uC158:")}
|
|
|
1348
1641
|
--no-interactive CI \uBAA8\uB4DC
|
|
1349
1642
|
|
|
1350
1643
|
${kleur8.bold("mimi-seed deploy \uC635\uC158:")}
|
|
1351
|
-
--platform android|ios
|
|
1352
|
-
--app <id>
|
|
1353
|
-
--version-code <n>
|
|
1354
|
-
--from <ref>
|
|
1355
|
-
--to <ref>
|
|
1356
|
-
--language <\uCF54\uB4DC>
|
|
1357
|
-
--dry-run
|
|
1358
|
-
--skip-build
|
|
1359
|
-
|
|
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
|
|
1360
1658
|
|
|
1361
1659
|
${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
|
|
1362
1660
|
MIMI_SEED_TOKEN PAT \uD1A0\uD070 (CI/CD \uBB34\uC778\uC99D \uBAA8\uB4DC)
|