ai-project-manage-cli 6.0.83 → 6.0.85
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 +666 -219
- package/package.json +1 -1
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,254 @@ 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] \u5DF2\u5F52\u6863\u90E8\u7F72\u4EA7\u7269: ${storage.bucket}/${objectKey} (${(body.length / 1024 / 1024).toFixed(2)} MB)`
|
|
2531
|
+
);
|
|
2532
|
+
} catch (error) {
|
|
2533
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2534
|
+
console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
|
|
2535
|
+
return null;
|
|
2536
|
+
}
|
|
2537
|
+
try {
|
|
2538
|
+
await api.cli.attachTaskDeploymentArtifact({
|
|
2539
|
+
id: deploymentRunId,
|
|
2540
|
+
artifactObjectKey: objectKey,
|
|
2541
|
+
artifactFileName: fileName
|
|
2542
|
+
});
|
|
2543
|
+
console.error(
|
|
2544
|
+
`[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
|
|
2545
|
+
);
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2548
|
+
console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
|
|
2549
|
+
return null;
|
|
2550
|
+
}
|
|
2551
|
+
return {
|
|
2552
|
+
objectKey,
|
|
2553
|
+
fileName,
|
|
2554
|
+
bucket: storage.bucket
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
// src/commands/connect/deploy-run.ts
|
|
2283
2559
|
var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
|
|
2284
2560
|
function resolveDeployCommand(environment) {
|
|
2285
2561
|
return `apm deploy ${environment}`;
|
|
@@ -2385,6 +2661,8 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2385
2661
|
console.log(`[apm] deploy command: ${command}`);
|
|
2386
2662
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
2387
2663
|
let latestLog = "";
|
|
2664
|
+
const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
2665
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
|
|
2388
2666
|
try {
|
|
2389
2667
|
const { log: log2 } = await runShellCommand(command, workdir, signal, (log3) => {
|
|
2390
2668
|
latestLog = log3;
|
|
@@ -2412,6 +2690,11 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
2412
2690
|
});
|
|
2413
2691
|
console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
|
|
2414
2692
|
} finally {
|
|
2693
|
+
if (previousDeploymentRunId === void 0) {
|
|
2694
|
+
delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
2695
|
+
} else {
|
|
2696
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
|
|
2697
|
+
}
|
|
2415
2698
|
logSyncer.dispose();
|
|
2416
2699
|
}
|
|
2417
2700
|
}
|
|
@@ -3939,6 +4222,99 @@ async function runConnect(options) {
|
|
|
3939
4222
|
});
|
|
3940
4223
|
}
|
|
3941
4224
|
|
|
4225
|
+
// src/commands/create-pr-errors.ts
|
|
4226
|
+
import { ApiError as ApiError2 } from "listpage-http";
|
|
4227
|
+
function extractMessage(error) {
|
|
4228
|
+
if (error instanceof ApiError2) {
|
|
4229
|
+
return String(error.message ?? "").trim();
|
|
4230
|
+
}
|
|
4231
|
+
if (error instanceof Error) {
|
|
4232
|
+
return error.message.trim();
|
|
4233
|
+
}
|
|
4234
|
+
return String(error).trim();
|
|
4235
|
+
}
|
|
4236
|
+
function extractHttpStatus(error) {
|
|
4237
|
+
if (error instanceof ApiError2) {
|
|
4238
|
+
return error.httpStatus;
|
|
4239
|
+
}
|
|
4240
|
+
return void 0;
|
|
4241
|
+
}
|
|
4242
|
+
function buildHints(message, httpStatus) {
|
|
4243
|
+
const hints = [];
|
|
4244
|
+
if (/未配置.*Gitee.*API Key/i.test(message)) {
|
|
4245
|
+
hints.push(
|
|
4246
|
+
"\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGitee API Key\u300D\uFF08Gitee \u79C1\u4EBA\u4EE4\u724C\uFF0C\u9700\u6709\u4ED3\u5E93 PR \u6743\u9650\uFF09"
|
|
4247
|
+
);
|
|
4248
|
+
} else if (/未配置.*GitHub.*API Key/i.test(message)) {
|
|
4249
|
+
hints.push(
|
|
4250
|
+
"\u8BF7\u5728 APM Web \u2192 \u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u7F16\u8F91\u5F53\u524D\u5BA2\u6237\u673A \u2192 \u586B\u5199\u300CGithub API Key\u300D\uFF08GitHub Personal Access Token\uFF0C\u9700\u6709 repo \u6743\u9650\uFF09"
|
|
4251
|
+
);
|
|
4252
|
+
} else if (httpStatus === 401 || /access token does not exist/i.test(message) || /bad credentials/i.test(message) || /Git 平台返回 401|认证失败 \(401\)/i.test(message)) {
|
|
4253
|
+
hints.push(
|
|
4254
|
+
"Git \u5E73\u53F0 Token \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF1A\u8BF7\u5728 Gitee/GitHub \u91CD\u65B0\u751F\u6210\u79C1\u4EBA\u4EE4\u724C\uFF0C\u5E76\u66F4\u65B0\u5230 APM\u300C\u5BA2\u6237\u673A\u7BA1\u7406\u300D"
|
|
4255
|
+
);
|
|
4256
|
+
}
|
|
4257
|
+
if (/未绑定仓库|未关联仓库|选择关联仓库/i.test(message)) {
|
|
4258
|
+
hints.push("\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u4E3A\u5F53\u524D\u8DEF\u5F84\u5173\u8054\u5BF9\u5E94 Git \u4ED3\u5E93");
|
|
4259
|
+
}
|
|
4260
|
+
if (/路径.*不匹配|尚未登记任何工作目录/i.test(message)) {
|
|
4261
|
+
hints.push(
|
|
4262
|
+
"\u5728 APM Web \u2192 \u5DE5\u4F5C\u76EE\u5F55 \u2192 \u767B\u8BB0\u5F53\u524D\u9879\u76EE\u8DEF\u5F84\uFF08\u9700\u4E0E apm create-pr \u6267\u884C\u65F6\u7684 cwd \u4E00\u81F4\uFF09"
|
|
4263
|
+
);
|
|
4264
|
+
}
|
|
4265
|
+
if (/no commits between|分支不存在|branch.*not found|找不到.*分支/i.test(
|
|
4266
|
+
message
|
|
4267
|
+
)) {
|
|
4268
|
+
hints.push(
|
|
4269
|
+
"\u7279\u6027\u5206\u652F\u53EF\u80FD\u672A\u63A8\u9001\uFF1A\u5148\u6267\u884C apm branch <sessionId> \u521B\u5EFA\u5206\u652F\u5E76 push\uFF0C\u518D\u91CD\u8BD5 create-pr"
|
|
4270
|
+
);
|
|
4271
|
+
}
|
|
4272
|
+
if (/pull request already exists|已存在.*pull/i.test(message)) {
|
|
4273
|
+
hints.push("\u8BE5\u5206\u652F\u53EF\u80FD\u5DF2\u6709\u5F00\u542F\u7684 PR\uFF0C\u8BF7\u5230 Git \u5E73\u53F0\u6216 APM \u4F1A\u8BDD\u8BE6\u60C5\u67E5\u770B");
|
|
4274
|
+
}
|
|
4275
|
+
if (/403|无权限|forbidden/i.test(message)) {
|
|
4276
|
+
hints.push("\u786E\u8BA4 Token \u5BF9\u76EE\u6807\u4ED3\u5E93\u6709\u521B\u5EFA Pull Request \u7684\u6743\u9650");
|
|
4277
|
+
}
|
|
4278
|
+
if (hints.length === 0) {
|
|
4279
|
+
hints.push(
|
|
4280
|
+
"\u82E5\u65E0\u6CD5\u81EA\u884C\u6392\u67E5\uFF0C\u8BF7\u5728\u4F1A\u8BDD\u4E2D @\u9879\u76EE\u7ECF\u7406\uFF0C\u5E76\u63D0\u4F9B\u4E0A\u8FF0\u4F1A\u8BDD ID \u4E0E\u5DE5\u4F5C\u76EE\u5F55"
|
|
4281
|
+
);
|
|
4282
|
+
}
|
|
4283
|
+
return hints;
|
|
4284
|
+
}
|
|
4285
|
+
function formatCreatePrErrorLines(error, context) {
|
|
4286
|
+
const message = extractMessage(error);
|
|
4287
|
+
const httpStatus = extractHttpStatus(error);
|
|
4288
|
+
const headBranch = `feat/session-${context.sessionId}`;
|
|
4289
|
+
const lines = [
|
|
4290
|
+
"[apm] \u521B\u5EFA PR \u5931\u8D25",
|
|
4291
|
+
"",
|
|
4292
|
+
`\u4F1A\u8BDD ID: ${context.sessionId}`,
|
|
4293
|
+
`\u5DE5\u4F5C\u76EE\u5F55: ${context.workdir}`,
|
|
4294
|
+
`\u7279\u6027\u5206\u652F: ${headBranch}`
|
|
4295
|
+
];
|
|
4296
|
+
if (context.clientMachineId) {
|
|
4297
|
+
lines.push(`\u5BA2\u6237\u673A ID: ${context.clientMachineId}`);
|
|
4298
|
+
}
|
|
4299
|
+
lines.push("", "\u539F\u56E0:", message || "(\u672A\u77E5\u9519\u8BEF)");
|
|
4300
|
+
const hints = buildHints(message, httpStatus);
|
|
4301
|
+
if (hints.length > 0) {
|
|
4302
|
+
lines.push("", "\u5EFA\u8BAE:");
|
|
4303
|
+
hints.forEach((hint, index) => {
|
|
4304
|
+
lines.push(`${index + 1}. ${hint}`);
|
|
4305
|
+
});
|
|
4306
|
+
}
|
|
4307
|
+
if (httpStatus !== void 0) {
|
|
4308
|
+
lines.push("", `HTTP \u72B6\u6001: ${httpStatus}`);
|
|
4309
|
+
}
|
|
4310
|
+
return lines;
|
|
4311
|
+
}
|
|
4312
|
+
function printCreatePrError(error, context) {
|
|
4313
|
+
for (const line of formatCreatePrErrorLines(error, context)) {
|
|
4314
|
+
console.error(line);
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
|
|
3942
4318
|
// src/commands/create-pr.ts
|
|
3943
4319
|
async function runCreatePr(options) {
|
|
3944
4320
|
const sessionId = options.sessionId.trim();
|
|
@@ -3954,18 +4330,27 @@ async function runCreatePr(options) {
|
|
|
3954
4330
|
const cfg = await ensureLoggedConfig();
|
|
3955
4331
|
const api = createApmApiClient(cfg);
|
|
3956
4332
|
const workdir = resolveWorkdirPath(options.cwd ?? process.cwd());
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
4333
|
+
try {
|
|
4334
|
+
const pr = await api.cli.createPullRequest({
|
|
4335
|
+
sessionId,
|
|
4336
|
+
workdir,
|
|
4337
|
+
title,
|
|
4338
|
+
content: options.content ?? ""
|
|
4339
|
+
});
|
|
4340
|
+
console.log(`[apm] PR \u5DF2\u5C31\u7EEA #${pr.number} (${pr.state}): ${pr.url}`);
|
|
4341
|
+
} catch (error) {
|
|
4342
|
+
printCreatePrError(error, {
|
|
4343
|
+
sessionId,
|
|
4344
|
+
workdir,
|
|
4345
|
+
clientMachineId: resolveClientMachineId(cfg)
|
|
4346
|
+
});
|
|
4347
|
+
process.exit(1);
|
|
4348
|
+
}
|
|
3964
4349
|
}
|
|
3965
4350
|
|
|
3966
4351
|
// src/commands/deploy/deploy-execute.ts
|
|
3967
4352
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3968
|
-
import
|
|
4353
|
+
import path5 from "node:path";
|
|
3969
4354
|
|
|
3970
4355
|
// src/commands/deploy/internal/apm-config.ts
|
|
3971
4356
|
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "node:fs";
|
|
@@ -4307,7 +4692,7 @@ var DeployExecutionError = class extends Error {
|
|
|
4307
4692
|
|
|
4308
4693
|
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
4309
4694
|
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
4310
|
-
import
|
|
4695
|
+
import path4 from "node:path";
|
|
4311
4696
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
4312
4697
|
|
|
4313
4698
|
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
@@ -4316,31 +4701,31 @@ import {
|
|
|
4316
4701
|
mkdirSync as mkdirSync8,
|
|
4317
4702
|
readdirSync as readdirSync5,
|
|
4318
4703
|
readFileSync as readFileSync13,
|
|
4319
|
-
statSync as
|
|
4704
|
+
statSync as statSync6,
|
|
4320
4705
|
writeFileSync as writeFileSync14
|
|
4321
4706
|
} from "node:fs";
|
|
4322
4707
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
4323
|
-
import
|
|
4324
|
-
import { Client } from "ssh2";
|
|
4708
|
+
import path3 from "node:path";
|
|
4709
|
+
import { Client as Client2 } from "ssh2";
|
|
4325
4710
|
import JSZip2 from "jszip";
|
|
4326
4711
|
import SftpClient2 from "ssh2-sftp-client";
|
|
4327
4712
|
|
|
4328
4713
|
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
4329
|
-
import { readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
4330
|
-
import
|
|
4714
|
+
import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
|
|
4715
|
+
import path2 from "node:path";
|
|
4331
4716
|
import JSZip from "jszip";
|
|
4332
4717
|
import SftpClient from "ssh2-sftp-client";
|
|
4333
4718
|
async function addDirToZip(dir, zipFolder) {
|
|
4334
|
-
const entries = await
|
|
4719
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
4335
4720
|
for (const entry of entries) {
|
|
4336
|
-
const fullPath =
|
|
4721
|
+
const fullPath = path2.join(dir, entry.name);
|
|
4337
4722
|
if (entry.isDirectory()) {
|
|
4338
4723
|
const folder = zipFolder.folder(entry.name);
|
|
4339
4724
|
if (folder) {
|
|
4340
4725
|
await addDirToZip(fullPath, folder);
|
|
4341
4726
|
}
|
|
4342
4727
|
} else {
|
|
4343
|
-
const content = await
|
|
4728
|
+
const content = await readFile3(fullPath);
|
|
4344
4729
|
zipFolder.file(entry.name, content);
|
|
4345
4730
|
}
|
|
4346
4731
|
}
|
|
@@ -4448,8 +4833,8 @@ async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
|
4448
4833
|
}
|
|
4449
4834
|
}
|
|
4450
4835
|
async function runWisdomSftpDeploy(params) {
|
|
4451
|
-
const zipPath =
|
|
4452
|
-
const resolvedZipPath =
|
|
4836
|
+
const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
4837
|
+
const resolvedZipPath = path2.resolve(zipPath);
|
|
4453
4838
|
let zipSizeBytes = 0;
|
|
4454
4839
|
try {
|
|
4455
4840
|
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
@@ -4458,6 +4843,11 @@ async function runWisdomSftpDeploy(params) {
|
|
|
4458
4843
|
resolvedZipPath,
|
|
4459
4844
|
params.extract
|
|
4460
4845
|
);
|
|
4846
|
+
await uploadDeployArtifactZip({
|
|
4847
|
+
zipPath: resolvedZipPath,
|
|
4848
|
+
projectName: params.projectName,
|
|
4849
|
+
kind: "frontend"
|
|
4850
|
+
});
|
|
4461
4851
|
} finally {
|
|
4462
4852
|
try {
|
|
4463
4853
|
await unlink(resolvedZipPath);
|
|
@@ -4492,7 +4882,7 @@ function fail(message) {
|
|
|
4492
4882
|
}
|
|
4493
4883
|
function expandPath(pathStr) {
|
|
4494
4884
|
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4495
|
-
return
|
|
4885
|
+
return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
|
|
4496
4886
|
}
|
|
4497
4887
|
function quoteForShell(value) {
|
|
4498
4888
|
if (process.platform === "win32") {
|
|
@@ -4507,19 +4897,19 @@ function formatMavenLocalRepoArg(repoPath) {
|
|
|
4507
4897
|
return `-Dmaven.repo.local=${repoPath}`;
|
|
4508
4898
|
}
|
|
4509
4899
|
function deployCacheDir() {
|
|
4510
|
-
return
|
|
4900
|
+
return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
|
|
4511
4901
|
}
|
|
4512
4902
|
function manifestFilePath() {
|
|
4513
|
-
return
|
|
4903
|
+
return path3.join(deployCacheDir(), "manifest.json");
|
|
4514
4904
|
}
|
|
4515
4905
|
function getTargetDir(projectRoot) {
|
|
4516
|
-
return
|
|
4906
|
+
return path3.join(projectRoot, MAVEN_MODULE, "target");
|
|
4517
4907
|
}
|
|
4518
4908
|
function relativeKey(projectRoot, filePath) {
|
|
4519
|
-
return
|
|
4909
|
+
return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
|
|
4520
4910
|
}
|
|
4521
4911
|
function fileSignature(filePath) {
|
|
4522
|
-
const stat2 =
|
|
4912
|
+
const stat2 = statSync6(filePath);
|
|
4523
4913
|
return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
|
|
4524
4914
|
}
|
|
4525
4915
|
function loadManifest4() {
|
|
@@ -4541,12 +4931,12 @@ function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
|
|
|
4541
4931
|
if (!remoteAttr) {
|
|
4542
4932
|
return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
|
|
4543
4933
|
}
|
|
4544
|
-
const localSize =
|
|
4934
|
+
const localSize = statSync6(localPath).size;
|
|
4545
4935
|
const remoteSize = remoteAttr.size;
|
|
4546
4936
|
if (localSize !== remoteSize) {
|
|
4547
4937
|
return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
|
|
4548
4938
|
}
|
|
4549
|
-
if (isProjectLibJar(
|
|
4939
|
+
if (isProjectLibJar(path3.basename(localPath)) && manifest) {
|
|
4550
4940
|
const key = relativeKey(projectRoot, localPath);
|
|
4551
4941
|
const current = fileSignature(localPath);
|
|
4552
4942
|
const previous = manifest[key];
|
|
@@ -4566,7 +4956,7 @@ function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest =
|
|
|
4566
4956
|
const entries = [];
|
|
4567
4957
|
const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
|
|
4568
4958
|
for (const jarName of jarFiles) {
|
|
4569
|
-
const jarPath =
|
|
4959
|
+
const jarPath = path3.join(localLibDir, jarName);
|
|
4570
4960
|
const remoteAttr = remoteStats.get(jarName);
|
|
4571
4961
|
const [shouldUpload, reason] = shouldUploadLibFile(
|
|
4572
4962
|
jarPath,
|
|
@@ -4589,8 +4979,8 @@ function updateManifestEntries(manifest, entries, projectRoot) {
|
|
|
4589
4979
|
async function createUpdatePackage(entries, packageName) {
|
|
4590
4980
|
const dir = deployCacheDir();
|
|
4591
4981
|
mkdirSync8(dir, { recursive: true });
|
|
4592
|
-
const zipPath =
|
|
4593
|
-
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${
|
|
4982
|
+
const zipPath = path3.join(dir, packageName);
|
|
4983
|
+
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
4594
4984
|
const zip = new JSZip2();
|
|
4595
4985
|
for (const entry of entries) {
|
|
4596
4986
|
const content = readFileSync13(entry.path);
|
|
@@ -4654,8 +5044,8 @@ function locateLibDir(projectRoot) {
|
|
|
4654
5044
|
if (!existsSync16(targetDir)) {
|
|
4655
5045
|
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4656
5046
|
}
|
|
4657
|
-
const libDir =
|
|
4658
|
-
if (!existsSync16(libDir) || !
|
|
5047
|
+
const libDir = path3.join(targetDir, "lib");
|
|
5048
|
+
if (!existsSync16(libDir) || !statSync6(libDir).isDirectory()) {
|
|
4659
5049
|
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
4660
5050
|
}
|
|
4661
5051
|
const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
|
|
@@ -4670,16 +5060,16 @@ function locateMainJar(projectRoot) {
|
|
|
4670
5060
|
if (!existsSync16(targetDir)) {
|
|
4671
5061
|
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4672
5062
|
}
|
|
4673
|
-
const jarFiles = readdirSync5(targetDir).filter((name) => name.endsWith(".jar") && !name.endsWith(".jar.original")).map((name) =>
|
|
5063
|
+
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);
|
|
4674
5064
|
if (jarFiles.length === 0) {
|
|
4675
5065
|
fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
|
|
4676
5066
|
}
|
|
4677
5067
|
const mainJar = jarFiles[0];
|
|
4678
|
-
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${
|
|
5068
|
+
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
|
|
4679
5069
|
return mainJar;
|
|
4680
5070
|
}
|
|
4681
5071
|
async function connectSsh(config) {
|
|
4682
|
-
const client = new
|
|
5072
|
+
const client = new Client2();
|
|
4683
5073
|
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
|
|
4684
5074
|
await new Promise((resolve5, reject) => {
|
|
4685
5075
|
client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
|
|
@@ -4724,7 +5114,7 @@ async function getRemoteFileStats(sftp, remoteDir) {
|
|
|
4724
5114
|
}
|
|
4725
5115
|
async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
4726
5116
|
const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
|
|
4727
|
-
const remotePath = `${remoteDir}/${
|
|
5117
|
+
const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
|
|
4728
5118
|
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
4729
5119
|
try {
|
|
4730
5120
|
await sftp.fastPut(zipPath, remotePath);
|
|
@@ -4737,7 +5127,7 @@ async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
|
4737
5127
|
async function uploadFullJar(sftp, localJarPath, config) {
|
|
4738
5128
|
const remoteDir = config.remoteAppDir.replace(/\/$/, "");
|
|
4739
5129
|
const remotePath = `${remoteDir}/${config.startupJar}`;
|
|
4740
|
-
log(`\u4E0A\u4F20\u5168\u91CF JAR (${
|
|
5130
|
+
log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
|
|
4741
5131
|
try {
|
|
4742
5132
|
await sftp.mkdir(remoteDir, true);
|
|
4743
5133
|
await sftp.fastPut(localJarPath, remotePath);
|
|
@@ -4996,10 +5386,10 @@ async function restartRemoteService(client, config) {
|
|
|
4996
5386
|
await runRemoteServiceScript(client, script, "restart");
|
|
4997
5387
|
}
|
|
4998
5388
|
async function runWisdomBackendDeploy(options) {
|
|
4999
|
-
const projectRoot =
|
|
5389
|
+
const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
|
|
5000
5390
|
const config = options.config;
|
|
5001
5391
|
log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
|
|
5002
|
-
log(`\u914D\u7F6E\u6587\u4EF6: ${
|
|
5392
|
+
log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
|
|
5003
5393
|
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
5004
5394
|
log(
|
|
5005
5395
|
`\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"}`
|
|
@@ -5013,6 +5403,21 @@ async function runWisdomBackendDeploy(options) {
|
|
|
5013
5403
|
if (config.mode === "full") {
|
|
5014
5404
|
const mainJar = locateMainJar(projectRoot);
|
|
5015
5405
|
await uploadFullJar(conn.sftp, mainJar, config);
|
|
5406
|
+
const archiveZipPath = await createUpdatePackage(
|
|
5407
|
+
[
|
|
5408
|
+
{
|
|
5409
|
+
path: mainJar,
|
|
5410
|
+
arcname: path3.basename(mainJar),
|
|
5411
|
+
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
5412
|
+
}
|
|
5413
|
+
],
|
|
5414
|
+
`.deploy-archive-${config.projectName}.jar.zip`
|
|
5415
|
+
);
|
|
5416
|
+
await uploadDeployArtifactZip({
|
|
5417
|
+
zipPath: archiveZipPath,
|
|
5418
|
+
projectName: config.projectName,
|
|
5419
|
+
kind: "backend"
|
|
5420
|
+
});
|
|
5016
5421
|
log("\u91CD\u542F\u670D\u52A1...");
|
|
5017
5422
|
await restartRemoteService(conn.client, config);
|
|
5018
5423
|
await healthCheckService(conn.client, config);
|
|
@@ -5043,6 +5448,11 @@ async function runWisdomBackendDeploy(options) {
|
|
|
5043
5448
|
zipPath,
|
|
5044
5449
|
config
|
|
5045
5450
|
);
|
|
5451
|
+
await uploadDeployArtifactZip({
|
|
5452
|
+
zipPath,
|
|
5453
|
+
projectName: config.projectName,
|
|
5454
|
+
kind: "backend"
|
|
5455
|
+
});
|
|
5046
5456
|
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
5047
5457
|
updated = await extractUpdatePackageOnRemote(
|
|
5048
5458
|
conn.client,
|
|
@@ -5086,10 +5496,10 @@ function isWisdomLegacyDeploy(cfg) {
|
|
|
5086
5496
|
return !hasNewFrontend && !hasNewBackend;
|
|
5087
5497
|
}
|
|
5088
5498
|
function detectWisdomProjectType(cwd) {
|
|
5089
|
-
return existsSync17(
|
|
5499
|
+
return existsSync17(path4.join(cwd, "package.json")) ? "frontend" : "backend";
|
|
5090
5500
|
}
|
|
5091
5501
|
function readPackageScripts(cwd) {
|
|
5092
|
-
const pkgPath =
|
|
5502
|
+
const pkgPath = path4.join(cwd, "package.json");
|
|
5093
5503
|
if (!existsSync17(pkgPath)) {
|
|
5094
5504
|
return {};
|
|
5095
5505
|
}
|
|
@@ -5134,7 +5544,7 @@ function runShellCommand2(command, cwd, captureOutput) {
|
|
|
5134
5544
|
return output;
|
|
5135
5545
|
}
|
|
5136
5546
|
async function runWisdomAutoDeploy(options) {
|
|
5137
|
-
const cwd =
|
|
5547
|
+
const cwd = path4.resolve(options.cwd ?? process.cwd());
|
|
5138
5548
|
const captureOutput = options.captureOutput ?? false;
|
|
5139
5549
|
const projectType = detectWisdomProjectType(cwd);
|
|
5140
5550
|
console.error(
|
|
@@ -5145,14 +5555,14 @@ async function runWisdomAutoDeploy(options) {
|
|
|
5145
5555
|
if (!deployCmd) {
|
|
5146
5556
|
const lines = formatWisdomFrontendDeployNotConfiguredLines(
|
|
5147
5557
|
options.env,
|
|
5148
|
-
|
|
5558
|
+
path4.join(cwd, "package.json")
|
|
5149
5559
|
);
|
|
5150
5560
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
5151
5561
|
}
|
|
5152
5562
|
runShellCommand2(deployCmd, cwd, captureOutput);
|
|
5153
5563
|
return { projectType };
|
|
5154
5564
|
}
|
|
5155
|
-
const configPath = options.configPath ??
|
|
5565
|
+
const configPath = options.configPath ?? path4.join(workspaceApmDir(cwd), "apm.config.json");
|
|
5156
5566
|
const cfg = loadApmConfig({ configPath });
|
|
5157
5567
|
const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
|
|
5158
5568
|
console.error(
|
|
@@ -5165,6 +5575,150 @@ async function runWisdomAutoDeploy(options) {
|
|
|
5165
5575
|
return { projectType };
|
|
5166
5576
|
}
|
|
5167
5577
|
|
|
5578
|
+
// src/commands/deploy/internal/deploy-baseline-sync.ts
|
|
5579
|
+
var LOG_PREFIX = "[apm] \u90E8\u7F72\u524D\u57FA\u7EBF\u540C\u6B65:";
|
|
5580
|
+
function formatGitError(error) {
|
|
5581
|
+
return error instanceof Error ? error.message : String(error);
|
|
5582
|
+
}
|
|
5583
|
+
async function resolveBaselineBranch2(cwd, api) {
|
|
5584
|
+
const workdirPath = resolveWorkdirPath(cwd);
|
|
5585
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
5586
|
+
if (!baseline.repositoryId) {
|
|
5587
|
+
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
|
|
5588
|
+
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
|
|
5589
|
+
throw new Error(detail);
|
|
5590
|
+
}
|
|
5591
|
+
const baselineBranch = (baseline.defaultBranch ?? "").trim();
|
|
5592
|
+
if (!baselineBranch) {
|
|
5593
|
+
throw new Error("\u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
|
|
5594
|
+
}
|
|
5595
|
+
return baselineBranch;
|
|
5596
|
+
}
|
|
5597
|
+
async function isMergeInProgress(cwd) {
|
|
5598
|
+
try {
|
|
5599
|
+
await execGit(cwd, ["rev-parse", "-q", "--verify", "MERGE_HEAD"], true);
|
|
5600
|
+
return true;
|
|
5601
|
+
} catch {
|
|
5602
|
+
return false;
|
|
5603
|
+
}
|
|
5604
|
+
}
|
|
5605
|
+
async function abortMergeIfNeeded(cwd) {
|
|
5606
|
+
if (await isMergeInProgress(cwd)) {
|
|
5607
|
+
await execGit(cwd, ["merge", "--abort"], true);
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
async function restoreWorkingTree(cwd) {
|
|
5611
|
+
await execGit(cwd, ["reset", "--hard", "HEAD"], true);
|
|
5612
|
+
}
|
|
5613
|
+
async function mergeBaselineBranch(cwd, baselineBranch, currentBranch, note) {
|
|
5614
|
+
const mergeTarget = `origin/${baselineBranch}`;
|
|
5615
|
+
note(`${LOG_PREFIX} \u5F00\u59CB\u5408\u5E76 ${mergeTarget} \u5230\u5F53\u524D\u5206\u652F ${currentBranch}`);
|
|
5616
|
+
try {
|
|
5617
|
+
if (currentBranch === baselineBranch) {
|
|
5618
|
+
await execGit(cwd, ["merge", "--ff-only", mergeTarget]);
|
|
5619
|
+
} else {
|
|
5620
|
+
await execGit(cwd, ["merge", mergeTarget, "--no-edit"]);
|
|
5621
|
+
}
|
|
5622
|
+
note(`${LOG_PREFIX} \u5DF2\u5408\u5E76 ${mergeTarget} \u6700\u65B0\u4EE3\u7801`);
|
|
5623
|
+
return { merged: true, conflictRollback: false };
|
|
5624
|
+
} catch (error) {
|
|
5625
|
+
await abortMergeIfNeeded(cwd);
|
|
5626
|
+
note(`${LOG_PREFIX} \u5408\u5E76 ${mergeTarget} \u5931\u8D25: ${formatGitError(error)}`);
|
|
5627
|
+
note(`${LOG_PREFIX} \u5DF2\u6267\u884C git merge --abort \u56DE\u9000\u5230\u5408\u5E76\u524D\u72B6\u6001`);
|
|
5628
|
+
note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
|
|
5629
|
+
return { merged: false, conflictRollback: true };
|
|
5630
|
+
}
|
|
5631
|
+
}
|
|
5632
|
+
async function restoreStashIfNeeded(cwd, stashed, note) {
|
|
5633
|
+
if (!stashed) {
|
|
5634
|
+
return false;
|
|
5635
|
+
}
|
|
5636
|
+
note(`${LOG_PREFIX} \u6B63\u5728\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5637
|
+
try {
|
|
5638
|
+
await execGit(cwd, ["stash", "pop"], true);
|
|
5639
|
+
note(`${LOG_PREFIX} \u5DF2\u6062\u590D stash \u4E2D\u7684\u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5640
|
+
return false;
|
|
5641
|
+
} catch (error) {
|
|
5642
|
+
await abortMergeIfNeeded(cwd);
|
|
5643
|
+
await restoreWorkingTree(cwd);
|
|
5644
|
+
note(`${LOG_PREFIX} \u6062\u590D stash \u5931\u8D25: ${formatGitError(error)}`);
|
|
5645
|
+
note(`${LOG_PREFIX} \u5DF2\u56DE\u9000\u5DE5\u4F5C\u533A\uFF08git reset --hard HEAD\uFF09\uFF0Cstash \u4ECD\u4FDD\u7559`);
|
|
5646
|
+
note(`${LOG_PREFIX} \u5C06\u4F7F\u7528\u56DE\u9000\u540E\u7684\u4EE3\u7801\u7EE7\u7EED\u90E8\u7F72`);
|
|
5647
|
+
return true;
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
async function syncDeployBaselineBeforeDeploy(cwd) {
|
|
5651
|
+
const lines = [];
|
|
5652
|
+
const note = (message) => {
|
|
5653
|
+
lines.push(message);
|
|
5654
|
+
console.error(message);
|
|
5655
|
+
};
|
|
5656
|
+
const emptyResult = (partial = {}) => ({
|
|
5657
|
+
log: lines.join("\n"),
|
|
5658
|
+
skipped: true,
|
|
5659
|
+
merged: false,
|
|
5660
|
+
conflictRollback: false,
|
|
5661
|
+
stashConflict: false,
|
|
5662
|
+
...partial
|
|
5663
|
+
});
|
|
5664
|
+
if (!await isGitRepo(cwd)) {
|
|
5665
|
+
note(`${LOG_PREFIX} \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8DF3\u8FC7`);
|
|
5666
|
+
return emptyResult();
|
|
5667
|
+
}
|
|
5668
|
+
const cfg = await tryReadApmConfig();
|
|
5669
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
5670
|
+
note(`${LOG_PREFIX} \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7`);
|
|
5671
|
+
return emptyResult();
|
|
5672
|
+
}
|
|
5673
|
+
await ensureGitRepo(cwd);
|
|
5674
|
+
const api = createApmApiClient(cfg);
|
|
5675
|
+
const baselineBranch = await resolveBaselineBranch2(cwd, api);
|
|
5676
|
+
note(`${LOG_PREFIX} \u57FA\u7EBF\u5206\u652F ${baselineBranch}`);
|
|
5677
|
+
note(`${LOG_PREFIX} \u6B63\u5728 fetch origin ${baselineBranch}`);
|
|
5678
|
+
await execGit(cwd, ["fetch", "origin", baselineBranch], true);
|
|
5679
|
+
if (!await remoteBranchExists(cwd, baselineBranch)) {
|
|
5680
|
+
throw new Error(
|
|
5681
|
+
`\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`
|
|
5682
|
+
);
|
|
5683
|
+
}
|
|
5684
|
+
note(`${LOG_PREFIX} fetch \u5B8C\u6210`);
|
|
5685
|
+
const currentBranch = await getCurrentBranch(cwd);
|
|
5686
|
+
let stashed = false;
|
|
5687
|
+
if (await isWorkingTreeDirty(cwd)) {
|
|
5688
|
+
note(`${LOG_PREFIX} \u68C0\u6D4B\u5230\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u5148 stash`);
|
|
5689
|
+
await execGit(cwd, [
|
|
5690
|
+
"stash",
|
|
5691
|
+
"push",
|
|
5692
|
+
"-u",
|
|
5693
|
+
"-m",
|
|
5694
|
+
"apm: deploy baseline sync"
|
|
5695
|
+
]);
|
|
5696
|
+
stashed = true;
|
|
5697
|
+
note(`${LOG_PREFIX} \u5DF2 stash \u672A\u63D0\u4EA4\u6539\u52A8`);
|
|
5698
|
+
}
|
|
5699
|
+
const { merged, conflictRollback } = await mergeBaselineBranch(
|
|
5700
|
+
cwd,
|
|
5701
|
+
baselineBranch,
|
|
5702
|
+
currentBranch,
|
|
5703
|
+
note
|
|
5704
|
+
);
|
|
5705
|
+
const stashConflict = await restoreStashIfNeeded(cwd, stashed, note);
|
|
5706
|
+
if (conflictRollback || stashConflict) {
|
|
5707
|
+
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`);
|
|
5708
|
+
} else if (merged) {
|
|
5709
|
+
note(
|
|
5710
|
+
`${LOG_PREFIX} \u5B8C\u6210\uFF08\u5F53\u524D\u5206\u652F ${currentBranch} \u5DF2\u5305\u542B ${baselineBranch} \u6700\u65B0\u4EE3\u7801\uFF09`
|
|
5711
|
+
);
|
|
5712
|
+
}
|
|
5713
|
+
return {
|
|
5714
|
+
log: lines.join("\n"),
|
|
5715
|
+
skipped: false,
|
|
5716
|
+
merged,
|
|
5717
|
+
conflictRollback,
|
|
5718
|
+
stashConflict
|
|
5719
|
+
};
|
|
5720
|
+
}
|
|
5721
|
+
|
|
5168
5722
|
// src/commands/deploy/deploy-execute.ts
|
|
5169
5723
|
function runShellCommand3(command, cwd, captureOutput) {
|
|
5170
5724
|
const result = spawnSync5(command, {
|
|
@@ -5191,8 +5745,22 @@ function runShellCommand3(command, cwd, captureOutput) {
|
|
|
5191
5745
|
return output;
|
|
5192
5746
|
}
|
|
5193
5747
|
async function executeDeploy(options) {
|
|
5194
|
-
const cwd =
|
|
5748
|
+
const cwd = path5.resolve(options.cwd ?? process.cwd());
|
|
5195
5749
|
const captureOutput = options.captureOutput ?? false;
|
|
5750
|
+
const outputParts = [];
|
|
5751
|
+
try {
|
|
5752
|
+
const syncResult = await syncDeployBaselineBeforeDeploy(cwd);
|
|
5753
|
+
if (syncResult.log.trim()) {
|
|
5754
|
+
outputParts.push(syncResult.log);
|
|
5755
|
+
}
|
|
5756
|
+
} catch (error) {
|
|
5757
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
5758
|
+
throw new DeployExecutionError(
|
|
5759
|
+
`\u90E8\u7F72\u524D\u540C\u6B65\u57FA\u7EBF\u5206\u652F\u5931\u8D25: ${detail}`,
|
|
5760
|
+
1,
|
|
5761
|
+
detail
|
|
5762
|
+
);
|
|
5763
|
+
}
|
|
5196
5764
|
const cfg = loadApmConfig({ configPath: options.configPath });
|
|
5197
5765
|
if (isWisdomLegacyDeploy(cfg)) {
|
|
5198
5766
|
try {
|
|
@@ -5202,7 +5770,7 @@ async function executeDeploy(options) {
|
|
|
5202
5770
|
configPath: options.configPath,
|
|
5203
5771
|
captureOutput
|
|
5204
5772
|
});
|
|
5205
|
-
return "";
|
|
5773
|
+
return outputParts.join("\n");
|
|
5206
5774
|
} catch (error) {
|
|
5207
5775
|
if (error instanceof DeployExecutionError) {
|
|
5208
5776
|
throw error;
|
|
@@ -5217,7 +5785,11 @@ async function executeDeploy(options) {
|
|
|
5217
5785
|
const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
|
|
5218
5786
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
5219
5787
|
}
|
|
5220
|
-
|
|
5788
|
+
const commandOutput = runShellCommand3(command, cwd, captureOutput);
|
|
5789
|
+
if (commandOutput.trim()) {
|
|
5790
|
+
outputParts.push(commandOutput);
|
|
5791
|
+
}
|
|
5792
|
+
return outputParts.join("\n");
|
|
5221
5793
|
}
|
|
5222
5794
|
function printDeployExecutionError(error) {
|
|
5223
5795
|
if (error.output.trim()) {
|
|
@@ -5282,6 +5854,8 @@ async function runDeployWithBackendTracking(options) {
|
|
|
5282
5854
|
status: "DEPLOYING"
|
|
5283
5855
|
});
|
|
5284
5856
|
const logSyncer = createDeployLogSyncer(api, deploymentRunId);
|
|
5857
|
+
const previousDeploymentRunId = process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
5858
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = deploymentRunId;
|
|
5285
5859
|
const chunks = [];
|
|
5286
5860
|
const appendLog = (line) => {
|
|
5287
5861
|
if (!line) return;
|
|
@@ -5328,6 +5902,11 @@ async function runDeployWithBackendTracking(options) {
|
|
|
5328
5902
|
printDeployExecutionError(deployError);
|
|
5329
5903
|
process.exit(deployError.exitCode);
|
|
5330
5904
|
} finally {
|
|
5905
|
+
if (previousDeploymentRunId === void 0) {
|
|
5906
|
+
delete process.env[APM_DEPLOYMENT_RUN_ID_ENV];
|
|
5907
|
+
} else {
|
|
5908
|
+
process.env[APM_DEPLOYMENT_RUN_ID_ENV] = previousDeploymentRunId;
|
|
5909
|
+
}
|
|
5331
5910
|
console.log = originalLog;
|
|
5332
5911
|
console.error = originalError;
|
|
5333
5912
|
logSyncer.dispose();
|
|
@@ -5371,23 +5950,23 @@ function registerDeployMainCommand(program) {
|
|
|
5371
5950
|
}
|
|
5372
5951
|
|
|
5373
5952
|
// src/commands/deploy/backend.ts
|
|
5374
|
-
import
|
|
5953
|
+
import path10 from "node:path";
|
|
5375
5954
|
|
|
5376
5955
|
// src/commands/deploy/internal/backend-deploy/backend-deploy-workflow.ts
|
|
5377
|
-
import
|
|
5956
|
+
import path9 from "node:path";
|
|
5378
5957
|
|
|
5379
5958
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/client.ts
|
|
5380
5959
|
import Docker from "dockerode";
|
|
5381
5960
|
|
|
5382
5961
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
|
|
5383
5962
|
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "node:fs";
|
|
5384
|
-
import
|
|
5963
|
+
import path6 from "node:path";
|
|
5385
5964
|
function asOptionalTlsBuffer(value) {
|
|
5386
5965
|
if (typeof value !== "string") {
|
|
5387
5966
|
console.log("tls filepath not exist");
|
|
5388
5967
|
return void 0;
|
|
5389
5968
|
}
|
|
5390
|
-
console.log("tls filepath",
|
|
5969
|
+
console.log("tls filepath", path6.join(process.cwd(), value));
|
|
5391
5970
|
const normalized = value.trim();
|
|
5392
5971
|
if (normalized === "") {
|
|
5393
5972
|
return void 0;
|
|
@@ -5603,8 +6182,8 @@ var DockerodeClient = class {
|
|
|
5603
6182
|
var createDockerodeClient = (config) => new DockerodeClient(config);
|
|
5604
6183
|
|
|
5605
6184
|
// src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
|
|
5606
|
-
import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as
|
|
5607
|
-
import
|
|
6185
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16, statSync as statSync7 } from "node:fs";
|
|
6186
|
+
import path7 from "node:path";
|
|
5608
6187
|
function stripSurroundingQuotes(value) {
|
|
5609
6188
|
const t = value.trim();
|
|
5610
6189
|
if (t.length >= 2) {
|
|
@@ -5619,8 +6198,8 @@ function loadEnvFromFile(envFilePath) {
|
|
|
5619
6198
|
if (!envFilePath) {
|
|
5620
6199
|
return {};
|
|
5621
6200
|
}
|
|
5622
|
-
const targetPath =
|
|
5623
|
-
if (!existsSync19(targetPath) || !
|
|
6201
|
+
const targetPath = path7.resolve(envFilePath);
|
|
6202
|
+
if (!existsSync19(targetPath) || !statSync7(targetPath).isFile()) {
|
|
5624
6203
|
return {};
|
|
5625
6204
|
}
|
|
5626
6205
|
const raw = readFileSync16(targetPath, "utf-8");
|
|
@@ -5795,9 +6374,9 @@ function dockerPushImage(params, cwd) {
|
|
|
5795
6374
|
|
|
5796
6375
|
// src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
|
|
5797
6376
|
import { existsSync as existsSync20 } from "node:fs";
|
|
5798
|
-
import
|
|
6377
|
+
import path8 from "node:path";
|
|
5799
6378
|
function resolveDockerBuildPaths(cwd) {
|
|
5800
|
-
const dockerfilePath =
|
|
6379
|
+
const dockerfilePath = path8.join(cwd, "Dockerfile");
|
|
5801
6380
|
Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
|
|
5802
6381
|
if (!existsSync20(dockerfilePath)) {
|
|
5803
6382
|
throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
|
|
@@ -5854,7 +6433,7 @@ var BackendDeployWorkflow = class {
|
|
|
5854
6433
|
serveraddress
|
|
5855
6434
|
});
|
|
5856
6435
|
Logger.success("\u8FDC\u7A0B\u62C9\u53D6\u955C\u50CF\u5B8C\u6210");
|
|
5857
|
-
const envFilePath =
|
|
6436
|
+
const envFilePath = path9.resolve(cwd, this.params.envFilePath || ".env");
|
|
5858
6437
|
const envObj = loadEnvFromFile(envFilePath);
|
|
5859
6438
|
if (this.params.dockerNetwork?.trim()) {
|
|
5860
6439
|
Logger.info(`\u8FDC\u7A0B\u5BB9\u5668\u5C06\u52A0\u5165 Docker \u7F51\u7EDC\uFF1A${this.params.dockerNetwork.trim()}`);
|
|
@@ -5894,7 +6473,7 @@ function registerDeployBackendCommands(program) {
|
|
|
5894
6473
|
assertDeployImageTag(tag);
|
|
5895
6474
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
5896
6475
|
const fromApm = resolveBackendDeployFromApmConfig(cfg);
|
|
5897
|
-
const dirAbs =
|
|
6476
|
+
const dirAbs = path10.resolve(process.cwd(), opts.dir || "servers/api");
|
|
5898
6477
|
const params = {
|
|
5899
6478
|
image: fromApm.name,
|
|
5900
6479
|
tag,
|
|
@@ -5926,146 +6505,6 @@ function registerDeployBackendCommands(program) {
|
|
|
5926
6505
|
// src/commands/deploy/frontend.ts
|
|
5927
6506
|
import { copyFile, readdir as readdir3, stat } from "node:fs/promises";
|
|
5928
6507
|
import path11 from "node:path";
|
|
5929
|
-
|
|
5930
|
-
// src/commands/deploy/internal/minio.ts
|
|
5931
|
-
import { statSync as statSync7 } from "node:fs";
|
|
5932
|
-
import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
|
|
5933
|
-
import path10 from "node:path";
|
|
5934
|
-
import * as Minio from "minio";
|
|
5935
|
-
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
5936
|
-
async function isDirectoryPath(dir) {
|
|
5937
|
-
try {
|
|
5938
|
-
const st = statSync7(dir);
|
|
5939
|
-
return st.isDirectory();
|
|
5940
|
-
} catch {
|
|
5941
|
-
return false;
|
|
5942
|
-
}
|
|
5943
|
-
}
|
|
5944
|
-
function sanitizeRelativePath(rel) {
|
|
5945
|
-
const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
5946
|
-
const segments = norm.split("/").filter(Boolean);
|
|
5947
|
-
for (const s of segments) {
|
|
5948
|
-
if (s === "." || s === "..") {
|
|
5949
|
-
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
5950
|
-
}
|
|
5951
|
-
}
|
|
5952
|
-
return segments.join("/");
|
|
5953
|
-
}
|
|
5954
|
-
async function collectFiles(root) {
|
|
5955
|
-
const out = [];
|
|
5956
|
-
async function walk(dir, prefix) {
|
|
5957
|
-
const entries = await readdir2(dir, { withFileTypes: true });
|
|
5958
|
-
for (const e of entries) {
|
|
5959
|
-
const name = e.name;
|
|
5960
|
-
if (name === "." || name === "..") {
|
|
5961
|
-
continue;
|
|
5962
|
-
}
|
|
5963
|
-
const abs = path10.join(dir, name);
|
|
5964
|
-
const rel = prefix ? `${prefix}/${name}` : name;
|
|
5965
|
-
if (e.isDirectory()) {
|
|
5966
|
-
await walk(abs, rel);
|
|
5967
|
-
} else if (e.isFile()) {
|
|
5968
|
-
const st = statSync7(abs);
|
|
5969
|
-
out.push({
|
|
5970
|
-
absPath: abs,
|
|
5971
|
-
relativePath: rel.replace(/\\/g, "/"),
|
|
5972
|
-
size: st.size
|
|
5973
|
-
});
|
|
5974
|
-
}
|
|
5975
|
-
}
|
|
5976
|
-
}
|
|
5977
|
-
await walk(root, "");
|
|
5978
|
-
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
5979
|
-
return out;
|
|
5980
|
-
}
|
|
5981
|
-
async function readArtifactFile(absPath) {
|
|
5982
|
-
return readFile2(absPath);
|
|
5983
|
-
}
|
|
5984
|
-
function toMB(bytes) {
|
|
5985
|
-
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
5986
|
-
}
|
|
5987
|
-
var MIME = {
|
|
5988
|
-
".html": "text/html; charset=utf-8",
|
|
5989
|
-
".css": "text/css; charset=utf-8",
|
|
5990
|
-
".js": "application/javascript; charset=utf-8",
|
|
5991
|
-
".json": "application/json; charset=utf-8",
|
|
5992
|
-
".svg": "image/svg+xml",
|
|
5993
|
-
".png": "image/png",
|
|
5994
|
-
".jpg": "image/jpeg",
|
|
5995
|
-
".jpeg": "image/jpeg",
|
|
5996
|
-
".gif": "image/gif",
|
|
5997
|
-
".webp": "image/webp",
|
|
5998
|
-
".woff": "font/woff",
|
|
5999
|
-
".woff2": "font/woff2",
|
|
6000
|
-
".ttf": "font/ttf",
|
|
6001
|
-
".ico": "image/x-icon",
|
|
6002
|
-
".txt": "text/plain; charset=utf-8",
|
|
6003
|
-
".map": "application/json"
|
|
6004
|
-
};
|
|
6005
|
-
function detectMimeType(filePath) {
|
|
6006
|
-
const ext = path10.extname(filePath).toLowerCase();
|
|
6007
|
-
return MIME[ext] ?? "";
|
|
6008
|
-
}
|
|
6009
|
-
var MinioClient = class {
|
|
6010
|
-
inner;
|
|
6011
|
-
constructor(opts) {
|
|
6012
|
-
const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
|
|
6013
|
-
this.inner = new Minio.Client({
|
|
6014
|
-
endPoint,
|
|
6015
|
-
port: opts.port,
|
|
6016
|
-
useSSL: opts.useSSL,
|
|
6017
|
-
accessKey: opts.accessKey,
|
|
6018
|
-
secretKey: opts.secretKey
|
|
6019
|
-
});
|
|
6020
|
-
}
|
|
6021
|
-
async ensureBucket(bucket) {
|
|
6022
|
-
const exists = await this.inner.bucketExists(bucket);
|
|
6023
|
-
if (!exists) {
|
|
6024
|
-
await this.inner.makeBucket(bucket);
|
|
6025
|
-
}
|
|
6026
|
-
}
|
|
6027
|
-
async deleteObjectsByPrefix(bucket, prefix) {
|
|
6028
|
-
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
6029
|
-
const keys = [];
|
|
6030
|
-
await new Promise((resolve5, reject) => {
|
|
6031
|
-
objectsStream.on("data", (obj) => {
|
|
6032
|
-
if (obj.name) {
|
|
6033
|
-
keys.push(obj.name);
|
|
6034
|
-
}
|
|
6035
|
-
});
|
|
6036
|
-
objectsStream.on("error", reject);
|
|
6037
|
-
objectsStream.on("end", resolve5);
|
|
6038
|
-
});
|
|
6039
|
-
const chunkSize = 500;
|
|
6040
|
-
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
6041
|
-
const chunk = keys.slice(i, i + chunkSize);
|
|
6042
|
-
await this.inner.removeObjects(
|
|
6043
|
-
bucket,
|
|
6044
|
-
chunk.map((name) => name)
|
|
6045
|
-
);
|
|
6046
|
-
}
|
|
6047
|
-
}
|
|
6048
|
-
async putObject(bucket, objectKey, body, meta) {
|
|
6049
|
-
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
6050
|
-
}
|
|
6051
|
-
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
6052
|
-
async setBucketPublicRead(bucket) {
|
|
6053
|
-
const policy = {
|
|
6054
|
-
Version: "2012-10-17",
|
|
6055
|
-
Statement: [
|
|
6056
|
-
{
|
|
6057
|
-
Effect: "Allow",
|
|
6058
|
-
Principal: { AWS: ["*"] },
|
|
6059
|
-
Action: ["s3:GetObject"],
|
|
6060
|
-
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
6061
|
-
}
|
|
6062
|
-
]
|
|
6063
|
-
};
|
|
6064
|
-
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
6065
|
-
}
|
|
6066
|
-
};
|
|
6067
|
-
|
|
6068
|
-
// src/commands/deploy/frontend.ts
|
|
6069
6508
|
function resolveArtifactNamePrefix(cfg) {
|
|
6070
6509
|
const nameRaw = (cfg.name ?? "").trim();
|
|
6071
6510
|
if (!nameRaw) {
|
|
@@ -6239,6 +6678,13 @@ function registerDeploySftpCommands(program) {
|
|
|
6239
6678
|
async (opts) => {
|
|
6240
6679
|
const cfg = loadApmConfig({ configPath: opts.config });
|
|
6241
6680
|
const settings = resolveWisdomDeployFromApmConfig(cfg);
|
|
6681
|
+
const projectName = (cfg.name ?? "").trim();
|
|
6682
|
+
if (!projectName) {
|
|
6683
|
+
console.error(
|
|
6684
|
+
"\u8BF7\u5728 .apm/apm.config.json \u9876\u5C42\u914D\u7F6E name\uFF08\u4F5C\u4E3A\u90E8\u7F72\u4EA7\u7269\u9879\u76EE\u540D\uFF09"
|
|
6685
|
+
);
|
|
6686
|
+
process.exit(1);
|
|
6687
|
+
}
|
|
6242
6688
|
const root = path12.resolve(process.cwd(), opts.dir || "apps/web/dist");
|
|
6243
6689
|
if (!await isDirectoryPath(root)) {
|
|
6244
6690
|
console.error(`\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728\uFF1A${root}`);
|
|
@@ -6248,7 +6694,8 @@ function registerDeploySftpCommands(program) {
|
|
|
6248
6694
|
const result = await runWisdomSftpDeploy({
|
|
6249
6695
|
localDir: root,
|
|
6250
6696
|
settings,
|
|
6251
|
-
extract: Boolean(opts.extract)
|
|
6697
|
+
extract: Boolean(opts.extract),
|
|
6698
|
+
projectName
|
|
6252
6699
|
});
|
|
6253
6700
|
console.log(JSON.stringify(result, null, 2));
|
|
6254
6701
|
} catch (err) {
|