mimi-seed 0.2.6 → 0.2.8
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 +332 -68
- package/package.json +6 -3
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,149 @@ 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, intervalMs = 15e3) {
|
|
917
|
+
const start = Date.now();
|
|
918
|
+
let consecutiveErrors = 0;
|
|
919
|
+
while (Date.now() - start < timeoutMs) {
|
|
920
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
921
|
+
let res;
|
|
922
|
+
try {
|
|
923
|
+
res = await fetch(
|
|
924
|
+
`${ghBase(cfg)}/repos/${cfg.owner}/${cfg.repo}/actions/runs/${runId}`,
|
|
925
|
+
{ headers: ghHeaders(cfg.token) }
|
|
926
|
+
);
|
|
927
|
+
} catch {
|
|
928
|
+
consecutiveErrors++;
|
|
929
|
+
if (consecutiveErrors >= 3) throw new Error("GitHub API \uC5F0\uC18D \uC624\uB958 3\uD68C");
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
if (!res.ok) {
|
|
933
|
+
consecutiveErrors++;
|
|
934
|
+
if (consecutiveErrors >= 3) throw new Error(`GitHub API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
consecutiveErrors = 0;
|
|
938
|
+
const data = await res.json();
|
|
939
|
+
onTick?.(data.status);
|
|
940
|
+
if (data.status === "completed") {
|
|
941
|
+
if (data.conclusion === "success") return "success";
|
|
942
|
+
if (data.conclusion === "cancelled") return "cancelled";
|
|
943
|
+
return "failure";
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return "timeout";
|
|
947
|
+
}
|
|
948
|
+
function glBase(cfg) {
|
|
949
|
+
return `${cfg.host ?? "https://gitlab.com"}/api/v4`;
|
|
950
|
+
}
|
|
951
|
+
function glProjectId(cfg) {
|
|
952
|
+
return encodeURIComponent(`${cfg.owner}/${cfg.repo}`);
|
|
953
|
+
}
|
|
954
|
+
async function glTriggerPipeline(cfg, ref, variables = {}) {
|
|
955
|
+
const vars = Object.entries(variables).map(([key, value]) => ({ key, value }));
|
|
956
|
+
const body = { ref };
|
|
957
|
+
if (vars.length > 0) body.variables = vars;
|
|
958
|
+
const res = await fetch(`${glBase(cfg)}/projects/${glProjectId(cfg)}/pipeline`, {
|
|
959
|
+
method: "POST",
|
|
960
|
+
headers: { "PRIVATE-TOKEN": cfg.token, "Content-Type": "application/json" },
|
|
961
|
+
body: JSON.stringify(body)
|
|
962
|
+
});
|
|
963
|
+
if (!res.ok) throw new Error(`GitLab trigger ${res.status}: ${await res.text()}`);
|
|
964
|
+
const data = await res.json();
|
|
965
|
+
return { pipelineId: data.id, url: data.web_url };
|
|
966
|
+
}
|
|
967
|
+
async function glPollPipeline(cfg, pipelineId, onTick, timeoutMs = 30 * 60 * 1e3, intervalMs = 15e3) {
|
|
968
|
+
const start = Date.now();
|
|
969
|
+
let consecutiveErrors = 0;
|
|
970
|
+
while (Date.now() - start < timeoutMs) {
|
|
971
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
972
|
+
let res;
|
|
973
|
+
try {
|
|
974
|
+
res = await fetch(
|
|
975
|
+
`${glBase(cfg)}/projects/${glProjectId(cfg)}/pipelines/${pipelineId}`,
|
|
976
|
+
{ headers: { "PRIVATE-TOKEN": cfg.token } }
|
|
977
|
+
);
|
|
978
|
+
} catch {
|
|
979
|
+
consecutiveErrors++;
|
|
980
|
+
if (consecutiveErrors >= 3) throw new Error("GitLab API \uC5F0\uC18D \uC624\uB958 3\uD68C");
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if (!res.ok) {
|
|
984
|
+
consecutiveErrors++;
|
|
985
|
+
if (consecutiveErrors >= 3) throw new Error(`GitLab API \uC5F0\uC18D \uC624\uB958 (HTTP ${res.status})`);
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
consecutiveErrors = 0;
|
|
989
|
+
const data = await res.json();
|
|
990
|
+
onTick?.(data.status);
|
|
991
|
+
if (data.status === "success") return "success";
|
|
992
|
+
if (data.status === "failed") return "failure";
|
|
993
|
+
if (data.status === "canceled" || data.status === "skipped") return "cancelled";
|
|
994
|
+
}
|
|
995
|
+
return "timeout";
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// src/deploy.ts
|
|
856
999
|
var PHASE_ICON = {
|
|
857
1000
|
init: "\u{1F680}",
|
|
858
1001
|
verify: "\u{1F50D}",
|
|
@@ -963,7 +1106,17 @@ async function streamDeploy(webBase, token, body) {
|
|
|
963
1106
|
}
|
|
964
1107
|
}
|
|
965
1108
|
function parseArgs4(argv) {
|
|
966
|
-
const args = {
|
|
1109
|
+
const args = {
|
|
1110
|
+
platform: "android",
|
|
1111
|
+
language: "ko-KR",
|
|
1112
|
+
dryRun: false,
|
|
1113
|
+
skipBuild: false,
|
|
1114
|
+
setupJenkins: false,
|
|
1115
|
+
setupGithub: false,
|
|
1116
|
+
setupGitlab: false,
|
|
1117
|
+
ci: "auto",
|
|
1118
|
+
ref: "main"
|
|
1119
|
+
};
|
|
967
1120
|
for (let i = 0; i < argv.length; i++) {
|
|
968
1121
|
if ((argv[i] === "--platform" || argv[i] === "-p") && argv[i + 1]) args.platform = argv[++i];
|
|
969
1122
|
if (argv[i] === "--app" && argv[i + 1]) args.appId = argv[++i];
|
|
@@ -973,7 +1126,12 @@ function parseArgs4(argv) {
|
|
|
973
1126
|
if (argv[i] === "--language" && argv[i + 1]) args.language = argv[++i];
|
|
974
1127
|
if (argv[i] === "--dry-run") args.dryRun = true;
|
|
975
1128
|
if (argv[i] === "--skip-build") args.skipBuild = true;
|
|
1129
|
+
if (argv[i] === "--ci" && argv[i + 1]) args.ci = argv[++i];
|
|
1130
|
+
if (argv[i] === "--workflow" && argv[i + 1]) args.workflow = argv[++i];
|
|
1131
|
+
if (argv[i] === "--ref" && argv[i + 1]) args.ref = argv[++i];
|
|
976
1132
|
if (argv[i] === "setup-jenkins") args.setupJenkins = true;
|
|
1133
|
+
if (argv[i] === "setup-github") args.setupGithub = true;
|
|
1134
|
+
if (argv[i] === "setup-gitlab") args.setupGitlab = true;
|
|
977
1135
|
}
|
|
978
1136
|
return args;
|
|
979
1137
|
}
|
|
@@ -989,6 +1147,78 @@ async function promptJenkinsSetup() {
|
|
|
989
1147
|
rl.close();
|
|
990
1148
|
return { url, user, token, jobAndroid: jobAndroid || void 0, jobIos: jobIos || void 0 };
|
|
991
1149
|
}
|
|
1150
|
+
async function promptGitProviderSetup(provider) {
|
|
1151
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1152
|
+
const ask = (q) => new Promise((resolve) => rl.question(q, (a) => resolve(a.trim())));
|
|
1153
|
+
const isGh = provider === "github";
|
|
1154
|
+
log2(kleur6.bold(isGh ? "GitHub Actions \uC124\uC815" : "GitLab CI \uC124\uC815"));
|
|
1155
|
+
const tokenLabel = isGh ? " GitHub Personal Access Token (repo+workflow \uC2A4\uCF54\uD504): " : " GitLab Personal Access Token: ";
|
|
1156
|
+
const token = await ask(tokenLabel);
|
|
1157
|
+
const owner = await ask(isGh ? " Owner (org/user): " : " Namespace/group: ");
|
|
1158
|
+
const repo = await ask(" Repo \uC774\uB984 (\uACBD\uB85C \uC5C6\uC774): ");
|
|
1159
|
+
const hostPrompt = isGh ? " GitHub Enterprise host (\uC120\uD0DD, \uC5D4\uD130=github.com): " : " GitLab self-hosted URL (\uC120\uD0DD, \uC5D4\uD130=gitlab.com): ";
|
|
1160
|
+
const host = await ask(hostPrompt);
|
|
1161
|
+
rl.close();
|
|
1162
|
+
return {
|
|
1163
|
+
provider,
|
|
1164
|
+
token,
|
|
1165
|
+
owner,
|
|
1166
|
+
repo,
|
|
1167
|
+
host: host || void 0
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
function resolveCi(ciOption, jenkins, ciProvider) {
|
|
1171
|
+
if (ciOption !== "auto") return ciOption;
|
|
1172
|
+
if (jenkins?.url && jenkins.token) return "jenkins";
|
|
1173
|
+
if (ciProvider) return ciProvider.provider;
|
|
1174
|
+
throw new Error(
|
|
1175
|
+
"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"
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
async function runGitProviderBuild(cfg, args) {
|
|
1179
|
+
let runUrl = "";
|
|
1180
|
+
let runId;
|
|
1181
|
+
if (cfg.provider === "github") {
|
|
1182
|
+
if (!args.workflow) {
|
|
1183
|
+
throw new Error("--workflow \uD544\uC694 (\uC608: --workflow deploy.yml)");
|
|
1184
|
+
}
|
|
1185
|
+
log2(`\u{1F528} GitHub Actions \uD2B8\uB9AC\uAC70: ${kleur6.cyan(args.workflow)} @ ${args.ref}`);
|
|
1186
|
+
const inputs = {};
|
|
1187
|
+
if (args.appId) inputs.MIMI_APP_ID = args.appId;
|
|
1188
|
+
inputs.PLATFORM = args.platform;
|
|
1189
|
+
const result2 = await ghTriggerWorkflow(cfg, args.workflow, args.ref, inputs);
|
|
1190
|
+
if (!result2) {
|
|
1191
|
+
throw new Error("GitHub Actions run_id \uC870\uD68C \uC2E4\uD328. \uC7A0\uC2DC \uD6C4 ci_list_recent_builds \uB85C \uD655\uC778\uD558\uC138\uC694.");
|
|
1192
|
+
}
|
|
1193
|
+
runId = result2.runId;
|
|
1194
|
+
runUrl = result2.url;
|
|
1195
|
+
log2(kleur6.dim(` Run ID: ${runId} \u2192 ${runUrl}`));
|
|
1196
|
+
} else {
|
|
1197
|
+
log2(`\u{1F528} GitLab Pipeline \uD2B8\uB9AC\uAC70: ${args.ref}`);
|
|
1198
|
+
const variables = { PLATFORM: args.platform };
|
|
1199
|
+
if (args.appId) variables.MIMI_APP_ID = args.appId;
|
|
1200
|
+
const result2 = await glTriggerPipeline(cfg, args.ref, variables);
|
|
1201
|
+
runId = result2.pipelineId;
|
|
1202
|
+
runUrl = result2.url;
|
|
1203
|
+
log2(kleur6.dim(` Pipeline ID: ${runId} \u2192 ${runUrl}`));
|
|
1204
|
+
}
|
|
1205
|
+
log2(" \uC644\uB8CC \uB300\uAE30 \uC911...");
|
|
1206
|
+
let dots = 0;
|
|
1207
|
+
const onTick = (status) => {
|
|
1208
|
+
dots = (dots + 1) % 4;
|
|
1209
|
+
process.stdout.write(`\r \u23F3 ${status}${".".repeat(dots + 1)} `);
|
|
1210
|
+
};
|
|
1211
|
+
const result = cfg.provider === "github" ? await ghPollRun(cfg, runId, onTick) : await glPollPipeline(cfg, runId, onTick);
|
|
1212
|
+
process.stdout.write("\n");
|
|
1213
|
+
if (result === "success") {
|
|
1214
|
+
log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${runId} \uC131\uACF5`));
|
|
1215
|
+
return runId;
|
|
1216
|
+
}
|
|
1217
|
+
log2(kleur6.red(`\uBE4C\uB4DC \uC885\uB8CC: ${result}`));
|
|
1218
|
+
log2(kleur6.dim(` ${runUrl}`));
|
|
1219
|
+
log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code <N> --platform ${args.platform}`));
|
|
1220
|
+
process.exit(1);
|
|
1221
|
+
}
|
|
992
1222
|
async function cmdDeploy(argv) {
|
|
993
1223
|
const args = parseArgs4(argv);
|
|
994
1224
|
const cfg = await getEffectiveConfig();
|
|
@@ -1002,56 +1232,85 @@ async function cmdDeploy(argv) {
|
|
|
1002
1232
|
log2(kleur6.green("\u2705 Jenkins \uC124\uC815 \uC800\uC7A5\uB428"));
|
|
1003
1233
|
return;
|
|
1004
1234
|
}
|
|
1235
|
+
if (args.setupGithub) {
|
|
1236
|
+
const ciCfg = await promptGitProviderSetup("github");
|
|
1237
|
+
saveCiProviderConfig(ciCfg);
|
|
1238
|
+
log2(kleur6.green(`\u2705 GitHub Actions \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
if (args.setupGitlab) {
|
|
1242
|
+
const ciCfg = await promptGitProviderSetup("gitlab");
|
|
1243
|
+
saveCiProviderConfig(ciCfg);
|
|
1244
|
+
log2(kleur6.green(`\u2705 GitLab CI \uC124\uC815 \uC800\uC7A5\uB428 \u2192 ~/.mimi-seed/ci.json`));
|
|
1245
|
+
return;
|
|
1246
|
+
}
|
|
1005
1247
|
log2(kleur6.bold(`mimi-seed deploy \u2014 ${args.platform}`));
|
|
1006
1248
|
if (args.dryRun) log2(kleur6.yellow(" [dry-run \uBAA8\uB4DC] \uC2E4\uC81C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4"));
|
|
1007
1249
|
log2("");
|
|
1008
1250
|
let versionCode = args.versionCode;
|
|
1009
1251
|
if (!args.skipBuild) {
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1252
|
+
const ciProvider = loadCiProviderConfig();
|
|
1253
|
+
const kind = resolveCi(args.ci, cfg.jenkins, ciProvider);
|
|
1254
|
+
log2(kleur6.dim(` CI: ${kind}`));
|
|
1255
|
+
if (kind === "jenkins") {
|
|
1256
|
+
if (!cfg.jenkins?.url || !cfg.jenkins?.token) {
|
|
1257
|
+
log2(kleur6.yellow("Jenkins \uC124\uC815 \uC5C6\uC74C. `mimi-seed deploy setup-jenkins` \uB85C \uC124\uC815\uD558\uAC70\uB098 --skip-build \uC0AC\uC6A9."));
|
|
1258
|
+
process.exit(1);
|
|
1259
|
+
}
|
|
1260
|
+
const jenkins = cfg.jenkins;
|
|
1261
|
+
const jobName = args.platform === "android" ? jenkins.jobAndroid : jenkins.jobIos;
|
|
1262
|
+
if (!jobName) {
|
|
1263
|
+
log2(kleur6.red(`${args.platform} Jenkins job\uC774 \uC124\uC815\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. setup-jenkins \uC2E4\uD589.`));
|
|
1264
|
+
process.exit(1);
|
|
1265
|
+
}
|
|
1266
|
+
log2(`\u{1F528} Jenkins \uBE4C\uB4DC \uD2B8\uB9AC\uAC70: ${kleur6.cyan(jobName)}`);
|
|
1267
|
+
const buildParams = {};
|
|
1268
|
+
if (args.appId) buildParams.MIMI_APP_ID = args.appId;
|
|
1269
|
+
const queueItemId = await triggerBuild(jenkins, jobName, buildParams);
|
|
1270
|
+
if (!queueItemId) {
|
|
1271
|
+
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."));
|
|
1272
|
+
} else {
|
|
1273
|
+
log2(kleur6.dim(` Queue item: ${queueItemId}`));
|
|
1274
|
+
}
|
|
1275
|
+
let buildNumber = null;
|
|
1276
|
+
if (queueItemId) {
|
|
1277
|
+
log2(" \uBE4C\uB4DC \uBC88\uD638 \uB300\uAE30 \uC911...");
|
|
1278
|
+
for (let i = 0; i < 6; i++) {
|
|
1279
|
+
await new Promise((r) => setTimeout(r, 5e3));
|
|
1280
|
+
buildNumber = await getQueueBuildNumber(jenkins, queueItemId).catch(() => null);
|
|
1281
|
+
if (buildNumber) break;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
if (!buildNumber) {
|
|
1285
|
+
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."));
|
|
1286
|
+
process.exit(1);
|
|
1287
|
+
}
|
|
1288
|
+
log2(` \uBE4C\uB4DC #${buildNumber} \uC2DC\uC791\uB428. \uC644\uB8CC \uB300\uAE30 \uC911...`);
|
|
1289
|
+
const result = await pollBuildComplete(jenkins, jobName, buildNumber);
|
|
1290
|
+
if (result !== "SUCCESS") {
|
|
1291
|
+
log2(kleur6.red(`\uBE4C\uB4DC \uC2E4\uD328: ${result}`));
|
|
1292
|
+
log2(kleur6.dim(` Jenkins: ${jenkins.url}/job/${encodeURIComponent(jobName)}/${buildNumber}/`));
|
|
1293
|
+
log2(kleur6.dim(` \uBE4C\uB4DC\uAC00 \uC774\uBBF8 \uC644\uB8CC\uB410\uB2E4\uBA74: mimi-seed deploy --skip-build --version-code ${buildNumber} --platform ${args.platform}`));
|
|
1294
|
+
process.exit(1);
|
|
1295
|
+
}
|
|
1296
|
+
log2(kleur6.green(`\u2705 \uBE4C\uB4DC #${buildNumber} \uC131\uACF5`));
|
|
1297
|
+
if (!versionCode) {
|
|
1298
|
+
versionCode = buildNumber;
|
|
1299
|
+
log2(kleur6.dim(` versionCode = buildNumber (${versionCode})`));
|
|
1300
|
+
}
|
|
1027
1301
|
} else {
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1302
|
+
if (!ciProvider) {
|
|
1303
|
+
log2(kleur6.red(`${kind} \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. setup-${kind} \uC2E4\uD589.`));
|
|
1304
|
+
process.exit(1);
|
|
1305
|
+
}
|
|
1306
|
+
const buildId = await runGitProviderBuild(ciProvider, args);
|
|
1307
|
+
if (!versionCode) {
|
|
1308
|
+
log2(kleur6.red(`\u2717 versionCode \uBBF8\uC9C0\uC815 (${kind} run_id ${buildId}\uB294 versionCode\uB85C \uBD80\uC801\uD569)`));
|
|
1309
|
+
log2(kleur6.dim(" \uAD8C\uC7A5 \uC0AC\uD56D:"));
|
|
1310
|
+
log2(kleur6.dim(" \u2022 CI \uC6CC\uD06C\uD50C\uB85C \uC548\uC5D0\uC11C versionCode\uB97C \uACB0\uC815\uD558\uACE0 \uACB0\uACFC\uB97C \uCD9C\uB825"));
|
|
1311
|
+
log2(kleur6.dim(" \u2022 \uB2E4\uC74C \uC2E4\uD589: mimi-seed deploy --skip-build --version-code <N>"));
|
|
1312
|
+
process.exit(1);
|
|
1037
1313
|
}
|
|
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
1314
|
}
|
|
1056
1315
|
}
|
|
1057
1316
|
if (!versionCode) {
|
|
@@ -1098,17 +1357,17 @@ async function cmdDeploy(argv) {
|
|
|
1098
1357
|
|
|
1099
1358
|
// src/mcp-restart.ts
|
|
1100
1359
|
import { execSync as execSync2 } from "child_process";
|
|
1101
|
-
import
|
|
1102
|
-
import
|
|
1103
|
-
import
|
|
1360
|
+
import fs4 from "fs";
|
|
1361
|
+
import os3 from "os";
|
|
1362
|
+
import path4 from "path";
|
|
1104
1363
|
import kleur7 from "kleur";
|
|
1105
1364
|
function log3(msg) {
|
|
1106
1365
|
process.stdout.write(msg + "\n");
|
|
1107
1366
|
}
|
|
1108
1367
|
function readClaudeJson() {
|
|
1109
|
-
const p =
|
|
1368
|
+
const p = path4.join(os3.homedir(), ".claude.json");
|
|
1110
1369
|
try {
|
|
1111
|
-
return JSON.parse(
|
|
1370
|
+
return JSON.parse(fs4.readFileSync(p, "utf8"));
|
|
1112
1371
|
} catch {
|
|
1113
1372
|
return {};
|
|
1114
1373
|
}
|
|
@@ -1124,7 +1383,7 @@ function findProcessMarker(cfg) {
|
|
|
1124
1383
|
return meaningful.at(-1) ?? null;
|
|
1125
1384
|
}
|
|
1126
1385
|
function killByMarker(marker) {
|
|
1127
|
-
const isWin =
|
|
1386
|
+
const isWin = os3.platform() === "win32";
|
|
1128
1387
|
if (isWin) {
|
|
1129
1388
|
const escaped = marker.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1130
1389
|
let pids = [];
|
|
@@ -1235,7 +1494,7 @@ async function cmdInit() {
|
|
|
1235
1494
|
return;
|
|
1236
1495
|
}
|
|
1237
1496
|
log4("\u{1F510} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778 \uB300\uAE30...");
|
|
1238
|
-
const hostName =
|
|
1497
|
+
const hostName = os4.hostname().slice(0, 32);
|
|
1239
1498
|
const name = `cli-${hostName}`;
|
|
1240
1499
|
const { port, promise } = await awaitHandshake(5 * 60 * 1e3);
|
|
1241
1500
|
const callback = `http://127.0.0.1:${port}/cb`;
|
|
@@ -1271,10 +1530,10 @@ async function cmdInit() {
|
|
|
1271
1530
|
}
|
|
1272
1531
|
log4("");
|
|
1273
1532
|
}
|
|
1274
|
-
const claudeDir =
|
|
1275
|
-
const agentMdPath =
|
|
1276
|
-
if (!
|
|
1277
|
-
|
|
1533
|
+
const claudeDir = path5.join(cwd, ".claude");
|
|
1534
|
+
const agentMdPath = path5.join(claudeDir, "mimi-seed.md");
|
|
1535
|
+
if (!fs5.existsSync(agentMdPath)) {
|
|
1536
|
+
fs5.mkdirSync(claudeDir, { recursive: true });
|
|
1278
1537
|
const appLines = hints.flatMap((h) => {
|
|
1279
1538
|
const parts = [
|
|
1280
1539
|
h.name && ` name: ${h.name}`,
|
|
@@ -1305,7 +1564,7 @@ async function cmdInit() {
|
|
|
1305
1564
|
"- `/mimi-seed:health` \u2014 \uC5F0\uACB0 \uC0C1\uD0DC \uBE60\uB978 \uD655\uC778",
|
|
1306
1565
|
"- `/mimi-seed:review-inbox` \u2014 \uBBF8\uB2F5\uBCC0 \uB9AC\uBDF0 \uB2F5\uBCC0"
|
|
1307
1566
|
].join("\n");
|
|
1308
|
-
|
|
1567
|
+
fs5.writeFileSync(agentMdPath, agentMd, { mode: 420 });
|
|
1309
1568
|
log4(kleur8.dim(` \uC5D0\uC774\uC804\uD2B8 \uC124\uC815: .claude/mimi-seed.md`));
|
|
1310
1569
|
}
|
|
1311
1570
|
log4(kleur8.bold("\u2713 \uC900\uBE44 \uC644\uB8CC."));
|
|
@@ -1387,15 +1646,20 @@ ${kleur8.bold("mimi-seed review \uC635\uC158:")}
|
|
|
1387
1646
|
--no-interactive CI \uBAA8\uB4DC
|
|
1388
1647
|
|
|
1389
1648
|
${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
|
-
|
|
1649
|
+
--platform android|ios \uBC30\uD3EC \uD50C\uB7AB\uD3FC (\uAE30\uBCF8: android)
|
|
1650
|
+
--app <id> \uC571 ID \uC9C0\uC815
|
|
1651
|
+
--version-code <n> \uBE4C\uB4DC \uBC88\uD638 \uC9C1\uC811 \uC9C0\uC815 (--skip-build \uC640 \uD568\uAED8)
|
|
1652
|
+
--from <ref> \uCEE4\uBC0B \uBC94\uC704 \uC2DC\uC791 (\uB9B4\uB9AC\uC988 \uB178\uD2B8\uC6A9)
|
|
1653
|
+
--to <ref> \uCEE4\uBC0B \uBC94\uC704 \uB05D (\uAE30\uBCF8: HEAD)
|
|
1654
|
+
--language <\uCF54\uB4DC> \uB9B4\uB9AC\uC988 \uB178\uD2B8 \uC5B8\uC5B4 (\uAE30\uBCF8: ko-KR)
|
|
1655
|
+
--dry-run \uC2E4\uC81C \uBC30\uD3EC \uC5C6\uC774 \uD30C\uC774\uD504\uB77C\uC778 \uD14C\uC2A4\uD2B8
|
|
1656
|
+
--skip-build CI \uBE4C\uB4DC \uAC74\uB108\uB700 (--version-code \uD544\uC218)
|
|
1657
|
+
--ci jenkins|github|gitlab CI \uAC15\uC81C \uC120\uD0DD (\uAE30\uBCF8: auto)
|
|
1658
|
+
--workflow <file> GitHub workflow \uD30C\uC77C (\uC608: deploy.yml)
|
|
1659
|
+
--ref <branch|tag> GitHub/GitLab \uBE0C\uB79C\uCE58/\uD0DC\uADF8 (\uAE30\uBCF8: main)
|
|
1660
|
+
setup-jenkins Jenkins \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
|
|
1661
|
+
setup-github GitHub Actions \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
|
|
1662
|
+
setup-gitlab GitLab CI \uC124\uC815 \uB300\uD654\uD615 \uB4F1\uB85D
|
|
1399
1663
|
|
|
1400
1664
|
${kleur8.bold("\uD658\uACBD\uBCC0\uC218:")}
|
|
1401
1665
|
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.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "Mimi Seed CLI — Claude Code에서 앱 출시 운영을 관리합니다.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"mimi-seed": "dist/index.js"
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsup",
|
|
16
|
-
"dev": "tsx src/index.ts"
|
|
16
|
+
"dev": "tsx src/index.ts",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest"
|
|
17
19
|
},
|
|
18
20
|
"engines": {
|
|
19
21
|
"node": ">=18"
|
|
@@ -27,7 +29,8 @@
|
|
|
27
29
|
"@types/node": "^20.11.0",
|
|
28
30
|
"tsup": "^8.0.0",
|
|
29
31
|
"tsx": "^4.7.0",
|
|
30
|
-
"typescript": "^5.4.0"
|
|
32
|
+
"typescript": "^5.4.0",
|
|
33
|
+
"vitest": "^4.1.5"
|
|
31
34
|
},
|
|
32
35
|
"publishConfig": {
|
|
33
36
|
"access": "public"
|