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.
- package/dist/index.js +327 -68
- 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
|
|
8
|
-
import
|
|
9
|
-
import
|
|
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 = {
|
|
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
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
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
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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
|
|
1102
|
-
import
|
|
1103
|
-
import
|
|
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 =
|
|
1363
|
+
const p = path4.join(os3.homedir(), ".claude.json");
|
|
1110
1364
|
try {
|
|
1111
|
-
return JSON.parse(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
1275
|
-
const agentMdPath =
|
|
1276
|
-
if (!
|
|
1277
|
-
|
|
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
|
-
|
|
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
|
|
1391
|
-
--app <id>
|
|
1392
|
-
--version-code <n>
|
|
1393
|
-
--from <ref>
|
|
1394
|
-
--to <ref>
|
|
1395
|
-
--language <\uCF54\uB4DC>
|
|
1396
|
-
--dry-run
|
|
1397
|
-
--skip-build
|
|
1398
|
-
|
|
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)
|