ai-project-manage-cli 6.0.65 → 6.0.67
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 +1151 -431
- package/package.json +3 -1
- package/template/apm.config.json +2 -6
- package/template/deploy/README.md +5 -6
- package/template/skills/apm-deploy/SKILL.md +4 -6
- package/template/deploy/deploy.py +0 -564
package/dist/index.js
CHANGED
|
@@ -110,8 +110,8 @@ function toFsPath(inputPath) {
|
|
|
110
110
|
}
|
|
111
111
|
return `\\\\?\\${normalized}`;
|
|
112
112
|
}
|
|
113
|
-
function normalizeWorkdirPath(
|
|
114
|
-
let normalized =
|
|
113
|
+
function normalizeWorkdirPath(path12) {
|
|
114
|
+
let normalized = path12.trim().replace(/\\/g, "/").normalize("NFC");
|
|
115
115
|
if (normalized.startsWith("//?/")) {
|
|
116
116
|
normalized = normalized.slice(4);
|
|
117
117
|
}
|
|
@@ -326,9 +326,9 @@ function assertTemplateCopiedToApm(apmDir, workdir) {
|
|
|
326
326
|
"sessions"
|
|
327
327
|
];
|
|
328
328
|
for (const item of required) {
|
|
329
|
-
const
|
|
330
|
-
if (!existsSync(toFsPath(
|
|
331
|
-
throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${
|
|
329
|
+
const path12 = join2(apmDir, item);
|
|
330
|
+
if (!existsSync(toFsPath(path12))) {
|
|
331
|
+
throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path12}`);
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
334
|
const leakedRules = join2(workdir, "rules");
|
|
@@ -613,14 +613,14 @@ function projectDocumentLocalPath(apmRoot, documentPath) {
|
|
|
613
613
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
614
614
|
return join4(projectDocumentsDir(apmRoot), ...normalized.split("/"));
|
|
615
615
|
}
|
|
616
|
-
function normalizeLocalDocumentPath(
|
|
617
|
-
const trimmed =
|
|
616
|
+
function normalizeLocalDocumentPath(path12) {
|
|
617
|
+
const trimmed = path12.trim().replace(/\\/g, "/");
|
|
618
618
|
if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
|
|
619
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${
|
|
619
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
|
|
620
620
|
}
|
|
621
621
|
const segments = trimmed.split("/").filter(Boolean);
|
|
622
622
|
if (segments.some((segment) => segment === ".." || segment === ".")) {
|
|
623
|
-
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${
|
|
623
|
+
throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path12}`);
|
|
624
624
|
}
|
|
625
625
|
return segments.join("/");
|
|
626
626
|
}
|
|
@@ -671,15 +671,15 @@ function diffManifestPaths(remote, local) {
|
|
|
671
671
|
(local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
|
|
672
672
|
);
|
|
673
673
|
const download = [];
|
|
674
|
-
for (const [
|
|
675
|
-
if (localMap.get(
|
|
676
|
-
download.push(
|
|
674
|
+
for (const [path12, hash] of remoteMap) {
|
|
675
|
+
if (localMap.get(path12) !== hash) {
|
|
676
|
+
download.push(path12);
|
|
677
677
|
}
|
|
678
678
|
}
|
|
679
679
|
const deleteLocal = [];
|
|
680
|
-
for (const
|
|
681
|
-
if (!remoteMap.has(
|
|
682
|
-
deleteLocal.push(
|
|
680
|
+
for (const path12 of localMap.keys()) {
|
|
681
|
+
if (!remoteMap.has(path12)) {
|
|
682
|
+
deleteLocal.push(path12);
|
|
683
683
|
}
|
|
684
684
|
}
|
|
685
685
|
return { download, deleteLocal };
|
|
@@ -739,8 +739,8 @@ ${diagnostic ?? ""}`
|
|
|
739
739
|
}
|
|
740
740
|
}
|
|
741
741
|
let deleted = 0;
|
|
742
|
-
for (const
|
|
743
|
-
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir,
|
|
742
|
+
for (const path12 of deleteLocal) {
|
|
743
|
+
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
|
|
744
744
|
if (existsSync2(absPath)) {
|
|
745
745
|
rmSync(absPath, { force: true });
|
|
746
746
|
deleted += 1;
|
|
@@ -785,21 +785,21 @@ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
|
|
|
785
785
|
])
|
|
786
786
|
);
|
|
787
787
|
let synced = 0;
|
|
788
|
-
for (const
|
|
789
|
-
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir,
|
|
788
|
+
for (const path12 of localPaths) {
|
|
789
|
+
const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path12));
|
|
790
790
|
const content = readFileSync3(absPath, "utf8");
|
|
791
791
|
const contentHash = hashLocalFileContent(content);
|
|
792
|
-
if (remoteHashByPath.get(
|
|
792
|
+
if (remoteHashByPath.get(path12) === contentHash) {
|
|
793
793
|
continue;
|
|
794
794
|
}
|
|
795
795
|
await api.cli.upsertRepositoryProjectDocument({
|
|
796
796
|
repositoryId,
|
|
797
|
-
path:
|
|
797
|
+
path: path12,
|
|
798
798
|
content,
|
|
799
|
-
description: remoteDescriptionByPath.get(
|
|
799
|
+
description: remoteDescriptionByPath.get(path12) ?? void 0
|
|
800
800
|
});
|
|
801
801
|
synced += 1;
|
|
802
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${
|
|
802
|
+
console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path12}`);
|
|
803
803
|
}
|
|
804
804
|
if (synced === 0) {
|
|
805
805
|
console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
|
|
@@ -1395,13 +1395,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
1395
1395
|
return Buffer.from(await res.arrayBuffer());
|
|
1396
1396
|
}
|
|
1397
1397
|
function loadManifest(dir) {
|
|
1398
|
-
const
|
|
1399
|
-
if (!existsSync4(
|
|
1398
|
+
const path12 = join6(dir, MANIFEST_FILE2);
|
|
1399
|
+
if (!existsSync4(path12)) {
|
|
1400
1400
|
return { version: 1, attachments: {} };
|
|
1401
1401
|
}
|
|
1402
1402
|
try {
|
|
1403
1403
|
const parsed = JSON.parse(
|
|
1404
|
-
readFileSync5(
|
|
1404
|
+
readFileSync5(path12, "utf8")
|
|
1405
1405
|
);
|
|
1406
1406
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
1407
1407
|
return parsed;
|
|
@@ -1482,8 +1482,8 @@ function sanitizeSkillDirName(name) {
|
|
|
1482
1482
|
function listBaseSkillDirNames() {
|
|
1483
1483
|
if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
1484
1484
|
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
1485
|
-
const
|
|
1486
|
-
return statSync2(
|
|
1485
|
+
const path12 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
1486
|
+
return statSync2(path12).isDirectory();
|
|
1487
1487
|
});
|
|
1488
1488
|
}
|
|
1489
1489
|
function syncAgentsGuide(apmDir) {
|
|
@@ -1495,8 +1495,8 @@ function syncAgentsGuide(apmDir) {
|
|
|
1495
1495
|
function listBaseRuleFileNames() {
|
|
1496
1496
|
if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
1497
1497
|
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
1498
|
-
const
|
|
1499
|
-
return statSync2(
|
|
1498
|
+
const path12 = join7(BASE_RULES_TEMPLATE_DIR, name);
|
|
1499
|
+
return statSync2(path12).isFile();
|
|
1500
1500
|
});
|
|
1501
1501
|
}
|
|
1502
1502
|
function syncBaseRules(rulesDir) {
|
|
@@ -1559,13 +1559,13 @@ function ruleLocalFileName(ruleName) {
|
|
|
1559
1559
|
return `${sanitized}.md`;
|
|
1560
1560
|
}
|
|
1561
1561
|
function loadManifest2(rulesDir) {
|
|
1562
|
-
const
|
|
1563
|
-
if (!existsSync6(toFsPath(
|
|
1562
|
+
const path12 = join8(rulesDir, MANIFEST_FILE3);
|
|
1563
|
+
if (!existsSync6(toFsPath(path12))) {
|
|
1564
1564
|
return { version: 1, rules: {} };
|
|
1565
1565
|
}
|
|
1566
1566
|
try {
|
|
1567
1567
|
const parsed = JSON.parse(
|
|
1568
|
-
readFileSync6(toFsPath(
|
|
1568
|
+
readFileSync6(toFsPath(path12), "utf8")
|
|
1569
1569
|
);
|
|
1570
1570
|
if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
|
|
1571
1571
|
return parsed;
|
|
@@ -2102,7 +2102,7 @@ async function runUpdateMessageStatus(options) {
|
|
|
2102
2102
|
}
|
|
2103
2103
|
|
|
2104
2104
|
// src/commands/connect.ts
|
|
2105
|
-
import { spawnSync as
|
|
2105
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
2106
2106
|
import WebSocket from "ws";
|
|
2107
2107
|
|
|
2108
2108
|
// src/ws/protocol.ts
|
|
@@ -2226,19 +2226,918 @@ function validateAgentWsMessage(value, kind) {
|
|
|
2226
2226
|
|
|
2227
2227
|
// src/commands/connect/deploy-run.ts
|
|
2228
2228
|
import { spawn } from "node:child_process";
|
|
2229
|
-
import { readFileSync as
|
|
2229
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
|
|
2230
2230
|
import { join as join13 } from "node:path";
|
|
2231
|
+
|
|
2232
|
+
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
2233
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
2234
|
+
import path3 from "node:path";
|
|
2235
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2236
|
+
|
|
2237
|
+
// src/commands/deploy/internal/apm-config.ts
|
|
2238
|
+
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
2239
|
+
import { resolve as resolve3 } from "node:path";
|
|
2240
|
+
function loadApmConfig(options) {
|
|
2241
|
+
const p = resolve3(
|
|
2242
|
+
process.cwd(),
|
|
2243
|
+
options?.configPath ?? resolve3(workspaceApmDir(), "apm.config.json")
|
|
2244
|
+
);
|
|
2245
|
+
if (!existsSync11(p)) {
|
|
2246
|
+
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
2247
|
+
process.exit(1);
|
|
2248
|
+
}
|
|
2249
|
+
try {
|
|
2250
|
+
const raw = readFileSync9(p, "utf8");
|
|
2251
|
+
return JSON.parse(raw);
|
|
2252
|
+
} catch (e) {
|
|
2253
|
+
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
2254
|
+
process.exit(1);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
function req(v, field) {
|
|
2258
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2259
|
+
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2260
|
+
process.exit(1);
|
|
2261
|
+
}
|
|
2262
|
+
return v;
|
|
2263
|
+
}
|
|
2264
|
+
function reqTopLevelName(cfg) {
|
|
2265
|
+
const n = (cfg.name ?? "").trim();
|
|
2266
|
+
if (!n) {
|
|
2267
|
+
console.error(
|
|
2268
|
+
"\u8BF7\u5728 apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4E0E\u524D\u7AEF\u5236\u54C1\u524D\u7F00\u3001\u540E\u7AEF\u955C\u50CF\u540D\u5171\u7528\uFF09"
|
|
2269
|
+
);
|
|
2270
|
+
process.exit(1);
|
|
2271
|
+
}
|
|
2272
|
+
return n;
|
|
2273
|
+
}
|
|
2274
|
+
function reqBackendPositiveInt(v, field) {
|
|
2275
|
+
const n = Number(v);
|
|
2276
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
2277
|
+
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
2278
|
+
process.exit(1);
|
|
2279
|
+
}
|
|
2280
|
+
return n;
|
|
2281
|
+
}
|
|
2282
|
+
function resolveBackendDeployFromApmConfig(cfg) {
|
|
2283
|
+
const b = cfg.backendDeploy ?? {};
|
|
2284
|
+
const protoRaw = req(b.remoteProtocol, "remoteProtocol").trim().toLowerCase();
|
|
2285
|
+
if (protoRaw !== "http" && protoRaw !== "https") {
|
|
2286
|
+
console.error(
|
|
2287
|
+
"apm.config.json \u4E2D backendDeploy.remoteProtocol \u53EA\u80FD\u4E3A http \u6216 https"
|
|
2288
|
+
);
|
|
2289
|
+
process.exit(1);
|
|
2290
|
+
}
|
|
2291
|
+
const remoteProtocol = protoRaw;
|
|
2292
|
+
let mappings = [];
|
|
2293
|
+
const rawPorts = b.containerPortsMappings;
|
|
2294
|
+
if (rawPorts !== void 0 && rawPorts !== null) {
|
|
2295
|
+
if (!Array.isArray(rawPorts)) {
|
|
2296
|
+
console.error(
|
|
2297
|
+
"apm.config.json \u4E2D backendDeploy.containerPortsMappings \u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u53EF\u4E3A\u7A7A\uFF0C\u7701\u7565\u5219\u4E0D\u52A0\u7AEF\u53E3\u6620\u5C04\uFF09"
|
|
2298
|
+
);
|
|
2299
|
+
process.exit(1);
|
|
2300
|
+
}
|
|
2301
|
+
mappings = rawPorts.map((x) => String(x).trim()).filter(Boolean);
|
|
2302
|
+
}
|
|
2303
|
+
return {
|
|
2304
|
+
name: reqTopLevelName(cfg),
|
|
2305
|
+
registryHost: req(b.registryHost, "registryHost").trim(),
|
|
2306
|
+
registryNamespace: req(b.registryNamespace, "registryNamespace").trim(),
|
|
2307
|
+
registryUser: req(b.registryUser, "registryUser").trim(),
|
|
2308
|
+
registryPassword: req(b.registryPassword, "registryPassword").trim(),
|
|
2309
|
+
remoteHost: req(b.remoteHost, "remoteHost").trim(),
|
|
2310
|
+
remotePort: reqBackendPositiveInt(b.remotePort, "remotePort"),
|
|
2311
|
+
remoteProtocol,
|
|
2312
|
+
caPath: b.caPath?.trim(),
|
|
2313
|
+
certPath: b.certPath?.trim(),
|
|
2314
|
+
keyPath: b.keyPath?.trim(),
|
|
2315
|
+
envFilePath: typeof b.envFilePath === "string" ? b.envFilePath.trim() : "",
|
|
2316
|
+
containerPortsMappings: mappings,
|
|
2317
|
+
dockerNetwork: b.dockerNetwork?.trim() || void 0
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
function reqFe(v, field) {
|
|
2321
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2322
|
+
console.error(`apm.config.json \u4E2D frontendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2323
|
+
process.exit(1);
|
|
2324
|
+
}
|
|
2325
|
+
return v;
|
|
2326
|
+
}
|
|
2327
|
+
function resolveFrontendDeployFromApmConfig(cfg) {
|
|
2328
|
+
const f = cfg.frontendDeploy ?? {};
|
|
2329
|
+
const port = Number(f.port);
|
|
2330
|
+
return {
|
|
2331
|
+
endpoint: reqFe(f.endpoint, "endpoint").trim(),
|
|
2332
|
+
port: Number.isFinite(port) && port > 0 ? port : 9e3,
|
|
2333
|
+
useSsl: Boolean(f.useSsl),
|
|
2334
|
+
accessKey: reqFe(f.accessKey, "accessKey").trim(),
|
|
2335
|
+
secretKey: reqFe(f.secretKey, "secretKey").trim(),
|
|
2336
|
+
bucket: reqFe(f.bucket, "bucket").trim()
|
|
2337
|
+
};
|
|
2338
|
+
}
|
|
2339
|
+
function reqWd(v, field) {
|
|
2340
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2341
|
+
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2342
|
+
process.exit(1);
|
|
2343
|
+
}
|
|
2344
|
+
return v;
|
|
2345
|
+
}
|
|
2346
|
+
function reqWisdomPositiveInt(v, field) {
|
|
2347
|
+
const n = Number(v);
|
|
2348
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
2349
|
+
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
2350
|
+
process.exit(1);
|
|
2351
|
+
}
|
|
2352
|
+
return n;
|
|
2353
|
+
}
|
|
2354
|
+
function resolveWisdomDeployFromApmConfig(cfg) {
|
|
2355
|
+
const w = cfg.wisdomDeploy ?? {};
|
|
2356
|
+
return {
|
|
2357
|
+
host: reqWd(w.host, "host").trim(),
|
|
2358
|
+
port: reqWisdomPositiveInt(w.port, "port"),
|
|
2359
|
+
username: reqWd(w.username, "username").trim(),
|
|
2360
|
+
password: reqWd(w.password, "password").trim(),
|
|
2361
|
+
remotePath: reqWd(w.remotePath, "remotePath").trim()
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
function reqHc(v, field) {
|
|
2365
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2366
|
+
console.error(`apm.config.json \u4E2D healthCheck.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2367
|
+
process.exit(1);
|
|
2368
|
+
}
|
|
2369
|
+
return v;
|
|
2370
|
+
}
|
|
2371
|
+
function reqWb(v, field) {
|
|
2372
|
+
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
2373
|
+
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
2374
|
+
process.exit(1);
|
|
2375
|
+
}
|
|
2376
|
+
return v;
|
|
2377
|
+
}
|
|
2378
|
+
function resolveWisdomBackendDeployFromApmConfig(cfg) {
|
|
2379
|
+
const projectName = reqTopLevelName(cfg);
|
|
2380
|
+
const w = cfg.wisdomDeploy ?? {};
|
|
2381
|
+
const h = cfg.healthCheck ?? {};
|
|
2382
|
+
const jarPath = reqWb(w.jarPath, "jarPath").trim();
|
|
2383
|
+
const remoteAppDir = posixDirname(jarPath);
|
|
2384
|
+
const healthPort = Number(reqHc(h.port, "port"));
|
|
2385
|
+
const healthTimeout = Number(reqHc(h.timeout, "timeout"));
|
|
2386
|
+
if (!Number.isFinite(healthPort) || !Number.isInteger(healthPort) || healthPort < 1) {
|
|
2387
|
+
console.error("apm.config.json \u4E2D healthCheck.port \u987B\u4E3A\u6B63\u6574\u6570");
|
|
2388
|
+
process.exit(1);
|
|
2389
|
+
}
|
|
2390
|
+
if (!Number.isFinite(healthTimeout) || !Number.isInteger(healthTimeout) || healthTimeout < 1) {
|
|
2391
|
+
console.error("apm.config.json \u4E2D healthCheck.timeout \u987B\u4E3A\u6B63\u6574\u6570");
|
|
2392
|
+
process.exit(1);
|
|
2393
|
+
}
|
|
2394
|
+
return {
|
|
2395
|
+
projectName,
|
|
2396
|
+
host: reqWd(w.host, "host").trim(),
|
|
2397
|
+
port: reqWisdomPositiveInt(w.port, "port"),
|
|
2398
|
+
username: reqWd(w.username, "username").trim(),
|
|
2399
|
+
password: reqWd(w.password, "password").trim(),
|
|
2400
|
+
remoteVueDistDir: reqWd(w.remotePath, "remotePath").trim(),
|
|
2401
|
+
remoteAppDir,
|
|
2402
|
+
remoteLibDir: `${remoteAppDir}/lib`,
|
|
2403
|
+
startupJar: posixBasename(jarPath),
|
|
2404
|
+
packageName: `${projectName}.jar.zip`,
|
|
2405
|
+
mavenLocalRepo: reqWb(w.mavenLocalRepo, "mavenLocalRepo").trim(),
|
|
2406
|
+
healthCheckPort: healthPort,
|
|
2407
|
+
healthCheckContext: reqHc(h.context, "context").trim(),
|
|
2408
|
+
healthCheckTimeout: healthTimeout
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
function posixDirname(p) {
|
|
2412
|
+
const normalized = p.replace(/\\/g, "/");
|
|
2413
|
+
const idx = normalized.lastIndexOf("/");
|
|
2414
|
+
if (idx <= 0) {
|
|
2415
|
+
return normalized.startsWith("/") ? "/" : ".";
|
|
2416
|
+
}
|
|
2417
|
+
return normalized.slice(0, idx);
|
|
2418
|
+
}
|
|
2419
|
+
function posixBasename(p) {
|
|
2420
|
+
const normalized = p.replace(/\\/g, "/");
|
|
2421
|
+
const idx = normalized.lastIndexOf("/");
|
|
2422
|
+
return idx >= 0 ? normalized.slice(idx + 1) : normalized;
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
2426
|
+
import {
|
|
2427
|
+
existsSync as existsSync12,
|
|
2428
|
+
mkdirSync as mkdirSync5,
|
|
2429
|
+
readdirSync as readdirSync5,
|
|
2430
|
+
readFileSync as readFileSync10,
|
|
2431
|
+
statSync as statSync5,
|
|
2432
|
+
writeFileSync as writeFileSync10
|
|
2433
|
+
} from "node:fs";
|
|
2434
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
2435
|
+
import path2 from "node:path";
|
|
2436
|
+
import { Client } from "ssh2";
|
|
2437
|
+
import JSZip2 from "jszip";
|
|
2438
|
+
import SftpClient2 from "ssh2-sftp-client";
|
|
2439
|
+
|
|
2440
|
+
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
2441
|
+
import { readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2442
|
+
import path from "node:path";
|
|
2443
|
+
import JSZip from "jszip";
|
|
2444
|
+
import SftpClient from "ssh2-sftp-client";
|
|
2445
|
+
async function addDirToZip(dir, zipFolder) {
|
|
2446
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2447
|
+
for (const entry of entries) {
|
|
2448
|
+
const fullPath = path.join(dir, entry.name);
|
|
2449
|
+
if (entry.isDirectory()) {
|
|
2450
|
+
const folder = zipFolder.folder(entry.name);
|
|
2451
|
+
if (folder) {
|
|
2452
|
+
await addDirToZip(fullPath, folder);
|
|
2453
|
+
}
|
|
2454
|
+
} else {
|
|
2455
|
+
const content = await readFile(fullPath);
|
|
2456
|
+
zipFolder.file(entry.name, content);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
async function zipDirectory(distDir, zipPath) {
|
|
2461
|
+
console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
|
|
2462
|
+
const zip = new JSZip();
|
|
2463
|
+
await addDirToZip(distDir, zip);
|
|
2464
|
+
const content = await zip.generateAsync({
|
|
2465
|
+
type: "nodebuffer",
|
|
2466
|
+
compression: "DEFLATE",
|
|
2467
|
+
compressionOptions: { level: 6 }
|
|
2468
|
+
});
|
|
2469
|
+
await writeFile(zipPath, content);
|
|
2470
|
+
const sizeMb = (content.length / 1024 / 1024).toFixed(2);
|
|
2471
|
+
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
2472
|
+
return content.length;
|
|
2473
|
+
}
|
|
2474
|
+
async function ensureRemoteDir(sftp, dir) {
|
|
2475
|
+
const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
2476
|
+
let current = dir.startsWith("/") ? "" : ".";
|
|
2477
|
+
for (const part of parts) {
|
|
2478
|
+
current = current ? `${current}/${part}` : `/${part}`;
|
|
2479
|
+
try {
|
|
2480
|
+
await sftp.mkdir(current, true);
|
|
2481
|
+
} catch {
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
function execCommand(client, command) {
|
|
2486
|
+
return new Promise((resolve5, reject) => {
|
|
2487
|
+
client.exec(command, (err, stream) => {
|
|
2488
|
+
if (err) return reject(err);
|
|
2489
|
+
let stdout = "";
|
|
2490
|
+
let stderr = "";
|
|
2491
|
+
stream.on("close", (code) => {
|
|
2492
|
+
if (code !== 0) {
|
|
2493
|
+
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
2494
|
+
return;
|
|
2495
|
+
}
|
|
2496
|
+
resolve5(stdout);
|
|
2497
|
+
}).on("data", (data) => {
|
|
2498
|
+
stdout += data.toString();
|
|
2499
|
+
});
|
|
2500
|
+
stream.stderr.on("data", (data) => {
|
|
2501
|
+
stderr += data.toString();
|
|
2502
|
+
});
|
|
2503
|
+
});
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2506
|
+
function shellSingleQuote(value) {
|
|
2507
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
2508
|
+
}
|
|
2509
|
+
function buildClearRemoteDirExceptZipCommand(target) {
|
|
2510
|
+
const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
|
|
2511
|
+
const quotedTarget = shellSingleQuote(normalized);
|
|
2512
|
+
const script = [
|
|
2513
|
+
`T=${quotedTarget}`,
|
|
2514
|
+
"S=$(mktemp -d)",
|
|
2515
|
+
'while IFS= read -r -d "" z; do r="${z#${T}/}"',
|
|
2516
|
+
'mkdir -p "${S}/$(dirname "$r")"',
|
|
2517
|
+
'mv "$z" "${S}/${r}"',
|
|
2518
|
+
'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
|
|
2519
|
+
'rm -rf "${T}"/*',
|
|
2520
|
+
'while IFS= read -r -d "" r; do r="${r#./}"',
|
|
2521
|
+
'mkdir -p "${T}/$(dirname "$r")"',
|
|
2522
|
+
'mv "${S}/${r}" "${T}/${r}"',
|
|
2523
|
+
'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
|
|
2524
|
+
'rm -rf "$S"'
|
|
2525
|
+
].join("; ");
|
|
2526
|
+
return `bash -c ${shellSingleQuote(script)}`;
|
|
2527
|
+
}
|
|
2528
|
+
async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
2529
|
+
const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
|
|
2530
|
+
console.error(
|
|
2531
|
+
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
2532
|
+
);
|
|
2533
|
+
const sftp = new SftpClient();
|
|
2534
|
+
try {
|
|
2535
|
+
await sftp.connect({
|
|
2536
|
+
host: settings.host,
|
|
2537
|
+
port: settings.port,
|
|
2538
|
+
username: settings.username,
|
|
2539
|
+
password: settings.password,
|
|
2540
|
+
readyTimeout: 2e4,
|
|
2541
|
+
tryKeyboard: true
|
|
2542
|
+
});
|
|
2543
|
+
await ensureRemoteDir(sftp, settings.remotePath);
|
|
2544
|
+
await sftp.put(localZip, remoteZipPath);
|
|
2545
|
+
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
2546
|
+
if (extract) {
|
|
2547
|
+
const target = settings.remotePath.replace(/\/$/, "");
|
|
2548
|
+
const client = sftp.client;
|
|
2549
|
+
const clearCmd = buildClearRemoteDirExceptZipCommand(target);
|
|
2550
|
+
console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
|
|
2551
|
+
await execCommand(client, clearCmd);
|
|
2552
|
+
const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
|
|
2553
|
+
console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
|
|
2554
|
+
await execCommand(client, unzipCmd);
|
|
2555
|
+
console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
|
|
2556
|
+
}
|
|
2557
|
+
console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
|
|
2558
|
+
} finally {
|
|
2559
|
+
await sftp.end();
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
async function runWisdomSftpDeploy(params) {
|
|
2563
|
+
const zipPath = path.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
2564
|
+
const resolvedZipPath = path.resolve(zipPath);
|
|
2565
|
+
let zipSizeBytes = 0;
|
|
2566
|
+
try {
|
|
2567
|
+
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
2568
|
+
await uploadAndMaybeExtract(
|
|
2569
|
+
params.settings,
|
|
2570
|
+
resolvedZipPath,
|
|
2571
|
+
params.extract
|
|
2572
|
+
);
|
|
2573
|
+
} finally {
|
|
2574
|
+
try {
|
|
2575
|
+
await unlink(resolvedZipPath);
|
|
2576
|
+
console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
|
|
2577
|
+
} catch {
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
return {
|
|
2581
|
+
ok: true,
|
|
2582
|
+
localDir: params.localDir,
|
|
2583
|
+
host: params.settings.host,
|
|
2584
|
+
remotePath: params.settings.remotePath,
|
|
2585
|
+
zipSizeBytes,
|
|
2586
|
+
extracted: params.extract
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2589
|
+
|
|
2590
|
+
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
2591
|
+
var SPRINGBOOT_SCRIPT = "springboot.sh";
|
|
2592
|
+
var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
|
|
2593
|
+
var MAVEN_PROFILE = "dev";
|
|
2594
|
+
function log(message) {
|
|
2595
|
+
const now = /* @__PURE__ */ new Date();
|
|
2596
|
+
const hh = String(now.getHours()).padStart(2, "0");
|
|
2597
|
+
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
2598
|
+
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
2599
|
+
console.error(`[${hh}:${mm}:${ss}] ${message}`);
|
|
2600
|
+
}
|
|
2601
|
+
function fail(message) {
|
|
2602
|
+
log(`ERROR: ${message}`);
|
|
2603
|
+
process.exit(1);
|
|
2604
|
+
}
|
|
2605
|
+
function expandPath(pathStr) {
|
|
2606
|
+
return path2.resolve(pathStr.replace(/^~(?=\/|$)/, process.env.HOME ?? ""));
|
|
2607
|
+
}
|
|
2608
|
+
function deployCacheDir() {
|
|
2609
|
+
return path2.join(workspaceApmDir(), "deploy", ".deploy_cache");
|
|
2610
|
+
}
|
|
2611
|
+
function manifestFilePath() {
|
|
2612
|
+
return path2.join(deployCacheDir(), "manifest.json");
|
|
2613
|
+
}
|
|
2614
|
+
function getTargetDir(projectRoot) {
|
|
2615
|
+
return path2.join(projectRoot, MAVEN_MODULE, "target");
|
|
2616
|
+
}
|
|
2617
|
+
function relativeKey(projectRoot, filePath) {
|
|
2618
|
+
return path2.relative(projectRoot, filePath).split(path2.sep).join("/");
|
|
2619
|
+
}
|
|
2620
|
+
function fileSignature(filePath) {
|
|
2621
|
+
const stat2 = statSync5(filePath);
|
|
2622
|
+
return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
|
|
2623
|
+
}
|
|
2624
|
+
function loadManifest3() {
|
|
2625
|
+
const manifestPath2 = manifestFilePath();
|
|
2626
|
+
if (!existsSync12(manifestPath2)) {
|
|
2627
|
+
return {};
|
|
2628
|
+
}
|
|
2629
|
+
return JSON.parse(readFileSync10(manifestPath2, "utf8"));
|
|
2630
|
+
}
|
|
2631
|
+
function saveManifest3(manifest) {
|
|
2632
|
+
const dir = deployCacheDir();
|
|
2633
|
+
mkdirSync5(dir, { recursive: true });
|
|
2634
|
+
writeFileSync10(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
2635
|
+
}
|
|
2636
|
+
function isProjectLibJar(jarName) {
|
|
2637
|
+
return jarName.startsWith("jeecg-");
|
|
2638
|
+
}
|
|
2639
|
+
function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
|
|
2640
|
+
if (!remoteAttr) {
|
|
2641
|
+
return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
|
|
2642
|
+
}
|
|
2643
|
+
const localSize = statSync5(localPath).size;
|
|
2644
|
+
const remoteSize = remoteAttr.size;
|
|
2645
|
+
if (localSize !== remoteSize) {
|
|
2646
|
+
return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
|
|
2647
|
+
}
|
|
2648
|
+
if (isProjectLibJar(path2.basename(localPath)) && manifest) {
|
|
2649
|
+
const key = relativeKey(projectRoot, localPath);
|
|
2650
|
+
const current = fileSignature(localPath);
|
|
2651
|
+
const previous = manifest[key];
|
|
2652
|
+
if (!previous) {
|
|
2653
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55"];
|
|
2654
|
+
}
|
|
2655
|
+
if (previous.size !== current.size) {
|
|
2656
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
|
|
2657
|
+
}
|
|
2658
|
+
if (previous.mtime < current.mtime) {
|
|
2659
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
return [false, "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7"];
|
|
2663
|
+
}
|
|
2664
|
+
function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest = null) {
|
|
2665
|
+
const entries = [];
|
|
2666
|
+
const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
|
|
2667
|
+
for (const jarName of jarFiles) {
|
|
2668
|
+
const jarPath = path2.join(localLibDir, jarName);
|
|
2669
|
+
const remoteAttr = remoteStats.get(jarName);
|
|
2670
|
+
const [shouldUpload, reason] = shouldUploadLibFile(
|
|
2671
|
+
jarPath,
|
|
2672
|
+
remoteAttr,
|
|
2673
|
+
manifest,
|
|
2674
|
+
projectRoot
|
|
2675
|
+
);
|
|
2676
|
+
if (shouldUpload) {
|
|
2677
|
+
entries.push({ path: jarPath, arcname: jarName, reason });
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
return entries;
|
|
2681
|
+
}
|
|
2682
|
+
function updateManifestEntries(manifest, entries, projectRoot) {
|
|
2683
|
+
for (const entry of entries) {
|
|
2684
|
+
manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
|
|
2685
|
+
}
|
|
2686
|
+
return manifest;
|
|
2687
|
+
}
|
|
2688
|
+
async function createUpdatePackage(entries, packageName) {
|
|
2689
|
+
const dir = deployCacheDir();
|
|
2690
|
+
mkdirSync5(dir, { recursive: true });
|
|
2691
|
+
const zipPath = path2.join(dir, packageName);
|
|
2692
|
+
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path2.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
2693
|
+
const zip = new JSZip2();
|
|
2694
|
+
for (const entry of entries) {
|
|
2695
|
+
const content = readFileSync10(entry.path);
|
|
2696
|
+
zip.file(entry.arcname, content);
|
|
2697
|
+
log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
|
|
2698
|
+
}
|
|
2699
|
+
const buffer = await zip.generateAsync({
|
|
2700
|
+
type: "nodebuffer",
|
|
2701
|
+
compression: "DEFLATE",
|
|
2702
|
+
compressionOptions: { level: 6 }
|
|
2703
|
+
});
|
|
2704
|
+
writeFileSync10(zipPath, buffer);
|
|
2705
|
+
return zipPath;
|
|
2706
|
+
}
|
|
2707
|
+
function getMvnExecutable() {
|
|
2708
|
+
const isWin = process.platform === "win32";
|
|
2709
|
+
const candidates = isWin ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
|
|
2710
|
+
for (const name of candidates) {
|
|
2711
|
+
const result = spawnSync2(isWin ? "where" : "which", [name], {
|
|
2712
|
+
encoding: "utf8"
|
|
2713
|
+
});
|
|
2714
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
2715
|
+
return result.stdout.trim().split(/\r?\n/)[0];
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
|
|
2719
|
+
}
|
|
2720
|
+
function runMavenBuild(projectRoot, mavenLocalRepo) {
|
|
2721
|
+
const mavenRepo = expandPath(mavenLocalRepo);
|
|
2722
|
+
const cmd = [
|
|
2723
|
+
getMvnExecutable(),
|
|
2724
|
+
"clean",
|
|
2725
|
+
"package",
|
|
2726
|
+
`-P${MAVEN_PROFILE}`,
|
|
2727
|
+
`-Dmaven.repo.local=${mavenRepo}`,
|
|
2728
|
+
"-DskipTests"
|
|
2729
|
+
];
|
|
2730
|
+
log(`\u5F00\u59CB Maven \u6784\u5EFA: ${cmd.join(" ")}`);
|
|
2731
|
+
log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
|
|
2732
|
+
const result = spawnSync2(cmd[0], cmd.slice(1), {
|
|
2733
|
+
cwd: projectRoot,
|
|
2734
|
+
stdio: "inherit"
|
|
2735
|
+
});
|
|
2736
|
+
if (result.status !== 0) {
|
|
2737
|
+
fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
function locateLibDir(projectRoot) {
|
|
2741
|
+
const targetDir = getTargetDir(projectRoot);
|
|
2742
|
+
if (!existsSync12(targetDir)) {
|
|
2743
|
+
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
2744
|
+
}
|
|
2745
|
+
const libDir = path2.join(targetDir, "lib");
|
|
2746
|
+
if (!existsSync12(libDir) || !statSync5(libDir).isDirectory()) {
|
|
2747
|
+
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
2748
|
+
}
|
|
2749
|
+
const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
|
|
2750
|
+
if (libJars.length === 0) {
|
|
2751
|
+
fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
|
|
2752
|
+
}
|
|
2753
|
+
log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
|
|
2754
|
+
return libDir;
|
|
2755
|
+
}
|
|
2756
|
+
async function connectSsh(config) {
|
|
2757
|
+
const client = new Client();
|
|
2758
|
+
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
|
|
2759
|
+
await new Promise((resolve5, reject) => {
|
|
2760
|
+
client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
|
|
2761
|
+
host: config.host,
|
|
2762
|
+
port: config.port,
|
|
2763
|
+
username: config.username,
|
|
2764
|
+
password: config.password,
|
|
2765
|
+
readyTimeout: 3e4,
|
|
2766
|
+
tryKeyboard: true
|
|
2767
|
+
});
|
|
2768
|
+
});
|
|
2769
|
+
const sftp = new SftpClient2();
|
|
2770
|
+
await sftp.connect({
|
|
2771
|
+
host: config.host,
|
|
2772
|
+
port: config.port,
|
|
2773
|
+
username: config.username,
|
|
2774
|
+
password: config.password,
|
|
2775
|
+
readyTimeout: 3e4,
|
|
2776
|
+
tryKeyboard: true
|
|
2777
|
+
});
|
|
2778
|
+
return { client, sftp };
|
|
2779
|
+
}
|
|
2780
|
+
async function closeSsh(conn) {
|
|
2781
|
+
try {
|
|
2782
|
+
await conn.sftp.end();
|
|
2783
|
+
} catch {
|
|
2784
|
+
}
|
|
2785
|
+
conn.client.end();
|
|
2786
|
+
}
|
|
2787
|
+
async function getRemoteFileStats(sftp, remoteDir) {
|
|
2788
|
+
const stats = /* @__PURE__ */ new Map();
|
|
2789
|
+
try {
|
|
2790
|
+
const listing = await sftp.list(remoteDir);
|
|
2791
|
+
for (const item of listing) {
|
|
2792
|
+
if (item.name.endsWith(".jar")) {
|
|
2793
|
+
stats.set(item.name, { filename: item.name, size: item.size });
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
} catch {
|
|
2797
|
+
}
|
|
2798
|
+
return stats;
|
|
2799
|
+
}
|
|
2800
|
+
async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
2801
|
+
const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
|
|
2802
|
+
const remotePath = `${remoteDir}/${path2.basename(zipPath)}`;
|
|
2803
|
+
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
2804
|
+
try {
|
|
2805
|
+
await sftp.fastPut(zipPath, remotePath);
|
|
2806
|
+
log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
|
|
2807
|
+
} catch (err) {
|
|
2808
|
+
fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
2809
|
+
}
|
|
2810
|
+
return remotePath;
|
|
2811
|
+
}
|
|
2812
|
+
async function runRemoteCommand(client, command, options) {
|
|
2813
|
+
const check = options?.check ?? true;
|
|
2814
|
+
const stream = options?.stream ?? false;
|
|
2815
|
+
log(`\u8FDC\u7A0B\u6267\u884C: ${command}`);
|
|
2816
|
+
return new Promise((resolve5, reject) => {
|
|
2817
|
+
client.exec(command, (err, execStream) => {
|
|
2818
|
+
if (err) {
|
|
2819
|
+
reject(err);
|
|
2820
|
+
return;
|
|
2821
|
+
}
|
|
2822
|
+
let out = "";
|
|
2823
|
+
let errText = "";
|
|
2824
|
+
execStream.on("data", (data) => {
|
|
2825
|
+
const text = data.toString();
|
|
2826
|
+
out += text;
|
|
2827
|
+
if (stream) {
|
|
2828
|
+
process.stdout.write(text);
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
execStream.stderr.on("data", (data) => {
|
|
2832
|
+
errText += data.toString();
|
|
2833
|
+
});
|
|
2834
|
+
execStream.on("close", (code) => {
|
|
2835
|
+
if (!stream) {
|
|
2836
|
+
if (out) {
|
|
2837
|
+
console.log(out.trimEnd());
|
|
2838
|
+
}
|
|
2839
|
+
if (errText) {
|
|
2840
|
+
console.error(errText.trimEnd());
|
|
2841
|
+
}
|
|
2842
|
+
} else if (out && !out.endsWith("\n")) {
|
|
2843
|
+
process.stdout.write("\n");
|
|
2844
|
+
}
|
|
2845
|
+
if (check && code !== 0) {
|
|
2846
|
+
fail(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (exit ${code}): ${command}`);
|
|
2847
|
+
}
|
|
2848
|
+
resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
|
|
2849
|
+
});
|
|
2850
|
+
});
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
function buildExtractUpdatePackageScript(remoteZipPath, remoteLibDir) {
|
|
2854
|
+
const quotedZip = shellSingleQuote(remoteZipPath);
|
|
2855
|
+
const quotedLib = shellSingleQuote(remoteLibDir);
|
|
2856
|
+
return `
|
|
2857
|
+
set -e
|
|
2858
|
+
TMP=$(mktemp -d)
|
|
2859
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
2860
|
+
unzip -oq ${quotedZip} -d "$TMP"
|
|
2861
|
+
updated=0
|
|
2862
|
+
while IFS= read -r -d '' src; do
|
|
2863
|
+
name=$(basename "$src")
|
|
2864
|
+
dest=${quotedLib}/"$name"
|
|
2865
|
+
if [ -f "$dest" ]; then
|
|
2866
|
+
cp -f "$src" "$dest"
|
|
2867
|
+
echo "\u8986\u76D6: $name"
|
|
2868
|
+
updated=$((updated + 1))
|
|
2869
|
+
else
|
|
2870
|
+
echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
|
|
2871
|
+
fi
|
|
2872
|
+
done < <(find "$TMP" -name '*.jar' -type f -print0)
|
|
2873
|
+
echo "UPDATED_COUNT=$updated"
|
|
2874
|
+
`.trim();
|
|
2875
|
+
}
|
|
2876
|
+
async function extractUpdatePackageOnRemote(client, config, remoteZipPath) {
|
|
2877
|
+
const script = buildExtractUpdatePackageScript(
|
|
2878
|
+
remoteZipPath,
|
|
2879
|
+
config.remoteLibDir
|
|
2880
|
+
);
|
|
2881
|
+
const { out } = await runRemoteCommand(client, script);
|
|
2882
|
+
const match = out.match(/UPDATED_COUNT=(\d+)/);
|
|
2883
|
+
if (!match) {
|
|
2884
|
+
fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
|
|
2885
|
+
\u8F93\u51FA: ${out || "(\u7A7A)"}`);
|
|
2886
|
+
}
|
|
2887
|
+
const updated = Number.parseInt(match[1], 10);
|
|
2888
|
+
log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
|
|
2889
|
+
return updated;
|
|
2890
|
+
}
|
|
2891
|
+
function springbootOutputIndicatesSuccess(action, combined) {
|
|
2892
|
+
const lower = combined.toLowerCase();
|
|
2893
|
+
if (action === "health") {
|
|
2894
|
+
return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
|
|
2895
|
+
}
|
|
2896
|
+
if (action === "start" || action === "restart") {
|
|
2897
|
+
return combined.includes("is starting") || lower.includes("is running");
|
|
2898
|
+
}
|
|
2899
|
+
if (action === "stop") {
|
|
2900
|
+
return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
|
|
2901
|
+
}
|
|
2902
|
+
if (action === "status") {
|
|
2903
|
+
return lower.includes("running") || lower.includes("not running");
|
|
2904
|
+
}
|
|
2905
|
+
return true;
|
|
2906
|
+
}
|
|
2907
|
+
async function runSpringbootAction(client, config, action, ...args) {
|
|
2908
|
+
if (action === "start" || action === "restart") {
|
|
2909
|
+
if (args.length === 0) {
|
|
2910
|
+
fail(`\u8FDC\u7A0B ${action} \u7F3A\u5C11 jar \u53C2\u6570`);
|
|
2911
|
+
}
|
|
2912
|
+
const jar = args[0].trim().split(/\r?\n/)[0].trim();
|
|
2913
|
+
if (!jar) {
|
|
2914
|
+
fail(`\u65E0\u6548\u7684 jar \u540D\u79F0: ${JSON.stringify(args[0])}`);
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
const quotedArgs = [action, ...args].map((arg) => shellSingleQuote(arg));
|
|
2918
|
+
const command = [
|
|
2919
|
+
`cd ${shellSingleQuote(config.remoteAppDir)}`,
|
|
2920
|
+
`./${SPRINGBOOT_SCRIPT} ${quotedArgs.join(" ")}`
|
|
2921
|
+
].join(" && ");
|
|
2922
|
+
const { exitCode, out, err } = await runRemoteCommand(client, command, {
|
|
2923
|
+
check: false
|
|
2924
|
+
});
|
|
2925
|
+
const combined = `${out}
|
|
2926
|
+
${err}`.trim();
|
|
2927
|
+
const outputOk = springbootOutputIndicatesSuccess(action, combined);
|
|
2928
|
+
if (action === "health") {
|
|
2929
|
+
if (exitCode !== 0 || !outputOk) {
|
|
2930
|
+
fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
|
|
2931
|
+
\u547D\u4EE4: ${command}
|
|
2932
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`);
|
|
2933
|
+
}
|
|
2934
|
+
return combined;
|
|
2935
|
+
}
|
|
2936
|
+
if (exitCode !== 0 && !outputOk) {
|
|
2937
|
+
fail(`\u8FDC\u7A0B ${action} \u5931\u8D25: ${args.join(" ")}
|
|
2938
|
+
${combined}`);
|
|
2939
|
+
}
|
|
2940
|
+
if ((action === "start" || action === "restart") && !outputOk) {
|
|
2941
|
+
fail(
|
|
2942
|
+
`\u8FDC\u7A0B ${action} \u672A\u6210\u529F: ${args.join(" ")}
|
|
2943
|
+
\u547D\u4EE4: ${command}
|
|
2944
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2947
|
+
return combined;
|
|
2948
|
+
}
|
|
2949
|
+
function stripAnsi(text) {
|
|
2950
|
+
return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
2951
|
+
}
|
|
2952
|
+
async function getRunningJar(client, config) {
|
|
2953
|
+
const combined = await runSpringbootAction(
|
|
2954
|
+
client,
|
|
2955
|
+
config,
|
|
2956
|
+
"status",
|
|
2957
|
+
config.startupJar
|
|
2958
|
+
);
|
|
2959
|
+
const text = stripAnsi(combined).trim().toLowerCase();
|
|
2960
|
+
if (text.includes("not running")) {
|
|
2961
|
+
return null;
|
|
2962
|
+
}
|
|
2963
|
+
if (text.includes("running")) {
|
|
2964
|
+
return config.startupJar;
|
|
2965
|
+
}
|
|
2966
|
+
return null;
|
|
2967
|
+
}
|
|
2968
|
+
async function healthCheckService(client, config) {
|
|
2969
|
+
const port = String(config.healthCheckPort);
|
|
2970
|
+
const context = config.healthCheckContext;
|
|
2971
|
+
const timeout = String(config.healthCheckTimeout);
|
|
2972
|
+
log(`\u5065\u5EB7\u68C0\u67E5: springboot.sh health ${port} ${context} ${timeout}`);
|
|
2973
|
+
await runSpringbootAction(client, config, "health", port, context, timeout);
|
|
2974
|
+
}
|
|
2975
|
+
async function runWisdomBackendDeploy(options) {
|
|
2976
|
+
const projectRoot = path2.resolve(options.projectRoot ?? process.cwd());
|
|
2977
|
+
const config = options.config;
|
|
2978
|
+
log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
|
|
2979
|
+
log(`\u914D\u7F6E\u6587\u4EF6: ${path2.join(workspaceApmDir(), "apm.config.json")}`);
|
|
2980
|
+
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
2981
|
+
if (!options.skipMaven) {
|
|
2982
|
+
runMavenBuild(projectRoot, config.mavenLocalRepo);
|
|
2983
|
+
}
|
|
2984
|
+
const libDir = locateLibDir(projectRoot);
|
|
2985
|
+
let manifest = loadManifest3();
|
|
2986
|
+
const conn = await connectSsh(config);
|
|
2987
|
+
try {
|
|
2988
|
+
const remoteLibStats = await getRemoteFileStats(
|
|
2989
|
+
conn.sftp,
|
|
2990
|
+
config.remoteLibDir
|
|
2991
|
+
);
|
|
2992
|
+
log("\u6536\u96C6 JAR \u66F4\u65B0...");
|
|
2993
|
+
const libUploadEntries = listLibFilesToUpload(
|
|
2994
|
+
libDir,
|
|
2995
|
+
remoteLibStats,
|
|
2996
|
+
projectRoot,
|
|
2997
|
+
manifest
|
|
2998
|
+
);
|
|
2999
|
+
let updated = 0;
|
|
3000
|
+
if (libUploadEntries.length > 0) {
|
|
3001
|
+
const zipPath = await createUpdatePackage(
|
|
3002
|
+
libUploadEntries,
|
|
3003
|
+
config.packageName
|
|
3004
|
+
);
|
|
3005
|
+
const remoteZipPath = await uploadUpdatePackage(
|
|
3006
|
+
conn.sftp,
|
|
3007
|
+
zipPath,
|
|
3008
|
+
config
|
|
3009
|
+
);
|
|
3010
|
+
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
3011
|
+
updated = await extractUpdatePackageOnRemote(
|
|
3012
|
+
conn.client,
|
|
3013
|
+
config,
|
|
3014
|
+
remoteZipPath
|
|
3015
|
+
);
|
|
3016
|
+
} else {
|
|
3017
|
+
log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
|
|
3018
|
+
}
|
|
3019
|
+
const runningJar = await getRunningJar(conn.client, config);
|
|
3020
|
+
let needRestart = updated > 0;
|
|
3021
|
+
if (!needRestart && !runningJar) {
|
|
3022
|
+
log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
|
|
3023
|
+
needRestart = true;
|
|
3024
|
+
} else if (!needRestart) {
|
|
3025
|
+
log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
|
|
3026
|
+
}
|
|
3027
|
+
if (needRestart) {
|
|
3028
|
+
log("\u91CD\u542F\u670D\u52A1...");
|
|
3029
|
+
await runSpringbootAction(
|
|
3030
|
+
conn.client,
|
|
3031
|
+
config,
|
|
3032
|
+
"restart",
|
|
3033
|
+
config.startupJar
|
|
3034
|
+
);
|
|
3035
|
+
}
|
|
3036
|
+
await healthCheckService(conn.client, config);
|
|
3037
|
+
if (libUploadEntries.length > 0) {
|
|
3038
|
+
manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
|
|
3039
|
+
saveManifest3(manifest);
|
|
3040
|
+
}
|
|
3041
|
+
} finally {
|
|
3042
|
+
await closeSsh(conn);
|
|
3043
|
+
}
|
|
3044
|
+
log("\u90E8\u7F72\u5B8C\u6210");
|
|
3045
|
+
}
|
|
3046
|
+
|
|
3047
|
+
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
3048
|
+
function isWisdomLegacyDeploy(cfg) {
|
|
3049
|
+
const w = cfg.wisdomDeploy;
|
|
3050
|
+
if (!w?.host?.trim() || !w.remotePath?.trim()) {
|
|
3051
|
+
return false;
|
|
3052
|
+
}
|
|
3053
|
+
const hasNewFrontend = Boolean(cfg.frontendDeploy?.endpoint?.trim());
|
|
3054
|
+
const hasNewBackend = Boolean(cfg.backendDeploy?.registryHost?.trim());
|
|
3055
|
+
return !hasNewFrontend && !hasNewBackend;
|
|
3056
|
+
}
|
|
3057
|
+
function detectWisdomProjectType(cwd) {
|
|
3058
|
+
return existsSync13(path3.join(cwd, "package.json")) ? "frontend" : "backend";
|
|
3059
|
+
}
|
|
3060
|
+
function readPackageScripts(cwd) {
|
|
3061
|
+
const pkgPath = path3.join(cwd, "package.json");
|
|
3062
|
+
if (!existsSync13(pkgPath)) {
|
|
3063
|
+
return {};
|
|
3064
|
+
}
|
|
3065
|
+
try {
|
|
3066
|
+
const raw = readFileSync11(pkgPath, "utf8");
|
|
3067
|
+
const parsed = JSON.parse(raw);
|
|
3068
|
+
return parsed.scripts ?? {};
|
|
3069
|
+
} catch {
|
|
3070
|
+
return {};
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
function resolveFrontendDeployCommand(env, cwd) {
|
|
3074
|
+
const scripts = readPackageScripts(cwd);
|
|
3075
|
+
const deployKey = `deploy:${env}`;
|
|
3076
|
+
if (scripts[deployKey]?.trim()) {
|
|
3077
|
+
return `npm run deploy:${env}`;
|
|
3078
|
+
}
|
|
3079
|
+
return null;
|
|
3080
|
+
}
|
|
3081
|
+
function runShellCommand(command, cwd) {
|
|
3082
|
+
console.error(`==> ${command}`);
|
|
3083
|
+
const result = spawnSync3(command, {
|
|
3084
|
+
cwd,
|
|
3085
|
+
stdio: "inherit",
|
|
3086
|
+
shell: true,
|
|
3087
|
+
env: process.env
|
|
3088
|
+
});
|
|
3089
|
+
if (result.status !== 0) {
|
|
3090
|
+
process.exit(result.status ?? 1);
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
async function runWisdomAutoDeploy(options) {
|
|
3094
|
+
const cwd = path3.resolve(options.cwd ?? process.cwd());
|
|
3095
|
+
const projectType = detectWisdomProjectType(cwd);
|
|
3096
|
+
console.error(
|
|
3097
|
+
`[apm] \u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u8BC6\u522B: ${projectType === "frontend" ? "\u524D\u7AEF Vue" : "\u540E\u7AEF Java"}`
|
|
3098
|
+
);
|
|
3099
|
+
if (projectType === "frontend") {
|
|
3100
|
+
const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
|
|
3101
|
+
if (!deployCmd) {
|
|
3102
|
+
console.error(
|
|
3103
|
+
`\u524D\u7AEF\u9879\u76EE ${path3.join(cwd, "package.json")} \u4E2D\u672A\u627E\u5230 deploy:${options.env} \u811A\u672C`
|
|
3104
|
+
);
|
|
3105
|
+
process.exit(1);
|
|
3106
|
+
}
|
|
3107
|
+
runShellCommand(deployCmd, cwd);
|
|
3108
|
+
return { projectType };
|
|
3109
|
+
}
|
|
3110
|
+
const configPath = options.configPath ?? path3.join(workspaceApmDir(cwd), "apm.config.json");
|
|
3111
|
+
const cfg = loadApmConfig({ configPath });
|
|
3112
|
+
const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
|
|
3113
|
+
console.error(
|
|
3114
|
+
`[apm] \u540E\u7AEF\u90E8\u7F72\u4E0D\u533A\u5206 test/online\uFF0C\u5FFD\u7565\u73AF\u5883\u53C2\u6570: ${options.env}`
|
|
3115
|
+
);
|
|
3116
|
+
await runWisdomBackendDeploy({
|
|
3117
|
+
config: settings,
|
|
3118
|
+
projectRoot: cwd,
|
|
3119
|
+
skipMaven: Boolean(options.skipMaven)
|
|
3120
|
+
});
|
|
3121
|
+
return { projectType };
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// src/commands/connect/deploy-run.ts
|
|
2231
3125
|
var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
|
|
2232
|
-
function
|
|
3126
|
+
function readApmConfig(workdir) {
|
|
2233
3127
|
const configPath = join13(workspaceApmDir(workdir), "apm.config.json");
|
|
3128
|
+
if (!existsSync14(configPath)) {
|
|
3129
|
+
return void 0;
|
|
3130
|
+
}
|
|
2234
3131
|
try {
|
|
2235
|
-
const raw =
|
|
2236
|
-
|
|
2237
|
-
return parsed.deploy;
|
|
3132
|
+
const raw = readFileSync12(configPath, "utf8");
|
|
3133
|
+
return JSON.parse(raw);
|
|
2238
3134
|
} catch {
|
|
2239
3135
|
return void 0;
|
|
2240
3136
|
}
|
|
2241
3137
|
}
|
|
3138
|
+
function readDeployConfig(workdir) {
|
|
3139
|
+
return readApmConfig(workdir)?.deploy;
|
|
3140
|
+
}
|
|
2242
3141
|
function resolveDeployCommand(workdir, environment) {
|
|
2243
3142
|
const command = readDeployConfig(workdir)?.[environment];
|
|
2244
3143
|
if (typeof command === "string" && command.trim()) {
|
|
@@ -2252,7 +3151,7 @@ function missingDeployCommandMessage(environment) {
|
|
|
2252
3151
|
function buildDeployLog(stdout, stderr) {
|
|
2253
3152
|
return [stdout, stderr].filter(Boolean).join("\n");
|
|
2254
3153
|
}
|
|
2255
|
-
function
|
|
3154
|
+
function runShellCommand2(command, cwd, signal, onOutput) {
|
|
2256
3155
|
return new Promise((resolve5, reject) => {
|
|
2257
3156
|
const child = spawn(command, {
|
|
2258
3157
|
cwd,
|
|
@@ -2287,15 +3186,15 @@ function runShellCommand(command, cwd, signal, onOutput) {
|
|
|
2287
3186
|
});
|
|
2288
3187
|
child.on("close", (code) => {
|
|
2289
3188
|
signal.removeEventListener("abort", onAbort);
|
|
2290
|
-
const
|
|
3189
|
+
const log2 = buildDeployLog(stdout, stderr);
|
|
2291
3190
|
if (code === 0) {
|
|
2292
|
-
resolve5({ log });
|
|
3191
|
+
resolve5({ log: log2 });
|
|
2293
3192
|
return;
|
|
2294
3193
|
}
|
|
2295
3194
|
const error = new Error(
|
|
2296
3195
|
`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${code ?? "unknown"}: ${command}`
|
|
2297
3196
|
);
|
|
2298
|
-
error.log =
|
|
3197
|
+
error.log = log2;
|
|
2299
3198
|
reject(error);
|
|
2300
3199
|
});
|
|
2301
3200
|
});
|
|
@@ -2322,8 +3221,8 @@ function createDeployLogSyncer(api, deploymentRunId) {
|
|
|
2322
3221
|
});
|
|
2323
3222
|
}, DEPLOY_LOG_SYNC_INTERVAL_MS);
|
|
2324
3223
|
return {
|
|
2325
|
-
updateLog(
|
|
2326
|
-
latestLog =
|
|
3224
|
+
updateLog(log2) {
|
|
3225
|
+
latestLog = log2;
|
|
2327
3226
|
},
|
|
2328
3227
|
async flush() {
|
|
2329
3228
|
clearInterval(timer);
|
|
@@ -2343,6 +3242,39 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2343
3242
|
status: "DEPLOYING"
|
|
2344
3243
|
});
|
|
2345
3244
|
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
3245
|
+
const apmConfig = readApmConfig(workdir);
|
|
3246
|
+
if (apmConfig && isWisdomLegacyDeploy(apmConfig)) {
|
|
3247
|
+
console.log(
|
|
3248
|
+
`[apm] deploy start id=${deploymentRunId} env=${msg.environment} cwd=${workdir} (wisdom auto)`
|
|
3249
|
+
);
|
|
3250
|
+
const logSyncer2 = createDeployLogSyncer(api, deploymentRunId);
|
|
3251
|
+
try {
|
|
3252
|
+
await runWisdomAutoDeploy({
|
|
3253
|
+
env: msg.environment,
|
|
3254
|
+
cwd: workdir
|
|
3255
|
+
});
|
|
3256
|
+
await logSyncer2.flush();
|
|
3257
|
+
await api.cli.completeTaskDeployment({
|
|
3258
|
+
id: deploymentRunId,
|
|
3259
|
+
status: "SUCCESS",
|
|
3260
|
+
log: "\u533B\u52A1\u5B58\u91CF\u9879\u76EE\u81EA\u52A8\u90E8\u7F72\u5B8C\u6210"
|
|
3261
|
+
});
|
|
3262
|
+
console.log(`[apm] deploy success id=${deploymentRunId}`);
|
|
3263
|
+
} catch (error) {
|
|
3264
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3265
|
+
await logSyncer2.flush();
|
|
3266
|
+
await api.cli.completeTaskDeployment({
|
|
3267
|
+
id: deploymentRunId,
|
|
3268
|
+
status: "FAILED",
|
|
3269
|
+
log: detail,
|
|
3270
|
+
error: detail
|
|
3271
|
+
});
|
|
3272
|
+
console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
|
|
3273
|
+
} finally {
|
|
3274
|
+
logSyncer2.dispose();
|
|
3275
|
+
}
|
|
3276
|
+
return;
|
|
3277
|
+
}
|
|
2346
3278
|
const command = resolveDeployCommand(workdir, msg.environment);
|
|
2347
3279
|
if (!command) {
|
|
2348
3280
|
const error = missingDeployCommandMessage(msg.environment);
|
|
@@ -2362,28 +3294,28 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2362
3294
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
2363
3295
|
let latestLog = "";
|
|
2364
3296
|
try {
|
|
2365
|
-
const { log } = await
|
|
2366
|
-
latestLog =
|
|
2367
|
-
logSyncer.updateLog(
|
|
3297
|
+
const { log: log2 } = await runShellCommand2(command, workdir, signal, (log3) => {
|
|
3298
|
+
latestLog = log3;
|
|
3299
|
+
logSyncer.updateLog(log3);
|
|
2368
3300
|
});
|
|
2369
|
-
latestLog =
|
|
2370
|
-
logSyncer.updateLog(
|
|
3301
|
+
latestLog = log2;
|
|
3302
|
+
logSyncer.updateLog(log2);
|
|
2371
3303
|
await logSyncer.flush();
|
|
2372
3304
|
await api.cli.completeTaskDeployment({
|
|
2373
3305
|
id: deploymentRunId,
|
|
2374
3306
|
status: "SUCCESS",
|
|
2375
|
-
log
|
|
3307
|
+
log: log2
|
|
2376
3308
|
});
|
|
2377
3309
|
console.log(`[apm] deploy success id=${deploymentRunId}`);
|
|
2378
3310
|
} catch (error) {
|
|
2379
3311
|
const detail = error instanceof Error ? error.message : String(error);
|
|
2380
|
-
const
|
|
2381
|
-
logSyncer.updateLog(
|
|
3312
|
+
const log2 = error && typeof error === "object" && "log" in error ? String(error.log ?? latestLog) : latestLog;
|
|
3313
|
+
logSyncer.updateLog(log2);
|
|
2382
3314
|
await logSyncer.flush();
|
|
2383
3315
|
await api.cli.completeTaskDeployment({
|
|
2384
3316
|
id: deploymentRunId,
|
|
2385
3317
|
status: "FAILED",
|
|
2386
|
-
log,
|
|
3318
|
+
log: log2,
|
|
2387
3319
|
error: detail
|
|
2388
3320
|
});
|
|
2389
3321
|
console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
|
|
@@ -2659,17 +3591,17 @@ ${JSON.stringify(event, null, 2)}
|
|
|
2659
3591
|
}
|
|
2660
3592
|
|
|
2661
3593
|
// src/commands/connect/agent-session-registry.ts
|
|
2662
|
-
import { existsSync as
|
|
2663
|
-
import { dirname as dirname4, resolve as
|
|
3594
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "node:fs";
|
|
3595
|
+
import { dirname as dirname4, resolve as resolve4 } from "node:path";
|
|
2664
3596
|
function registryPath(workdir, sessionId) {
|
|
2665
|
-
return
|
|
3597
|
+
return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
2666
3598
|
}
|
|
2667
|
-
function readRegistry(
|
|
2668
|
-
if (!
|
|
3599
|
+
function readRegistry(path12) {
|
|
3600
|
+
if (!existsSync15(path12)) {
|
|
2669
3601
|
return {};
|
|
2670
3602
|
}
|
|
2671
3603
|
try {
|
|
2672
|
-
const parsed = JSON.parse(
|
|
3604
|
+
const parsed = JSON.parse(readFileSync13(path12, "utf8"));
|
|
2673
3605
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2674
3606
|
const result = {};
|
|
2675
3607
|
for (const [key, value] of Object.entries(
|
|
@@ -2685,9 +3617,9 @@ function readRegistry(path10) {
|
|
|
2685
3617
|
}
|
|
2686
3618
|
return {};
|
|
2687
3619
|
}
|
|
2688
|
-
function writeRegistry(
|
|
2689
|
-
|
|
2690
|
-
|
|
3620
|
+
function writeRegistry(path12, registry) {
|
|
3621
|
+
mkdirSync6(dirname4(path12), { recursive: true });
|
|
3622
|
+
writeFileSync11(path12, `${JSON.stringify(registry, null, 2)}
|
|
2691
3623
|
`, "utf8");
|
|
2692
3624
|
}
|
|
2693
3625
|
function loadSessionAgentId(workdir, sessionId, user) {
|
|
@@ -2695,19 +3627,19 @@ function loadSessionAgentId(workdir, sessionId, user) {
|
|
|
2695
3627
|
return registry[user];
|
|
2696
3628
|
}
|
|
2697
3629
|
function saveSessionAgentId(workdir, sessionId, user, agentId) {
|
|
2698
|
-
const
|
|
2699
|
-
const registry = readRegistry(
|
|
3630
|
+
const path12 = registryPath(workdir, sessionId);
|
|
3631
|
+
const registry = readRegistry(path12);
|
|
2700
3632
|
registry[user] = agentId;
|
|
2701
|
-
writeRegistry(
|
|
3633
|
+
writeRegistry(path12, registry);
|
|
2702
3634
|
}
|
|
2703
3635
|
function clearSessionAgentId(workdir, sessionId, user) {
|
|
2704
|
-
const
|
|
2705
|
-
const registry = readRegistry(
|
|
3636
|
+
const path12 = registryPath(workdir, sessionId);
|
|
3637
|
+
const registry = readRegistry(path12);
|
|
2706
3638
|
if (!(user in registry)) {
|
|
2707
3639
|
return;
|
|
2708
3640
|
}
|
|
2709
3641
|
delete registry[user];
|
|
2710
|
-
writeRegistry(
|
|
3642
|
+
writeRegistry(path12, registry);
|
|
2711
3643
|
}
|
|
2712
3644
|
|
|
2713
3645
|
// src/commands/connect/cursor-message-log.ts
|
|
@@ -3101,20 +4033,20 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
3101
4033
|
}
|
|
3102
4034
|
|
|
3103
4035
|
// src/commands/connect/cli-version-sync.ts
|
|
3104
|
-
import { existsSync as
|
|
4036
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
|
|
3105
4037
|
import { join as join14 } from "path";
|
|
3106
4038
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3107
4039
|
function manifestPath(apmDir) {
|
|
3108
4040
|
return join14(apmDir, CLI_VERSION_FILE);
|
|
3109
4041
|
}
|
|
3110
|
-
function
|
|
3111
|
-
const
|
|
3112
|
-
if (!
|
|
4042
|
+
function loadManifest4(apmDir) {
|
|
4043
|
+
const path12 = toFsPath(manifestPath(apmDir));
|
|
4044
|
+
if (!existsSync16(path12)) {
|
|
3113
4045
|
return null;
|
|
3114
4046
|
}
|
|
3115
4047
|
try {
|
|
3116
4048
|
const parsed = JSON.parse(
|
|
3117
|
-
|
|
4049
|
+
readFileSync14(path12, "utf8")
|
|
3118
4050
|
);
|
|
3119
4051
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
3120
4052
|
return parsed;
|
|
@@ -3123,9 +4055,9 @@ function loadManifest3(apmDir) {
|
|
|
3123
4055
|
}
|
|
3124
4056
|
return null;
|
|
3125
4057
|
}
|
|
3126
|
-
function
|
|
4058
|
+
function saveManifest4(apmDir, cliVersion) {
|
|
3127
4059
|
const manifest = { version: 1, cliVersion };
|
|
3128
|
-
|
|
4060
|
+
writeFileSync12(
|
|
3129
4061
|
toFsPath(manifestPath(apmDir)),
|
|
3130
4062
|
`${JSON.stringify(manifest, null, 2)}
|
|
3131
4063
|
`,
|
|
@@ -3138,7 +4070,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
|
3138
4070
|
if (cached === currentVersion) {
|
|
3139
4071
|
return false;
|
|
3140
4072
|
}
|
|
3141
|
-
const stored =
|
|
4073
|
+
const stored = loadManifest4(workspaceApmDir(workdir));
|
|
3142
4074
|
if (stored?.cliVersion === currentVersion) {
|
|
3143
4075
|
syncedInSession.set(workdir, currentVersion);
|
|
3144
4076
|
return false;
|
|
@@ -3146,7 +4078,7 @@ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
|
|
|
3146
4078
|
return true;
|
|
3147
4079
|
}
|
|
3148
4080
|
function markSkillsSyncedForCliVersion(workdir, cliVersion) {
|
|
3149
|
-
|
|
4081
|
+
saveManifest4(workspaceApmDir(workdir), cliVersion);
|
|
3150
4082
|
syncedInSession.set(workdir, cliVersion);
|
|
3151
4083
|
}
|
|
3152
4084
|
|
|
@@ -3373,7 +4305,7 @@ function reexecConnect(options) {
|
|
|
3373
4305
|
if (server) {
|
|
3374
4306
|
args.push("--server", server);
|
|
3375
4307
|
}
|
|
3376
|
-
const result =
|
|
4308
|
+
const result = spawnSync4(process.execPath, args, { stdio: "inherit" });
|
|
3377
4309
|
if (result.error) {
|
|
3378
4310
|
console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
|
|
3379
4311
|
process.exit(1);
|
|
@@ -3509,202 +4441,123 @@ async function runConnect(options) {
|
|
|
3509
4441
|
activeTasks.delete(task);
|
|
3510
4442
|
});
|
|
3511
4443
|
});
|
|
3512
|
-
ws.on("close", (code, reason) => {
|
|
3513
|
-
console.log(
|
|
3514
|
-
`[apm] \u8FDE\u63A5\u5DF2\u65AD\u5F00 code=${code}${reason ? ` reason=${reason.toString()}` : ""}`
|
|
3515
|
-
);
|
|
3516
|
-
void shutdown();
|
|
3517
|
-
});
|
|
3518
|
-
ws.on("error", (err) => {
|
|
3519
|
-
console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
|
|
3520
|
-
reject(err);
|
|
3521
|
-
});
|
|
3522
|
-
process.on("SIGINT", () => {
|
|
3523
|
-
console.log("[apm] \u6B63\u5728\u5173\u95ED\u2026");
|
|
3524
|
-
void shutdown();
|
|
3525
|
-
});
|
|
3526
|
-
process.on("SIGTERM", () => {
|
|
3527
|
-
void shutdown();
|
|
3528
|
-
});
|
|
3529
|
-
});
|
|
3530
|
-
}
|
|
3531
|
-
|
|
3532
|
-
// src/commands/create-pr.ts
|
|
3533
|
-
async function runCreatePr(options) {
|
|
3534
|
-
const sessionId = options.sessionId.trim();
|
|
3535
|
-
if (!sessionId) {
|
|
3536
|
-
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3537
|
-
process.exit(1);
|
|
3538
|
-
}
|
|
3539
|
-
const title = options.title.trim();
|
|
3540
|
-
if (!title) {
|
|
3541
|
-
console.error("[apm] \u8BF7\u901A\u8FC7 --title \u6307\u5B9A PR \u6807\u9898");
|
|
3542
|
-
process.exit(1);
|
|
3543
|
-
}
|
|
3544
|
-
const cfg = await ensureLoggedConfig();
|
|
3545
|
-
const api = createApmApiClient(cfg);
|
|
3546
|
-
const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
|
|
3547
|
-
const pr = await api.cli.createPullRequest({
|
|
3548
|
-
sessionId,
|
|
3549
|
-
workdir,
|
|
3550
|
-
title,
|
|
3551
|
-
content: options.content ?? ""
|
|
3552
|
-
});
|
|
3553
|
-
console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
|
|
3554
|
-
}
|
|
3555
|
-
|
|
3556
|
-
// src/commands/deploy/backend.ts
|
|
3557
|
-
import path5 from "node:path";
|
|
3558
|
-
|
|
3559
|
-
// src/commands/deploy/internal/apm-config.ts
|
|
3560
|
-
import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
|
|
3561
|
-
import { resolve as resolve4 } from "node:path";
|
|
3562
|
-
function loadApmConfig(options) {
|
|
3563
|
-
const p = resolve4(
|
|
3564
|
-
process.cwd(),
|
|
3565
|
-
options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
|
|
3566
|
-
);
|
|
3567
|
-
if (!existsSync13(p)) {
|
|
3568
|
-
console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
|
|
3569
|
-
process.exit(1);
|
|
3570
|
-
}
|
|
3571
|
-
try {
|
|
3572
|
-
const raw = readFileSync12(p, "utf8");
|
|
3573
|
-
return JSON.parse(raw);
|
|
3574
|
-
} catch (e) {
|
|
3575
|
-
console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
|
|
3576
|
-
process.exit(1);
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
function req(v, field) {
|
|
3580
|
-
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
3581
|
-
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
3582
|
-
process.exit(1);
|
|
3583
|
-
}
|
|
3584
|
-
return v;
|
|
3585
|
-
}
|
|
3586
|
-
function reqTopLevelName(cfg) {
|
|
3587
|
-
const n = (cfg.name ?? "").trim();
|
|
3588
|
-
if (!n) {
|
|
3589
|
-
console.error(
|
|
3590
|
-
"\u8BF7\u5728 apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4E0E\u524D\u7AEF\u5236\u54C1\u524D\u7F00\u3001\u540E\u7AEF\u955C\u50CF\u540D\u5171\u7528\uFF09"
|
|
3591
|
-
);
|
|
3592
|
-
process.exit(1);
|
|
3593
|
-
}
|
|
3594
|
-
return n;
|
|
3595
|
-
}
|
|
3596
|
-
function reqBackendPositiveInt(v, field) {
|
|
3597
|
-
const n = Number(v);
|
|
3598
|
-
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
|
|
3599
|
-
console.error(`apm.config.json \u4E2D backendDeploy.${field} \u987B\u4E3A\u6B63\u6574\u6570`);
|
|
3600
|
-
process.exit(1);
|
|
3601
|
-
}
|
|
3602
|
-
return n;
|
|
3603
|
-
}
|
|
3604
|
-
function resolveBackendDeployFromApmConfig(cfg) {
|
|
3605
|
-
const b = cfg.backendDeploy ?? {};
|
|
3606
|
-
const protoRaw = req(b.remoteProtocol, "remoteProtocol").trim().toLowerCase();
|
|
3607
|
-
if (protoRaw !== "http" && protoRaw !== "https") {
|
|
3608
|
-
console.error(
|
|
3609
|
-
"apm.config.json \u4E2D backendDeploy.remoteProtocol \u53EA\u80FD\u4E3A http \u6216 https"
|
|
3610
|
-
);
|
|
3611
|
-
process.exit(1);
|
|
3612
|
-
}
|
|
3613
|
-
const remoteProtocol = protoRaw;
|
|
3614
|
-
let mappings = [];
|
|
3615
|
-
const rawPorts = b.containerPortsMappings;
|
|
3616
|
-
if (rawPorts !== void 0 && rawPorts !== null) {
|
|
3617
|
-
if (!Array.isArray(rawPorts)) {
|
|
3618
|
-
console.error(
|
|
3619
|
-
"apm.config.json \u4E2D backendDeploy.containerPortsMappings \u987B\u4E3A\u5B57\u7B26\u4E32\u6570\u7EC4\uFF08\u53EF\u4E3A\u7A7A\uFF0C\u7701\u7565\u5219\u4E0D\u52A0\u7AEF\u53E3\u6620\u5C04\uFF09"
|
|
3620
|
-
);
|
|
3621
|
-
process.exit(1);
|
|
3622
|
-
}
|
|
3623
|
-
mappings = rawPorts.map((x) => String(x).trim()).filter(Boolean);
|
|
3624
|
-
}
|
|
3625
|
-
return {
|
|
3626
|
-
name: reqTopLevelName(cfg),
|
|
3627
|
-
registryHost: req(b.registryHost, "registryHost").trim(),
|
|
3628
|
-
registryNamespace: req(b.registryNamespace, "registryNamespace").trim(),
|
|
3629
|
-
registryUser: req(b.registryUser, "registryUser").trim(),
|
|
3630
|
-
registryPassword: req(b.registryPassword, "registryPassword").trim(),
|
|
3631
|
-
remoteHost: req(b.remoteHost, "remoteHost").trim(),
|
|
3632
|
-
remotePort: reqBackendPositiveInt(b.remotePort, "remotePort"),
|
|
3633
|
-
remoteProtocol,
|
|
3634
|
-
caPath: b.caPath?.trim(),
|
|
3635
|
-
certPath: b.certPath?.trim(),
|
|
3636
|
-
keyPath: b.keyPath?.trim(),
|
|
3637
|
-
envFilePath: typeof b.envFilePath === "string" ? b.envFilePath.trim() : "",
|
|
3638
|
-
containerPortsMappings: mappings,
|
|
3639
|
-
dockerNetwork: b.dockerNetwork?.trim() || void 0
|
|
3640
|
-
};
|
|
4444
|
+
ws.on("close", (code, reason) => {
|
|
4445
|
+
console.log(
|
|
4446
|
+
`[apm] \u8FDE\u63A5\u5DF2\u65AD\u5F00 code=${code}${reason ? ` reason=${reason.toString()}` : ""}`
|
|
4447
|
+
);
|
|
4448
|
+
void shutdown();
|
|
4449
|
+
});
|
|
4450
|
+
ws.on("error", (err) => {
|
|
4451
|
+
console.error("[apm] WebSocket \u9519\u8BEF:", err.message);
|
|
4452
|
+
reject(err);
|
|
4453
|
+
});
|
|
4454
|
+
process.on("SIGINT", () => {
|
|
4455
|
+
console.log("[apm] \u6B63\u5728\u5173\u95ED\u2026");
|
|
4456
|
+
void shutdown();
|
|
4457
|
+
});
|
|
4458
|
+
process.on("SIGTERM", () => {
|
|
4459
|
+
void shutdown();
|
|
4460
|
+
});
|
|
4461
|
+
});
|
|
3641
4462
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
4463
|
+
|
|
4464
|
+
// src/commands/create-pr.ts
|
|
4465
|
+
async function runCreatePr(options) {
|
|
4466
|
+
const sessionId = options.sessionId.trim();
|
|
4467
|
+
if (!sessionId) {
|
|
4468
|
+
console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
3645
4469
|
process.exit(1);
|
|
3646
4470
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
const f = cfg.frontendDeploy ?? {};
|
|
3651
|
-
const port = Number(f.port);
|
|
3652
|
-
return {
|
|
3653
|
-
endpoint: reqFe(f.endpoint, "endpoint").trim(),
|
|
3654
|
-
port: Number.isFinite(port) && port > 0 ? port : 9e3,
|
|
3655
|
-
useSsl: Boolean(f.useSsl),
|
|
3656
|
-
accessKey: reqFe(f.accessKey, "accessKey").trim(),
|
|
3657
|
-
secretKey: reqFe(f.secretKey, "secretKey").trim(),
|
|
3658
|
-
bucket: reqFe(f.bucket, "bucket").trim()
|
|
3659
|
-
};
|
|
3660
|
-
}
|
|
3661
|
-
function reqWd(v, field) {
|
|
3662
|
-
if (v === void 0 || v === null || typeof v === "string" && !v.trim()) {
|
|
3663
|
-
console.error(`apm.config.json \u4E2D wisdomDeploy.${field} \u4E0D\u80FD\u4E3A\u7A7A`);
|
|
4471
|
+
const title = options.title.trim();
|
|
4472
|
+
if (!title) {
|
|
4473
|
+
console.error("[apm] \u8BF7\u901A\u8FC7 --title \u6307\u5B9A PR \u6807\u9898");
|
|
3664
4474
|
process.exit(1);
|
|
3665
4475
|
}
|
|
3666
|
-
|
|
4476
|
+
const cfg = await ensureLoggedConfig();
|
|
4477
|
+
const api = createApmApiClient(cfg);
|
|
4478
|
+
const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
|
|
4479
|
+
const pr = await api.cli.createPullRequest({
|
|
4480
|
+
sessionId,
|
|
4481
|
+
workdir,
|
|
4482
|
+
title,
|
|
4483
|
+
content: options.content ?? ""
|
|
4484
|
+
});
|
|
4485
|
+
console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
|
|
3667
4486
|
}
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
4487
|
+
|
|
4488
|
+
// src/commands/deploy/deploy.ts
|
|
4489
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4490
|
+
function runShellCommand3(command, cwd) {
|
|
4491
|
+
console.error(`==> ${command}`);
|
|
4492
|
+
const result = spawnSync5(command, {
|
|
4493
|
+
cwd,
|
|
4494
|
+
stdio: "inherit",
|
|
4495
|
+
shell: true,
|
|
4496
|
+
env: process.env
|
|
4497
|
+
});
|
|
4498
|
+
if (result.status !== 0) {
|
|
4499
|
+
process.exit(result.status ?? 1);
|
|
3673
4500
|
}
|
|
3674
|
-
return n;
|
|
3675
4501
|
}
|
|
3676
|
-
function
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
4502
|
+
function registerDeployMainCommand(program) {
|
|
4503
|
+
program.command("deploy").description(
|
|
4504
|
+
"\u7EDF\u4E00\u90E8\u7F72\uFF1A\u533B\u52A1\u5B58\u91CF\u81EA\u52A8\u8BC6\u522B\u524D\u540E\u7AEF\uFF08\u524D\u7AEF\u6309 deploy:<env> \u811A\u672C\uFF1B\u540E\u7AEF test/online \u540C\u4E00\u5957\uFF09\uFF1B\u5176\u4ED6\u9879\u76EE\u6309 deploy.<\u73AF\u5883> \u6267\u884C shell \u547D\u4EE4"
|
|
4505
|
+
).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
|
|
4506
|
+
"--config <path>",
|
|
4507
|
+
"apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
|
|
4508
|
+
).option("--skip-maven", "\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF08\u4EC5\u533B\u52A1\u540E\u7AEF\uFF09").action(
|
|
4509
|
+
async (env, opts) => {
|
|
4510
|
+
const cwd = process.cwd();
|
|
4511
|
+
const cfg = loadApmConfig({ configPath: opts.config });
|
|
4512
|
+
if (isWisdomLegacyDeploy(cfg)) {
|
|
4513
|
+
await runWisdomAutoDeploy({
|
|
4514
|
+
env,
|
|
4515
|
+
cwd,
|
|
4516
|
+
configPath: opts.config,
|
|
4517
|
+
skipMaven: opts.skipMaven
|
|
4518
|
+
});
|
|
4519
|
+
return;
|
|
4520
|
+
}
|
|
4521
|
+
const deployCommands = cfg.deploy ?? {};
|
|
4522
|
+
const command = deployCommands[env]?.trim();
|
|
4523
|
+
if (!command) {
|
|
4524
|
+
const keys = Object.keys(deployCommands);
|
|
4525
|
+
console.error(
|
|
4526
|
+
keys.length > 0 ? `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}\uFF0C\u53EF\u7528\u73AF\u5883: ${keys.join(
|
|
4527
|
+
", "
|
|
4528
|
+
)}` : `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy \u6BB5`
|
|
4529
|
+
);
|
|
4530
|
+
process.exit(1);
|
|
4531
|
+
}
|
|
4532
|
+
runShellCommand3(command, cwd);
|
|
4533
|
+
}
|
|
4534
|
+
);
|
|
3685
4535
|
}
|
|
3686
4536
|
|
|
4537
|
+
// src/commands/deploy/backend.ts
|
|
4538
|
+
import path8 from "node:path";
|
|
4539
|
+
|
|
3687
4540
|
// src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
|
|
3688
|
-
import
|
|
4541
|
+
import path7 from "node:path";
|
|
3689
4542
|
|
|
3690
4543
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
|
|
3691
4544
|
import Docker from "dockerode";
|
|
3692
4545
|
|
|
3693
4546
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
3694
|
-
import { existsSync as
|
|
3695
|
-
import
|
|
4547
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "node:fs";
|
|
4548
|
+
import path4 from "node:path";
|
|
3696
4549
|
function asOptionalTlsBuffer(value) {
|
|
3697
4550
|
if (typeof value !== "string") {
|
|
3698
4551
|
console.log("tls filepath not exist");
|
|
3699
4552
|
return void 0;
|
|
3700
4553
|
}
|
|
3701
|
-
console.log("tls filepath",
|
|
4554
|
+
console.log("tls filepath", path4.join(process.cwd(), value));
|
|
3702
4555
|
const normalized = value.trim();
|
|
3703
4556
|
if (normalized === "") {
|
|
3704
4557
|
return void 0;
|
|
3705
4558
|
}
|
|
3706
|
-
if (
|
|
3707
|
-
return
|
|
4559
|
+
if (existsSync17(normalized)) {
|
|
4560
|
+
return readFileSync15(normalized);
|
|
3708
4561
|
}
|
|
3709
4562
|
const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
|
|
3710
4563
|
if (looksLikePath) {
|
|
@@ -3914,8 +4767,8 @@ var DockerodeClient = class {
|
|
|
3914
4767
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
3915
4768
|
|
|
3916
4769
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
3917
|
-
import { existsSync as
|
|
3918
|
-
import
|
|
4770
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16, statSync as statSync6 } from "node:fs";
|
|
4771
|
+
import path5 from "node:path";
|
|
3919
4772
|
function stripSurroundingQuotes(value) {
|
|
3920
4773
|
const t = value.trim();
|
|
3921
4774
|
if (t.length >= 2) {
|
|
@@ -3930,11 +4783,11 @@ function loadEnvFromFile(envFilePath) {
|
|
|
3930
4783
|
if (!envFilePath) {
|
|
3931
4784
|
return {};
|
|
3932
4785
|
}
|
|
3933
|
-
const targetPath =
|
|
3934
|
-
if (!
|
|
4786
|
+
const targetPath = path5.resolve(envFilePath);
|
|
4787
|
+
if (!existsSync18(targetPath) || !statSync6(targetPath).isFile()) {
|
|
3935
4788
|
return {};
|
|
3936
4789
|
}
|
|
3937
|
-
const raw =
|
|
4790
|
+
const raw = readFileSync16(targetPath, "utf-8");
|
|
3938
4791
|
const result = {};
|
|
3939
4792
|
for (const line of raw.split(/\r?\n/)) {
|
|
3940
4793
|
const normalized = line.trim();
|
|
@@ -4105,12 +4958,12 @@ function dockerPushImage(params, cwd) {
|
|
|
4105
4958
|
}
|
|
4106
4959
|
|
|
4107
4960
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
4108
|
-
import { existsSync as
|
|
4109
|
-
import
|
|
4961
|
+
import { existsSync as existsSync19 } from "node:fs";
|
|
4962
|
+
import path6 from "node:path";
|
|
4110
4963
|
function resolveDockerBuildPaths(cwd) {
|
|
4111
|
-
const dockerfilePath =
|
|
4964
|
+
const dockerfilePath = path6.join(cwd, "Dockerfile");
|
|
4112
4965
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
4113
|
-
if (!
|
|
4966
|
+
if (!existsSync19(dockerfilePath)) {
|
|
4114
4967
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
4115
4968
|
}
|
|
4116
4969
|
Logger.info("\u2713 Dockerfile \u5B58\u5728");
|
|
@@ -4165,7 +5018,7 @@ var BackendDeployWorkflow = class {
|
|
|
4165
5018
|
serveraddress
|
|
4166
5019
|
});
|
|
4167
5020
|
Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
|
|
4168
|
-
const envFilePath =
|
|
5021
|
+
const envFilePath = path7.resolve(cwd, this.params.envFilePath || ".env");
|
|
4169
5022
|
const envObj = loadEnvFromFile(envFilePath);
|
|
4170
5023
|
if (this.params.dockerNetwork?.trim()) {
|
|
4171
5024
|
Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
|
|
@@ -4205,7 +5058,7 @@ function registerDeployBackendCommands(program) {
|
|
|
4205
5058
|
assertDeployImageTag(tag);
|
|
4206
5059
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
4207
5060
|
const fromApm = resolveBackendDeployFromApmConfig(cfg);
|
|
4208
|
-
const dirAbs =
|
|
5061
|
+
const dirAbs = path8.resolve(process.cwd(), opts.dir || "servers/api");
|
|
4209
5062
|
const params = {
|
|
4210
5063
|
image: fromApm.name,
|
|
4211
5064
|
tag,
|
|
@@ -4235,18 +5088,18 @@ function registerDeployBackendCommands(program) {
|
|
|
4235
5088
|
}
|
|
4236
5089
|
|
|
4237
5090
|
// src/commands/deploy/frontend.ts
|
|
4238
|
-
import { copyFile, readdir as
|
|
4239
|
-
import
|
|
5091
|
+
import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
|
|
5092
|
+
import path10 from "node:path";
|
|
4240
5093
|
|
|
4241
5094
|
// src/commands/deploy/internal/minio.ts
|
|
4242
|
-
import { statSync as
|
|
4243
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
4244
|
-
import
|
|
5095
|
+
import { statSync as statSync7 } from "node:fs";
|
|
5096
|
+
import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
|
|
5097
|
+
import path9 from "node:path";
|
|
4245
5098
|
import * as Minio from "minio";
|
|
4246
5099
|
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
4247
5100
|
async function isDirectoryPath(dir) {
|
|
4248
5101
|
try {
|
|
4249
|
-
const st =
|
|
5102
|
+
const st = statSync7(dir);
|
|
4250
5103
|
return st.isDirectory();
|
|
4251
5104
|
} catch {
|
|
4252
5105
|
return false;
|
|
@@ -4265,18 +5118,18 @@ function sanitizeRelativePath(rel) {
|
|
|
4265
5118
|
async function collectFiles(root) {
|
|
4266
5119
|
const out = [];
|
|
4267
5120
|
async function walk(dir, prefix) {
|
|
4268
|
-
const entries = await
|
|
5121
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
4269
5122
|
for (const e of entries) {
|
|
4270
5123
|
const name = e.name;
|
|
4271
5124
|
if (name === "." || name === "..") {
|
|
4272
5125
|
continue;
|
|
4273
5126
|
}
|
|
4274
|
-
const abs =
|
|
5127
|
+
const abs = path9.join(dir, name);
|
|
4275
5128
|
const rel = prefix ? `${prefix}/${name}` : name;
|
|
4276
5129
|
if (e.isDirectory()) {
|
|
4277
5130
|
await walk(abs, rel);
|
|
4278
5131
|
} else if (e.isFile()) {
|
|
4279
|
-
const st =
|
|
5132
|
+
const st = statSync7(abs);
|
|
4280
5133
|
out.push({
|
|
4281
5134
|
absPath: abs,
|
|
4282
5135
|
relativePath: rel.replace(/\\/g, "/"),
|
|
@@ -4290,7 +5143,7 @@ async function collectFiles(root) {
|
|
|
4290
5143
|
return out;
|
|
4291
5144
|
}
|
|
4292
5145
|
async function readArtifactFile(absPath) {
|
|
4293
|
-
return
|
|
5146
|
+
return readFile2(absPath);
|
|
4294
5147
|
}
|
|
4295
5148
|
function toMB(bytes) {
|
|
4296
5149
|
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
@@ -4314,7 +5167,7 @@ var MIME = {
|
|
|
4314
5167
|
".map": "application/json"
|
|
4315
5168
|
};
|
|
4316
5169
|
function detectMimeType(filePath) {
|
|
4317
|
-
const ext =
|
|
5170
|
+
const ext = path9.extname(filePath).toLowerCase();
|
|
4318
5171
|
return MIME[ext] ?? "";
|
|
4319
5172
|
}
|
|
4320
5173
|
var MinioClient = class {
|
|
@@ -4402,7 +5255,7 @@ function artifactObjectKey(namePrefix, branchSegment, relativePath) {
|
|
|
4402
5255
|
return `${base}/${branchSegment}/dist/${rel}`;
|
|
4403
5256
|
}
|
|
4404
5257
|
async function ensureArtifactRootIndexHtml(root) {
|
|
4405
|
-
const indexHtmlPath =
|
|
5258
|
+
const indexHtmlPath = path10.join(root, "index.html");
|
|
4406
5259
|
try {
|
|
4407
5260
|
const st = await stat(indexHtmlPath);
|
|
4408
5261
|
if (st.isFile()) {
|
|
@@ -4412,14 +5265,14 @@ async function ensureArtifactRootIndexHtml(root) {
|
|
|
4412
5265
|
}
|
|
4413
5266
|
let entries;
|
|
4414
5267
|
try {
|
|
4415
|
-
entries = await
|
|
5268
|
+
entries = await readdir3(root, { withFileTypes: true });
|
|
4416
5269
|
} catch (e) {
|
|
4417
5270
|
console.error(`\u65E0\u6CD5\u8BFB\u53D6\u4EA7\u7269\u76EE\u5F55\uFF1A${root}`, e);
|
|
4418
5271
|
process.exit(1);
|
|
4419
5272
|
}
|
|
4420
5273
|
const dirNames = entries.filter((e) => e.isDirectory()).map((e) => String(e.name)).sort();
|
|
4421
5274
|
for (const name of dirNames) {
|
|
4422
|
-
const candidate =
|
|
5275
|
+
const candidate = path10.join(root, name, "index.html");
|
|
4423
5276
|
try {
|
|
4424
5277
|
const st = await stat(candidate);
|
|
4425
5278
|
if (!st.isFile()) {
|
|
@@ -4430,7 +5283,7 @@ async function ensureArtifactRootIndexHtml(root) {
|
|
|
4430
5283
|
}
|
|
4431
5284
|
await copyFile(candidate, indexHtmlPath);
|
|
4432
5285
|
console.error(
|
|
4433
|
-
`\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${
|
|
5286
|
+
`\u4EA7\u7269\u534F\u8BAE\uFF1A\u5DF2\u5C06 ${path10.join(name, "index.html")} \u590D\u5236\u4E3A\u6839\u76EE\u5F55 index.html`
|
|
4434
5287
|
);
|
|
4435
5288
|
return;
|
|
4436
5289
|
}
|
|
@@ -4466,7 +5319,7 @@ function registerDeployFrontendCommands(program) {
|
|
|
4466
5319
|
secretKey: settings.secretKey
|
|
4467
5320
|
});
|
|
4468
5321
|
const bucket = settings.bucket;
|
|
4469
|
-
const root =
|
|
5322
|
+
const root = path10.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
4470
5323
|
if (!await isDirectoryPath(root)) {
|
|
4471
5324
|
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
4472
5325
|
process.exit(1);
|
|
@@ -4535,159 +5388,7 @@ function registerDeployFrontendCommands(program) {
|
|
|
4535
5388
|
}
|
|
4536
5389
|
|
|
4537
5390
|
// src/commands/deploy/sftp.ts
|
|
4538
|
-
import
|
|
4539
|
-
|
|
4540
|
-
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
4541
|
-
import { readdir as readdir3, readFile as readFile2, unlink, writeFile } from "node:fs/promises";
|
|
4542
|
-
import path8 from "node:path";
|
|
4543
|
-
import JSZip from "jszip";
|
|
4544
|
-
import SftpClient from "ssh2-sftp-client";
|
|
4545
|
-
async function addDirToZip(dir, zipFolder) {
|
|
4546
|
-
const entries = await readdir3(dir, { withFileTypes: true });
|
|
4547
|
-
for (const entry of entries) {
|
|
4548
|
-
const fullPath = path8.join(dir, entry.name);
|
|
4549
|
-
if (entry.isDirectory()) {
|
|
4550
|
-
const folder = zipFolder.folder(entry.name);
|
|
4551
|
-
if (folder) {
|
|
4552
|
-
await addDirToZip(fullPath, folder);
|
|
4553
|
-
}
|
|
4554
|
-
} else {
|
|
4555
|
-
const content = await readFile2(fullPath);
|
|
4556
|
-
zipFolder.file(entry.name, content);
|
|
4557
|
-
}
|
|
4558
|
-
}
|
|
4559
|
-
}
|
|
4560
|
-
async function zipDirectory(distDir, zipPath) {
|
|
4561
|
-
console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
|
|
4562
|
-
const zip = new JSZip();
|
|
4563
|
-
await addDirToZip(distDir, zip);
|
|
4564
|
-
const content = await zip.generateAsync({
|
|
4565
|
-
type: "nodebuffer",
|
|
4566
|
-
compression: "DEFLATE",
|
|
4567
|
-
compressionOptions: { level: 6 }
|
|
4568
|
-
});
|
|
4569
|
-
await writeFile(zipPath, content);
|
|
4570
|
-
const sizeMb = (content.length / 1024 / 1024).toFixed(2);
|
|
4571
|
-
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
4572
|
-
return content.length;
|
|
4573
|
-
}
|
|
4574
|
-
async function ensureRemoteDir(sftp, dir) {
|
|
4575
|
-
const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4576
|
-
let current = dir.startsWith("/") ? "" : ".";
|
|
4577
|
-
for (const part of parts) {
|
|
4578
|
-
current = current ? `${current}/${part}` : `/${part}`;
|
|
4579
|
-
try {
|
|
4580
|
-
await sftp.mkdir(current, true);
|
|
4581
|
-
} catch {
|
|
4582
|
-
}
|
|
4583
|
-
}
|
|
4584
|
-
}
|
|
4585
|
-
function execCommand(client, command) {
|
|
4586
|
-
return new Promise((resolve5, reject) => {
|
|
4587
|
-
client.exec(command, (err, stream) => {
|
|
4588
|
-
if (err) return reject(err);
|
|
4589
|
-
let stdout = "";
|
|
4590
|
-
let stderr = "";
|
|
4591
|
-
stream.on("close", (code) => {
|
|
4592
|
-
if (code !== 0) {
|
|
4593
|
-
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
4594
|
-
return;
|
|
4595
|
-
}
|
|
4596
|
-
resolve5(stdout);
|
|
4597
|
-
}).on("data", (data) => {
|
|
4598
|
-
stdout += data.toString();
|
|
4599
|
-
});
|
|
4600
|
-
stream.stderr.on("data", (data) => {
|
|
4601
|
-
stderr += data.toString();
|
|
4602
|
-
});
|
|
4603
|
-
});
|
|
4604
|
-
});
|
|
4605
|
-
}
|
|
4606
|
-
function shellSingleQuote(value) {
|
|
4607
|
-
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
4608
|
-
}
|
|
4609
|
-
function buildClearRemoteDirExceptZipCommand(target) {
|
|
4610
|
-
const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
|
|
4611
|
-
const quotedTarget = shellSingleQuote(normalized);
|
|
4612
|
-
const script = [
|
|
4613
|
-
`T=${quotedTarget}`,
|
|
4614
|
-
"S=$(mktemp -d)",
|
|
4615
|
-
'while IFS= read -r -d "" z; do r="${z#${T}/}"',
|
|
4616
|
-
'mkdir -p "${S}/$(dirname "$r")"',
|
|
4617
|
-
'mv "$z" "${S}/${r}"',
|
|
4618
|
-
'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
|
|
4619
|
-
'rm -rf "${T}"/*',
|
|
4620
|
-
'while IFS= read -r -d "" r; do r="${r#./}"',
|
|
4621
|
-
'mkdir -p "${T}/$(dirname "$r")"',
|
|
4622
|
-
'mv "${S}/${r}" "${T}/${r}"',
|
|
4623
|
-
'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
|
|
4624
|
-
'rm -rf "$S"'
|
|
4625
|
-
].join("; ");
|
|
4626
|
-
return `bash -c ${shellSingleQuote(script)}`;
|
|
4627
|
-
}
|
|
4628
|
-
async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
4629
|
-
const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
|
|
4630
|
-
console.error(
|
|
4631
|
-
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
4632
|
-
);
|
|
4633
|
-
const sftp = new SftpClient();
|
|
4634
|
-
try {
|
|
4635
|
-
await sftp.connect({
|
|
4636
|
-
host: settings.host,
|
|
4637
|
-
port: settings.port,
|
|
4638
|
-
username: settings.username,
|
|
4639
|
-
password: settings.password,
|
|
4640
|
-
readyTimeout: 2e4,
|
|
4641
|
-
tryKeyboard: true
|
|
4642
|
-
});
|
|
4643
|
-
await ensureRemoteDir(sftp, settings.remotePath);
|
|
4644
|
-
await sftp.put(localZip, remoteZipPath);
|
|
4645
|
-
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
4646
|
-
if (extract) {
|
|
4647
|
-
const target = settings.remotePath.replace(/\/$/, "");
|
|
4648
|
-
const client = sftp.client;
|
|
4649
|
-
const clearCmd = buildClearRemoteDirExceptZipCommand(target);
|
|
4650
|
-
console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
|
|
4651
|
-
await execCommand(client, clearCmd);
|
|
4652
|
-
const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
|
|
4653
|
-
console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
|
|
4654
|
-
await execCommand(client, unzipCmd);
|
|
4655
|
-
console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
|
|
4656
|
-
}
|
|
4657
|
-
console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
|
|
4658
|
-
} finally {
|
|
4659
|
-
await sftp.end();
|
|
4660
|
-
}
|
|
4661
|
-
}
|
|
4662
|
-
async function runWisdomSftpDeploy(params) {
|
|
4663
|
-
const zipPath = path8.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
4664
|
-
const resolvedZipPath = path8.resolve(zipPath);
|
|
4665
|
-
let zipSizeBytes = 0;
|
|
4666
|
-
try {
|
|
4667
|
-
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
4668
|
-
await uploadAndMaybeExtract(
|
|
4669
|
-
params.settings,
|
|
4670
|
-
resolvedZipPath,
|
|
4671
|
-
params.extract
|
|
4672
|
-
);
|
|
4673
|
-
} finally {
|
|
4674
|
-
try {
|
|
4675
|
-
await unlink(resolvedZipPath);
|
|
4676
|
-
console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
|
|
4677
|
-
} catch {
|
|
4678
|
-
}
|
|
4679
|
-
}
|
|
4680
|
-
return {
|
|
4681
|
-
ok: true,
|
|
4682
|
-
localDir: params.localDir,
|
|
4683
|
-
host: params.settings.host,
|
|
4684
|
-
remotePath: params.settings.remotePath,
|
|
4685
|
-
zipSizeBytes,
|
|
4686
|
-
extracted: params.extract
|
|
4687
|
-
};
|
|
4688
|
-
}
|
|
4689
|
-
|
|
4690
|
-
// src/commands/deploy/sftp.ts
|
|
5391
|
+
import path11 from "node:path";
|
|
4691
5392
|
function registerDeploySftpCommands(program) {
|
|
4692
5393
|
program.command("deploy-sftp").description(
|
|
4693
5394
|
"\u5C06\u6307\u5B9A\u76EE\u5F55\u538B\u7F29\u4E3A zip \u540E\u901A\u8FC7 SFTP \u4E0A\u4F20\u5230\u8FDC\u7A0B\u670D\u52A1\u5668\uFF08\u8FDE\u63A5\u4FE1\u606F\u89C1 apm.config.json wisdomDeploy\uFF09"
|
|
@@ -4702,7 +5403,7 @@ function registerDeploySftpCommands(program) {
|
|
|
4702
5403
|
async (opts) => {
|
|
4703
5404
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
4704
5405
|
const settings = resolveWisdomDeployFromApmConfig(cfg);
|
|
4705
|
-
const root =
|
|
5406
|
+
const root = path11.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
4706
5407
|
if (!await isDirectoryPath(root)) {
|
|
4707
5408
|
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
4708
5409
|
process.exit(1);
|
|
@@ -4723,11 +5424,30 @@ function registerDeploySftpCommands(program) {
|
|
|
4723
5424
|
);
|
|
4724
5425
|
}
|
|
4725
5426
|
|
|
5427
|
+
// src/commands/deploy/wisdom-backend.ts
|
|
5428
|
+
function registerDeployWisdomBackendCommands(program) {
|
|
5429
|
+
program.command("deploy-wisdom-backend").description(
|
|
5430
|
+
"Maven \u6784\u5EFA\u540E\u901A\u8FC7 SFTP \u589E\u91CF\u66F4\u65B0\u8FDC\u7A0B lib \u76EE\u5F55\u5E76\u91CD\u542F Spring Boot \u670D\u52A1\uFF08\u914D\u7F6E\u89C1 apm.config.json \u7684 wisdomDeploy \u4E0E healthCheck\uFF09"
|
|
5431
|
+
).option(
|
|
5432
|
+
"--config <path>",
|
|
5433
|
+
"apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
|
|
5434
|
+
).option("--skip-maven", "\u8DF3\u8FC7 Maven \u6784\u5EFA\uFF0C\u4EC5\u4E0A\u4F20\u5E76\u91CD\u542F").action(async (opts) => {
|
|
5435
|
+
const cfg = loadApmConfig({ configPath: opts.config });
|
|
5436
|
+
const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
|
|
5437
|
+
await runWisdomBackendDeploy({
|
|
5438
|
+
config: settings,
|
|
5439
|
+
skipMaven: Boolean(opts.skipMaven)
|
|
5440
|
+
});
|
|
5441
|
+
});
|
|
5442
|
+
}
|
|
5443
|
+
|
|
4726
5444
|
// src/commands/deploy/index.ts
|
|
4727
5445
|
function registerDeployCommands(program) {
|
|
5446
|
+
registerDeployMainCommand(program);
|
|
4728
5447
|
registerDeployBackendCommands(program);
|
|
4729
5448
|
registerDeployFrontendCommands(program);
|
|
4730
5449
|
registerDeploySftpCommands(program);
|
|
5450
|
+
registerDeployWisdomBackendCommands(program);
|
|
4731
5451
|
}
|
|
4732
5452
|
|
|
4733
5453
|
// src/index.ts
|