ai-project-manage-cli 6.0.84 → 6.0.86
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/README.md +1 -0
- package/dist/index.js +569 -213
- package/package.json +1 -1
- package/template/skills/apm-deploy/SKILL.md +7 -0
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -490,6 +490,16 @@ var requestConfig = {
|
|
|
490
490
|
completeTaskDeployment: defineEndpoint({
|
|
491
491
|
method: "PUT",
|
|
492
492
|
path: "/cli/task-deployments/complete"
|
|
493
|
+
}),
|
|
494
|
+
getDeployArtifactStorage: defineEndpoint(
|
|
495
|
+
{
|
|
496
|
+
method: "GET",
|
|
497
|
+
path: "/cli/deploy-artifact-storage"
|
|
498
|
+
}
|
|
499
|
+
),
|
|
500
|
+
attachTaskDeploymentArtifact: defineEndpoint({
|
|
501
|
+
method: "PUT",
|
|
502
|
+
path: "/cli/task-deployments/artifact"
|
|
493
503
|
})
|
|
494
504
|
}
|
|
495
505
|
};
|
|
@@ -835,6 +845,24 @@ async function isGitRepo(cwd) {
|
|
|
835
845
|
return false;
|
|
836
846
|
}
|
|
837
847
|
}
|
|
848
|
+
async function ensureGitRepo(cwd) {
|
|
849
|
+
await execGit(cwd, ["rev-parse", "--git-dir"], true);
|
|
850
|
+
}
|
|
851
|
+
async function getCurrentBranch(cwd) {
|
|
852
|
+
return (await execGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
|
|
853
|
+
}
|
|
854
|
+
async function isWorkingTreeDirty(cwd) {
|
|
855
|
+
const out = await execGit(cwd, ["status", "--porcelain"], true);
|
|
856
|
+
return out.trim().length > 0;
|
|
857
|
+
}
|
|
858
|
+
async function remoteBranchExists(cwd, branch) {
|
|
859
|
+
const out = await execGit(
|
|
860
|
+
cwd,
|
|
861
|
+
["ls-remote", "--heads", "origin", branch],
|
|
862
|
+
true
|
|
863
|
+
);
|
|
864
|
+
return out.trim().length > 0;
|
|
865
|
+
}
|
|
838
866
|
async function hasUpstream(cwd) {
|
|
839
867
|
try {
|
|
840
868
|
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
@@ -1023,14 +1051,14 @@ async function execGit2(cwd, args, quiet) {
|
|
|
1023
1051
|
);
|
|
1024
1052
|
}
|
|
1025
1053
|
}
|
|
1026
|
-
async function
|
|
1054
|
+
async function ensureGitRepo2(cwd) {
|
|
1027
1055
|
await execGit2(cwd, ["rev-parse", "--git-dir"], true);
|
|
1028
1056
|
}
|
|
1029
|
-
async function
|
|
1057
|
+
async function getCurrentBranch2(cwd) {
|
|
1030
1058
|
const name = (await execGit2(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
|
|
1031
1059
|
return name;
|
|
1032
1060
|
}
|
|
1033
|
-
async function
|
|
1061
|
+
async function isWorkingTreeDirty2(cwd) {
|
|
1034
1062
|
const out = await execGit2(cwd, ["status", "--porcelain"], true);
|
|
1035
1063
|
return out.trim().length > 0;
|
|
1036
1064
|
}
|
|
@@ -1055,11 +1083,11 @@ async function localBranchExists(cwd, branch) {
|
|
|
1055
1083
|
}
|
|
1056
1084
|
}
|
|
1057
1085
|
async function commitWorkingTreeIfDirty(cwd, message) {
|
|
1058
|
-
await
|
|
1059
|
-
if (!await
|
|
1086
|
+
await ensureGitRepo2(cwd);
|
|
1087
|
+
if (!await isWorkingTreeDirty2(cwd)) {
|
|
1060
1088
|
return false;
|
|
1061
1089
|
}
|
|
1062
|
-
const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await
|
|
1090
|
+
const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch2(cwd)})`;
|
|
1063
1091
|
await execGit2(cwd, ["add", "-A"]);
|
|
1064
1092
|
await execGit2(cwd, ["commit", "-m", commitMessage]);
|
|
1065
1093
|
console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
|
|
@@ -1068,14 +1096,14 @@ async function commitWorkingTreeIfDirty(cwd, message) {
|
|
|
1068
1096
|
async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
1069
1097
|
const cwd = options.cwd ?? process.cwd();
|
|
1070
1098
|
const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
|
|
1071
|
-
await
|
|
1099
|
+
await ensureGitRepo2(cwd);
|
|
1072
1100
|
if (!await remoteHeadBranchExists(cwd, baselineBranch)) {
|
|
1073
1101
|
throw new Error(
|
|
1074
1102
|
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F ${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
1075
1103
|
);
|
|
1076
1104
|
}
|
|
1077
|
-
const current = await
|
|
1078
|
-
const dirty = await
|
|
1105
|
+
const current = await getCurrentBranch2(cwd);
|
|
1106
|
+
const dirty = await isWorkingTreeDirty2(cwd);
|
|
1079
1107
|
if (dirty) {
|
|
1080
1108
|
if (current === branch) {
|
|
1081
1109
|
await commitWorkingTreeIfDirty(cwd, commitMessage);
|
|
@@ -1089,7 +1117,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
|
1089
1117
|
]);
|
|
1090
1118
|
}
|
|
1091
1119
|
}
|
|
1092
|
-
const onTargetBranch = await
|
|
1120
|
+
const onTargetBranch = await getCurrentBranch2(cwd) === branch;
|
|
1093
1121
|
if (onTargetBranch) {
|
|
1094
1122
|
await execGit2(cwd, ["fetch", "origin", baselineBranch]);
|
|
1095
1123
|
await execGit2(cwd, ["merge", `origin/${baselineBranch}`, "--no-edit"]);
|
|
@@ -1099,7 +1127,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
|
1099
1127
|
await execGit2(cwd, ["fetch", "origin", branch]);
|
|
1100
1128
|
await execGit2(cwd, ["checkout", "-B", branch, `origin/${branch}`]);
|
|
1101
1129
|
} else if (await localBranchExists(cwd, branch)) {
|
|
1102
|
-
if (await
|
|
1130
|
+
if (await getCurrentBranch2(cwd) !== branch) {
|
|
1103
1131
|
await execGit2(cwd, ["checkout", branch]);
|
|
1104
1132
|
}
|
|
1105
1133
|
console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
|
|
@@ -1115,7 +1143,7 @@ async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
|
1115
1143
|
await execGit2(cwd, ["push", "-u", "origin", branch]);
|
|
1116
1144
|
} catch (err) {
|
|
1117
1145
|
if (await localBranchExists(cwd, branch)) {
|
|
1118
|
-
if (await
|
|
1146
|
+
if (await getCurrentBranch2(cwd) !== branch) {
|
|
1119
1147
|
await execGit2(cwd, ["checkout", branch]);
|
|
1120
1148
|
}
|
|
1121
1149
|
console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
|
|
@@ -1178,10 +1206,10 @@ async function execGit3(cwd, args, quiet) {
|
|
|
1178
1206
|
);
|
|
1179
1207
|
}
|
|
1180
1208
|
}
|
|
1181
|
-
async function
|
|
1209
|
+
async function ensureGitRepo3(cwd) {
|
|
1182
1210
|
await execGit3(cwd, ["rev-parse", "--git-dir"], true);
|
|
1183
1211
|
}
|
|
1184
|
-
async function
|
|
1212
|
+
async function getCurrentBranch3(cwd) {
|
|
1185
1213
|
return (await execGit3(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
|
|
1186
1214
|
}
|
|
1187
1215
|
async function resolveBaselineBranch(cwd, api) {
|
|
@@ -1197,7 +1225,7 @@ async function resolveBaselineBranch(cwd, api) {
|
|
|
1197
1225
|
throw new Error("[apm] \u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
|
|
1198
1226
|
}
|
|
1199
1227
|
await execGit3(cwd, ["fetch", "origin", baselineBranch], true);
|
|
1200
|
-
if (!await
|
|
1228
|
+
if (!await remoteBranchExists2(cwd, baselineBranch)) {
|
|
1201
1229
|
throw new Error(
|
|
1202
1230
|
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
1203
1231
|
);
|
|
@@ -1264,7 +1292,7 @@ async function localBranchExists2(cwd, branch) {
|
|
|
1264
1292
|
return false;
|
|
1265
1293
|
}
|
|
1266
1294
|
}
|
|
1267
|
-
async function
|
|
1295
|
+
async function remoteBranchExists2(cwd, branch) {
|
|
1268
1296
|
const out = await execGit3(
|
|
1269
1297
|
cwd,
|
|
1270
1298
|
["ls-remote", "--heads", "origin", branch],
|
|
@@ -1298,7 +1326,7 @@ async function runCleanBranches(options = {}) {
|
|
|
1298
1326
|
const cwd = options.cwd ?? process.cwd();
|
|
1299
1327
|
const dryRun = options.dryRun ?? false;
|
|
1300
1328
|
const includeRemote = options.includeRemote ?? false;
|
|
1301
|
-
await
|
|
1329
|
+
await ensureGitRepo3(cwd);
|
|
1302
1330
|
await execGit3(cwd, ["fetch", "--prune", "origin"], true);
|
|
1303
1331
|
const cfg = await ensureLoggedConfig();
|
|
1304
1332
|
const api = createApmApiClient(cfg);
|
|
@@ -1335,7 +1363,7 @@ async function runCleanBranches(options = {}) {
|
|
|
1335
1363
|
console.log("[apm] \u6CA1\u6709\u9700\u8981\u6E05\u7406\u7684 feat/session-* \u5206\u652F");
|
|
1336
1364
|
return;
|
|
1337
1365
|
}
|
|
1338
|
-
let currentBranch = await
|
|
1366
|
+
let currentBranch = await getCurrentBranch3(cwd);
|
|
1339
1367
|
for (const item of toDelete) {
|
|
1340
1368
|
const { branch, sessionId, reason } = item;
|
|
1341
1369
|
const label = `${branch} (${sessionId}: ${reason})`;
|
|
@@ -1357,7 +1385,7 @@ async function runCleanBranches(options = {}) {
|
|
|
1357
1385
|
await execGit3(cwd, ["branch", "-D", branch], true);
|
|
1358
1386
|
console.log(`[apm] \u5DF2\u5220\u9664\u672C\u5730\u5206\u652F ${branch}`);
|
|
1359
1387
|
}
|
|
1360
|
-
if (await
|
|
1388
|
+
if (await remoteBranchExists2(cwd, branch)) {
|
|
1361
1389
|
await execGit3(cwd, ["push", "origin", "--delete", branch], true);
|
|
1362
1390
|
console.log(`[apm] \u5DF2\u5220\u9664\u8FDC\u7A0B\u5206\u652F origin/${branch}`);
|
|
1363
1391
|
}
|
|
@@ -2280,6 +2308,256 @@ function validateAgentWsMessage(value, kind) {
|
|
|
2280
2308
|
|
|
2281
2309
|
// src/commands/connect/deploy-run.ts
|
|
2282
2310
|
import { spawn } from "node:child_process";
|
|
2311
|
+
|
|
2312
|
+
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
2313
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
2314
|
+
|
|
2315
|
+
// src/commands/deploy/internal/minio.ts
|
|
2316
|
+
import { statSync as statSync5 } from "node:fs";
|
|
2317
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2318
|
+
import path from "node:path";
|
|
2319
|
+
import * as Minio from "minio";
|
|
2320
|
+
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
2321
|
+
async function isDirectoryPath(dir) {
|
|
2322
|
+
try {
|
|
2323
|
+
const st = statSync5(dir);
|
|
2324
|
+
return st.isDirectory();
|
|
2325
|
+
} catch {
|
|
2326
|
+
return false;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
function sanitizeRelativePath(rel) {
|
|
2330
|
+
const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2331
|
+
const segments = norm.split("/").filter(Boolean);
|
|
2332
|
+
for (const s of segments) {
|
|
2333
|
+
if (s === "." || s === "..") {
|
|
2334
|
+
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
return segments.join("/");
|
|
2338
|
+
}
|
|
2339
|
+
async function collectFiles(root) {
|
|
2340
|
+
const out = [];
|
|
2341
|
+
async function walk(dir, prefix) {
|
|
2342
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
2343
|
+
for (const e of entries) {
|
|
2344
|
+
const name = e.name;
|
|
2345
|
+
if (name === "." || name === "..") {
|
|
2346
|
+
continue;
|
|
2347
|
+
}
|
|
2348
|
+
const abs = path.join(dir, name);
|
|
2349
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
2350
|
+
if (e.isDirectory()) {
|
|
2351
|
+
await walk(abs, rel);
|
|
2352
|
+
} else if (e.isFile()) {
|
|
2353
|
+
const st = statSync5(abs);
|
|
2354
|
+
out.push({
|
|
2355
|
+
absPath: abs,
|
|
2356
|
+
relativePath: rel.replace(/\\/g, "/"),
|
|
2357
|
+
size: st.size
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
await walk(root, "");
|
|
2363
|
+
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
2364
|
+
return out;
|
|
2365
|
+
}
|
|
2366
|
+
async function readArtifactFile(absPath) {
|
|
2367
|
+
return readFile(absPath);
|
|
2368
|
+
}
|
|
2369
|
+
function toMB(bytes) {
|
|
2370
|
+
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
2371
|
+
}
|
|
2372
|
+
var MIME = {
|
|
2373
|
+
".html": "text/html; charset=utf-8",
|
|
2374
|
+
".css": "text/css; charset=utf-8",
|
|
2375
|
+
".js": "application/javascript; charset=utf-8",
|
|
2376
|
+
".json": "application/json; charset=utf-8",
|
|
2377
|
+
".svg": "image/svg+xml",
|
|
2378
|
+
".png": "image/png",
|
|
2379
|
+
".jpg": "image/jpeg",
|
|
2380
|
+
".jpeg": "image/jpeg",
|
|
2381
|
+
".gif": "image/gif",
|
|
2382
|
+
".webp": "image/webp",
|
|
2383
|
+
".woff": "font/woff",
|
|
2384
|
+
".woff2": "font/woff2",
|
|
2385
|
+
".ttf": "font/ttf",
|
|
2386
|
+
".ico": "image/x-icon",
|
|
2387
|
+
".txt": "text/plain; charset=utf-8",
|
|
2388
|
+
".map": "application/json"
|
|
2389
|
+
};
|
|
2390
|
+
function detectMimeType(filePath) {
|
|
2391
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
2392
|
+
return MIME[ext] ?? "";
|
|
2393
|
+
}
|
|
2394
|
+
var MinioClient = class {
|
|
2395
|
+
inner;
|
|
2396
|
+
constructor(opts) {
|
|
2397
|
+
const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
|
|
2398
|
+
this.inner = new Minio.Client({
|
|
2399
|
+
endPoint,
|
|
2400
|
+
port: opts.port,
|
|
2401
|
+
useSSL: opts.useSSL,
|
|
2402
|
+
accessKey: opts.accessKey,
|
|
2403
|
+
secretKey: opts.secretKey
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
async ensureBucket(bucket) {
|
|
2407
|
+
const exists = await this.inner.bucketExists(bucket);
|
|
2408
|
+
if (!exists) {
|
|
2409
|
+
await this.inner.makeBucket(bucket);
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
async deleteObjectsByPrefix(bucket, prefix) {
|
|
2413
|
+
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
2414
|
+
const keys = [];
|
|
2415
|
+
await new Promise((resolve5, reject) => {
|
|
2416
|
+
objectsStream.on("data", (obj) => {
|
|
2417
|
+
if (obj.name) {
|
|
2418
|
+
keys.push(obj.name);
|
|
2419
|
+
}
|
|
2420
|
+
});
|
|
2421
|
+
objectsStream.on("error", reject);
|
|
2422
|
+
objectsStream.on("end", resolve5);
|
|
2423
|
+
});
|
|
2424
|
+
const chunkSize = 500;
|
|
2425
|
+
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
2426
|
+
const chunk = keys.slice(i, i + chunkSize);
|
|
2427
|
+
await this.inner.removeObjects(
|
|
2428
|
+
bucket,
|
|
2429
|
+
chunk.map((name) => name)
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
async putObject(bucket, objectKey, body, meta) {
|
|
2434
|
+
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
2435
|
+
}
|
|
2436
|
+
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
2437
|
+
async setBucketPublicRead(bucket) {
|
|
2438
|
+
const policy = {
|
|
2439
|
+
Version: "2012-10-17",
|
|
2440
|
+
Statement: [
|
|
2441
|
+
{
|
|
2442
|
+
Effect: "Allow",
|
|
2443
|
+
Principal: { AWS: ["*"] },
|
|
2444
|
+
Action: ["s3:GetObject"],
|
|
2445
|
+
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
2446
|
+
}
|
|
2447
|
+
]
|
|
2448
|
+
};
|
|
2449
|
+
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
|
|
2453
|
+
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
2454
|
+
var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
|
|
2455
|
+
function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
2456
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
2457
|
+
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
2458
|
+
}
|
|
2459
|
+
function sanitizeDeployProjectName(name) {
|
|
2460
|
+
const trimmed = name.trim().replace(/\\/g, "/");
|
|
2461
|
+
const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
|
|
2462
|
+
try {
|
|
2463
|
+
return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
|
|
2464
|
+
} catch {
|
|
2465
|
+
return "project";
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
|
|
2469
|
+
const safeName = sanitizeDeployProjectName(projectName);
|
|
2470
|
+
const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
|
|
2471
|
+
return `${timestamp}-${safeName}.${suffix}`;
|
|
2472
|
+
}
|
|
2473
|
+
function buildDeployArtifactObjectKey(projectName, fileName) {
|
|
2474
|
+
const safeProject = sanitizeDeployProjectName(projectName);
|
|
2475
|
+
return `deploy/${safeProject}/${fileName}`;
|
|
2476
|
+
}
|
|
2477
|
+
function resolveDeploymentRunIdFromEnv() {
|
|
2478
|
+
const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
|
|
2479
|
+
return raw || null;
|
|
2480
|
+
}
|
|
2481
|
+
async function fetchDeployArtifactStorage(api) {
|
|
2482
|
+
return api.cli.getDeployArtifactStorage(void 0);
|
|
2483
|
+
}
|
|
2484
|
+
async function uploadDeployArtifactZip(options) {
|
|
2485
|
+
const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
|
|
2486
|
+
if (!deploymentRunId) {
|
|
2487
|
+
return null;
|
|
2488
|
+
}
|
|
2489
|
+
const cfg = await tryReadApmConfig();
|
|
2490
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
2491
|
+
console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
|
|
2492
|
+
return null;
|
|
2493
|
+
}
|
|
2494
|
+
const api = options.api ?? createApmApiClient(cfg);
|
|
2495
|
+
let storage;
|
|
2496
|
+
try {
|
|
2497
|
+
storage = await fetchDeployArtifactStorage(api);
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2500
|
+
console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
|
|
2501
|
+
return null;
|
|
2502
|
+
}
|
|
2503
|
+
const fileName = buildDeployArtifactFileName(
|
|
2504
|
+
options.kind,
|
|
2505
|
+
options.projectName,
|
|
2506
|
+
options.timestamp
|
|
2507
|
+
);
|
|
2508
|
+
const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
|
|
2509
|
+
let body;
|
|
2510
|
+
try {
|
|
2511
|
+
body = await readFile2(options.zipPath);
|
|
2512
|
+
} catch (error) {
|
|
2513
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2514
|
+
console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
|
|
2515
|
+
return null;
|
|
2516
|
+
}
|
|
2517
|
+
try {
|
|
2518
|
+
const client = new MinioClient({
|
|
2519
|
+
endPoint: storage.endpoint,
|
|
2520
|
+
port: storage.port,
|
|
2521
|
+
useSSL: storage.useSsl,
|
|
2522
|
+
accessKey: storage.accessKey,
|
|
2523
|
+
secretKey: storage.secretKey
|
|
2524
|
+
});
|
|
2525
|
+
await client.ensureBucket(storage.bucket);
|
|
2526
|
+
await client.putObject(storage.bucket, objectKey, body, {
|
|
2527
|
+
"Content-Type": "application/zip"
|
|
2528
|
+
});
|
|
2529
|
+
console.error(
|
|
2530
|
+
`[apm] \u672C\u5730\u76F4\u4F20 MinIO\uFF08\u4E0D\u7ECF\u5E73\u53F0\u670D\u52A1\u5668\uFF09: ${storage.endpoint}:${storage.port}/${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(
|
|
2531
|
+
2
|
|
2532
|
+
)} MB)`
|
|
2533
|
+
);
|
|
2534
|
+
} catch (error) {
|
|
2535
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2536
|
+
console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
|
|
2537
|
+
return null;
|
|
2538
|
+
}
|
|
2539
|
+
try {
|
|
2540
|
+
await api.cli.attachTaskDeploymentArtifact({
|
|
2541
|
+
id: deploymentRunId,
|
|
2542
|
+
artifactObjectKey: objectKey,
|
|
2543
|
+
artifactFileName: fileName
|
|
2544
|
+
});
|
|
2545
|
+
console.error(
|
|
2546
|
+
`[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
|
|
2547
|
+
);
|
|
2548
|
+
} catch (error) {
|
|
2549
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2550
|
+
console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
|
|
2551
|
+
return null;
|
|
2552
|
+
}
|
|
2553
|
+
return {
|
|
2554
|
+
objectKey,
|
|
2555
|
+
fileName,
|
|
2556
|
+
bucket: storage.bucket
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
// src/commands/connect/deploy-run.ts
|
|
2283
2561
|
var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
|
|
2284
2562
|
function resolveDeployCommand(environment) {
|
|
2285
2563
|
return `apm deploy ${environment}`;
|
|
@@ -2385,6 +2663,8 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2385
2663
|
console.log(`[apm] deploy command: ${command}`);
|
|
2386
2664
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
2387
2665
|
let latestLog = "";
|
|
2666
|
+
const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
2667
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
|
|
2388
2668
|
try {
|
|
2389
2669
|
const { log: log2 } = await runShellCommand(command, workdir, signal, (log3) => {
|
|
2390
2670
|
latestLog = log3;
|
|
@@ -2412,6 +2692,11 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2412
2692
|
});
|
|
2413
2693
|
console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
|
|
2414
2694
|
} finally {
|
|
2695
|
+
if (previousDeploymentRunId === void 0) {
|
|
2696
|
+
delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
2697
|
+
} else {
|
|
2698
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
|
|
2699
|
+
}
|
|
2415
2700
|
logSyncer.dispose();
|
|
2416
2701
|
}
|
|
2417
2702
|
}
|
|
@@ -4067,7 +4352,7 @@ async function runCreatePr(options) {
|
|
|
4067
4352
|
|
|
4068
4353
|
// src/commands/deploy/deploy-execute.ts
|
|
4069
4354
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
4070
|
-
import
|
|
4355
|
+
import path5 from "node:path";
|
|
4071
4356
|
|
|
4072
4357
|
// src/commands/deploy/internal/apm-config.ts
|
|
4073
4358
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
|
|
@@ -4409,7 +4694,7 @@ var DeployExecutionError = class extends Error {
|
|
|
4409
4694
|
|
|
4410
4695
|
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
4411
4696
|
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
4412
|
-
import
|
|
4697
|
+
import path4 from "node:path";
|
|
4413
4698
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4414
4699
|
|
|
4415
4700
|
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
@@ -4418,31 +4703,31 @@ import {
|
|
|
4418
4703
|
mkdirSync as mkdirSync8,
|
|
4419
4704
|
readdirSync as readdirSync5,
|
|
4420
4705
|
readFileSync as readFileSync13,
|
|
4421
|
-
statSync as
|
|
4706
|
+
statSync as statSync6,
|
|
4422
4707
|
writeFileSync as writeFileSync14
|
|
4423
4708
|
} from "node:fs";
|
|
4424
4709
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
4425
|
-
import
|
|
4426
|
-
import { Client } from "ssh2";
|
|
4710
|
+
import path3 from "node:path";
|
|
4711
|
+
import { Client as Client2 } from "ssh2";
|
|
4427
4712
|
import JSZip2 from "jszip";
|
|
4428
4713
|
import SftpClient2 from "ssh2-sftp-client";
|
|
4429
4714
|
|
|
4430
4715
|
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
4431
|
-
import { readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
4432
|
-
import
|
|
4716
|
+
import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
|
|
4717
|
+
import path2 from "node:path";
|
|
4433
4718
|
import JSZip from "jszip";
|
|
4434
4719
|
import SftpClient from "ssh2-sftp-client";
|
|
4435
4720
|
async function addDirToZip(dir, zipFolder) {
|
|
4436
|
-
const entries = await
|
|
4721
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
4437
4722
|
for (const entry of entries) {
|
|
4438
|
-
const fullPath =
|
|
4723
|
+
const fullPath = path2.join(dir, entry.name);
|
|
4439
4724
|
if (entry.isDirectory()) {
|
|
4440
4725
|
const folder = zipFolder.folder(entry.name);
|
|
4441
4726
|
if (folder) {
|
|
4442
4727
|
await addDirToZip(fullPath, folder);
|
|
4443
4728
|
}
|
|
4444
4729
|
} else {
|
|
4445
|
-
const content = await
|
|
4730
|
+
const content = await readFile3(fullPath);
|
|
4446
4731
|
zipFolder.file(entry.name, content);
|
|
4447
4732
|
}
|
|
4448
4733
|
}
|
|
@@ -4550,8 +4835,8 @@ async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
|
4550
4835
|
}
|
|
4551
4836
|
}
|
|
4552
4837
|
async function runWisdomSftpDeploy(params) {
|
|
4553
|
-
const zipPath =
|
|
4554
|
-
const resolvedZipPath =
|
|
4838
|
+
const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
4839
|
+
const resolvedZipPath = path2.resolve(zipPath);
|
|
4555
4840
|
let zipSizeBytes = 0;
|
|
4556
4841
|
try {
|
|
4557
4842
|
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
@@ -4560,6 +4845,11 @@ async function runWisdomSftpDeploy(params) {
|
|
|
4560
4845
|
resolvedZipPath,
|
|
4561
4846
|
params.extract
|
|
4562
4847
|
);
|
|
4848
|
+
await uploadDeployArtifactZip({
|
|
4849
|
+
zipPath: resolvedZipPath,
|
|
4850
|
+
projectName: params.projectName,
|
|
4851
|
+
kind: "frontend"
|
|
4852
|
+
});
|
|
4563
4853
|
} finally {
|
|
4564
4854
|
try {
|
|
4565
4855
|
await unlink(resolvedZipPath);
|
|
@@ -4594,7 +4884,7 @@ function fail(message) {
|
|
|
4594
4884
|
}
|
|
4595
4885
|
function expandPath(pathStr) {
|
|
4596
4886
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4597
|
-
return
|
|
4887
|
+
return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
|
|
4598
4888
|
}
|
|
4599
4889
|
function quoteForShell(value) {
|
|
4600
4890
|
if (process.platform === "win32") {
|
|
@@ -4609,19 +4899,19 @@ function formatMavenLocalRepoArg(repoPath) {
|
|
|
4609
4899
|
return `-Dmaven.repo.local=${repoPath}`;
|
|
4610
4900
|
}
|
|
4611
4901
|
function deployCacheDir() {
|
|
4612
|
-
return
|
|
4902
|
+
return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
|
|
4613
4903
|
}
|
|
4614
4904
|
function manifestFilePath() {
|
|
4615
|
-
return
|
|
4905
|
+
return path3.join(deployCacheDir(), "manifest.json");
|
|
4616
4906
|
}
|
|
4617
4907
|
function getTargetDir(projectRoot) {
|
|
4618
|
-
return
|
|
4908
|
+
return path3.join(projectRoot, MAVEN_MODULE, "target");
|
|
4619
4909
|
}
|
|
4620
4910
|
function relativeKey(projectRoot, filePath) {
|
|
4621
|
-
return
|
|
4911
|
+
return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
|
|
4622
4912
|
}
|
|
4623
4913
|
function fileSignature(filePath) {
|
|
4624
|
-
const stat2 =
|
|
4914
|
+
const stat2 = statSync6(filePath);
|
|
4625
4915
|
return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
|
|
4626
4916
|
}
|
|
4627
4917
|
function loadManifest4() {
|
|
@@ -4643,12 +4933,12 @@ function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
|
|
|
4643
4933
|
if (!remoteAttr) {
|
|
4644
4934
|
return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
|
|
4645
4935
|
}
|
|
4646
|
-
const localSize =
|
|
4936
|
+
const localSize = statSync6(localPath).size;
|
|
4647
4937
|
const remoteSize = remoteAttr.size;
|
|
4648
4938
|
if (localSize !== remoteSize) {
|
|
4649
4939
|
return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
|
|
4650
4940
|
}
|
|
4651
|
-
if (isProjectLibJar(
|
|
4941
|
+
if (isProjectLibJar(path3.basename(localPath)) && manifest) {
|
|
4652
4942
|
const key = relativeKey(projectRoot, localPath);
|
|
4653
4943
|
const current = fileSignature(localPath);
|
|
4654
4944
|
const previous = manifest[key];
|
|
@@ -4668,7 +4958,7 @@ function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest =
|
|
|
4668
4958
|
const entries = [];
|
|
4669
4959
|
const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
|
|
4670
4960
|
for (const jarName of jarFiles) {
|
|
4671
|
-
const jarPath =
|
|
4961
|
+
const jarPath = path3.join(localLibDir, jarName);
|
|
4672
4962
|
const remoteAttr = remoteStats.get(jarName);
|
|
4673
4963
|
const [shouldUpload, reason] = shouldUploadLibFile(
|
|
4674
4964
|
jarPath,
|
|
@@ -4691,8 +4981,8 @@ function updateManifestEntries(manifest, entries, projectRoot) {
|
|
|
4691
4981
|
async function createUpdatePackage(entries, packageName) {
|
|
4692
4982
|
const dir = deployCacheDir();
|
|
4693
4983
|
mkdirSync8(dir, { recursive: true });
|
|
4694
|
-
const zipPath =
|
|
4695
|
-
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${
|
|
4984
|
+
const zipPath = path3.join(dir, packageName);
|
|
4985
|
+
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
4696
4986
|
const zip = new JSZip2();
|
|
4697
4987
|
for (const entry of entries) {
|
|
4698
4988
|
const content = readFileSync13(entry.path);
|
|
@@ -4756,8 +5046,8 @@ function locateLibDir(projectRoot) {
|
|
|
4756
5046
|
if (!existsSync16(targetDir)) {
|
|
4757
5047
|
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4758
5048
|
}
|
|
4759
|
-
const libDir =
|
|
4760
|
-
if (!existsSync16(libDir) || !
|
|
5049
|
+
const libDir = path3.join(targetDir, "lib");
|
|
5050
|
+
if (!existsSync16(libDir) || !statSync6(libDir).isDirectory()) {
|
|
4761
5051
|
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
4762
5052
|
}
|
|
4763
5053
|
const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
|
|
@@ -4772,16 +5062,16 @@ function locateMainJar(projectRoot) {
|
|
|
4772
5062
|
if (!existsSync16(targetDir)) {
|
|
4773
5063
|
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4774
5064
|
}
|
|
4775
|
-
const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) =>
|
|
5065
|
+
const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) => path3.join(targetDir, name)).sort((a, b) => statSync6(b).mtimeMs - statSync6(a).mtimeMs);
|
|
4776
5066
|
if (jarFiles.length === 0) {
|
|
4777
5067
|
fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
|
|
4778
5068
|
}
|
|
4779
5069
|
const mainJar = jarFiles[0];
|
|
4780
|
-
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${
|
|
5070
|
+
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
|
|
4781
5071
|
return mainJar;
|
|
4782
5072
|
}
|
|
4783
5073
|
async function connectSsh(config) {
|
|
4784
|
-
const client = new
|
|
5074
|
+
const client = new Client2();
|
|
4785
5075
|
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
|
|
4786
5076
|
await new Promise((resolve5, reject) => {
|
|
4787
5077
|
client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
|
|
@@ -4826,7 +5116,7 @@ async function getRemoteFileStats(sftp, remoteDir) {
|
|
|
4826
5116
|
}
|
|
4827
5117
|
async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
4828
5118
|
const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
|
|
4829
|
-
const remotePath = `${remoteDir}/${
|
|
5119
|
+
const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
|
|
4830
5120
|
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
4831
5121
|
try {
|
|
4832
5122
|
await sftp.fastPut(zipPath, remotePath);
|
|
@@ -4839,7 +5129,7 @@ async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
|
4839
5129
|
async function uploadFullJar(sftp, localJarPath, config) {
|
|
4840
5130
|
const remoteDir = config.remoteAppDir.replace(/\/$/, "");
|
|
4841
5131
|
const remotePath = `${remoteDir}/${config.startupJar}`;
|
|
4842
|
-
log(`\u4E0A\u4F20\u5168\u91CF JAR (${
|
|
5132
|
+
log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
|
|
4843
5133
|
try {
|
|
4844
5134
|
await sftp.mkdir(remoteDir, true);
|
|
4845
5135
|
await sftp.fastPut(localJarPath, remotePath);
|
|
@@ -5098,10 +5388,10 @@ async function restartRemoteService(client, config) {
|
|
|
5098
5388
|
await runRemoteServiceScript(client, script, "restart");
|
|
5099
5389
|
}
|
|
5100
5390
|
async function runWisdomBackendDeploy(options) {
|
|
5101
|
-
const projectRoot =
|
|
5391
|
+
const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
|
|
5102
5392
|
const config = options.config;
|
|
5103
5393
|
log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
|
|
5104
|
-
log(`\u914D\u7F6E\u6587\u4EF6: ${
|
|
5394
|
+
log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
|
|
5105
5395
|
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
5106
5396
|
log(
|
|
5107
5397
|
`\u90E8\u7F72\u6A21\u5F0F: ${config.mode}${config.mode === "full" ? "\uFF08\u5168\u91CF JAR\uFF0C\u8DF3\u8FC7 lib \u68C0\u67E5\uFF09" : "\uFF08\u589E\u91CF lib \u66F4\u65B0\uFF09"}`
|
|
@@ -5115,6 +5405,21 @@ async function runWisdomBackendDeploy(options) {
|
|
|
5115
5405
|
if (config.mode === "full") {
|
|
5116
5406
|
const mainJar = locateMainJar(projectRoot);
|
|
5117
5407
|
await uploadFullJar(conn.sftp, mainJar, config);
|
|
5408
|
+
const archiveZipPath = await createUpdatePackage(
|
|
5409
|
+
[
|
|
5410
|
+
{
|
|
5411
|
+
path: mainJar,
|
|
5412
|
+
arcname: path3.basename(mainJar),
|
|
5413
|
+
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
5414
|
+
}
|
|
5415
|
+
],
|
|
5416
|
+
`.deploy-archive-${config.projectName}.jar.zip`
|
|
5417
|
+
);
|
|
5418
|
+
await uploadDeployArtifactZip({
|
|
5419
|
+
zipPath: archiveZipPath,
|
|
5420
|
+
projectName: config.projectName,
|
|
5421
|
+
kind: "backend"
|
|
5422
|
+
});
|
|
5118
5423
|
log("\u91CD\u542F\u670D\u52A1...");
|
|
5119
5424
|
await restartRemoteService(conn.client, config);
|
|
5120
5425
|
await healthCheckService(conn.client, config);
|
|
@@ -5145,6 +5450,11 @@ async function runWisdomBackendDeploy(options) {
|
|
|
5145
5450
|
zipPath,
|
|
5146
5451
|
config
|
|
5147
5452
|
);
|
|
5453
|
+
await uploadDeployArtifactZip({
|
|
5454
|
+
zipPath,
|
|
5455
|
+
projectName: config.projectName,
|
|
5456
|
+
kind: "backend"
|
|
5457
|
+
});
|
|
5148
5458
|
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
5149
5459
|
updated = await extractUpdatePackageOnRemote(
|
|
5150
5460
|
conn.client,
|
|
@@ -5188,10 +5498,10 @@ function isWisdomLegacyDeploy(cfg) {
|
|
|
5188
5498
|
return !hasNewFrontend && !hasNewBackend;
|
|
5189
5499
|
}
|
|
5190
5500
|
function detectWisdomProjectType(cwd) {
|
|
5191
|
-
return existsSync17(
|
|
5501
|
+
return existsSync17(path4.join(cwd, "package.json")) ? "frontend" : "backend";
|
|
5192
5502
|
}
|
|
5193
5503
|
function readPackageScripts(cwd) {
|
|
5194
|
-
const pkgPath =
|
|
5504
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
5195
5505
|
if (!existsSync17(pkgPath)) {
|
|
5196
5506
|
return {};
|
|
5197
5507
|
}
|
|
@@ -5236,7 +5546,7 @@ function runShellCommand2(command, cwd, captureOutput) {
|
|
|
5236
5546
|
return output;
|
|
5237
5547
|
}
|
|
5238
5548
|
async function runWisdomAutoDeploy(options) {
|
|
5239
|
-
const cwd =
|
|
5549
|
+
const cwd = path4.resolve(options.cwd ?? process.cwd());
|
|
5240
5550
|
const captureOutput = options.captureOutput ?? false;
|
|
5241
5551
|
const projectType = detectWisdomProjectType(cwd);
|
|
5242
5552
|
console.error(
|
|
@@ -5247,14 +5557,14 @@ async function runWisdomAutoDeploy(options) {
|
|
|
5247
5557
|
if (!deployCmd) {
|
|
5248
5558
|
const lines = formatWisdomFrontendDeployNotConfiguredLines(
|
|
5249
5559
|
options.env,
|
|
5250
|
-
|
|
5560
|
+
path4.join(cwd, "package.json")
|
|
5251
5561
|
);
|
|
5252
5562
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
5253
5563
|
}
|
|
5254
5564
|
runShellCommand2(deployCmd, cwd, captureOutput);
|
|
5255
5565
|
return { projectType };
|
|
5256
5566
|
}
|
|
5257
|
-
const configPath = options.configPath ??
|
|
5567
|
+
const configPath = options.configPath ?? path4.join(workspaceApmDir(cwd), "apm.config.json");
|
|
5258
5568
|
const cfg = loadApmConfig({ configPath });
|
|
5259
5569
|
const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
|
|
5260
5570
|
console.error(
|
|
@@ -5267,6 +5577,150 @@ async function runWisdomAutoDeploy(options) {
|
|
|
5267
5577
|
return { projectType };
|
|
5268
5578
|
}
|
|
5269
5579
|
|
|
5580
|
+
// src/commands/deploy/internal/deploy-baseline-sync.ts
|
|
5581
|
+
var LOG_PREFIX = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
|
|
5582
|
+
function formatGitError(error) {
|
|
5583
|
+
return error instanceof Error ? error.message : String(error);
|
|
5584
|
+
}
|
|
5585
|
+
async function resolveBaselineBranch2(cwd, api) {
|
|
5586
|
+
const workdirPath = resolveWorkdirPath(cwd);
|
|
5587
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
5588
|
+
if (!baseline.repositoryId) {
|
|
5589
|
+
const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
|
|
5590
|
+
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
|
|
5591
|
+
throw new Error(detail);
|
|
5592
|
+
}
|
|
5593
|
+
const baselineBranch = (baseline.defaultBranch ?? "").trim();
|
|
5594
|
+
if (!baselineBranch) {
|
|
5595
|
+
throw new Error("\u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
|
|
5596
|
+
}
|
|
5597
|
+
return baselineBranch;
|
|
5598
|
+
}
|
|
5599
|
+
async function isMergeInProgress(cwd) {
|
|
5600
|
+
try {
|
|
5601
|
+
await execGit(cwd, ["rev-parse", "-q", "--verify", "MERGE_HEAD"], true);
|
|
5602
|
+
return true;
|
|
5603
|
+
} catch {
|
|
5604
|
+
return false;
|
|
5605
|
+
}
|
|
5606
|
+
}
|
|
5607
|
+
async function abortMergeIfNeeded(cwd) {
|
|
5608
|
+
if (await isMergeInProgress(cwd)) {
|
|
5609
|
+
await execGit(cwd, ["merge", "--abort"], true);
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
async function restoreWorkingTree(cwd) {
|
|
5613
|
+
await execGit(cwd, ["reset", "--hard", "HEAD"], true);
|
|
5614
|
+
}
|
|
5615
|
+
async function mergeBaselineBranch(cwd, baselineBranch, currentBranch, note) {
|
|
5616
|
+
const mergeTarget = `origin/${baselineBranch}`;
|
|
5617
|
+
note(`${LOG_PREFIX} \u5F00\u59CB\u5408\u5E76 ${mergeTarget} \u5230\u5F53\u524D\u5206\u652F ${currentBranch}`);
|
|
5618
|
+
try {
|
|
5619
|
+
if (currentBranch === baselineBranch) {
|
|
5620
|
+
await execGit(cwd, ["merge", "--ff-only", mergeTarget]);
|
|
5621
|
+
} else {
|
|
5622
|
+
await execGit(cwd, ["merge", mergeTarget, "--no-edit"]);
|
|
5623
|
+
}
|
|
5624
|
+
note(`${LOG_PREFIX} \u5DF2\u5408\u5E76 ${mergeTarget} \u6700\u65B0\u4EE3\u7801`);
|
|
5625
|
+
return { merged: true, conflictRollback: false };
|
|
5626
|
+
} catch (error) {
|
|
5627
|
+
await abortMergeIfNeeded(cwd);
|
|
5628
|
+
note(`${LOG_PREFIX} \u5408\u5E76 ${mergeTarget} \u5931\u8D25: ${formatGitError(error)}`);
|
|
5629
|
+
note(`${LOG_PREFIX} \u5DF2\u6267\u884C git merge --abort \u56DE\u9000\u5230\u5408\u5E76\u524D\u72B6\u6001`);
|
|
5630
|
+
note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
|
|
5631
|
+
return { merged: false, conflictRollback: true };
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
async function restoreStashIfNeeded(cwd, stashed, note) {
|
|
5635
|
+
if (!stashed) {
|
|
5636
|
+
return false;
|
|
5637
|
+
}
|
|
5638
|
+
note(`${LOG_PREFIX} \u6B63\u5728\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5639
|
+
try {
|
|
5640
|
+
await execGit(cwd, ["stash", "pop"], true);
|
|
5641
|
+
note(`${LOG_PREFIX} \u5DF2\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5642
|
+
return false;
|
|
5643
|
+
} catch (error) {
|
|
5644
|
+
await abortMergeIfNeeded(cwd);
|
|
5645
|
+
await restoreWorkingTree(cwd);
|
|
5646
|
+
note(`${LOG_PREFIX} \u6062\u590D stash \u5931\u8D25: ${formatGitError(error)}`);
|
|
5647
|
+
note(`${LOG_PREFIX} \u5DF2\u56DE\u9000\u5DE5\u4F5C\u533A\uFF08git reset --hard HEAD\uFF09\uFF0Cstash \u4ECD\u4FDD\u7559`);
|
|
5648
|
+
note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
|
|
5649
|
+
return true;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
async function syncDeployBaselineBeforeDeploy(cwd) {
|
|
5653
|
+
const lines = [];
|
|
5654
|
+
const note = (message) => {
|
|
5655
|
+
lines.push(message);
|
|
5656
|
+
console.error(message);
|
|
5657
|
+
};
|
|
5658
|
+
const emptyResult = (partial = {}) => ({
|
|
5659
|
+
log: lines.join("\n"),
|
|
5660
|
+
skipped: true,
|
|
5661
|
+
merged: false,
|
|
5662
|
+
conflictRollback: false,
|
|
5663
|
+
stashConflict: false,
|
|
5664
|
+
...partial
|
|
5665
|
+
});
|
|
5666
|
+
if (!await isGitRepo(cwd)) {
|
|
5667
|
+
note(`${LOG_PREFIX} \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8DF3\u8FC7`);
|
|
5668
|
+
return emptyResult();
|
|
5669
|
+
}
|
|
5670
|
+
const cfg = await tryReadApmConfig();
|
|
5671
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
5672
|
+
note(`${LOG_PREFIX} \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7`);
|
|
5673
|
+
return emptyResult();
|
|
5674
|
+
}
|
|
5675
|
+
await ensureGitRepo(cwd);
|
|
5676
|
+
const api = createApmApiClient(cfg);
|
|
5677
|
+
const baselineBranch = await resolveBaselineBranch2(cwd, api);
|
|
5678
|
+
note(`${LOG_PREFIX} \u57FA\u7EBF\u5206\u652F ${baselineBranch}`);
|
|
5679
|
+
note(`${LOG_PREFIX} \u6B63\u5728 fetch origin ${baselineBranch}`);
|
|
5680
|
+
await execGit(cwd, ["fetch", "origin", baselineBranch], true);
|
|
5681
|
+
if (!await remoteBranchExists(cwd, baselineBranch)) {
|
|
5682
|
+
throw new Error(
|
|
5683
|
+
`\u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
5684
|
+
);
|
|
5685
|
+
}
|
|
5686
|
+
note(`${LOG_PREFIX} fetch \u5B8C\u6210`);
|
|
5687
|
+
const currentBranch = await getCurrentBranch(cwd);
|
|
5688
|
+
let stashed = false;
|
|
5689
|
+
if (await isWorkingTreeDirty(cwd)) {
|
|
5690
|
+
note(`${LOG_PREFIX} \u68C0\u6D4B\u5230\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u5148 stash`);
|
|
5691
|
+
await execGit(cwd, [
|
|
5692
|
+
"stash",
|
|
5693
|
+
"push",
|
|
5694
|
+
"-u",
|
|
5695
|
+
"-m",
|
|
5696
|
+
"apm: deploy baseline sync"
|
|
5697
|
+
]);
|
|
5698
|
+
stashed = true;
|
|
5699
|
+
note(`${LOG_PREFIX} \u5DF2 stash \u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5700
|
+
}
|
|
5701
|
+
const { merged, conflictRollback } = await mergeBaselineBranch(
|
|
5702
|
+
cwd,
|
|
5703
|
+
baselineBranch,
|
|
5704
|
+
currentBranch,
|
|
5705
|
+
note
|
|
5706
|
+
);
|
|
5707
|
+
const stashConflict = await restoreStashIfNeeded(cwd, stashed, note);
|
|
5708
|
+
if (conflictRollback || stashConflict) {
|
|
5709
|
+
note(`${LOG_PREFIX} \u57FA\u7EBF\u540C\u6B65\u672A\u5B8C\u6210\uFF08\u5B58\u5728\u51B2\u7A81\u5E76\u5DF2\u56DE\u9000\uFF09\uFF0C\u7EE7\u7EED\u6267\u884C\u90E8\u7F72`);
|
|
5710
|
+
} else if (merged) {
|
|
5711
|
+
note(
|
|
5712
|
+
`${LOG_PREFIX} \u5B8C\u6210\uFF08\u5F53\u524D\u5206\u652F ${currentBranch} \u5DF2\u5305\u542B ${baselineBranch} \u6700\u65B0\u4EE3\u7801\uFF09`
|
|
5713
|
+
);
|
|
5714
|
+
}
|
|
5715
|
+
return {
|
|
5716
|
+
log: lines.join("\n"),
|
|
5717
|
+
skipped: false,
|
|
5718
|
+
merged,
|
|
5719
|
+
conflictRollback,
|
|
5720
|
+
stashConflict
|
|
5721
|
+
};
|
|
5722
|
+
}
|
|
5723
|
+
|
|
5270
5724
|
// src/commands/deploy/deploy-execute.ts
|
|
5271
5725
|
function runShellCommand3(command, cwd, captureOutput) {
|
|
5272
5726
|
const result = spawnSync5(command, {
|
|
@@ -5293,8 +5747,31 @@ function runShellCommand3(command, cwd, captureOutput) {
|
|
|
5293
5747
|
return output;
|
|
5294
5748
|
}
|
|
5295
5749
|
async function executeDeploy(options) {
|
|
5296
|
-
const cwd =
|
|
5750
|
+
const cwd = path5.resolve(options.cwd ?? process.cwd());
|
|
5297
5751
|
const captureOutput = options.captureOutput ?? false;
|
|
5752
|
+
const outputParts = [];
|
|
5753
|
+
try {
|
|
5754
|
+
const configSyncResult = await syncRemoteDeploymentConfig(
|
|
5755
|
+
cwd,
|
|
5756
|
+
workspaceApmDir(cwd)
|
|
5757
|
+
);
|
|
5758
|
+
if (configSyncResult.synced && configSyncResult.configName) {
|
|
5759
|
+
outputParts.push(
|
|
5760
|
+
`[apm] \u90E8\u7F72\u524D\u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configSyncResult.configName}`
|
|
5761
|
+
);
|
|
5762
|
+
}
|
|
5763
|
+
const syncResult = await syncDeployBaselineBeforeDeploy(cwd);
|
|
5764
|
+
if (syncResult.log.trim()) {
|
|
5765
|
+
outputParts.push(syncResult.log);
|
|
5766
|
+
}
|
|
5767
|
+
} catch (error) {
|
|
5768
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5769
|
+
throw new DeployExecutionError(
|
|
5770
|
+
`\u90E8\u7F72\u524D\u540C\u6B65\u57FA\u7EBF\u5206\u652F\u5931\u8D25: ${detail}`,
|
|
5771
|
+
1,
|
|
5772
|
+
detail
|
|
5773
|
+
);
|
|
5774
|
+
}
|
|
5298
5775
|
const cfg = loadApmConfig({ configPath: options.configPath });
|
|
5299
5776
|
if (isWisdomLegacyDeploy(cfg)) {
|
|
5300
5777
|
try {
|
|
@@ -5304,7 +5781,7 @@ async function executeDeploy(options) {
|
|
|
5304
5781
|
configPath: options.configPath,
|
|
5305
5782
|
captureOutput
|
|
5306
5783
|
});
|
|
5307
|
-
return "";
|
|
5784
|
+
return outputParts.join("\n");
|
|
5308
5785
|
} catch (error) {
|
|
5309
5786
|
if (error instanceof DeployExecutionError) {
|
|
5310
5787
|
throw error;
|
|
@@ -5319,7 +5796,11 @@ async function executeDeploy(options) {
|
|
|
5319
5796
|
const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
|
|
5320
5797
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
5321
5798
|
}
|
|
5322
|
-
|
|
5799
|
+
const commandOutput = runShellCommand3(command, cwd, captureOutput);
|
|
5800
|
+
if (commandOutput.trim()) {
|
|
5801
|
+
outputParts.push(commandOutput);
|
|
5802
|
+
}
|
|
5803
|
+
return outputParts.join("\n");
|
|
5323
5804
|
}
|
|
5324
5805
|
function printDeployExecutionError(error) {
|
|
5325
5806
|
if (error.output.trim()) {
|
|
@@ -5384,6 +5865,8 @@ async function runDeployWithBackendTracking(options) {
|
|
|
5384
5865
|
status: "DEPLOYING"
|
|
5385
5866
|
});
|
|
5386
5867
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
5868
|
+
const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
5869
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
|
|
5387
5870
|
const chunks = [];
|
|
5388
5871
|
const appendLog = (line) => {
|
|
5389
5872
|
if (!line) return;
|
|
@@ -5430,6 +5913,11 @@ async function runDeployWithBackendTracking(options) {
|
|
|
5430
5913
|
printDeployExecutionError(deployError);
|
|
5431
5914
|
process.exit(deployError.exitCode);
|
|
5432
5915
|
} finally {
|
|
5916
|
+
if (previousDeploymentRunId === void 0) {
|
|
5917
|
+
delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
5918
|
+
} else {
|
|
5919
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
|
|
5920
|
+
}
|
|
5433
5921
|
console.log = originalLog;
|
|
5434
5922
|
console.error = originalError;
|
|
5435
5923
|
logSyncer.dispose();
|
|
@@ -5439,7 +5927,7 @@ async function runDeployWithBackendTracking(options) {
|
|
|
5439
5927
|
// src/commands/deploy/deploy.ts
|
|
5440
5928
|
function registerDeployMainCommand(program) {
|
|
5441
5929
|
program.command("deploy").description(
|
|
5442
|
-
"\u7EDF\u4E00\u90E8\u7F72\
|
|
5930
|
+
"\u7EDF\u4E00\u90E8\u7F72\uFF08\u6267\u884C\u524D\u81EA\u52A8\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u5E76\u5408\u5E76\u57FA\u7EBF\u5206\u652F\uFF09\uFF1A\u533B\u52A1\u5B58\u91CF\u81EA\u52A8\u8BC6\u522B\u524D\u540E\u7AEF\uFF1B\u5176\u4ED6\u9879\u76EE\u6309 deploy.<\u73AF\u5883> \u6267\u884C shell \u547D\u4EE4"
|
|
5443
5931
|
).argument("<env>", "\u90E8\u7F72\u73AF\u5883\u540D\uFF08\u5982 test\u3001online\uFF09").option(
|
|
5444
5932
|
"--config <path>",
|
|
5445
5933
|
"apm.config.json \u8DEF\u5F84\uFF08\u9ED8\u8BA4 .apm/apm.config.json\uFF09"
|
|
@@ -5473,23 +5961,23 @@ function registerDeployMainCommand(program) {
|
|
|
5473
5961
|
}
|
|
5474
5962
|
|
|
5475
5963
|
// src/commands/deploy/backend.ts
|
|
5476
|
-
import
|
|
5964
|
+
import path10 from "node:path";
|
|
5477
5965
|
|
|
5478
5966
|
// src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
|
|
5479
|
-
import
|
|
5967
|
+
import path9 from "node:path";
|
|
5480
5968
|
|
|
5481
5969
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
|
|
5482
5970
|
import Docker from "dockerode";
|
|
5483
5971
|
|
|
5484
5972
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
5485
5973
|
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
|
|
5486
|
-
import
|
|
5974
|
+
import path6 from "node:path";
|
|
5487
5975
|
function asOptionalTlsBuffer(value) {
|
|
5488
5976
|
if (typeof value !== "string") {
|
|
5489
5977
|
console.log("tls filepath not exist");
|
|
5490
5978
|
return void 0;
|
|
5491
5979
|
}
|
|
5492
|
-
console.log("tls filepath",
|
|
5980
|
+
console.log("tls filepath", path6.join(process.cwd(), value));
|
|
5493
5981
|
const normalized = value.trim();
|
|
5494
5982
|
if (normalized === "") {
|
|
5495
5983
|
return void 0;
|
|
@@ -5705,8 +6193,8 @@ var DockerodeClient = class {
|
|
|
5705
6193
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
5706
6194
|
|
|
5707
6195
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
5708
|
-
import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as
|
|
5709
|
-
import
|
|
6196
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync7 } from "node:fs";
|
|
6197
|
+
import path7 from "node:path";
|
|
5710
6198
|
function stripSurroundingQuotes(value) {
|
|
5711
6199
|
const t = value.trim();
|
|
5712
6200
|
if (t.length >= 2) {
|
|
@@ -5721,8 +6209,8 @@ function loadEnvFromFile(envFilePath) {
|
|
|
5721
6209
|
if (!envFilePath) {
|
|
5722
6210
|
return {};
|
|
5723
6211
|
}
|
|
5724
|
-
const targetPath =
|
|
5725
|
-
if (!existsSync19(targetPath) || !
|
|
6212
|
+
const targetPath = path7.resolve(envFilePath);
|
|
6213
|
+
if (!existsSync19(targetPath) || !statSync7(targetPath).isFile()) {
|
|
5726
6214
|
return {};
|
|
5727
6215
|
}
|
|
5728
6216
|
const raw = readFileSync16(targetPath, "utf-8");
|
|
@@ -5897,9 +6385,9 @@ function dockerPushImage(params, cwd) {
|
|
|
5897
6385
|
|
|
5898
6386
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
5899
6387
|
import { existsSync as existsSync20 } from "node:fs";
|
|
5900
|
-
import
|
|
6388
|
+
import path8 from "node:path";
|
|
5901
6389
|
function resolveDockerBuildPaths(cwd) {
|
|
5902
|
-
const dockerfilePath =
|
|
6390
|
+
const dockerfilePath = path8.join(cwd, "Dockerfile");
|
|
5903
6391
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
5904
6392
|
if (!existsSync20(dockerfilePath)) {
|
|
5905
6393
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
@@ -5956,7 +6444,7 @@ var BackendDeployWorkflow = class {
|
|
|
5956
6444
|
serveraddress
|
|
5957
6445
|
});
|
|
5958
6446
|
Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
|
|
5959
|
-
const envFilePath =
|
|
6447
|
+
const envFilePath = path9.resolve(cwd, this.params.envFilePath || ".env");
|
|
5960
6448
|
const envObj = loadEnvFromFile(envFilePath);
|
|
5961
6449
|
if (this.params.dockerNetwork?.trim()) {
|
|
5962
6450
|
Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
|
|
@@ -5996,7 +6484,7 @@ function registerDeployBackendCommands(program) {
|
|
|
5996
6484
|
assertDeployImageTag(tag);
|
|
5997
6485
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
5998
6486
|
const fromApm = resolveBackendDeployFromApmConfig(cfg);
|
|
5999
|
-
const dirAbs =
|
|
6487
|
+
const dirAbs = path10.resolve(process.cwd(), opts.dir || "servers/api");
|
|
6000
6488
|
const params = {
|
|
6001
6489
|
image: fromApm.name,
|
|
6002
6490
|
tag,
|
|
@@ -6028,146 +6516,6 @@ function registerDeployBackendCommands(program) {
|
|
|
6028
6516
|
// src/commands/deploy/frontend.ts
|
|
6029
6517
|
import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
|
|
6030
6518
|
import path11 from "node:path";
|
|
6031
|
-
|
|
6032
|
-
// src/commands/deploy/internal/minio.ts
|
|
6033
|
-
import { statSync as statSync7 } from "node:fs";
|
|
6034
|
-
import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
|
|
6035
|
-
import path10 from "node:path";
|
|
6036
|
-
import * as Minio from "minio";
|
|
6037
|
-
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
6038
|
-
async function isDirectoryPath(dir) {
|
|
6039
|
-
try {
|
|
6040
|
-
const st = statSync7(dir);
|
|
6041
|
-
return st.isDirectory();
|
|
6042
|
-
} catch {
|
|
6043
|
-
return false;
|
|
6044
|
-
}
|
|
6045
|
-
}
|
|
6046
|
-
function sanitizeRelativePath(rel) {
|
|
6047
|
-
const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
6048
|
-
const segments = norm.split("/").filter(Boolean);
|
|
6049
|
-
for (const s of segments) {
|
|
6050
|
-
if (s === "." || s === "..") {
|
|
6051
|
-
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
6052
|
-
}
|
|
6053
|
-
}
|
|
6054
|
-
return segments.join("/");
|
|
6055
|
-
}
|
|
6056
|
-
async function collectFiles(root) {
|
|
6057
|
-
const out = [];
|
|
6058
|
-
async function walk(dir, prefix) {
|
|
6059
|
-
const entries = await readdir2(dir, { withFileTypes: true });
|
|
6060
|
-
for (const e of entries) {
|
|
6061
|
-
const name = e.name;
|
|
6062
|
-
if (name === "." || name === "..") {
|
|
6063
|
-
continue;
|
|
6064
|
-
}
|
|
6065
|
-
const abs = path10.join(dir, name);
|
|
6066
|
-
const rel = prefix ? `${prefix}/${name}` : name;
|
|
6067
|
-
if (e.isDirectory()) {
|
|
6068
|
-
await walk(abs, rel);
|
|
6069
|
-
} else if (e.isFile()) {
|
|
6070
|
-
const st = statSync7(abs);
|
|
6071
|
-
out.push({
|
|
6072
|
-
absPath: abs,
|
|
6073
|
-
relativePath: rel.replace(/\\/g, "/"),
|
|
6074
|
-
size: st.size
|
|
6075
|
-
});
|
|
6076
|
-
}
|
|
6077
|
-
}
|
|
6078
|
-
}
|
|
6079
|
-
await walk(root, "");
|
|
6080
|
-
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
6081
|
-
return out;
|
|
6082
|
-
}
|
|
6083
|
-
async function readArtifactFile(absPath) {
|
|
6084
|
-
return readFile2(absPath);
|
|
6085
|
-
}
|
|
6086
|
-
function toMB(bytes) {
|
|
6087
|
-
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
6088
|
-
}
|
|
6089
|
-
var MIME = {
|
|
6090
|
-
".html": "text/html; charset=utf-8",
|
|
6091
|
-
".css": "text/css; charset=utf-8",
|
|
6092
|
-
".js": "application/javascript; charset=utf-8",
|
|
6093
|
-
".json": "application/json; charset=utf-8",
|
|
6094
|
-
".svg": "image/svg+xml",
|
|
6095
|
-
".png": "image/png",
|
|
6096
|
-
".jpg": "image/jpeg",
|
|
6097
|
-
".jpeg": "image/jpeg",
|
|
6098
|
-
".gif": "image/gif",
|
|
6099
|
-
".webp": "image/webp",
|
|
6100
|
-
".woff": "font/woff",
|
|
6101
|
-
".woff2": "font/woff2",
|
|
6102
|
-
".ttf": "font/ttf",
|
|
6103
|
-
".ico": "image/x-icon",
|
|
6104
|
-
".txt": "text/plain; charset=utf-8",
|
|
6105
|
-
".map": "application/json"
|
|
6106
|
-
};
|
|
6107
|
-
function detectMimeType(filePath) {
|
|
6108
|
-
const ext = path10.extname(filePath).toLowerCase();
|
|
6109
|
-
return MIME[ext] ?? "";
|
|
6110
|
-
}
|
|
6111
|
-
var MinioClient = class {
|
|
6112
|
-
inner;
|
|
6113
|
-
constructor(opts) {
|
|
6114
|
-
const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
|
|
6115
|
-
this.inner = new Minio.Client({
|
|
6116
|
-
endPoint,
|
|
6117
|
-
port: opts.port,
|
|
6118
|
-
useSSL: opts.useSSL,
|
|
6119
|
-
accessKey: opts.accessKey,
|
|
6120
|
-
secretKey: opts.secretKey
|
|
6121
|
-
});
|
|
6122
|
-
}
|
|
6123
|
-
async ensureBucket(bucket) {
|
|
6124
|
-
const exists = await this.inner.bucketExists(bucket);
|
|
6125
|
-
if (!exists) {
|
|
6126
|
-
await this.inner.makeBucket(bucket);
|
|
6127
|
-
}
|
|
6128
|
-
}
|
|
6129
|
-
async deleteObjectsByPrefix(bucket, prefix) {
|
|
6130
|
-
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
6131
|
-
const keys = [];
|
|
6132
|
-
await new Promise((resolve5, reject) => {
|
|
6133
|
-
objectsStream.on("data", (obj) => {
|
|
6134
|
-
if (obj.name) {
|
|
6135
|
-
keys.push(obj.name);
|
|
6136
|
-
}
|
|
6137
|
-
});
|
|
6138
|
-
objectsStream.on("error", reject);
|
|
6139
|
-
objectsStream.on("end", resolve5);
|
|
6140
|
-
});
|
|
6141
|
-
const chunkSize = 500;
|
|
6142
|
-
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
6143
|
-
const chunk = keys.slice(i, i + chunkSize);
|
|
6144
|
-
await this.inner.removeObjects(
|
|
6145
|
-
bucket,
|
|
6146
|
-
chunk.map((name) => name)
|
|
6147
|
-
);
|
|
6148
|
-
}
|
|
6149
|
-
}
|
|
6150
|
-
async putObject(bucket, objectKey, body, meta) {
|
|
6151
|
-
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
6152
|
-
}
|
|
6153
|
-
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
6154
|
-
async setBucketPublicRead(bucket) {
|
|
6155
|
-
const policy = {
|
|
6156
|
-
Version: "2012-10-17",
|
|
6157
|
-
Statement: [
|
|
6158
|
-
{
|
|
6159
|
-
Effect: "Allow",
|
|
6160
|
-
Principal: { AWS: ["*"] },
|
|
6161
|
-
Action: ["s3:GetObject"],
|
|
6162
|
-
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
6163
|
-
}
|
|
6164
|
-
]
|
|
6165
|
-
};
|
|
6166
|
-
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
6167
|
-
}
|
|
6168
|
-
};
|
|
6169
|
-
|
|
6170
|
-
// src/commands/deploy/frontend.ts
|
|
6171
6519
|
function resolveArtifactNamePrefix(cfg) {
|
|
6172
6520
|
const nameRaw = (cfg.name ?? "").trim();
|
|
6173
6521
|
if (!nameRaw) {
|
|
@@ -6341,6 +6689,13 @@ function registerDeploySftpCommands(program) {
|
|
|
6341
6689
|
async (opts) => {
|
|
6342
6690
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
6343
6691
|
const settings = resolveWisdomDeployFromApmConfig(cfg);
|
|
6692
|
+
const projectName = (cfg.name ?? "").trim();
|
|
6693
|
+
if (!projectName) {
|
|
6694
|
+
console.error(
|
|
6695
|
+
"\u8BF7\u5728 .apm/apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4F5C\u4E3A\u90E8\u7F72\u4EA7\u7269\u9879\u76EE\u540D\uFF09"
|
|
6696
|
+
);
|
|
6697
|
+
process.exit(1);
|
|
6698
|
+
}
|
|
6344
6699
|
const root = path12.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
6345
6700
|
if (!await isDirectoryPath(root)) {
|
|
6346
6701
|
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
@@ -6350,7 +6705,8 @@ function registerDeploySftpCommands(program) {
|
|
|
6350
6705
|
const result = await runWisdomSftpDeploy({
|
|
6351
6706
|
localDir: root,
|
|
6352
6707
|
settings,
|
|
6353
|
-
extract: Boolean(opts.extract)
|
|
6708
|
+
extract: Boolean(opts.extract),
|
|
6709
|
+
projectName
|
|
6354
6710
|
});
|
|
6355
6711
|
console.log(JSON.stringify(result, null, 2));
|
|
6356
6712
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
**sessionId**:从当前工作项目录 `.apm/sessions/<sessionId>/` 或 `session.yaml` 获取;必须传入 `--session`,平台才会记录部署。
|
|
8
8
|
|
|
9
|
+
部署前会自动:
|
|
10
|
+
|
|
11
|
+
1. 从平台同步最新部署配置(`.apm/apm.config.json`)
|
|
12
|
+
2. 合并仓库基线分支最新代码到当前分支
|
|
13
|
+
|
|
14
|
+
若平台刚更新过部署配置,也可先手动执行 `apm sync-deploy-config` 再部署。
|
|
15
|
+
|
|
9
16
|
- **成功**:部署完成,在回信中说明测试环境地址或部署结果。
|
|
10
17
|
- **失败且输出含「未配置自动化部署」或「可跳过部署」**:本项目不支持自动化部署,属正常情况;放弃部署,在回信说明已跳过即可,无需修复配置或重试。
|
|
11
18
|
- **其他失败**:按输出排查并修复后重试。
|