ai-project-manage-cli 6.0.102 → 6.0.103
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 +1896 -1800
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2389,7 +2389,7 @@ async function runUpdateMessageStatus(options) {
|
|
|
2389
2389
|
|
|
2390
2390
|
// src/commands/connect.ts
|
|
2391
2391
|
import WebSocket from "ws";
|
|
2392
|
-
import { setTimeout as
|
|
2392
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
2393
2393
|
|
|
2394
2394
|
// src/ws/protocol.ts
|
|
2395
2395
|
function nonEmptyString(v) {
|
|
@@ -2511,8 +2511,67 @@ function validateAgentWsMessage(value, kind) {
|
|
|
2511
2511
|
return validateMessagePush(o);
|
|
2512
2512
|
}
|
|
2513
2513
|
|
|
2514
|
+
// src/commands/deploy/deploy-log-utils.ts
|
|
2515
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
2516
|
+
var DEPLOY_SPAWN_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
2517
|
+
var DEPLOY_LOG_SYNC_MAX_CHUNK_BYTES = 4 * 1024 * 1024;
|
|
2518
|
+
var DEPLOY_COMPLETE_LOG_MAX_BYTES = 9 * 1024 * 1024;
|
|
2519
|
+
var DEPLOY_LOG_SYNC_MAX_ATTEMPTS = 3;
|
|
2520
|
+
async function retryDeployLogSync(fn, attempts = DEPLOY_LOG_SYNC_MAX_ATTEMPTS) {
|
|
2521
|
+
let lastError;
|
|
2522
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
2523
|
+
try {
|
|
2524
|
+
return await fn();
|
|
2525
|
+
} catch (error) {
|
|
2526
|
+
lastError = error;
|
|
2527
|
+
if (attempt < attempts) {
|
|
2528
|
+
await delay(1e3 * attempt);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
throw lastError;
|
|
2533
|
+
}
|
|
2534
|
+
function splitUtf8StringByMaxBytes(value, maxBytes) {
|
|
2535
|
+
if (maxBytes <= 0) {
|
|
2536
|
+
throw new Error("maxBytes must be positive");
|
|
2537
|
+
}
|
|
2538
|
+
const bytes = Buffer.from(value, "utf8");
|
|
2539
|
+
if (bytes.length <= maxBytes) {
|
|
2540
|
+
return [value];
|
|
2541
|
+
}
|
|
2542
|
+
const chunks = [];
|
|
2543
|
+
let offset = 0;
|
|
2544
|
+
while (offset < bytes.length) {
|
|
2545
|
+
let end = Math.min(offset + maxBytes, bytes.length);
|
|
2546
|
+
while (end > offset && (bytes[end] & 192) === 128) {
|
|
2547
|
+
end -= 1;
|
|
2548
|
+
}
|
|
2549
|
+
if (end <= offset) {
|
|
2550
|
+
end = Math.min(offset + maxBytes, bytes.length);
|
|
2551
|
+
}
|
|
2552
|
+
chunks.push(bytes.subarray(offset, end).toString("utf8"));
|
|
2553
|
+
offset = end;
|
|
2554
|
+
}
|
|
2555
|
+
return chunks;
|
|
2556
|
+
}
|
|
2557
|
+
function truncateDeployLogForComplete(log2) {
|
|
2558
|
+
const bytes = Buffer.from(log2, "utf8");
|
|
2559
|
+
if (bytes.length <= DEPLOY_COMPLETE_LOG_MAX_BYTES) {
|
|
2560
|
+
return { log: log2, truncated: false };
|
|
2561
|
+
}
|
|
2562
|
+
const tailBudget = Math.max(DEPLOY_COMPLETE_LOG_MAX_BYTES - 512, 1024);
|
|
2563
|
+
const prefix = `[apm] \u65E5\u5FD7\u8FC7\u957F\uFF08${Math.ceil(bytes.length / 1024)}KB\uFF09\uFF0C\u4EC5\u4FDD\u7559\u672B\u5C3E ${Math.floor(tailBudget / 1024)}KB
|
|
2564
|
+
`;
|
|
2565
|
+
let tail = bytes.subarray(bytes.length - tailBudget).toString("utf8");
|
|
2566
|
+
const brokenPrefix = tail.indexOf("\uFFFD");
|
|
2567
|
+
if (brokenPrefix >= 0) {
|
|
2568
|
+
const newline = tail.indexOf("\n", brokenPrefix);
|
|
2569
|
+
tail = newline >= 0 ? tail.slice(newline + 1) : tail.slice(brokenPrefix + 1);
|
|
2570
|
+
}
|
|
2571
|
+
return { log: prefix + tail, truncated: true };
|
|
2572
|
+
}
|
|
2573
|
+
|
|
2514
2574
|
// src/commands/deploy/deploy-execute.ts
|
|
2515
|
-
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
2516
2575
|
import path5 from "node:path";
|
|
2517
2576
|
|
|
2518
2577
|
// src/commands/deploy/internal/apm-config.ts
|
|
@@ -2859,1969 +2918,2005 @@ var DeployExecutionError = class extends Error {
|
|
|
2859
2918
|
}
|
|
2860
2919
|
};
|
|
2861
2920
|
|
|
2862
|
-
// src/commands/deploy/
|
|
2863
|
-
import {
|
|
2864
|
-
import path4 from "node:path";
|
|
2865
|
-
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
2921
|
+
// src/commands/deploy/deploy-shell-run.ts
|
|
2922
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
2866
2923
|
|
|
2867
|
-
// src/commands/deploy/internal/
|
|
2924
|
+
// src/commands/deploy/internal/deploy-shell-env.ts
|
|
2925
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2926
|
+
import { dirname as dirname4 } from "node:path";
|
|
2927
|
+
|
|
2928
|
+
// src/commands/daemon.ts
|
|
2929
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
2930
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
2931
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
|
|
2932
|
+
import { join as join15 } from "path";
|
|
2933
|
+
|
|
2934
|
+
// src/commands/connect-lock.ts
|
|
2868
2935
|
import {
|
|
2869
2936
|
existsSync as existsSync12,
|
|
2870
2937
|
mkdirSync as mkdirSync5,
|
|
2871
|
-
readdirSync as readdirSync5,
|
|
2872
2938
|
readFileSync as readFileSync11,
|
|
2873
|
-
|
|
2939
|
+
unlinkSync,
|
|
2874
2940
|
writeFileSync as writeFileSync10
|
|
2875
|
-
} from "
|
|
2876
|
-
import {
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
import SftpClient2 from "ssh2-sftp-client";
|
|
2881
|
-
|
|
2882
|
-
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
2883
|
-
import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
|
|
2884
|
-
import path2 from "node:path";
|
|
2885
|
-
import JSZip from "jszip";
|
|
2886
|
-
import SftpClient from "ssh2-sftp-client";
|
|
2887
|
-
|
|
2888
|
-
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
2889
|
-
import { readFile as readFile2 } from "node:fs/promises";
|
|
2890
|
-
|
|
2891
|
-
// src/commands/deploy/internal/minio.ts
|
|
2892
|
-
import { statSync as statSync5 } from "node:fs";
|
|
2893
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
2894
|
-
import path from "node:path";
|
|
2895
|
-
import * as Minio from "minio";
|
|
2896
|
-
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
2897
|
-
async function isDirectoryPath(dir) {
|
|
2941
|
+
} from "fs";
|
|
2942
|
+
import { join as join14 } from "path";
|
|
2943
|
+
var CONNECT_LOCK_PATH = join14(APM_CONFIG_DIR, "connect.lock");
|
|
2944
|
+
function isProcessAlive(pid) {
|
|
2945
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
2898
2946
|
try {
|
|
2899
|
-
|
|
2900
|
-
return
|
|
2947
|
+
process.kill(pid, 0);
|
|
2948
|
+
return true;
|
|
2901
2949
|
} catch {
|
|
2902
2950
|
return false;
|
|
2903
2951
|
}
|
|
2904
2952
|
}
|
|
2905
|
-
function
|
|
2906
|
-
const
|
|
2907
|
-
|
|
2908
|
-
for (const s of segments) {
|
|
2909
|
-
if (s === "." || s === "..") {
|
|
2910
|
-
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
2911
|
-
}
|
|
2953
|
+
function sleepSync(ms) {
|
|
2954
|
+
const deadline = Date.now() + ms;
|
|
2955
|
+
while (Date.now() < deadline) {
|
|
2912
2956
|
}
|
|
2913
|
-
return segments.join("/");
|
|
2914
2957
|
}
|
|
2915
|
-
|
|
2916
|
-
const
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
continue;
|
|
2923
|
-
}
|
|
2924
|
-
const abs = path.join(dir, name);
|
|
2925
|
-
const rel = prefix ? `${prefix}/${name}` : name;
|
|
2926
|
-
if (e.isDirectory()) {
|
|
2927
|
-
await walk(abs, rel);
|
|
2928
|
-
} else if (e.isFile()) {
|
|
2929
|
-
const st = statSync5(abs);
|
|
2930
|
-
out.push({
|
|
2931
|
-
absPath: abs,
|
|
2932
|
-
relativePath: rel.replace(/\\/g, "/"),
|
|
2933
|
-
size: st.size
|
|
2934
|
-
});
|
|
2935
|
-
}
|
|
2958
|
+
function waitForPm2LockHandoff(timeoutMs = 5e3) {
|
|
2959
|
+
const deadline = Date.now() + timeoutMs;
|
|
2960
|
+
while (Date.now() < deadline) {
|
|
2961
|
+
pruneStaleConnectLock();
|
|
2962
|
+
const lock = readConnectLock();
|
|
2963
|
+
if (!lock || lock.mode !== "pm2" || lock.pid === process.pid || !isProcessAlive(lock.pid)) {
|
|
2964
|
+
return;
|
|
2936
2965
|
}
|
|
2966
|
+
sleepSync(100);
|
|
2937
2967
|
}
|
|
2938
|
-
await walk(root, "");
|
|
2939
|
-
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
2940
|
-
return out;
|
|
2941
|
-
}
|
|
2942
|
-
async function readArtifactFile(absPath) {
|
|
2943
|
-
return readFile(absPath);
|
|
2944
2968
|
}
|
|
2945
|
-
function
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
".gif": "image/gif",
|
|
2958
|
-
".webp": "image/webp",
|
|
2959
|
-
".woff": "font/woff",
|
|
2960
|
-
".woff2": "font/woff2",
|
|
2961
|
-
".ttf": "font/ttf",
|
|
2962
|
-
".ico": "image/x-icon",
|
|
2963
|
-
".txt": "text/plain; charset=utf-8",
|
|
2964
|
-
".map": "application/json"
|
|
2965
|
-
};
|
|
2966
|
-
function detectMimeType(filePath) {
|
|
2967
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
2968
|
-
return MIME[ext] ?? "";
|
|
2969
|
+
function readConnectLock() {
|
|
2970
|
+
if (!existsSync12(CONNECT_LOCK_PATH)) return null;
|
|
2971
|
+
try {
|
|
2972
|
+
const raw = readFileSync11(CONNECT_LOCK_PATH, "utf8");
|
|
2973
|
+
const parsed = JSON.parse(raw);
|
|
2974
|
+
if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
|
|
2975
|
+
return null;
|
|
2976
|
+
}
|
|
2977
|
+
return parsed;
|
|
2978
|
+
} catch {
|
|
2979
|
+
return null;
|
|
2980
|
+
}
|
|
2969
2981
|
}
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
useSSL: opts.useSSL,
|
|
2978
|
-
accessKey: opts.accessKey,
|
|
2979
|
-
secretKey: opts.secretKey
|
|
2980
|
-
});
|
|
2982
|
+
function pruneStaleConnectLock() {
|
|
2983
|
+
const lock = readConnectLock();
|
|
2984
|
+
if (!lock) return;
|
|
2985
|
+
if (isProcessAlive(lock.pid)) return;
|
|
2986
|
+
try {
|
|
2987
|
+
unlinkSync(CONNECT_LOCK_PATH);
|
|
2988
|
+
} catch {
|
|
2981
2989
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2990
|
+
}
|
|
2991
|
+
function acquireConnectLock(mode) {
|
|
2992
|
+
pruneStaleConnectLock();
|
|
2993
|
+
const existing = readConnectLock();
|
|
2994
|
+
if (existing && isProcessAlive(existing.pid)) {
|
|
2995
|
+
if (existing.pid === process.pid) {
|
|
2996
|
+
return;
|
|
2986
2997
|
}
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
objectsStream.on("data", (obj) => {
|
|
2993
|
-
if (obj.name) {
|
|
2994
|
-
keys.push(obj.name);
|
|
2995
|
-
}
|
|
2996
|
-
});
|
|
2997
|
-
objectsStream.on("error", reject);
|
|
2998
|
-
objectsStream.on("end", resolve5);
|
|
2999
|
-
});
|
|
3000
|
-
const chunkSize = 500;
|
|
3001
|
-
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
3002
|
-
const chunk = keys.slice(i, i + chunkSize);
|
|
3003
|
-
await this.inner.removeObjects(
|
|
3004
|
-
bucket,
|
|
3005
|
-
chunk.map((name) => name)
|
|
2998
|
+
if (mode === "pm2" && existing.mode === "pm2") {
|
|
2999
|
+
forceReleaseConnectLock();
|
|
3000
|
+
} else {
|
|
3001
|
+
console.error(
|
|
3002
|
+
`[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid}, mode=${existing.mode})`
|
|
3006
3003
|
);
|
|
3004
|
+
process.exit(1);
|
|
3007
3005
|
}
|
|
3008
3006
|
}
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3007
|
+
mkdirSync5(APM_CONFIG_DIR, { recursive: true });
|
|
3008
|
+
const lock = {
|
|
3009
|
+
pid: process.pid,
|
|
3010
|
+
mode,
|
|
3011
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3012
|
+
};
|
|
3013
|
+
writeFileSync10(
|
|
3014
|
+
CONNECT_LOCK_PATH,
|
|
3015
|
+
JSON.stringify(lock, null, 2) + "\n",
|
|
3016
|
+
"utf8"
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
function releaseConnectLock() {
|
|
3020
|
+
const lock = readConnectLock();
|
|
3021
|
+
if (lock?.pid !== process.pid) return;
|
|
3022
|
+
try {
|
|
3023
|
+
unlinkSync(CONNECT_LOCK_PATH);
|
|
3024
|
+
} catch {
|
|
3026
3025
|
}
|
|
3027
|
-
};
|
|
3028
|
-
|
|
3029
|
-
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
3030
|
-
var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
|
|
3031
|
-
function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
3032
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
3033
|
-
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
3034
3026
|
}
|
|
3035
|
-
function
|
|
3036
|
-
const trimmed = name.trim().replace(/\\/g, "/");
|
|
3037
|
-
const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
|
|
3027
|
+
function forceReleaseConnectLock() {
|
|
3038
3028
|
try {
|
|
3039
|
-
|
|
3029
|
+
if (existsSync12(CONNECT_LOCK_PATH)) {
|
|
3030
|
+
unlinkSync(CONNECT_LOCK_PATH);
|
|
3031
|
+
}
|
|
3040
3032
|
} catch {
|
|
3041
|
-
return "project";
|
|
3042
3033
|
}
|
|
3043
3034
|
}
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3035
|
+
|
|
3036
|
+
// src/commands/daemon.ts
|
|
3037
|
+
var PM2_APP_NAME = "apm-connect";
|
|
3038
|
+
var PM2_ECOSYSTEM_PATH = join15(
|
|
3039
|
+
APM_CONFIG_DIR,
|
|
3040
|
+
"connect.ecosystem.config.cjs"
|
|
3041
|
+
);
|
|
3042
|
+
var PM2_CONNECT_ENTRY_PATH = join15(APM_CONFIG_DIR, "connect.entry.cjs");
|
|
3043
|
+
var PM2_CONNECT_LAUNCH_PATH = join15(
|
|
3044
|
+
APM_CONFIG_DIR,
|
|
3045
|
+
"connect.launch.json"
|
|
3046
|
+
);
|
|
3047
|
+
var LEGACY_PM2_ECOSYSTEM_PATH = join15(
|
|
3048
|
+
APM_CONFIG_DIR,
|
|
3049
|
+
"connect.ecosystem.cjs"
|
|
3050
|
+
);
|
|
3051
|
+
var LEGACY_PM2_APP_NAMES = ["connect.ecosystem"];
|
|
3052
|
+
var CONNECT_ENTRY_SCRIPT = `'use strict';
|
|
3053
|
+
const { spawnSync } = require('node:child_process');
|
|
3054
|
+
const { readFileSync } = require('node:fs');
|
|
3055
|
+
const { join } = require('node:path');
|
|
3056
|
+
|
|
3057
|
+
const launchConfigPath = join(__dirname, 'connect.launch.json');
|
|
3058
|
+
let config;
|
|
3059
|
+
try {
|
|
3060
|
+
config = JSON.parse(readFileSync(launchConfigPath, 'utf8'));
|
|
3061
|
+
} catch (err) {
|
|
3062
|
+
console.error('[apm] \u65E0\u6CD5\u8BFB\u53D6 connect.launch.json:', err instanceof Error ? err.message : err);
|
|
3063
|
+
process.exit(1);
|
|
3056
3064
|
}
|
|
3057
|
-
|
|
3058
|
-
|
|
3065
|
+
|
|
3066
|
+
const nodeArgs = [config.apmScript, ...(config.connectArgs || [])];
|
|
3067
|
+
const childEnv = {
|
|
3068
|
+
...process.env,
|
|
3069
|
+
...(config.env || {}),
|
|
3070
|
+
APM_CONNECT_UNDER_PM2: '1',
|
|
3071
|
+
};
|
|
3072
|
+
|
|
3073
|
+
const result = spawnSync(config.nodePath, nodeArgs, {
|
|
3074
|
+
cwd: config.cwd || undefined,
|
|
3075
|
+
env: childEnv,
|
|
3076
|
+
stdio: 'inherit',
|
|
3077
|
+
windowsHide: true,
|
|
3078
|
+
});
|
|
3079
|
+
|
|
3080
|
+
if (result.error) {
|
|
3081
|
+
console.error('[apm] connect \u542F\u52A8\u5931\u8D25:', result.error.message);
|
|
3082
|
+
process.exit(1);
|
|
3059
3083
|
}
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
|
|
3068
|
-
return null;
|
|
3069
|
-
}
|
|
3070
|
-
const api = options.api ?? createApmApiClient(cfg);
|
|
3071
|
-
let storage;
|
|
3072
|
-
try {
|
|
3073
|
-
storage = await fetchDeployArtifactStorage(api);
|
|
3074
|
-
} catch (error) {
|
|
3075
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
3076
|
-
console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
|
|
3077
|
-
return null;
|
|
3084
|
+
process.exit(result.status ?? 0);
|
|
3085
|
+
`;
|
|
3086
|
+
function resolveConnectArgs(server) {
|
|
3087
|
+
const args = ["connect"];
|
|
3088
|
+
const trimmed = server?.trim();
|
|
3089
|
+
if (trimmed) {
|
|
3090
|
+
args.push("--server", trimmed.replace(/\/+$/, ""));
|
|
3078
3091
|
}
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
+
return args;
|
|
3093
|
+
}
|
|
3094
|
+
var PM2_LAUNCH_ENV_KEYS = [
|
|
3095
|
+
"PATH",
|
|
3096
|
+
"HOME",
|
|
3097
|
+
"USER",
|
|
3098
|
+
"SHELL",
|
|
3099
|
+
"LANG",
|
|
3100
|
+
"LC_ALL",
|
|
3101
|
+
"LC_CTYPE",
|
|
3102
|
+
"NVM_DIR",
|
|
3103
|
+
"NVM_BIN",
|
|
3104
|
+
"NVM_INC",
|
|
3105
|
+
"FNM_DIR",
|
|
3106
|
+
"VOLTA_HOME",
|
|
3107
|
+
"PNPM_HOME",
|
|
3108
|
+
"npm_config_prefix",
|
|
3109
|
+
"NODE_HOME"
|
|
3110
|
+
];
|
|
3111
|
+
function collectPm2LaunchEnv() {
|
|
3112
|
+
const env = {};
|
|
3113
|
+
for (const key of PM2_LAUNCH_ENV_KEYS) {
|
|
3114
|
+
const value = process.env[key]?.trim();
|
|
3115
|
+
if (value) {
|
|
3116
|
+
env[key] = value;
|
|
3117
|
+
}
|
|
3092
3118
|
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
});
|
|
3101
|
-
await client.ensureBucket(storage.bucket);
|
|
3102
|
-
await client.putObject(storage.bucket, objectKey, body, {
|
|
3103
|
-
"Content-Type": "application/zip"
|
|
3104
|
-
});
|
|
3105
|
-
console.error(
|
|
3106
|
-
`[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(
|
|
3107
|
-
2
|
|
3108
|
-
)} MB)`
|
|
3109
|
-
);
|
|
3110
|
-
} catch (error) {
|
|
3111
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
3112
|
-
console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
|
|
3113
|
-
return null;
|
|
3119
|
+
return env;
|
|
3120
|
+
}
|
|
3121
|
+
function buildConnectPm2Ecosystem(options) {
|
|
3122
|
+
const env = {};
|
|
3123
|
+
const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
|
|
3124
|
+
if (baseUrl) {
|
|
3125
|
+
env.AI_PM_SERVER = baseUrl;
|
|
3114
3126
|
}
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3127
|
+
const app = {
|
|
3128
|
+
name: PM2_APP_NAME,
|
|
3129
|
+
script: options.entryScript,
|
|
3130
|
+
cwd: APM_CONFIG_DIR,
|
|
3131
|
+
autorestart: true,
|
|
3132
|
+
min_uptime: "10s",
|
|
3133
|
+
max_restarts: 10,
|
|
3134
|
+
restart_delay: 3e3,
|
|
3135
|
+
exp_backoff_restart_delay: 1e3,
|
|
3136
|
+
max_memory_restart: "2G",
|
|
3137
|
+
kill_timeout: 5e3,
|
|
3138
|
+
shutdown_with_message: true,
|
|
3139
|
+
instances: 1,
|
|
3140
|
+
exec_mode: "fork",
|
|
3141
|
+
env
|
|
3142
|
+
};
|
|
3143
|
+
if (process.platform === "win32") {
|
|
3144
|
+
app.windowsHide = true;
|
|
3130
3145
|
}
|
|
3131
3146
|
return {
|
|
3132
|
-
|
|
3133
|
-
fileName,
|
|
3134
|
-
bucket: storage.bucket
|
|
3147
|
+
apps: [app]
|
|
3135
3148
|
};
|
|
3136
3149
|
}
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
const entries = await readdir2(dir, { withFileTypes: true });
|
|
3141
|
-
for (const entry of entries) {
|
|
3142
|
-
const fullPath = path2.join(dir, entry.name);
|
|
3143
|
-
if (entry.isDirectory()) {
|
|
3144
|
-
const folder = zipFolder.folder(entry.name);
|
|
3145
|
-
if (folder) {
|
|
3146
|
-
await addDirToZip(fullPath, folder);
|
|
3147
|
-
}
|
|
3148
|
-
} else {
|
|
3149
|
-
const content = await readFile3(fullPath);
|
|
3150
|
-
zipFolder.file(entry.name, content);
|
|
3151
|
-
}
|
|
3152
|
-
}
|
|
3150
|
+
function formatConnectPm2EcosystemFile(ecosystem) {
|
|
3151
|
+
return `module.exports = ${JSON.stringify(ecosystem, null, 2)};
|
|
3152
|
+
`;
|
|
3153
3153
|
}
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
type: "nodebuffer",
|
|
3160
|
-
compression: "DEFLATE",
|
|
3161
|
-
compressionOptions: { level: 6 }
|
|
3154
|
+
var useNpmShell2 = process.platform === "win32";
|
|
3155
|
+
function runNpm2(args, options = {}) {
|
|
3156
|
+
return spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", args, {
|
|
3157
|
+
...options,
|
|
3158
|
+
shell: useNpmShell2
|
|
3162
3159
|
});
|
|
3163
|
-
await writeFile(zipPath, content);
|
|
3164
|
-
const sizeMb = (content.length / 1024 / 1024).toFixed(2);
|
|
3165
|
-
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
3166
|
-
return content.length;
|
|
3167
|
-
}
|
|
3168
|
-
var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
|
|
3169
|
-
var SFTP_FAST_PUT_OPTIONS = {
|
|
3170
|
-
chunkSize: 64 * 1024,
|
|
3171
|
-
concurrency: 4
|
|
3172
|
-
};
|
|
3173
|
-
function buildSftpConnectOptions(settings) {
|
|
3174
|
-
return {
|
|
3175
|
-
host: settings.host,
|
|
3176
|
-
port: settings.port,
|
|
3177
|
-
username: settings.username,
|
|
3178
|
-
password: settings.password,
|
|
3179
|
-
readyTimeout: 3e4,
|
|
3180
|
-
tryKeyboard: true,
|
|
3181
|
-
keepaliveInterval: 1e4,
|
|
3182
|
-
keepaliveCountMax: 3
|
|
3183
|
-
};
|
|
3184
3160
|
}
|
|
3185
|
-
|
|
3186
|
-
|
|
3161
|
+
function resolveNpmGlobalBin() {
|
|
3162
|
+
const binResult = runNpm2(["bin", "-g"], {
|
|
3163
|
+
encoding: "utf8",
|
|
3164
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3165
|
+
});
|
|
3166
|
+
if (binResult.status !== 0) return null;
|
|
3167
|
+
return binResult.stdout?.toString().trim() || null;
|
|
3187
3168
|
}
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
await sftp.fastPut(localZip, remoteZipPath, SFTP_FAST_PUT_OPTIONS);
|
|
3201
|
-
return sftp;
|
|
3202
|
-
} catch (err) {
|
|
3203
|
-
lastError = err;
|
|
3204
|
-
try {
|
|
3205
|
-
await sftp.end();
|
|
3206
|
-
} catch {
|
|
3207
|
-
}
|
|
3208
|
-
if (attempt < SFTP_UPLOAD_MAX_ATTEMPTS) {
|
|
3209
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3210
|
-
const delaySec = attempt * 2;
|
|
3211
|
-
console.error(`SFTP \u4E0A\u4F20\u5931\u8D25 (${message})\uFF0C${delaySec}s \u540E\u91CD\u8BD5...`);
|
|
3212
|
-
await sleep(delaySec * 1e3);
|
|
3213
|
-
}
|
|
3169
|
+
function resolvePm2FromNpmRoot() {
|
|
3170
|
+
const rootResult = runNpm2(["root", "-g"], {
|
|
3171
|
+
encoding: "utf8",
|
|
3172
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3173
|
+
});
|
|
3174
|
+
if (rootResult.status !== 0) return null;
|
|
3175
|
+
const root = rootResult.stdout?.toString().trim();
|
|
3176
|
+
if (!root) return null;
|
|
3177
|
+
for (const rel of ["pm2/bin/pm2", "pm2/bin/pm2.js"]) {
|
|
3178
|
+
const candidate = join15(root, ...rel.split("/"));
|
|
3179
|
+
if (existsSync13(candidate)) {
|
|
3180
|
+
return candidate;
|
|
3214
3181
|
}
|
|
3215
3182
|
}
|
|
3216
|
-
|
|
3183
|
+
return null;
|
|
3217
3184
|
}
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3185
|
+
function isPm2NodeScript(path13) {
|
|
3186
|
+
return path13.endsWith(".js") || /[\\/]pm2[\\/]bin[\\/]pm2$/.test(path13);
|
|
3187
|
+
}
|
|
3188
|
+
function buildPm2SpawnTarget(binPath) {
|
|
3189
|
+
if (isPm2NodeScript(binPath)) {
|
|
3190
|
+
return {
|
|
3191
|
+
command: process.execPath,
|
|
3192
|
+
prefixArgs: [binPath],
|
|
3193
|
+
displayPath: binPath
|
|
3194
|
+
};
|
|
3227
3195
|
}
|
|
3196
|
+
return {
|
|
3197
|
+
command: binPath,
|
|
3198
|
+
prefixArgs: [],
|
|
3199
|
+
displayPath: binPath
|
|
3200
|
+
};
|
|
3228
3201
|
}
|
|
3229
|
-
function
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
3238
|
-
return;
|
|
3239
|
-
}
|
|
3240
|
-
resolve5(stdout);
|
|
3241
|
-
}).on("data", (data) => {
|
|
3242
|
-
stdout += data.toString();
|
|
3243
|
-
});
|
|
3244
|
-
stream.stderr.on("data", (data) => {
|
|
3245
|
-
stderr += data.toString();
|
|
3246
|
-
});
|
|
3247
|
-
});
|
|
3202
|
+
function spawnPm2Target(target, args, options = {}) {
|
|
3203
|
+
const useShell = useNpmShell2 && target.prefixArgs.length === 0;
|
|
3204
|
+
return spawnSync2(target.command, [...target.prefixArgs, ...args], {
|
|
3205
|
+
encoding: "utf8",
|
|
3206
|
+
env: process.env,
|
|
3207
|
+
shell: useShell,
|
|
3208
|
+
windowsHide: useNpmShell2,
|
|
3209
|
+
...options
|
|
3248
3210
|
});
|
|
3249
3211
|
}
|
|
3250
|
-
function
|
|
3251
|
-
return
|
|
3252
|
-
}
|
|
3253
|
-
function buildClearRemoteDirExceptZipCommand(target) {
|
|
3254
|
-
const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
|
|
3255
|
-
const quotedTarget = shellSingleQuote(normalized);
|
|
3256
|
-
const script = [
|
|
3257
|
-
`T=${quotedTarget}`,
|
|
3258
|
-
"S=$(mktemp -d)",
|
|
3259
|
-
'while IFS= read -r -d "" z; do r="${z#${T}/}"',
|
|
3260
|
-
'mkdir -p "${S}/$(dirname "$r")"',
|
|
3261
|
-
'mv "$z" "${S}/${r}"',
|
|
3262
|
-
'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
|
|
3263
|
-
'rm -rf "${T}"/*',
|
|
3264
|
-
'while IFS= read -r -d "" r; do r="${r#./}"',
|
|
3265
|
-
'mkdir -p "${T}/$(dirname "$r")"',
|
|
3266
|
-
'mv "${S}/${r}" "${T}/${r}"',
|
|
3267
|
-
'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
|
|
3268
|
-
'rm -rf "$S"'
|
|
3269
|
-
].join("; ");
|
|
3270
|
-
return `bash -c ${shellSingleQuote(script)}`;
|
|
3212
|
+
function spawnPm2At(binPath, args, options = {}) {
|
|
3213
|
+
return spawnPm2Target(buildPm2SpawnTarget(binPath), args, options);
|
|
3271
3214
|
}
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
3276
|
-
);
|
|
3277
|
-
const sftp = await uploadZipWithRetry(settings, localZip, remoteZipPath);
|
|
3278
|
-
try {
|
|
3279
|
-
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
3280
|
-
if (extract) {
|
|
3281
|
-
const target = settings.remotePath.replace(/\/$/, "");
|
|
3282
|
-
const client = sftp.client;
|
|
3283
|
-
const clearCmd = buildClearRemoteDirExceptZipCommand(target);
|
|
3284
|
-
console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
|
|
3285
|
-
await execCommand(client, clearCmd);
|
|
3286
|
-
const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
|
|
3287
|
-
console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
|
|
3288
|
-
await execCommand(client, unzipCmd);
|
|
3289
|
-
console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
|
|
3290
|
-
}
|
|
3291
|
-
console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
|
|
3292
|
-
} finally {
|
|
3293
|
-
await sftp.end();
|
|
3215
|
+
function verifyPm2Bin(pm2Bin) {
|
|
3216
|
+
if (!pm2Bin.trim() || !existsSync13(pm2Bin)) {
|
|
3217
|
+
return false;
|
|
3294
3218
|
}
|
|
3219
|
+
const result = spawnPm2At(pm2Bin, ["--version"], {
|
|
3220
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3221
|
+
});
|
|
3222
|
+
return !result.error && result.status === 0;
|
|
3295
3223
|
}
|
|
3296
|
-
|
|
3297
|
-
const
|
|
3298
|
-
const
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
3304
|
-
if (!packOnly) {
|
|
3305
|
-
await uploadAndMaybeExtract(
|
|
3306
|
-
params.settings,
|
|
3307
|
-
resolvedZipPath,
|
|
3308
|
-
params.extract
|
|
3309
|
-
);
|
|
3224
|
+
function collectPm2Candidates() {
|
|
3225
|
+
const candidates = [];
|
|
3226
|
+
const globalBin = resolveNpmGlobalBin();
|
|
3227
|
+
if (globalBin) {
|
|
3228
|
+
if (useNpmShell2) {
|
|
3229
|
+
candidates.push(join15(globalBin, "pm2.cmd"));
|
|
3230
|
+
candidates.push(join15(globalBin, "pm2"));
|
|
3310
3231
|
} else {
|
|
3311
|
-
|
|
3312
|
-
}
|
|
3313
|
-
const artifact = await uploadDeployArtifactZip({
|
|
3314
|
-
zipPath: resolvedZipPath,
|
|
3315
|
-
projectName: params.projectName,
|
|
3316
|
-
kind: "frontend",
|
|
3317
|
-
uploadWithoutRunId: packOnly && !resolveDeploymentRunIdFromEnv()
|
|
3318
|
-
});
|
|
3319
|
-
artifactUploaded = artifact !== null;
|
|
3320
|
-
if (packOnly && artifactUploaded) {
|
|
3321
|
-
console.error("\u6253\u5305\u5E76\u4E0A\u4F20 MinIO \u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
|
|
3232
|
+
candidates.push(join15(globalBin, "pm2"));
|
|
3322
3233
|
}
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3234
|
+
}
|
|
3235
|
+
const fromRoot = resolvePm2FromNpmRoot();
|
|
3236
|
+
if (fromRoot) {
|
|
3237
|
+
candidates.push(fromRoot);
|
|
3238
|
+
}
|
|
3239
|
+
const whichResult = spawnSync2(useNpmShell2 ? "where" : "which", ["pm2"], {
|
|
3240
|
+
encoding: "utf8",
|
|
3241
|
+
shell: useNpmShell2,
|
|
3242
|
+
env: process.env,
|
|
3243
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3244
|
+
});
|
|
3245
|
+
if (whichResult.status === 0) {
|
|
3246
|
+
const fromPath = whichResult.stdout?.toString().trim().split(/\r?\n/).map((line) => line.trim()).filter(Boolean) ?? [];
|
|
3247
|
+
if (useNpmShell2) {
|
|
3248
|
+
fromPath.sort((a, b) => {
|
|
3249
|
+
const aCmd = a.toLowerCase().endsWith(".cmd") ? 0 : 1;
|
|
3250
|
+
const bCmd = b.toLowerCase().endsWith(".cmd") ? 0 : 1;
|
|
3251
|
+
return aCmd - bCmd;
|
|
3252
|
+
});
|
|
3328
3253
|
}
|
|
3254
|
+
candidates.push(...fromPath);
|
|
3329
3255
|
}
|
|
3330
|
-
return
|
|
3331
|
-
ok: true,
|
|
3332
|
-
localDir: params.localDir,
|
|
3333
|
-
host: params.settings.host,
|
|
3334
|
-
remotePath: params.settings.remotePath,
|
|
3335
|
-
zipSizeBytes,
|
|
3336
|
-
extracted: packOnly ? false : params.extract,
|
|
3337
|
-
packOnly,
|
|
3338
|
-
artifactUploaded
|
|
3339
|
-
};
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
3343
|
-
var SPRINGBOOT_JAVA_OPTS = "-XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -Xms512M -Xmx1G";
|
|
3344
|
-
var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
|
|
3345
|
-
var MAVEN_PROFILE = "dev";
|
|
3346
|
-
function log(message) {
|
|
3347
|
-
const now = /* @__PURE__ */ new Date();
|
|
3348
|
-
const hh = String(now.getHours()).padStart(2, "0");
|
|
3349
|
-
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
3350
|
-
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
3351
|
-
console.error(`[${hh}:${mm}:${ss}] ${message}`);
|
|
3352
|
-
}
|
|
3353
|
-
function fail(message) {
|
|
3354
|
-
log(`ERROR: ${message}`);
|
|
3355
|
-
process.exit(1);
|
|
3356
|
-
}
|
|
3357
|
-
function expandPath(pathStr) {
|
|
3358
|
-
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
3359
|
-
return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
|
|
3256
|
+
return candidates;
|
|
3360
3257
|
}
|
|
3361
|
-
function
|
|
3362
|
-
|
|
3363
|
-
|
|
3258
|
+
function findGlobalPm2() {
|
|
3259
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3260
|
+
for (const candidate of collectPm2Candidates()) {
|
|
3261
|
+
const key = useNpmShell2 ? candidate.toLowerCase() : candidate;
|
|
3262
|
+
if (seen.has(key)) continue;
|
|
3263
|
+
seen.add(key);
|
|
3264
|
+
if (verifyPm2Bin(candidate)) {
|
|
3265
|
+
return candidate;
|
|
3266
|
+
}
|
|
3364
3267
|
}
|
|
3365
|
-
return
|
|
3268
|
+
return null;
|
|
3366
3269
|
}
|
|
3367
|
-
function
|
|
3368
|
-
|
|
3369
|
-
|
|
3270
|
+
function installGlobalPm2() {
|
|
3271
|
+
console.log("[apm] \u672A\u68C0\u6D4B\u5230\u5168\u5C40 pm2\uFF0C\u6B63\u5728\u5B89\u88C5 npm install -g pm2 \u2026");
|
|
3272
|
+
const result = runNpm2(["install", "-g", "pm2"], {
|
|
3273
|
+
encoding: "utf8",
|
|
3274
|
+
stdio: "inherit"
|
|
3275
|
+
});
|
|
3276
|
+
if (result.status !== 0) {
|
|
3277
|
+
console.error("[apm] \u5B89\u88C5 pm2 \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2");
|
|
3278
|
+
process.exit(result.status ?? 1);
|
|
3370
3279
|
}
|
|
3371
|
-
return `-Dmaven.repo.local=${repoPath}`;
|
|
3372
3280
|
}
|
|
3373
|
-
function
|
|
3374
|
-
|
|
3281
|
+
function readPm2Version(pm2Bin) {
|
|
3282
|
+
const result = spawnPm2At(pm2Bin, ["--version"], {
|
|
3283
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3284
|
+
});
|
|
3285
|
+
if (result.error || result.status !== 0) {
|
|
3286
|
+
return null;
|
|
3287
|
+
}
|
|
3288
|
+
return result.stdout?.toString().trim() || null;
|
|
3375
3289
|
}
|
|
3376
|
-
function
|
|
3377
|
-
|
|
3378
|
-
}
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
}
|
|
3382
|
-
|
|
3383
|
-
return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
|
|
3384
|
-
}
|
|
3385
|
-
function fileSignature(filePath) {
|
|
3386
|
-
const stat2 = statSync6(filePath);
|
|
3387
|
-
return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
|
|
3388
|
-
}
|
|
3389
|
-
function loadManifest3() {
|
|
3390
|
-
const manifestPath2 = manifestFilePath();
|
|
3391
|
-
if (!existsSync12(manifestPath2)) {
|
|
3392
|
-
return {};
|
|
3290
|
+
function logPm2Ready(pm2Bin, installed2) {
|
|
3291
|
+
const version = readPm2Version(pm2Bin);
|
|
3292
|
+
const versionSuffix = version ? ` ${version}` : "";
|
|
3293
|
+
if (installed2) {
|
|
3294
|
+
console.log(`[apm] \u5168\u5C40 pm2 \u5DF2\u5B89\u88C5${versionSuffix}: ${pm2Bin}`);
|
|
3295
|
+
} else {
|
|
3296
|
+
console.log(`[apm] \u5DF2\u68C0\u6D4B\u5230\u5168\u5C40 pm2${versionSuffix}: ${pm2Bin}`);
|
|
3393
3297
|
}
|
|
3394
|
-
return JSON.parse(readFileSync11(manifestPath2, "utf8"));
|
|
3395
|
-
}
|
|
3396
|
-
function saveManifest3(manifest) {
|
|
3397
|
-
const dir = deployCacheDir();
|
|
3398
|
-
mkdirSync5(dir, { recursive: true });
|
|
3399
|
-
writeFileSync10(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
3400
3298
|
}
|
|
3401
|
-
function
|
|
3402
|
-
return
|
|
3299
|
+
function isPm2DaemonOutOfDate(output) {
|
|
3300
|
+
return /In-memory PM2 is out-of-date/i.test(output);
|
|
3403
3301
|
}
|
|
3404
|
-
function
|
|
3405
|
-
|
|
3406
|
-
|
|
3302
|
+
function ensurePm2DaemonUpdated(pm2Bin) {
|
|
3303
|
+
const probe = spawnPm2At(pm2Bin, ["jlist"], {
|
|
3304
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3305
|
+
});
|
|
3306
|
+
const combined = `${probe.stdout ?? ""}
|
|
3307
|
+
${probe.stderr ?? ""}`;
|
|
3308
|
+
if (!isPm2DaemonOutOfDate(combined)) {
|
|
3309
|
+
return;
|
|
3407
3310
|
}
|
|
3408
|
-
|
|
3409
|
-
const
|
|
3410
|
-
|
|
3411
|
-
|
|
3311
|
+
console.log("[apm] \u68C0\u6D4B\u5230 PM2 \u5185\u5B58\u7248\u672C\u4E0E\u672C\u5730\u4E0D\u4E00\u81F4\uFF0C\u6B63\u5728\u6267\u884C pm2 update \u2026");
|
|
3312
|
+
const update = spawnPm2At(pm2Bin, ["update"], {
|
|
3313
|
+
stdio: "inherit"
|
|
3314
|
+
});
|
|
3315
|
+
if (update.error || update.status !== 0) {
|
|
3316
|
+
console.error("[apm] pm2 update \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: pm2 update");
|
|
3412
3317
|
}
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
if (previous.size !== current.size) {
|
|
3421
|
-
return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
|
|
3422
|
-
}
|
|
3423
|
-
if (previous.mtime < current.mtime) {
|
|
3424
|
-
return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
|
|
3318
|
+
}
|
|
3319
|
+
function ensureGlobalPm2(options) {
|
|
3320
|
+
const existing = findGlobalPm2();
|
|
3321
|
+
if (existing) {
|
|
3322
|
+
ensurePm2DaemonUpdated(existing);
|
|
3323
|
+
if (!options?.quiet) {
|
|
3324
|
+
logPm2Ready(existing, false);
|
|
3425
3325
|
}
|
|
3326
|
+
return existing;
|
|
3426
3327
|
}
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
const remoteAttr = remoteStats.get(jarName);
|
|
3435
|
-
const [shouldUpload, reason] = shouldUploadLibFile(
|
|
3436
|
-
jarPath,
|
|
3437
|
-
remoteAttr,
|
|
3438
|
-
manifest,
|
|
3439
|
-
projectRoot
|
|
3328
|
+
installGlobalPm2();
|
|
3329
|
+
const installed2 = findGlobalPm2();
|
|
3330
|
+
if (!installed2) {
|
|
3331
|
+
const globalBin = resolveNpmGlobalBin();
|
|
3332
|
+
const moduleEntry = resolvePm2FromNpmRoot();
|
|
3333
|
+
console.error(
|
|
3334
|
+
"[apm] \u5B89\u88C5 pm2 \u540E\u4ECD\u65E0\u6CD5\u627E\u5230\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: npm install -g pm2"
|
|
3440
3335
|
);
|
|
3441
|
-
if (
|
|
3442
|
-
|
|
3336
|
+
if (globalBin) {
|
|
3337
|
+
console.error(`[apm] npm \u5168\u5C40 bin: ${globalBin}`);
|
|
3338
|
+
}
|
|
3339
|
+
if (moduleEntry) {
|
|
3340
|
+
console.error(`[apm] \u5DF2\u68C0\u6D4B\u5230 pm2 \u6A21\u5757: ${moduleEntry}`);
|
|
3341
|
+
console.error(
|
|
3342
|
+
"[apm] \u82E5 pm2 --version \u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5 Node/npm \u5168\u5C40\u5B89\u88C5\u6743\u9650\u6216 PATH"
|
|
3343
|
+
);
|
|
3443
3344
|
}
|
|
3345
|
+
process.exit(1);
|
|
3444
3346
|
}
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
for (const entry of entries) {
|
|
3449
|
-
manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
|
|
3347
|
+
ensurePm2DaemonUpdated(installed2);
|
|
3348
|
+
if (!options?.quiet) {
|
|
3349
|
+
logPm2Ready(installed2, true);
|
|
3450
3350
|
}
|
|
3451
|
-
return
|
|
3351
|
+
return installed2;
|
|
3452
3352
|
}
|
|
3453
|
-
|
|
3454
|
-
const
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
3458
|
-
const zip = new JSZip2();
|
|
3459
|
-
for (const entry of entries) {
|
|
3460
|
-
const content = readFileSync11(entry.path);
|
|
3461
|
-
zip.file(entry.arcname, content);
|
|
3462
|
-
log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
|
|
3353
|
+
function resolveApmEntryPath(entryArg = process.argv[1]) {
|
|
3354
|
+
const fromArgv = entryArg?.trim();
|
|
3355
|
+
if (fromArgv && existsSync13(fromArgv)) {
|
|
3356
|
+
return fromArgv;
|
|
3463
3357
|
}
|
|
3464
|
-
const
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3358
|
+
const npmResult = spawnSync2(useNpmShell2 ? "npm.cmd" : "npm", ["root", "-g"], {
|
|
3359
|
+
encoding: "utf8",
|
|
3360
|
+
shell: useNpmShell2,
|
|
3361
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3468
3362
|
});
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
const result = spawnSync2(isWin ? `where ${name}` : `which ${name}`, {
|
|
3477
|
-
encoding: "utf8",
|
|
3478
|
-
shell: true
|
|
3479
|
-
});
|
|
3480
|
-
if (result.status === 0 && result.stdout.trim()) {
|
|
3481
|
-
return result.stdout.trim().split(/\r?\n/)[0].trim();
|
|
3363
|
+
if (npmResult.status === 0) {
|
|
3364
|
+
const globalRoot = npmResult.stdout?.toString().trim();
|
|
3365
|
+
if (globalRoot) {
|
|
3366
|
+
const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
|
|
3367
|
+
if (existsSync13(candidate)) {
|
|
3368
|
+
return candidate;
|
|
3369
|
+
}
|
|
3482
3370
|
}
|
|
3483
3371
|
}
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
|
|
3487
|
-
const mavenRepo = expandPath(mavenLocalRepo);
|
|
3488
|
-
const mvn = getMvnExecutable();
|
|
3489
|
-
const command = [
|
|
3490
|
-
quoteForShell(mvn),
|
|
3491
|
-
"clean",
|
|
3492
|
-
"package",
|
|
3493
|
-
`-P${MAVEN_PROFILE}`,
|
|
3494
|
-
formatMavenLocalRepoArg(mavenRepo),
|
|
3495
|
-
"-DskipTests"
|
|
3496
|
-
].join(" ");
|
|
3497
|
-
log("\u5F00\u59CB Maven \u6784\u5EFA...");
|
|
3498
|
-
if (repoSource) {
|
|
3499
|
-
log(
|
|
3500
|
-
`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo} (\u6765\u6E90: ${repoSource.source}/${repoSource.sourceDetail})`
|
|
3501
|
-
);
|
|
3502
|
-
} else {
|
|
3503
|
-
log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
|
|
3504
|
-
}
|
|
3505
|
-
log(`\u6784\u5EFA\u76EE\u5F55: ${projectRoot}`);
|
|
3506
|
-
const result = spawnSync2(command, {
|
|
3507
|
-
cwd: projectRoot,
|
|
3508
|
-
stdio: "inherit",
|
|
3509
|
-
shell: true,
|
|
3510
|
-
env: process.env
|
|
3511
|
-
});
|
|
3512
|
-
if (result.status !== 0) {
|
|
3513
|
-
fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
|
|
3372
|
+
if (fromArgv) {
|
|
3373
|
+
return fromArgv;
|
|
3514
3374
|
}
|
|
3375
|
+
console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
|
|
3376
|
+
process.exit(1);
|
|
3515
3377
|
}
|
|
3516
|
-
function
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
3520
|
-
}
|
|
3521
|
-
const libDir = path3.join(targetDir, "lib");
|
|
3522
|
-
if (!existsSync12(libDir) || !statSync6(libDir).isDirectory()) {
|
|
3523
|
-
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
3378
|
+
function isRunningUnderPm2() {
|
|
3379
|
+
if (process.env.APM_CONNECT_UNDER_PM2 === "1") {
|
|
3380
|
+
return true;
|
|
3524
3381
|
}
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
|
|
3382
|
+
if (process.env.pm_id === void 0) {
|
|
3383
|
+
return false;
|
|
3528
3384
|
}
|
|
3529
|
-
|
|
3530
|
-
return
|
|
3385
|
+
const name = process.env.name;
|
|
3386
|
+
return name === PM2_APP_NAME || name !== void 0 && LEGACY_PM2_APP_NAMES.includes(name);
|
|
3531
3387
|
}
|
|
3532
|
-
function
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
3536
|
-
}
|
|
3537
|
-
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);
|
|
3538
|
-
if (jarFiles.length === 0) {
|
|
3539
|
-
fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
|
|
3388
|
+
function isConnectPm2Process(app) {
|
|
3389
|
+
if (app.name === PM2_APP_NAME) {
|
|
3390
|
+
return true;
|
|
3540
3391
|
}
|
|
3541
|
-
|
|
3542
|
-
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
|
|
3543
|
-
return mainJar;
|
|
3544
|
-
}
|
|
3545
|
-
async function connectSsh(config) {
|
|
3546
|
-
const client = new Client2();
|
|
3547
|
-
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
|
|
3548
|
-
await new Promise((resolve5, reject) => {
|
|
3549
|
-
client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
|
|
3550
|
-
host: config.host,
|
|
3551
|
-
port: config.port,
|
|
3552
|
-
username: config.username,
|
|
3553
|
-
password: config.password,
|
|
3554
|
-
readyTimeout: 3e4,
|
|
3555
|
-
tryKeyboard: true
|
|
3556
|
-
});
|
|
3557
|
-
});
|
|
3558
|
-
const sftp = new SftpClient2();
|
|
3559
|
-
await sftp.connect({
|
|
3560
|
-
host: config.host,
|
|
3561
|
-
port: config.port,
|
|
3562
|
-
username: config.username,
|
|
3563
|
-
password: config.password,
|
|
3564
|
-
readyTimeout: 3e4,
|
|
3565
|
-
tryKeyboard: true
|
|
3566
|
-
});
|
|
3567
|
-
return { client, sftp };
|
|
3392
|
+
return app.name !== void 0 && LEGACY_PM2_APP_NAMES.includes(app.name);
|
|
3568
3393
|
}
|
|
3569
|
-
|
|
3394
|
+
function listPm2Processes() {
|
|
3570
3395
|
try {
|
|
3571
|
-
|
|
3396
|
+
const raw = runPm2Json(["jlist"]);
|
|
3397
|
+
return JSON.parse(raw);
|
|
3572
3398
|
} catch {
|
|
3399
|
+
return [];
|
|
3573
3400
|
}
|
|
3574
|
-
conn.client.end();
|
|
3575
3401
|
}
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
try {
|
|
3579
|
-
const listing = await sftp.list(remoteDir);
|
|
3580
|
-
for (const item of listing) {
|
|
3581
|
-
if (item.name.endsWith(".jar")) {
|
|
3582
|
-
stats.set(item.name, { filename: item.name, size: item.size });
|
|
3583
|
-
}
|
|
3584
|
-
}
|
|
3585
|
-
} catch {
|
|
3586
|
-
}
|
|
3587
|
-
return stats;
|
|
3402
|
+
function findPm2ConnectProcess() {
|
|
3403
|
+
return listPm2Processes().find(isConnectPm2Process) ?? null;
|
|
3588
3404
|
}
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
|
|
3592
|
-
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
3593
|
-
try {
|
|
3594
|
-
await sftp.fastPut(zipPath, remotePath);
|
|
3595
|
-
log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
|
|
3596
|
-
} catch (err) {
|
|
3597
|
-
fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
3598
|
-
}
|
|
3599
|
-
return remotePath;
|
|
3405
|
+
function resolvePm2ConnectName(app) {
|
|
3406
|
+
return app?.name ?? PM2_APP_NAME;
|
|
3600
3407
|
}
|
|
3601
|
-
|
|
3602
|
-
const
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
try {
|
|
3606
|
-
await sftp.mkdir(remoteDir, true);
|
|
3607
|
-
await sftp.fastPut(localJarPath, remotePath);
|
|
3608
|
-
log("\u5168\u91CF JAR \u4E0A\u4F20\u6210\u529F");
|
|
3609
|
-
} catch (err) {
|
|
3610
|
-
fail(`\u5168\u91CF JAR \u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
3408
|
+
function deletePm2AppIfExists(name) {
|
|
3409
|
+
const pm2Bin = findGlobalPm2();
|
|
3410
|
+
if (!pm2Bin) {
|
|
3411
|
+
return;
|
|
3611
3412
|
}
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
const stream = options?.stream ?? false;
|
|
3616
|
-
const label = options?.label ?? "\u8FDC\u7A0B\u547D\u4EE4";
|
|
3617
|
-
return new Promise((resolve5, reject) => {
|
|
3618
|
-
client.exec(command, (err, execStream) => {
|
|
3619
|
-
if (err) {
|
|
3620
|
-
reject(err);
|
|
3621
|
-
return;
|
|
3622
|
-
}
|
|
3623
|
-
let out = "";
|
|
3624
|
-
let errText = "";
|
|
3625
|
-
execStream.on("data", (data) => {
|
|
3626
|
-
const text = data.toString();
|
|
3627
|
-
out += text;
|
|
3628
|
-
if (stream) {
|
|
3629
|
-
process.stdout.write(text);
|
|
3630
|
-
}
|
|
3631
|
-
});
|
|
3632
|
-
execStream.stderr.on("data", (data) => {
|
|
3633
|
-
errText += data.toString();
|
|
3634
|
-
});
|
|
3635
|
-
execStream.on("close", (code) => {
|
|
3636
|
-
if (stream && out && !out.endsWith("\n")) {
|
|
3637
|
-
process.stdout.write("\n");
|
|
3638
|
-
}
|
|
3639
|
-
if (check && code !== 0) {
|
|
3640
|
-
const combined = `${out}
|
|
3641
|
-
${errText}`.trim();
|
|
3642
|
-
fail(
|
|
3643
|
-
`${label}\u5931\u8D25 (exit ${code})` + (combined ? `
|
|
3644
|
-
\u8F93\u51FA: ${combined}` : "")
|
|
3645
|
-
);
|
|
3646
|
-
}
|
|
3647
|
-
resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
|
|
3648
|
-
});
|
|
3649
|
-
});
|
|
3413
|
+
ensurePm2DaemonUpdated(pm2Bin);
|
|
3414
|
+
spawnPm2At(pm2Bin, ["delete", name], {
|
|
3415
|
+
stdio: "ignore"
|
|
3650
3416
|
});
|
|
3651
3417
|
}
|
|
3652
|
-
function
|
|
3653
|
-
const
|
|
3654
|
-
const
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
cp -f "$src" "$dest"
|
|
3666
|
-
echo "\u8986\u76D6: $name"
|
|
3667
|
-
updated=$((updated + 1))
|
|
3668
|
-
else
|
|
3669
|
-
echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
|
|
3670
|
-
fi
|
|
3671
|
-
done < <(find "$TMP" -name '*.jar' -type f -print0)
|
|
3672
|
-
echo "UPDATED_COUNT=$updated"
|
|
3673
|
-
`.trim();
|
|
3418
|
+
function removePm2ConnectProcesses() {
|
|
3419
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3420
|
+
for (const app of listPm2Processes()) {
|
|
3421
|
+
if (!isConnectPm2Process(app) || !app.name || seen.has(app.name)) {
|
|
3422
|
+
continue;
|
|
3423
|
+
}
|
|
3424
|
+
seen.add(app.name);
|
|
3425
|
+
deletePm2AppIfExists(app.name);
|
|
3426
|
+
}
|
|
3427
|
+
for (const legacyName of LEGACY_PM2_APP_NAMES) {
|
|
3428
|
+
deletePm2AppIfExists(legacyName);
|
|
3429
|
+
}
|
|
3430
|
+
deletePm2AppIfExists(PM2_APP_NAME);
|
|
3674
3431
|
}
|
|
3675
|
-
|
|
3676
|
-
const
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
);
|
|
3680
|
-
const { out } = await runRemoteCommand(client, script, {
|
|
3681
|
-
label: "\u8FDC\u7A0B\u89E3\u538B"
|
|
3432
|
+
function runPm2(args, options) {
|
|
3433
|
+
const pm2Bin = ensureGlobalPm2({ quiet: true });
|
|
3434
|
+
const result = spawnPm2At(pm2Bin, args, {
|
|
3435
|
+
stdio: options?.inherit ? "inherit" : ["ignore", "pipe", "pipe"]
|
|
3682
3436
|
});
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
\u8F93\u51FA: ${out || "(\u7A7A)"}`);
|
|
3437
|
+
if (result.error) {
|
|
3438
|
+
console.error("[apm] \u6267\u884C pm2 \u5931\u8D25:", result.error.message);
|
|
3439
|
+
process.exit(1);
|
|
3687
3440
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
}
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
if (action === "health") {
|
|
3695
|
-
return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
|
|
3441
|
+
if (result.status !== 0) {
|
|
3442
|
+
const stderr = result.stderr?.toString().trim();
|
|
3443
|
+
const stdout = result.stdout?.toString().trim();
|
|
3444
|
+
const detail = stderr || stdout || `exit code ${result.status}`;
|
|
3445
|
+
console.error(`[apm] pm2 ${args.join(" ")} \u5931\u8D25: ${detail}`);
|
|
3446
|
+
process.exit(result.status ?? 1);
|
|
3696
3447
|
}
|
|
3697
|
-
|
|
3698
|
-
|
|
3448
|
+
}
|
|
3449
|
+
function runPm2Json(args) {
|
|
3450
|
+
const pm2Bin = findGlobalPm2();
|
|
3451
|
+
if (!pm2Bin) {
|
|
3452
|
+
return "[]";
|
|
3699
3453
|
}
|
|
3700
|
-
|
|
3701
|
-
|
|
3454
|
+
ensurePm2DaemonUpdated(pm2Bin);
|
|
3455
|
+
const result = spawnPm2At(pm2Bin, args, {
|
|
3456
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3457
|
+
});
|
|
3458
|
+
if (result.status !== 0) return "[]";
|
|
3459
|
+
return result.stdout?.toString() ?? "[]";
|
|
3460
|
+
}
|
|
3461
|
+
function isPm2ConnectOnline() {
|
|
3462
|
+
const app = findPm2ConnectProcess();
|
|
3463
|
+
if (!app) return false;
|
|
3464
|
+
const status = app.pm2_env?.status;
|
|
3465
|
+
return status === "online" || status === "launching";
|
|
3466
|
+
}
|
|
3467
|
+
function printConnectNotRunningHint() {
|
|
3468
|
+
console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
|
|
3469
|
+
console.log("[apm] \u542F\u52A8\u5B88\u62A4: apm connect --daemon");
|
|
3470
|
+
}
|
|
3471
|
+
function assertConnectNotRunning() {
|
|
3472
|
+
pruneStaleConnectLock();
|
|
3473
|
+
const lock = readConnectLock();
|
|
3474
|
+
if (lock && isProcessAlive(lock.pid) && lock.pid !== process.pid) {
|
|
3475
|
+
console.error(
|
|
3476
|
+
`[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${lock.pid}, mode=${lock.mode})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8`
|
|
3477
|
+
);
|
|
3478
|
+
process.exit(1);
|
|
3702
3479
|
}
|
|
3703
|
-
if (
|
|
3704
|
-
|
|
3480
|
+
if (!isRunningUnderPm2() && isPm2ConnectOnline()) {
|
|
3481
|
+
console.error(
|
|
3482
|
+
"[apm] \u5DF2\u6709 apm connect \u5B88\u62A4\u8FDB\u7A0B\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u6267\u884C apm daemon stop"
|
|
3483
|
+
);
|
|
3484
|
+
process.exit(1);
|
|
3705
3485
|
}
|
|
3706
|
-
return true;
|
|
3707
|
-
}
|
|
3708
|
-
function buildRemoteStatusScript(remoteAppDir, appName) {
|
|
3709
|
-
const dir = shellSingleQuote(remoteAppDir);
|
|
3710
|
-
const jar = shellSingleQuote(appName);
|
|
3711
|
-
return `
|
|
3712
|
-
set -e
|
|
3713
|
-
cd ${dir}
|
|
3714
|
-
appName=${jar}
|
|
3715
|
-
appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
|
|
3716
|
-
if [ -z "$appIds" ]; then
|
|
3717
|
-
echo -e "\\033[31m Not running \\033[0m"
|
|
3718
|
-
else
|
|
3719
|
-
echo -e "\\033[32m Running [$appIds] \\033[0m"
|
|
3720
|
-
fi
|
|
3721
|
-
`.trim();
|
|
3722
3486
|
}
|
|
3723
|
-
function
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
cd ${dir}
|
|
3729
|
-
releaseApp=$(ls -t | grep '.jar$' | head -n1)
|
|
3730
|
-
lastVersionApp=$(ls -t | grep '.jar$' | head -n2 | tail -n1)
|
|
3731
|
-
appName=$lastVersionApp
|
|
3732
|
-
appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
|
|
3733
|
-
if [ -z "$appIds" ]; then
|
|
3734
|
-
echo "Maybe $appName not running, please check it..."
|
|
3735
|
-
else
|
|
3736
|
-
echo "The $appName is stopping..."
|
|
3737
|
-
echo "$appIds" | xargs kill
|
|
3738
|
-
fi
|
|
3739
|
-
for i in $(seq 15 -1 1); do
|
|
3740
|
-
echo -n "$i "
|
|
3741
|
-
sleep 1
|
|
3742
|
-
done
|
|
3743
|
-
echo 0
|
|
3744
|
-
if [ ! -d "backup" ]; then
|
|
3745
|
-
mkdir backup
|
|
3746
|
-
fi
|
|
3747
|
-
for i in $(ls | grep '.jar$' | grep -vFx "$releaseApp"); do
|
|
3748
|
-
echo "backup $i"
|
|
3749
|
-
mv "$i" backup/
|
|
3750
|
-
done
|
|
3751
|
-
appName=$releaseApp
|
|
3752
|
-
count=$(ps -ef | grep java | grep "$appName" | wc -l)
|
|
3753
|
-
if [ "$count" != "0" ]; then
|
|
3754
|
-
echo "Maybe $appName is running, please check it..."
|
|
3755
|
-
else
|
|
3756
|
-
echo "The $appName is starting..."
|
|
3757
|
-
nohup java -jar "./$appName" ${javaOpts} > nohup.out 2>&1 &
|
|
3758
|
-
fi
|
|
3759
|
-
`.trim();
|
|
3487
|
+
function resolveRuntimePaths() {
|
|
3488
|
+
return {
|
|
3489
|
+
apmScript: resolveApmEntryPath(),
|
|
3490
|
+
nodePath: process.execPath
|
|
3491
|
+
};
|
|
3760
3492
|
}
|
|
3761
|
-
function
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3493
|
+
function reexecConnectWithResolvedPath(options) {
|
|
3494
|
+
const apmScript = resolveApmEntryPath();
|
|
3495
|
+
const args = [apmScript, "connect"];
|
|
3496
|
+
const server = options.server?.trim();
|
|
3497
|
+
if (server) {
|
|
3498
|
+
args.push("--server", server);
|
|
3765
3499
|
}
|
|
3766
|
-
|
|
3767
|
-
|
|
3500
|
+
const result = spawnSync2(process.execPath, args, { stdio: "inherit" });
|
|
3501
|
+
if (result.error) {
|
|
3502
|
+
console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
|
|
3503
|
+
process.exit(1);
|
|
3768
3504
|
}
|
|
3769
|
-
|
|
3505
|
+
process.exit(result.status ?? 0);
|
|
3770
3506
|
}
|
|
3771
|
-
function
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
check_url="http://127.0.0.1:${portStr}${normalizedContext}"
|
|
3781
|
-
echo "\u5065\u5EB7\u68C0\u67E5: \${check_url} (\u8D85\u65F6 \${timeout}s)"
|
|
3782
|
-
deadline=$(($(date +%s) + timeout))
|
|
3783
|
-
attempt=0
|
|
3784
|
-
while [ $(date +%s) -lt $deadline ]; do
|
|
3785
|
-
attempt=$((attempt + 1))
|
|
3786
|
-
code=$(curl -s -o /dev/null -w "%{http_code}" "$check_url" 2>/dev/null || echo "000")
|
|
3787
|
-
code=$(echo "$code" | tail -n1 | tr -d '[:space:]')
|
|
3788
|
-
if [ \${#code} -ge 3 ]; then
|
|
3789
|
-
status=\${code:0:3}
|
|
3790
|
-
if echo "$status" | grep -qE '^[0-9]+$' && [ "$status" -ge 200 ] && [ "$status" -lt 500 ]; then
|
|
3791
|
-
echo -e "\\033[32m\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7 (HTTP \${status})\\033[0m"
|
|
3792
|
-
exit 0
|
|
3793
|
-
fi
|
|
3794
|
-
fi
|
|
3795
|
-
remaining=$((deadline - $(date +%s)))
|
|
3796
|
-
if [ $remaining -lt 0 ]; then
|
|
3797
|
-
remaining=0
|
|
3798
|
-
fi
|
|
3799
|
-
echo "\u7B49\u5F85\u670D\u52A1\u542F\u52A8... \u7B2C \${attempt} \u6B21\uFF0C\u5269\u4F59 \${remaining}s"
|
|
3800
|
-
sleep 5
|
|
3801
|
-
done
|
|
3802
|
-
echo -e "\\033[31m\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6 (\${timeout}s): \${check_url}\\033[0m"
|
|
3803
|
-
exit 1
|
|
3804
|
-
`.trim();
|
|
3507
|
+
async function handleConnectAfterUpdate(options) {
|
|
3508
|
+
console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
|
|
3509
|
+
await prepareEcosystem(options.server);
|
|
3510
|
+
if (isRunningUnderPm2()) {
|
|
3511
|
+
runPm2(["reload", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
|
|
3512
|
+
console.log("[apm] \u5DF2\u901A\u77E5 PM2 \u4F7F\u7528\u65B0\u7248\u672C\u91CD\u65B0\u52A0\u8F7D connect");
|
|
3513
|
+
process.exit(0);
|
|
3514
|
+
}
|
|
3515
|
+
reexecConnectWithResolvedPath(options);
|
|
3805
3516
|
}
|
|
3806
|
-
async function
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3517
|
+
async function resolveBaseUrl(server) {
|
|
3518
|
+
if (server?.trim()) {
|
|
3519
|
+
return server.trim().replace(/\/+$/, "");
|
|
3520
|
+
}
|
|
3521
|
+
const cfg = await tryReadApmConfig();
|
|
3522
|
+
return cfg?.baseUrl;
|
|
3523
|
+
}
|
|
3524
|
+
function writeConnectLaunchFiles(options) {
|
|
3525
|
+
mkdirSync6(APM_CONFIG_DIR, { recursive: true });
|
|
3526
|
+
writeFileSync11(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
|
|
3527
|
+
const env = {
|
|
3528
|
+
...collectPm2LaunchEnv()
|
|
3529
|
+
};
|
|
3530
|
+
const baseUrl = options.baseUrl?.trim().replace(/\/+$/, "");
|
|
3531
|
+
if (baseUrl) {
|
|
3532
|
+
env.AI_PM_SERVER = baseUrl;
|
|
3533
|
+
}
|
|
3534
|
+
const launch = {
|
|
3535
|
+
nodePath: options.nodePath,
|
|
3536
|
+
apmScript: options.apmScript,
|
|
3537
|
+
connectArgs: options.connectArgs,
|
|
3538
|
+
cwd: options.cwd,
|
|
3539
|
+
env
|
|
3540
|
+
};
|
|
3541
|
+
writeFileSync11(
|
|
3542
|
+
PM2_CONNECT_LAUNCH_PATH,
|
|
3543
|
+
JSON.stringify(launch, null, 2) + "\n",
|
|
3544
|
+
"utf8"
|
|
3545
|
+
);
|
|
3546
|
+
}
|
|
3547
|
+
function writeEcosystemFile(options) {
|
|
3548
|
+
writeConnectLaunchFiles(options);
|
|
3549
|
+
const ecosystem = buildConnectPm2Ecosystem({
|
|
3550
|
+
entryScript: PM2_CONNECT_ENTRY_PATH,
|
|
3551
|
+
baseUrl: options.baseUrl
|
|
3810
3552
|
});
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3553
|
+
writeFileSync11(
|
|
3554
|
+
PM2_ECOSYSTEM_PATH,
|
|
3555
|
+
formatConnectPm2EcosystemFile(ecosystem),
|
|
3556
|
+
"utf8"
|
|
3557
|
+
);
|
|
3558
|
+
if (existsSync13(LEGACY_PM2_ECOSYSTEM_PATH)) {
|
|
3559
|
+
try {
|
|
3560
|
+
unlinkSync2(LEGACY_PM2_ECOSYSTEM_PATH);
|
|
3561
|
+
} catch {
|
|
3818
3562
|
}
|
|
3819
|
-
return combined;
|
|
3820
3563
|
}
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3564
|
+
}
|
|
3565
|
+
async function prepareEcosystem(server) {
|
|
3566
|
+
await ensureLoggedConfig();
|
|
3567
|
+
const cfg = await ensureApmConfig();
|
|
3568
|
+
const { apmScript, nodePath } = resolveRuntimePaths();
|
|
3569
|
+
const connectArgs = resolveConnectArgs(server);
|
|
3570
|
+
const baseUrl = await resolveBaseUrl(server);
|
|
3571
|
+
writeEcosystemFile({
|
|
3572
|
+
apmScript,
|
|
3573
|
+
nodePath,
|
|
3574
|
+
connectArgs,
|
|
3575
|
+
baseUrl,
|
|
3576
|
+
cwd: process.cwd()
|
|
3577
|
+
});
|
|
3578
|
+
return cfg;
|
|
3579
|
+
}
|
|
3580
|
+
async function runDaemonStart(options) {
|
|
3581
|
+
assertConnectNotRunning();
|
|
3582
|
+
await runUpdate();
|
|
3583
|
+
ensureGlobalPm2();
|
|
3584
|
+
const cfg = await prepareEcosystem(options.server);
|
|
3585
|
+
removePm2ConnectProcesses();
|
|
3586
|
+
runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
|
|
3587
|
+
await delay2(1e3);
|
|
3588
|
+
if (!isPm2ConnectOnline()) {
|
|
3589
|
+
console.error(
|
|
3590
|
+
`[apm] ${PM2_APP_NAME} \u542F\u52A8\u540E\u672A\u5904\u4E8E online \u72B6\u6001\uFF0C\u8BF7\u6267\u884C: apm daemon logs -n 200`
|
|
3591
|
+
);
|
|
3824
3592
|
}
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3593
|
+
console.log(
|
|
3594
|
+
`[apm] ${PM2_APP_NAME} \u5DF2\u7531 PM2 \u542F\u52A8\uFF08server=${options.server?.trim() || cfg.baseUrl}\uFF09`
|
|
3595
|
+
);
|
|
3596
|
+
if (options.follow) {
|
|
3597
|
+
console.log(
|
|
3598
|
+
"[apm] \u6B63\u5728\u8DDF\u8E2A\u65E5\u5FD7\uFF08Ctrl+C \u4EC5\u9000\u51FA\u65E5\u5FD7\u67E5\u770B\uFF0C\u5B88\u62A4\u8FDB\u7A0B\u7EE7\u7EED\u8FD0\u884C\uFF09"
|
|
3599
|
+
);
|
|
3600
|
+
runDaemonLogs({ follow: true });
|
|
3601
|
+
return;
|
|
3828
3602
|
}
|
|
3829
|
-
|
|
3603
|
+
console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
|
|
3604
|
+
console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
|
|
3605
|
+
console.log("[apm] \u505C\u6B62: apm daemon stop");
|
|
3830
3606
|
}
|
|
3831
|
-
function
|
|
3832
|
-
|
|
3607
|
+
async function runDaemonStop() {
|
|
3608
|
+
pruneStaleConnectLock();
|
|
3609
|
+
if (!isPm2ConnectOnline()) {
|
|
3610
|
+
killConnectLockProcessIfAlive();
|
|
3611
|
+
forceReleaseConnectLock();
|
|
3612
|
+
console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728\u8FD0\u884C`);
|
|
3613
|
+
return;
|
|
3614
|
+
}
|
|
3615
|
+
runPm2(["stop", resolvePm2ConnectName(findPm2ConnectProcess())], {
|
|
3616
|
+
inherit: true
|
|
3617
|
+
});
|
|
3618
|
+
for (let i = 0; i < 10; i += 1) {
|
|
3619
|
+
if (!isPm2ConnectOnline()) break;
|
|
3620
|
+
await delay2(500);
|
|
3621
|
+
}
|
|
3622
|
+
if (isPm2ConnectOnline()) {
|
|
3623
|
+
console.log(`[apm] \u4F18\u96C5\u505C\u6B62\u8D85\u65F6\uFF0C\u5F3A\u5236\u79FB\u9664 ${PM2_APP_NAME}\u2026`);
|
|
3624
|
+
runPm2(["delete", resolvePm2ConnectName(findPm2ConnectProcess())], {
|
|
3625
|
+
inherit: true
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
killConnectLockProcessIfAlive();
|
|
3629
|
+
forceReleaseConnectLock();
|
|
3630
|
+
console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u505C\u6B62`);
|
|
3833
3631
|
}
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
return
|
|
3632
|
+
function killConnectLockProcessIfAlive() {
|
|
3633
|
+
pruneStaleConnectLock();
|
|
3634
|
+
const lock = readConnectLock();
|
|
3635
|
+
if (!lock || !isProcessAlive(lock.pid)) return;
|
|
3636
|
+
try {
|
|
3637
|
+
process.kill(lock.pid, "SIGTERM");
|
|
3638
|
+
} catch {
|
|
3639
|
+
forceReleaseConnectLock();
|
|
3640
|
+
return;
|
|
3843
3641
|
}
|
|
3844
|
-
if (
|
|
3845
|
-
|
|
3642
|
+
if (isProcessAlive(lock.pid)) {
|
|
3643
|
+
try {
|
|
3644
|
+
process.kill(lock.pid, "SIGKILL");
|
|
3645
|
+
} catch {
|
|
3646
|
+
}
|
|
3846
3647
|
}
|
|
3847
|
-
|
|
3648
|
+
forceReleaseConnectLock();
|
|
3848
3649
|
}
|
|
3849
|
-
async function
|
|
3850
|
-
|
|
3851
|
-
const
|
|
3852
|
-
|
|
3853
|
-
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3650
|
+
async function runDaemonRestart(options) {
|
|
3651
|
+
pruneStaleConnectLock();
|
|
3652
|
+
const lock = readConnectLock();
|
|
3653
|
+
if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
|
|
3654
|
+
console.error(
|
|
3655
|
+
`[apm] \u5DF2\u6709\u524D\u53F0 apm connect \u5728\u8FD0\u884C (pid=${lock.pid})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B`
|
|
3656
|
+
);
|
|
3657
|
+
process.exit(1);
|
|
3658
|
+
}
|
|
3659
|
+
await runUpdate();
|
|
3660
|
+
await prepareEcosystem(options.server);
|
|
3661
|
+
removePm2ConnectProcesses();
|
|
3662
|
+
runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
|
|
3663
|
+
console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
|
|
3857
3664
|
}
|
|
3858
|
-
async function
|
|
3859
|
-
|
|
3860
|
-
|
|
3665
|
+
async function runDaemonDelete() {
|
|
3666
|
+
ensureGlobalPm2();
|
|
3667
|
+
const app = findPm2ConnectProcess();
|
|
3668
|
+
if (!app?.name) {
|
|
3669
|
+
console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728 PM2 \u4E2D\u6CE8\u518C`);
|
|
3670
|
+
return;
|
|
3671
|
+
}
|
|
3672
|
+
runPm2(["delete", app.name], { inherit: true });
|
|
3673
|
+
forceReleaseConnectLock();
|
|
3674
|
+
console.log(`[apm] ${app.name} \u5DF2\u4ECE PM2 \u79FB\u9664`);
|
|
3861
3675
|
}
|
|
3862
|
-
function
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3676
|
+
async function runDaemonStatus() {
|
|
3677
|
+
ensureGlobalPm2();
|
|
3678
|
+
const app = findPm2ConnectProcess();
|
|
3679
|
+
if (!app?.name) {
|
|
3680
|
+
printConnectNotRunningHint();
|
|
3681
|
+
return;
|
|
3682
|
+
}
|
|
3683
|
+
runPm2(["describe", app.name], { inherit: true });
|
|
3868
3684
|
}
|
|
3869
|
-
async function
|
|
3870
|
-
|
|
3871
|
-
const
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
|
|
3875
|
-
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
3876
|
-
log(
|
|
3877
|
-
`\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"}${packOnly ? "\uFF1B\u4EC5\u6253\u5305\uFF08\u4E0D\u4E0A\u4F20\u8FDC\u7A0B\uFF09" : ""}`
|
|
3878
|
-
);
|
|
3879
|
-
runMavenBuild(projectRoot, config.mavenLocalRepo, {
|
|
3880
|
-
source: config.mavenLocalRepoSource,
|
|
3881
|
-
sourceDetail: config.mavenLocalRepoSourceDetail
|
|
3882
|
-
});
|
|
3883
|
-
if (packOnly) {
|
|
3884
|
-
const uploadWithoutRunId = !resolveDeploymentRunIdFromEnv();
|
|
3885
|
-
if (config.mode === "full") {
|
|
3886
|
-
const mainJar = locateMainJar(projectRoot);
|
|
3887
|
-
const archiveZipPath = await createUpdatePackage(
|
|
3888
|
-
[
|
|
3889
|
-
{
|
|
3890
|
-
path: mainJar,
|
|
3891
|
-
arcname: path3.basename(mainJar),
|
|
3892
|
-
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
3893
|
-
}
|
|
3894
|
-
],
|
|
3895
|
-
`.deploy-archive-${config.projectName}.jar.zip`
|
|
3896
|
-
);
|
|
3897
|
-
await uploadDeployArtifactZip({
|
|
3898
|
-
zipPath: archiveZipPath,
|
|
3899
|
-
projectName: config.projectName,
|
|
3900
|
-
kind: "backend",
|
|
3901
|
-
uploadWithoutRunId
|
|
3902
|
-
});
|
|
3903
|
-
} else {
|
|
3904
|
-
const libDir = locateLibDir(projectRoot);
|
|
3905
|
-
const entries = listAllLibFilesForArchive(libDir);
|
|
3906
|
-
if (entries.length === 0) {
|
|
3907
|
-
fail("lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u53EF\u5F52\u6863\u7684 JAR");
|
|
3908
|
-
}
|
|
3909
|
-
const zipPath = await createUpdatePackage(entries, config.packageName);
|
|
3910
|
-
await uploadDeployArtifactZip({
|
|
3911
|
-
zipPath,
|
|
3912
|
-
projectName: config.projectName,
|
|
3913
|
-
kind: "backend",
|
|
3914
|
-
uploadWithoutRunId
|
|
3915
|
-
});
|
|
3916
|
-
}
|
|
3917
|
-
log("\u4EC5\u6253\u5305\u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
|
|
3685
|
+
async function runDaemonLogs(options) {
|
|
3686
|
+
ensureGlobalPm2();
|
|
3687
|
+
const app = findPm2ConnectProcess();
|
|
3688
|
+
if (!app?.name) {
|
|
3689
|
+
printConnectNotRunningHint();
|
|
3918
3690
|
return;
|
|
3919
3691
|
}
|
|
3920
|
-
const
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
const mainJar = locateMainJar(projectRoot);
|
|
3924
|
-
await uploadFullJar(conn.sftp, mainJar, config);
|
|
3925
|
-
const archiveZipPath = await createUpdatePackage(
|
|
3926
|
-
[
|
|
3927
|
-
{
|
|
3928
|
-
path: mainJar,
|
|
3929
|
-
arcname: path3.basename(mainJar),
|
|
3930
|
-
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
3931
|
-
}
|
|
3932
|
-
],
|
|
3933
|
-
`.deploy-archive-${config.projectName}.jar.zip`
|
|
3934
|
-
);
|
|
3935
|
-
await uploadDeployArtifactZip({
|
|
3936
|
-
zipPath: archiveZipPath,
|
|
3937
|
-
projectName: config.projectName,
|
|
3938
|
-
kind: "backend"
|
|
3939
|
-
});
|
|
3940
|
-
log("\u91CD\u542F\u670D\u52A1...");
|
|
3941
|
-
await restartRemoteService(conn.client, config);
|
|
3942
|
-
await healthCheckService(conn.client, config);
|
|
3943
|
-
log("\u90E8\u7F72\u5B8C\u6210");
|
|
3944
|
-
return;
|
|
3945
|
-
}
|
|
3946
|
-
const libDir = locateLibDir(projectRoot);
|
|
3947
|
-
let manifest = loadManifest3();
|
|
3948
|
-
const remoteLibStats = await getRemoteFileStats(
|
|
3949
|
-
conn.sftp,
|
|
3950
|
-
config.remoteLibDir
|
|
3951
|
-
);
|
|
3952
|
-
log("\u6536\u96C6 JAR \u66F4\u65B0...");
|
|
3953
|
-
const libUploadEntries = listLibFilesToUpload(
|
|
3954
|
-
libDir,
|
|
3955
|
-
remoteLibStats,
|
|
3956
|
-
projectRoot,
|
|
3957
|
-
manifest
|
|
3958
|
-
);
|
|
3959
|
-
let updated = 0;
|
|
3960
|
-
if (libUploadEntries.length > 0) {
|
|
3961
|
-
const zipPath = await createUpdatePackage(
|
|
3962
|
-
libUploadEntries,
|
|
3963
|
-
config.packageName
|
|
3964
|
-
);
|
|
3965
|
-
const remoteZipPath = await uploadUpdatePackage(
|
|
3966
|
-
conn.sftp,
|
|
3967
|
-
zipPath,
|
|
3968
|
-
config
|
|
3969
|
-
);
|
|
3970
|
-
await uploadDeployArtifactZip({
|
|
3971
|
-
zipPath,
|
|
3972
|
-
projectName: config.projectName,
|
|
3973
|
-
kind: "backend"
|
|
3974
|
-
});
|
|
3975
|
-
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
3976
|
-
updated = await extractUpdatePackageOnRemote(
|
|
3977
|
-
conn.client,
|
|
3978
|
-
config,
|
|
3979
|
-
remoteZipPath
|
|
3980
|
-
);
|
|
3981
|
-
} else {
|
|
3982
|
-
log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
|
|
3983
|
-
}
|
|
3984
|
-
const runningJar = await getRunningJar(conn.client, config);
|
|
3985
|
-
let needRestart = updated > 0;
|
|
3986
|
-
if (!needRestart && !runningJar) {
|
|
3987
|
-
log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
|
|
3988
|
-
needRestart = true;
|
|
3989
|
-
} else if (!needRestart) {
|
|
3990
|
-
log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
|
|
3991
|
-
}
|
|
3992
|
-
if (needRestart) {
|
|
3993
|
-
log("\u91CD\u542F\u670D\u52A1...");
|
|
3994
|
-
await restartRemoteService(conn.client, config);
|
|
3995
|
-
}
|
|
3996
|
-
await healthCheckService(conn.client, config);
|
|
3997
|
-
if (libUploadEntries.length > 0) {
|
|
3998
|
-
manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
|
|
3999
|
-
saveManifest3(manifest);
|
|
4000
|
-
}
|
|
4001
|
-
} finally {
|
|
4002
|
-
await closeSsh(conn);
|
|
3692
|
+
const args = ["logs", app.name, "--lines", String(options.lines ?? 100)];
|
|
3693
|
+
if (!options.follow) {
|
|
3694
|
+
args.push("--nostream");
|
|
4003
3695
|
}
|
|
4004
|
-
|
|
3696
|
+
runPm2(args, { inherit: true });
|
|
4005
3697
|
}
|
|
4006
3698
|
|
|
4007
3699
|
// src/commands/deploy/internal/deploy-shell-env.ts
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
4029
|
-
try {
|
|
4030
|
-
process.kill(pid, 0);
|
|
4031
|
-
return true;
|
|
4032
|
-
} catch {
|
|
4033
|
-
return false;
|
|
3700
|
+
var PATH_SEP = process.platform === "win32" ? ";" : ":";
|
|
3701
|
+
var useNpmShell3 = process.platform === "win32";
|
|
3702
|
+
function resolveNpmGlobalBin2() {
|
|
3703
|
+
const result = spawnSync3(useNpmShell3 ? "npm.cmd" : "npm", ["bin", "-g"], {
|
|
3704
|
+
encoding: "utf8",
|
|
3705
|
+
shell: useNpmShell3,
|
|
3706
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3707
|
+
});
|
|
3708
|
+
if (result.status !== 0) {
|
|
3709
|
+
return null;
|
|
3710
|
+
}
|
|
3711
|
+
const bin = result.stdout?.toString().trim();
|
|
3712
|
+
return bin || null;
|
|
3713
|
+
}
|
|
3714
|
+
function prependPath(pathValue, segment) {
|
|
3715
|
+
if (!segment) {
|
|
3716
|
+
return pathValue ?? "";
|
|
3717
|
+
}
|
|
3718
|
+
if (!pathValue) {
|
|
3719
|
+
return segment;
|
|
4034
3720
|
}
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
while (Date.now() < deadline) {
|
|
3721
|
+
const segments = pathValue.split(PATH_SEP);
|
|
3722
|
+
if (segments.includes(segment)) {
|
|
3723
|
+
return pathValue;
|
|
4039
3724
|
}
|
|
3725
|
+
return `${segment}${PATH_SEP}${pathValue}`;
|
|
4040
3726
|
}
|
|
4041
|
-
function
|
|
4042
|
-
const
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
return;
|
|
4048
|
-
}
|
|
4049
|
-
sleepSync(100);
|
|
3727
|
+
function buildDeployShellEnv(extra) {
|
|
3728
|
+
const env = { ...process.env, ...extra };
|
|
3729
|
+
const pathSegments = [dirname4(process.execPath)];
|
|
3730
|
+
const globalBin = resolveNpmGlobalBin2();
|
|
3731
|
+
if (globalBin) {
|
|
3732
|
+
pathSegments.push(globalBin);
|
|
4050
3733
|
}
|
|
4051
|
-
}
|
|
4052
|
-
function readConnectLock() {
|
|
4053
|
-
if (!existsSync13(CONNECT_LOCK_PATH)) return null;
|
|
4054
3734
|
try {
|
|
4055
|
-
|
|
4056
|
-
const parsed = JSON.parse(raw);
|
|
4057
|
-
if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
|
|
4058
|
-
return null;
|
|
4059
|
-
}
|
|
4060
|
-
return parsed;
|
|
3735
|
+
resolveApmEntryPath();
|
|
4061
3736
|
} catch {
|
|
4062
|
-
return null;
|
|
4063
3737
|
}
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
const lock = readConnectLock();
|
|
4067
|
-
if (!lock) return;
|
|
4068
|
-
if (isProcessAlive(lock.pid)) return;
|
|
4069
|
-
try {
|
|
4070
|
-
unlinkSync(CONNECT_LOCK_PATH);
|
|
4071
|
-
} catch {
|
|
3738
|
+
for (const segment of pathSegments) {
|
|
3739
|
+
env.PATH = prependPath(env.PATH, segment);
|
|
4072
3740
|
}
|
|
3741
|
+
return env;
|
|
4073
3742
|
}
|
|
4074
|
-
|
|
4075
|
-
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
3743
|
+
|
|
3744
|
+
// src/commands/deploy/deploy-shell-run.ts
|
|
3745
|
+
function runDeployShellCommand(command, cwd, captureOutput) {
|
|
3746
|
+
const result = spawnSync4(command, {
|
|
3747
|
+
cwd,
|
|
3748
|
+
shell: true,
|
|
3749
|
+
env: buildDeployShellEnv(),
|
|
3750
|
+
encoding: "utf8",
|
|
3751
|
+
maxBuffer: DEPLOY_SPAWN_MAX_BUFFER_BYTES,
|
|
3752
|
+
stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
|
|
3753
|
+
});
|
|
3754
|
+
const stdout = captureOutput ? String(result.stdout ?? "") : "";
|
|
3755
|
+
const stderr = captureOutput ? String(result.stderr ?? "") : "";
|
|
3756
|
+
const output = [stdout, stderr].filter(Boolean).join("\n");
|
|
3757
|
+
if (captureOutput) {
|
|
3758
|
+
if (stdout) process.stdout.write(stdout);
|
|
3759
|
+
if (stderr) process.stderr.write(stderr);
|
|
4089
3760
|
}
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
writeFileSync11(
|
|
4097
|
-
CONNECT_LOCK_PATH,
|
|
4098
|
-
JSON.stringify(lock, null, 2) + "\n",
|
|
4099
|
-
"utf8"
|
|
4100
|
-
);
|
|
4101
|
-
}
|
|
4102
|
-
function releaseConnectLock() {
|
|
4103
|
-
const lock = readConnectLock();
|
|
4104
|
-
if (lock?.pid !== process.pid) return;
|
|
4105
|
-
try {
|
|
4106
|
-
unlinkSync(CONNECT_LOCK_PATH);
|
|
4107
|
-
} catch {
|
|
3761
|
+
if (result.error) {
|
|
3762
|
+
throw new DeployExecutionError(
|
|
3763
|
+
`\u90E8\u7F72\u547D\u4EE4\u6267\u884C\u5931\u8D25: ${result.error.message}`,
|
|
3764
|
+
1,
|
|
3765
|
+
output
|
|
3766
|
+
);
|
|
4108
3767
|
}
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
} catch {
|
|
3768
|
+
if (result.status !== 0) {
|
|
3769
|
+
throw new DeployExecutionError(
|
|
3770
|
+
`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
|
|
3771
|
+
result.status ?? 1,
|
|
3772
|
+
output
|
|
3773
|
+
);
|
|
4116
3774
|
}
|
|
3775
|
+
return output;
|
|
4117
3776
|
}
|
|
4118
3777
|
|
|
4119
|
-
// src/commands/
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
APM_CONFIG_DIR,
|
|
4123
|
-
"connect.ecosystem.config.cjs"
|
|
4124
|
-
);
|
|
4125
|
-
var PM2_CONNECT_ENTRY_PATH = join15(APM_CONFIG_DIR, "connect.entry.cjs");
|
|
4126
|
-
var PM2_CONNECT_LAUNCH_PATH = join15(
|
|
4127
|
-
APM_CONFIG_DIR,
|
|
4128
|
-
"connect.launch.json"
|
|
4129
|
-
);
|
|
4130
|
-
var LEGACY_PM2_ECOSYSTEM_PATH = join15(
|
|
4131
|
-
APM_CONFIG_DIR,
|
|
4132
|
-
"connect.ecosystem.cjs"
|
|
4133
|
-
);
|
|
4134
|
-
var LEGACY_PM2_APP_NAMES = ["connect.ecosystem"];
|
|
4135
|
-
var CONNECT_ENTRY_SCRIPT = `'use strict';
|
|
4136
|
-
const { spawnSync } = require('node:child_process');
|
|
4137
|
-
const { readFileSync } = require('node:fs');
|
|
4138
|
-
const { join } = require('node:path');
|
|
3778
|
+
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
3779
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13, statSync as statSync7 } from "node:fs";
|
|
3780
|
+
import path4 from "node:path";
|
|
4139
3781
|
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
3782
|
+
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
3783
|
+
import {
|
|
3784
|
+
existsSync as existsSync14,
|
|
3785
|
+
mkdirSync as mkdirSync7,
|
|
3786
|
+
readdirSync as readdirSync5,
|
|
3787
|
+
readFileSync as readFileSync12,
|
|
3788
|
+
statSync as statSync6,
|
|
3789
|
+
writeFileSync as writeFileSync12
|
|
3790
|
+
} from "node:fs";
|
|
3791
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
3792
|
+
import path3 from "node:path";
|
|
3793
|
+
import { Client as Client2 } from "ssh2";
|
|
3794
|
+
import JSZip2 from "jszip";
|
|
3795
|
+
import SftpClient2 from "ssh2-sftp-client";
|
|
4148
3796
|
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
};
|
|
3797
|
+
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
3798
|
+
import { readdir as readdir2, readFile as readFile3, unlink, writeFile } from "node:fs/promises";
|
|
3799
|
+
import path2 from "node:path";
|
|
3800
|
+
import JSZip from "jszip";
|
|
3801
|
+
import SftpClient from "ssh2-sftp-client";
|
|
4155
3802
|
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
env: childEnv,
|
|
4159
|
-
stdio: 'inherit',
|
|
4160
|
-
windowsHide: true,
|
|
4161
|
-
});
|
|
3803
|
+
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
3804
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
4162
3805
|
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
function
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
3806
|
+
// src/commands/deploy/internal/minio.ts
|
|
3807
|
+
import { statSync as statSync5 } from "node:fs";
|
|
3808
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3809
|
+
import path from "node:path";
|
|
3810
|
+
import * as Minio from "minio";
|
|
3811
|
+
var DEFAULT_MAX_FILE_SIZE_MB = 50;
|
|
3812
|
+
async function isDirectoryPath(dir) {
|
|
3813
|
+
try {
|
|
3814
|
+
const st = statSync5(dir);
|
|
3815
|
+
return st.isDirectory();
|
|
3816
|
+
} catch {
|
|
3817
|
+
return false;
|
|
4174
3818
|
}
|
|
4175
|
-
return args;
|
|
4176
3819
|
}
|
|
4177
|
-
|
|
4178
|
-
"
|
|
4179
|
-
"
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
"LC_ALL",
|
|
4184
|
-
"LC_CTYPE",
|
|
4185
|
-
"NVM_DIR",
|
|
4186
|
-
"NVM_BIN",
|
|
4187
|
-
"NVM_INC",
|
|
4188
|
-
"FNM_DIR",
|
|
4189
|
-
"VOLTA_HOME",
|
|
4190
|
-
"PNPM_HOME",
|
|
4191
|
-
"npm_config_prefix",
|
|
4192
|
-
"NODE_HOME"
|
|
4193
|
-
];
|
|
4194
|
-
function collectPm2LaunchEnv() {
|
|
4195
|
-
const env = {};
|
|
4196
|
-
for (const key of PM2_LAUNCH_ENV_KEYS) {
|
|
4197
|
-
const value = process.env[key]?.trim();
|
|
4198
|
-
if (value) {
|
|
4199
|
-
env[key] = value;
|
|
3820
|
+
function sanitizeRelativePath(rel) {
|
|
3821
|
+
const norm = rel.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
3822
|
+
const segments = norm.split("/").filter(Boolean);
|
|
3823
|
+
for (const s of segments) {
|
|
3824
|
+
if (s === "." || s === "..") {
|
|
3825
|
+
throw new Error(`\u975E\u6CD5\u76F8\u5BF9\u8DEF\u5F84\u7247\u6BB5\uFF1A${s}`);
|
|
4200
3826
|
}
|
|
4201
3827
|
}
|
|
4202
|
-
return
|
|
3828
|
+
return segments.join("/");
|
|
4203
3829
|
}
|
|
4204
|
-
function
|
|
4205
|
-
const
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
if (process.platform === "win32") {
|
|
4227
|
-
app.windowsHide = true;
|
|
3830
|
+
async function collectFiles(root) {
|
|
3831
|
+
const out = [];
|
|
3832
|
+
async function walk(dir, prefix) {
|
|
3833
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
3834
|
+
for (const e of entries) {
|
|
3835
|
+
const name = e.name;
|
|
3836
|
+
if (name === "." || name === "..") {
|
|
3837
|
+
continue;
|
|
3838
|
+
}
|
|
3839
|
+
const abs = path.join(dir, name);
|
|
3840
|
+
const rel = prefix ? `${prefix}/${name}` : name;
|
|
3841
|
+
if (e.isDirectory()) {
|
|
3842
|
+
await walk(abs, rel);
|
|
3843
|
+
} else if (e.isFile()) {
|
|
3844
|
+
const st = statSync5(abs);
|
|
3845
|
+
out.push({
|
|
3846
|
+
absPath: abs,
|
|
3847
|
+
relativePath: rel.replace(/\\/g, "/"),
|
|
3848
|
+
size: st.size
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
4228
3852
|
}
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
3853
|
+
await walk(root, "");
|
|
3854
|
+
out.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
3855
|
+
return out;
|
|
4232
3856
|
}
|
|
4233
|
-
function
|
|
4234
|
-
return
|
|
4235
|
-
`;
|
|
3857
|
+
async function readArtifactFile(absPath) {
|
|
3858
|
+
return readFile(absPath);
|
|
4236
3859
|
}
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
return spawnSync3(useNpmShell2 ? "npm.cmd" : "npm", args, {
|
|
4240
|
-
...options,
|
|
4241
|
-
shell: useNpmShell2
|
|
4242
|
-
});
|
|
3860
|
+
function toMB(bytes) {
|
|
3861
|
+
return Math.round(bytes / (1024 * 1024) * 1e3) / 1e3;
|
|
4243
3862
|
}
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
3863
|
+
var MIME = {
|
|
3864
|
+
".html": "text/html; charset=utf-8",
|
|
3865
|
+
".css": "text/css; charset=utf-8",
|
|
3866
|
+
".js": "application/javascript; charset=utf-8",
|
|
3867
|
+
".json": "application/json; charset=utf-8",
|
|
3868
|
+
".svg": "image/svg+xml",
|
|
3869
|
+
".png": "image/png",
|
|
3870
|
+
".jpg": "image/jpeg",
|
|
3871
|
+
".jpeg": "image/jpeg",
|
|
3872
|
+
".gif": "image/gif",
|
|
3873
|
+
".webp": "image/webp",
|
|
3874
|
+
".woff": "font/woff",
|
|
3875
|
+
".woff2": "font/woff2",
|
|
3876
|
+
".ttf": "font/ttf",
|
|
3877
|
+
".ico": "image/x-icon",
|
|
3878
|
+
".txt": "text/plain; charset=utf-8",
|
|
3879
|
+
".map": "application/json"
|
|
3880
|
+
};
|
|
3881
|
+
function detectMimeType(filePath) {
|
|
3882
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
3883
|
+
return MIME[ext] ?? "";
|
|
4251
3884
|
}
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
3885
|
+
var MinioClient = class {
|
|
3886
|
+
inner;
|
|
3887
|
+
constructor(opts) {
|
|
3888
|
+
const endPoint = opts.endPoint.replace(/^https?:\/\//i, "").split("/")[0] ?? opts.endPoint;
|
|
3889
|
+
this.inner = new Minio.Client({
|
|
3890
|
+
endPoint,
|
|
3891
|
+
port: opts.port,
|
|
3892
|
+
useSSL: opts.useSSL,
|
|
3893
|
+
accessKey: opts.accessKey,
|
|
3894
|
+
secretKey: opts.secretKey
|
|
3895
|
+
});
|
|
3896
|
+
}
|
|
3897
|
+
async ensureBucket(bucket) {
|
|
3898
|
+
const exists = await this.inner.bucketExists(bucket);
|
|
3899
|
+
if (!exists) {
|
|
3900
|
+
await this.inner.makeBucket(bucket);
|
|
4264
3901
|
}
|
|
4265
3902
|
}
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
3903
|
+
async deleteObjectsByPrefix(bucket, prefix) {
|
|
3904
|
+
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
3905
|
+
const keys = [];
|
|
3906
|
+
await new Promise((resolve5, reject) => {
|
|
3907
|
+
objectsStream.on("data", (obj) => {
|
|
3908
|
+
if (obj.name) {
|
|
3909
|
+
keys.push(obj.name);
|
|
3910
|
+
}
|
|
3911
|
+
});
|
|
3912
|
+
objectsStream.on("error", reject);
|
|
3913
|
+
objectsStream.on("end", resolve5);
|
|
3914
|
+
});
|
|
3915
|
+
const chunkSize = 500;
|
|
3916
|
+
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
3917
|
+
const chunk = keys.slice(i, i + chunkSize);
|
|
3918
|
+
await this.inner.removeObjects(
|
|
3919
|
+
bucket,
|
|
3920
|
+
chunk.map((name) => name)
|
|
3921
|
+
);
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
async putObject(bucket, objectKey, body, meta) {
|
|
3925
|
+
await this.inner.putObject(bucket, objectKey, body, body.length, meta);
|
|
3926
|
+
}
|
|
3927
|
+
/** 匿名可读当前桶全部对象(便于静态站点直链) */
|
|
3928
|
+
async setBucketPublicRead(bucket) {
|
|
3929
|
+
const policy = {
|
|
3930
|
+
Version: "2012-10-17",
|
|
3931
|
+
Statement: [
|
|
3932
|
+
{
|
|
3933
|
+
Effect: "Allow",
|
|
3934
|
+
Principal: { AWS: ["*"] },
|
|
3935
|
+
Action: ["s3:GetObject"],
|
|
3936
|
+
Resource: [`arn:aws:s3:::${bucket}/*`]
|
|
3937
|
+
}
|
|
3938
|
+
]
|
|
4277
3939
|
};
|
|
3940
|
+
await this.inner.setBucketPolicy(bucket, JSON.stringify(policy));
|
|
4278
3941
|
}
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
3942
|
+
};
|
|
3943
|
+
|
|
3944
|
+
// src/commands/deploy/internal/deploy-artifact-minio.ts
|
|
3945
|
+
var APM_DEPLOYMENT_RUN_ID_ENV = "APM_DEPLOYMENT_RUN_ID";
|
|
3946
|
+
function formatDeployArtifactTimestamp(date = /* @__PURE__ */ new Date()) {
|
|
3947
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
3948
|
+
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
|
|
4284
3949
|
}
|
|
4285
|
-
function
|
|
4286
|
-
const
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
});
|
|
3950
|
+
function sanitizeDeployProjectName(name) {
|
|
3951
|
+
const trimmed = name.trim().replace(/\\/g, "/");
|
|
3952
|
+
const base = trimmed.split("/").filter(Boolean).pop() ?? trimmed;
|
|
3953
|
+
try {
|
|
3954
|
+
return sanitizeRelativePath(base.replace(/[/\\:*?"<>|]/g, "_"));
|
|
3955
|
+
} catch {
|
|
3956
|
+
return "project";
|
|
3957
|
+
}
|
|
4294
3958
|
}
|
|
4295
|
-
function
|
|
4296
|
-
|
|
3959
|
+
function buildDeployArtifactFileName(kind, projectName, timestamp = formatDeployArtifactTimestamp()) {
|
|
3960
|
+
const safeName = sanitizeDeployProjectName(projectName);
|
|
3961
|
+
const suffix = kind === "frontend" ? "dist.zip" : "jar.zip";
|
|
3962
|
+
return `${timestamp}-${safeName}.${suffix}`;
|
|
4297
3963
|
}
|
|
4298
|
-
function
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
}
|
|
4302
|
-
const result = spawnPm2At(pm2Bin, ["--version"], {
|
|
4303
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
4304
|
-
});
|
|
4305
|
-
return !result.error && result.status === 0;
|
|
3964
|
+
function buildDeployArtifactObjectKey(projectName, fileName) {
|
|
3965
|
+
const safeProject = sanitizeDeployProjectName(projectName);
|
|
3966
|
+
return `deploy/${safeProject}/${fileName}`;
|
|
4306
3967
|
}
|
|
4307
|
-
function
|
|
4308
|
-
const
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
3968
|
+
function resolveDeploymentRunIdFromEnv() {
|
|
3969
|
+
const raw = process.env[APM_DEPLOYMENT_RUN_ID_ENV]?.trim();
|
|
3970
|
+
return raw || null;
|
|
3971
|
+
}
|
|
3972
|
+
async function fetchDeployArtifactStorage(api) {
|
|
3973
|
+
return api.cli.getDeployArtifactStorage(void 0);
|
|
3974
|
+
}
|
|
3975
|
+
async function uploadDeployArtifactZip(options) {
|
|
3976
|
+
const deploymentRunId = options.deploymentRunId ?? resolveDeploymentRunIdFromEnv();
|
|
3977
|
+
if (!deploymentRunId && !options.uploadWithoutRunId) {
|
|
3978
|
+
return null;
|
|
4317
3979
|
}
|
|
4318
|
-
const
|
|
4319
|
-
if (
|
|
4320
|
-
|
|
3980
|
+
const cfg = await tryReadApmConfig();
|
|
3981
|
+
if (!cfg || !resolveApiKey(cfg)) {
|
|
3982
|
+
console.warn("[apm] \u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7 MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863");
|
|
3983
|
+
return null;
|
|
4321
3984
|
}
|
|
4322
|
-
const
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
3985
|
+
const api = options.api ?? createApmApiClient(cfg);
|
|
3986
|
+
let storage;
|
|
3987
|
+
try {
|
|
3988
|
+
storage = await fetchDeployArtifactStorage(api);
|
|
3989
|
+
} catch (error) {
|
|
3990
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3991
|
+
console.warn(`[apm] \u83B7\u53D6 MinIO \u914D\u7F6E\u5931\u8D25\uFF0C\u8DF3\u8FC7\u90E8\u7F72\u4EA7\u7269\u5F52\u6863: ${detail}`);
|
|
3992
|
+
return null;
|
|
3993
|
+
}
|
|
3994
|
+
const fileName = buildDeployArtifactFileName(
|
|
3995
|
+
options.kind,
|
|
3996
|
+
options.projectName,
|
|
3997
|
+
options.timestamp
|
|
3998
|
+
);
|
|
3999
|
+
const objectKey = buildDeployArtifactObjectKey(options.projectName, fileName);
|
|
4000
|
+
let body;
|
|
4001
|
+
try {
|
|
4002
|
+
body = await readFile2(options.zipPath);
|
|
4003
|
+
} catch (error) {
|
|
4004
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4005
|
+
console.warn(`[apm] \u8BFB\u53D6\u90E8\u7F72\u4EA7\u7269 zip \u5931\u8D25\uFF0C\u8DF3\u8FC7 MinIO \u5F52\u6863: ${detail}`);
|
|
4006
|
+
return null;
|
|
4007
|
+
}
|
|
4008
|
+
try {
|
|
4009
|
+
const client = new MinioClient({
|
|
4010
|
+
endPoint: storage.endpoint,
|
|
4011
|
+
port: storage.port,
|
|
4012
|
+
useSSL: storage.useSsl,
|
|
4013
|
+
accessKey: storage.accessKey,
|
|
4014
|
+
secretKey: storage.secretKey
|
|
4015
|
+
});
|
|
4016
|
+
await client.ensureBucket(storage.bucket);
|
|
4017
|
+
await client.putObject(storage.bucket, objectKey, body, {
|
|
4018
|
+
"Content-Type": "application/zip"
|
|
4019
|
+
});
|
|
4020
|
+
console.error(
|
|
4021
|
+
`[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(
|
|
4022
|
+
2
|
|
4023
|
+
)} MB)`
|
|
4024
|
+
);
|
|
4025
|
+
} catch (error) {
|
|
4026
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4027
|
+
console.warn(`[apm] MinIO \u90E8\u7F72\u4EA7\u7269\u5F52\u6863\u5931\u8D25: ${detail}`);
|
|
4028
|
+
return null;
|
|
4029
|
+
}
|
|
4030
|
+
if (deploymentRunId) {
|
|
4031
|
+
try {
|
|
4032
|
+
await api.cli.attachTaskDeploymentArtifact({
|
|
4033
|
+
id: deploymentRunId,
|
|
4034
|
+
artifactObjectKey: objectKey,
|
|
4035
|
+
artifactFileName: fileName
|
|
4335
4036
|
});
|
|
4037
|
+
console.error(
|
|
4038
|
+
`[apm] \u5DF2\u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55 id=${deploymentRunId} file=${fileName}`
|
|
4039
|
+
);
|
|
4040
|
+
} catch (error) {
|
|
4041
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
4042
|
+
console.warn(`[apm] \u7ED1\u5B9A\u90E8\u7F72\u4EA7\u7269\u5230\u8BB0\u5F55\u5931\u8D25: ${detail}`);
|
|
4043
|
+
return null;
|
|
4336
4044
|
}
|
|
4337
|
-
candidates.push(...fromPath);
|
|
4338
4045
|
}
|
|
4339
|
-
return
|
|
4046
|
+
return {
|
|
4047
|
+
objectKey,
|
|
4048
|
+
fileName,
|
|
4049
|
+
bucket: storage.bucket
|
|
4050
|
+
};
|
|
4340
4051
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
if (
|
|
4348
|
-
|
|
4052
|
+
|
|
4053
|
+
// src/commands/deploy/internal/wisdom-sftp.ts
|
|
4054
|
+
async function addDirToZip(dir, zipFolder) {
|
|
4055
|
+
const entries = await readdir2(dir, { withFileTypes: true });
|
|
4056
|
+
for (const entry of entries) {
|
|
4057
|
+
const fullPath = path2.join(dir, entry.name);
|
|
4058
|
+
if (entry.isDirectory()) {
|
|
4059
|
+
const folder = zipFolder.folder(entry.name);
|
|
4060
|
+
if (folder) {
|
|
4061
|
+
await addDirToZip(fullPath, folder);
|
|
4062
|
+
}
|
|
4063
|
+
} else {
|
|
4064
|
+
const content = await readFile3(fullPath);
|
|
4065
|
+
zipFolder.file(entry.name, content);
|
|
4349
4066
|
}
|
|
4350
4067
|
}
|
|
4351
|
-
return null;
|
|
4352
4068
|
}
|
|
4353
|
-
function
|
|
4354
|
-
console.
|
|
4355
|
-
const
|
|
4356
|
-
|
|
4357
|
-
|
|
4069
|
+
async function zipDirectory(distDir, zipPath) {
|
|
4070
|
+
console.error(`\u538B\u7F29\u76EE\u5F55: ${distDir}`);
|
|
4071
|
+
const zip = new JSZip();
|
|
4072
|
+
await addDirToZip(distDir, zip);
|
|
4073
|
+
const content = await zip.generateAsync({
|
|
4074
|
+
type: "nodebuffer",
|
|
4075
|
+
compression: "DEFLATE",
|
|
4076
|
+
compressionOptions: { level: 6 }
|
|
4358
4077
|
});
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4078
|
+
await writeFile(zipPath, content);
|
|
4079
|
+
const sizeMb = (content.length / 1024 / 1024).toFixed(2);
|
|
4080
|
+
console.error(`\u5DF2\u751F\u6210: ${zipPath} (${sizeMb} MB)`);
|
|
4081
|
+
return content.length;
|
|
4363
4082
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4083
|
+
var SFTP_UPLOAD_MAX_ATTEMPTS = 3;
|
|
4084
|
+
var SFTP_FAST_PUT_OPTIONS = {
|
|
4085
|
+
chunkSize: 64 * 1024,
|
|
4086
|
+
concurrency: 4
|
|
4087
|
+
};
|
|
4088
|
+
function buildSftpConnectOptions(settings) {
|
|
4089
|
+
return {
|
|
4090
|
+
host: settings.host,
|
|
4091
|
+
port: settings.port,
|
|
4092
|
+
username: settings.username,
|
|
4093
|
+
password: settings.password,
|
|
4094
|
+
readyTimeout: 3e4,
|
|
4095
|
+
tryKeyboard: true,
|
|
4096
|
+
keepaliveInterval: 1e4,
|
|
4097
|
+
keepaliveCountMax: 3
|
|
4098
|
+
};
|
|
4372
4099
|
}
|
|
4373
|
-
function
|
|
4374
|
-
|
|
4375
|
-
const versionSuffix = version ? ` ${version}` : "";
|
|
4376
|
-
if (installed2) {
|
|
4377
|
-
console.log(`[apm] \u5168\u5C40 pm2 \u5DF2\u5B89\u88C5${versionSuffix}: ${pm2Bin}`);
|
|
4378
|
-
} else {
|
|
4379
|
-
console.log(`[apm] \u5DF2\u68C0\u6D4B\u5230\u5168\u5C40 pm2${versionSuffix}: ${pm2Bin}`);
|
|
4380
|
-
}
|
|
4100
|
+
async function sleep(ms) {
|
|
4101
|
+
await new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
4381
4102
|
}
|
|
4382
|
-
function
|
|
4383
|
-
|
|
4103
|
+
async function uploadZipWithRetry(settings, localZip, remoteZipPath) {
|
|
4104
|
+
let lastError;
|
|
4105
|
+
for (let attempt = 1; attempt <= SFTP_UPLOAD_MAX_ATTEMPTS; attempt++) {
|
|
4106
|
+
const sftp = new SftpClient();
|
|
4107
|
+
try {
|
|
4108
|
+
if (attempt > 1) {
|
|
4109
|
+
console.error(
|
|
4110
|
+
`SFTP \u4E0A\u4F20\u91CD\u8BD5 (${attempt}/${SFTP_UPLOAD_MAX_ATTEMPTS})...`
|
|
4111
|
+
);
|
|
4112
|
+
}
|
|
4113
|
+
await sftp.connect(buildSftpConnectOptions(settings));
|
|
4114
|
+
await ensureRemoteDir(sftp, settings.remotePath);
|
|
4115
|
+
await sftp.fastPut(localZip, remoteZipPath, SFTP_FAST_PUT_OPTIONS);
|
|
4116
|
+
return sftp;
|
|
4117
|
+
} catch (err) {
|
|
4118
|
+
lastError = err;
|
|
4119
|
+
try {
|
|
4120
|
+
await sftp.end();
|
|
4121
|
+
} catch {
|
|
4122
|
+
}
|
|
4123
|
+
if (attempt < SFTP_UPLOAD_MAX_ATTEMPTS) {
|
|
4124
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
4125
|
+
const delaySec = attempt * 2;
|
|
4126
|
+
console.error(`SFTP \u4E0A\u4F20\u5931\u8D25 (${message})\uFF0C${delaySec}s \u540E\u91CD\u8BD5...`);
|
|
4127
|
+
await sleep(delaySec * 1e3);
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
throw lastError;
|
|
4384
4132
|
}
|
|
4385
|
-
function
|
|
4386
|
-
const
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4133
|
+
async function ensureRemoteDir(sftp, dir) {
|
|
4134
|
+
const parts = dir.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4135
|
+
let current = dir.startsWith("/") ? "" : ".";
|
|
4136
|
+
for (const part of parts) {
|
|
4137
|
+
current = current ? `${current}/${part}` : `/${part}`;
|
|
4138
|
+
try {
|
|
4139
|
+
await sftp.mkdir(current, true);
|
|
4140
|
+
} catch {
|
|
4141
|
+
}
|
|
4393
4142
|
}
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4143
|
+
}
|
|
4144
|
+
function execCommand(client, command) {
|
|
4145
|
+
return new Promise((resolve5, reject) => {
|
|
4146
|
+
client.exec(command, (err, stream) => {
|
|
4147
|
+
if (err) return reject(err);
|
|
4148
|
+
let stdout = "";
|
|
4149
|
+
let stderr = "";
|
|
4150
|
+
stream.on("close", (code) => {
|
|
4151
|
+
if (code !== 0) {
|
|
4152
|
+
reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
|
|
4153
|
+
return;
|
|
4154
|
+
}
|
|
4155
|
+
resolve5(stdout);
|
|
4156
|
+
}).on("data", (data) => {
|
|
4157
|
+
stdout += data.toString();
|
|
4158
|
+
});
|
|
4159
|
+
stream.stderr.on("data", (data) => {
|
|
4160
|
+
stderr += data.toString();
|
|
4161
|
+
});
|
|
4162
|
+
});
|
|
4397
4163
|
});
|
|
4398
|
-
if (update.error || update.status !== 0) {
|
|
4399
|
-
console.error("[apm] pm2 update \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: pm2 update");
|
|
4400
|
-
}
|
|
4401
4164
|
}
|
|
4402
|
-
function
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4165
|
+
function shellSingleQuote(value) {
|
|
4166
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
4167
|
+
}
|
|
4168
|
+
function buildClearRemoteDirExceptZipCommand(target) {
|
|
4169
|
+
const normalized = target.replace(/\\/g, "/").replace(/\/$/, "");
|
|
4170
|
+
const quotedTarget = shellSingleQuote(normalized);
|
|
4171
|
+
const script = [
|
|
4172
|
+
`T=${quotedTarget}`,
|
|
4173
|
+
"S=$(mktemp -d)",
|
|
4174
|
+
'while IFS= read -r -d "" z; do r="${z#${T}/}"',
|
|
4175
|
+
'mkdir -p "${S}/$(dirname "$r")"',
|
|
4176
|
+
'mv "$z" "${S}/${r}"',
|
|
4177
|
+
'done < <(find "$T" -mindepth 1 -type f -iname "*.zip" -print0)',
|
|
4178
|
+
'rm -rf "${T}"/*',
|
|
4179
|
+
'while IFS= read -r -d "" r; do r="${r#./}"',
|
|
4180
|
+
'mkdir -p "${T}/$(dirname "$r")"',
|
|
4181
|
+
'mv "${S}/${r}" "${T}/${r}"',
|
|
4182
|
+
'done < <(cd "$S" 2>/dev/null && find . -type f -print0)',
|
|
4183
|
+
'rm -rf "$S"'
|
|
4184
|
+
].join("; ");
|
|
4185
|
+
return `bash -c ${shellSingleQuote(script)}`;
|
|
4186
|
+
}
|
|
4187
|
+
async function uploadAndMaybeExtract(settings, localZip, extract) {
|
|
4188
|
+
const remoteZipPath = `${settings.remotePath.replace(/\/$/, "")}/dist.zip`;
|
|
4189
|
+
console.error(
|
|
4190
|
+
`\u8FDE\u63A5 ${settings.username}@${settings.host}:${settings.port} ...`
|
|
4191
|
+
);
|
|
4192
|
+
const sftp = await uploadZipWithRetry(settings, localZip, remoteZipPath);
|
|
4193
|
+
try {
|
|
4194
|
+
console.error(` \u2713 ${localZip} -> ${remoteZipPath}`);
|
|
4195
|
+
if (extract) {
|
|
4196
|
+
const target = settings.remotePath.replace(/\/$/, "");
|
|
4197
|
+
const client = sftp.client;
|
|
4198
|
+
const clearCmd = buildClearRemoteDirExceptZipCommand(target);
|
|
4199
|
+
console.error(`\u8FDC\u7A0B\u6E05\u7406\u89E3\u538B\u76EE\u5F55: ${clearCmd}`);
|
|
4200
|
+
await execCommand(client, clearCmd);
|
|
4201
|
+
const unzipCmd = `unzip -o "${remoteZipPath}" -d "${target}"`;
|
|
4202
|
+
console.error(`\u8FDC\u7A0B\u89E3\u538B: ${unzipCmd}`);
|
|
4203
|
+
await execCommand(client, unzipCmd);
|
|
4204
|
+
console.error("\u8FDC\u7A0B\u89E3\u538B\u5B8C\u6210!");
|
|
4408
4205
|
}
|
|
4409
|
-
|
|
4206
|
+
console.error(extract ? "\u90E8\u7F72\u5B8C\u6210!" : "\u4E0A\u4F20\u5B8C\u6210!");
|
|
4207
|
+
} finally {
|
|
4208
|
+
await sftp.end();
|
|
4410
4209
|
}
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
"[apm] \u82E5 pm2 --version \u53EF\u7528\uFF0C\u8BF7\u68C0\u67E5 Node/npm \u5168\u5C40\u5B89\u88C5\u6743\u9650\u6216 PATH"
|
|
4210
|
+
}
|
|
4211
|
+
async function runWisdomSftpDeploy(params) {
|
|
4212
|
+
const zipPath = path2.join(params.localDir, "..", ".deploy-sftp-dist.zip");
|
|
4213
|
+
const resolvedZipPath = path2.resolve(zipPath);
|
|
4214
|
+
const packOnly = Boolean(params.packOnly);
|
|
4215
|
+
let zipSizeBytes = 0;
|
|
4216
|
+
let artifactUploaded = false;
|
|
4217
|
+
try {
|
|
4218
|
+
zipSizeBytes = await zipDirectory(params.localDir, resolvedZipPath);
|
|
4219
|
+
if (!packOnly) {
|
|
4220
|
+
await uploadAndMaybeExtract(
|
|
4221
|
+
params.settings,
|
|
4222
|
+
resolvedZipPath,
|
|
4223
|
+
params.extract
|
|
4426
4224
|
);
|
|
4225
|
+
} else {
|
|
4226
|
+
console.error("[apm] \u4EC5\u6253\u5305\u6A21\u5F0F\uFF1A\u8DF3\u8FC7 SFTP \u8FDC\u7A0B\u4E0A\u4F20");
|
|
4427
4227
|
}
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
shell: useNpmShell2,
|
|
4444
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
4445
|
-
});
|
|
4446
|
-
if (npmResult.status === 0) {
|
|
4447
|
-
const globalRoot = npmResult.stdout?.toString().trim();
|
|
4448
|
-
if (globalRoot) {
|
|
4449
|
-
const candidate = join15(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
|
|
4450
|
-
if (existsSync14(candidate)) {
|
|
4451
|
-
return candidate;
|
|
4452
|
-
}
|
|
4228
|
+
const artifact = await uploadDeployArtifactZip({
|
|
4229
|
+
zipPath: resolvedZipPath,
|
|
4230
|
+
projectName: params.projectName,
|
|
4231
|
+
kind: "frontend",
|
|
4232
|
+
uploadWithoutRunId: packOnly && !resolveDeploymentRunIdFromEnv()
|
|
4233
|
+
});
|
|
4234
|
+
artifactUploaded = artifact !== null;
|
|
4235
|
+
if (packOnly && artifactUploaded) {
|
|
4236
|
+
console.error("\u6253\u5305\u5E76\u4E0A\u4F20 MinIO \u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
|
|
4237
|
+
}
|
|
4238
|
+
} finally {
|
|
4239
|
+
try {
|
|
4240
|
+
await unlink(resolvedZipPath);
|
|
4241
|
+
console.error(`\u5DF2\u6E05\u7406\u672C\u5730\u4E34\u65F6\u6587\u4EF6: ${resolvedZipPath}`);
|
|
4242
|
+
} catch {
|
|
4453
4243
|
}
|
|
4454
4244
|
}
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4245
|
+
return {
|
|
4246
|
+
ok: true,
|
|
4247
|
+
localDir: params.localDir,
|
|
4248
|
+
host: params.settings.host,
|
|
4249
|
+
remotePath: params.settings.remotePath,
|
|
4250
|
+
zipSizeBytes,
|
|
4251
|
+
extracted: packOnly ? false : params.extract,
|
|
4252
|
+
packOnly,
|
|
4253
|
+
artifactUploaded
|
|
4254
|
+
};
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
// src/commands/deploy/internal/wisdom-backend-deploy.ts
|
|
4258
|
+
var SPRINGBOOT_JAVA_OPTS = "-XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -Xms512M -Xmx1G";
|
|
4259
|
+
var MAVEN_MODULE = "jeecg-module-system/jeecg-system-start";
|
|
4260
|
+
var MAVEN_PROFILE = "dev";
|
|
4261
|
+
function log(message) {
|
|
4262
|
+
const now = /* @__PURE__ */ new Date();
|
|
4263
|
+
const hh = String(now.getHours()).padStart(2, "0");
|
|
4264
|
+
const mm = String(now.getMinutes()).padStart(2, "0");
|
|
4265
|
+
const ss = String(now.getSeconds()).padStart(2, "0");
|
|
4266
|
+
console.error(`[${hh}:${mm}:${ss}] ${message}`);
|
|
4267
|
+
}
|
|
4268
|
+
function fail(message) {
|
|
4269
|
+
log(`ERROR: ${message}`);
|
|
4459
4270
|
process.exit(1);
|
|
4460
4271
|
}
|
|
4461
|
-
function
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
}
|
|
4465
|
-
if (process.env.pm_id === void 0) {
|
|
4466
|
-
return false;
|
|
4467
|
-
}
|
|
4468
|
-
const name = process.env.name;
|
|
4469
|
-
return name === PM2_APP_NAME || name !== void 0 && LEGACY_PM2_APP_NAMES.includes(name);
|
|
4272
|
+
function expandPath(pathStr) {
|
|
4273
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
4274
|
+
return path3.resolve(pathStr.replace(/^~(?=\/|\\|$)/, home));
|
|
4470
4275
|
}
|
|
4471
|
-
function
|
|
4472
|
-
if (
|
|
4473
|
-
return
|
|
4276
|
+
function quoteForShell(value) {
|
|
4277
|
+
if (process.platform === "win32") {
|
|
4278
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
4474
4279
|
}
|
|
4475
|
-
return
|
|
4280
|
+
return shellSingleQuote(value);
|
|
4476
4281
|
}
|
|
4477
|
-
function
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
return JSON.parse(raw);
|
|
4481
|
-
} catch {
|
|
4482
|
-
return [];
|
|
4282
|
+
function formatMavenLocalRepoArg(repoPath) {
|
|
4283
|
+
if (process.platform === "win32") {
|
|
4284
|
+
return `-Dmaven.repo.local=${quoteForShell(repoPath)}`;
|
|
4483
4285
|
}
|
|
4286
|
+
return `-Dmaven.repo.local=${repoPath}`;
|
|
4484
4287
|
}
|
|
4485
|
-
function
|
|
4486
|
-
return
|
|
4288
|
+
function deployCacheDir() {
|
|
4289
|
+
return path3.join(workspaceApmDir(), "deploy", ".deploy_cache");
|
|
4487
4290
|
}
|
|
4488
|
-
function
|
|
4489
|
-
return
|
|
4291
|
+
function manifestFilePath() {
|
|
4292
|
+
return path3.join(deployCacheDir(), "manifest.json");
|
|
4490
4293
|
}
|
|
4491
|
-
function
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4294
|
+
function getTargetDir(projectRoot) {
|
|
4295
|
+
return path3.join(projectRoot, MAVEN_MODULE, "target");
|
|
4296
|
+
}
|
|
4297
|
+
function relativeKey(projectRoot, filePath) {
|
|
4298
|
+
return path3.relative(projectRoot, filePath).split(path3.sep).join("/");
|
|
4299
|
+
}
|
|
4300
|
+
function fileSignature(filePath) {
|
|
4301
|
+
const stat2 = statSync6(filePath);
|
|
4302
|
+
return { size: stat2.size, mtime: stat2.mtimeMs / 1e3 };
|
|
4303
|
+
}
|
|
4304
|
+
function loadManifest3() {
|
|
4305
|
+
const manifestPath2 = manifestFilePath();
|
|
4306
|
+
if (!existsSync14(manifestPath2)) {
|
|
4307
|
+
return {};
|
|
4495
4308
|
}
|
|
4496
|
-
|
|
4497
|
-
spawnPm2At(pm2Bin, ["delete", name], {
|
|
4498
|
-
stdio: "ignore"
|
|
4499
|
-
});
|
|
4309
|
+
return JSON.parse(readFileSync12(manifestPath2, "utf8"));
|
|
4500
4310
|
}
|
|
4501
|
-
function
|
|
4502
|
-
const
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4311
|
+
function saveManifest3(manifest) {
|
|
4312
|
+
const dir = deployCacheDir();
|
|
4313
|
+
mkdirSync7(dir, { recursive: true });
|
|
4314
|
+
writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
|
|
4315
|
+
}
|
|
4316
|
+
function isProjectLibJar(jarName) {
|
|
4317
|
+
return jarName.startsWith("jeecg-");
|
|
4318
|
+
}
|
|
4319
|
+
function shouldUploadLibFile(localPath, remoteAttr, manifest, projectRoot) {
|
|
4320
|
+
if (!remoteAttr) {
|
|
4321
|
+
return [false, "\u8FDC\u7A0B\u4E0D\u5B58\u5728\uFF0C\u8DF3\u8FC7"];
|
|
4509
4322
|
}
|
|
4510
|
-
|
|
4511
|
-
|
|
4323
|
+
const localSize = statSync6(localPath).size;
|
|
4324
|
+
const remoteSize = remoteAttr.size;
|
|
4325
|
+
if (localSize !== remoteSize) {
|
|
4326
|
+
return [true, `\u5927\u5C0F\u53D8\u5316 ${remoteSize} -> ${localSize}`];
|
|
4512
4327
|
}
|
|
4513
|
-
|
|
4328
|
+
if (isProjectLibJar(path3.basename(localPath)) && manifest) {
|
|
4329
|
+
const key = relativeKey(projectRoot, localPath);
|
|
4330
|
+
const current = fileSignature(localPath);
|
|
4331
|
+
const previous = manifest[key];
|
|
4332
|
+
if (!previous) {
|
|
4333
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u672A\u8BB0\u5F55"];
|
|
4334
|
+
}
|
|
4335
|
+
if (previous.size !== current.size) {
|
|
4336
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u5927\u5C0F\u53D8\u5316"];
|
|
4337
|
+
}
|
|
4338
|
+
if (previous.mtime < current.mtime) {
|
|
4339
|
+
return [true, "\u9879\u76EE\u6A21\u5757\u91CD\u65B0\u6784\u5EFA"];
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
return [false, "\u5927\u5C0F\u4E00\u81F4\uFF0C\u8DF3\u8FC7"];
|
|
4514
4343
|
}
|
|
4515
|
-
function
|
|
4516
|
-
const
|
|
4517
|
-
const
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
|
|
4344
|
+
function listLibFilesToUpload(localLibDir, remoteStats, projectRoot, manifest = null) {
|
|
4345
|
+
const entries = [];
|
|
4346
|
+
const jarFiles = readdirSync5(localLibDir).filter((name) => name.endsWith(".jar")).sort();
|
|
4347
|
+
for (const jarName of jarFiles) {
|
|
4348
|
+
const jarPath = path3.join(localLibDir, jarName);
|
|
4349
|
+
const remoteAttr = remoteStats.get(jarName);
|
|
4350
|
+
const [shouldUpload, reason] = shouldUploadLibFile(
|
|
4351
|
+
jarPath,
|
|
4352
|
+
remoteAttr,
|
|
4353
|
+
manifest,
|
|
4354
|
+
projectRoot
|
|
4355
|
+
);
|
|
4356
|
+
if (shouldUpload) {
|
|
4357
|
+
entries.push({ path: jarPath, arcname: jarName, reason });
|
|
4358
|
+
}
|
|
4523
4359
|
}
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
process.exit(result.status ?? 1);
|
|
4360
|
+
return entries;
|
|
4361
|
+
}
|
|
4362
|
+
function updateManifestEntries(manifest, entries, projectRoot) {
|
|
4363
|
+
for (const entry of entries) {
|
|
4364
|
+
manifest[relativeKey(projectRoot, entry.path)] = fileSignature(entry.path);
|
|
4530
4365
|
}
|
|
4366
|
+
return manifest;
|
|
4531
4367
|
}
|
|
4532
|
-
function
|
|
4533
|
-
const
|
|
4534
|
-
|
|
4535
|
-
|
|
4368
|
+
async function createUpdatePackage(entries, packageName) {
|
|
4369
|
+
const dir = deployCacheDir();
|
|
4370
|
+
mkdirSync7(dir, { recursive: true });
|
|
4371
|
+
const zipPath = path3.join(dir, packageName);
|
|
4372
|
+
log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path3.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
|
|
4373
|
+
const zip = new JSZip2();
|
|
4374
|
+
for (const entry of entries) {
|
|
4375
|
+
const content = readFileSync12(entry.path);
|
|
4376
|
+
zip.file(entry.arcname, content);
|
|
4377
|
+
log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
|
|
4536
4378
|
}
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4379
|
+
const buffer = await zip.generateAsync({
|
|
4380
|
+
type: "nodebuffer",
|
|
4381
|
+
compression: "DEFLATE",
|
|
4382
|
+
compressionOptions: { level: 6 }
|
|
4540
4383
|
});
|
|
4541
|
-
|
|
4542
|
-
return
|
|
4543
|
-
}
|
|
4544
|
-
function isPm2ConnectOnline() {
|
|
4545
|
-
const app = findPm2ConnectProcess();
|
|
4546
|
-
if (!app) return false;
|
|
4547
|
-
const status = app.pm2_env?.status;
|
|
4548
|
-
return status === "online" || status === "launching";
|
|
4384
|
+
writeFileSync12(zipPath, buffer);
|
|
4385
|
+
return zipPath;
|
|
4549
4386
|
}
|
|
4550
|
-
function
|
|
4551
|
-
|
|
4552
|
-
|
|
4387
|
+
function getMvnExecutable() {
|
|
4388
|
+
const isWin = process.platform === "win32";
|
|
4389
|
+
const candidates = isWin ? ["mvn.cmd", "mvn.bat", "mvn"] : ["mvn"];
|
|
4390
|
+
for (const name of candidates) {
|
|
4391
|
+
const result = spawnSync5(isWin ? `where ${name}` : `which ${name}`, {
|
|
4392
|
+
encoding: "utf8",
|
|
4393
|
+
shell: true
|
|
4394
|
+
});
|
|
4395
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
4396
|
+
return result.stdout.trim().split(/\r?\n/)[0].trim();
|
|
4397
|
+
}
|
|
4398
|
+
}
|
|
4399
|
+
fail("\u672A\u627E\u5230 mvn \u547D\u4EE4\uFF0C\u8BF7\u786E\u8BA4 Maven \u5DF2\u5B89\u88C5\u5E76\u52A0\u5165 PATH");
|
|
4553
4400
|
}
|
|
4554
|
-
function
|
|
4555
|
-
|
|
4556
|
-
const
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4401
|
+
function runMavenBuild(projectRoot, mavenLocalRepo, repoSource) {
|
|
4402
|
+
const mavenRepo = expandPath(mavenLocalRepo);
|
|
4403
|
+
const mvn = getMvnExecutable();
|
|
4404
|
+
const command = [
|
|
4405
|
+
quoteForShell(mvn),
|
|
4406
|
+
"clean",
|
|
4407
|
+
"package",
|
|
4408
|
+
`-P${MAVEN_PROFILE}`,
|
|
4409
|
+
formatMavenLocalRepoArg(mavenRepo),
|
|
4410
|
+
"-DskipTests"
|
|
4411
|
+
].join(" ");
|
|
4412
|
+
log("\u5F00\u59CB Maven \u6784\u5EFA...");
|
|
4413
|
+
if (repoSource) {
|
|
4414
|
+
log(
|
|
4415
|
+
`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo} (\u6765\u6E90: ${repoSource.source}/${repoSource.sourceDetail})`
|
|
4560
4416
|
);
|
|
4561
|
-
|
|
4417
|
+
} else {
|
|
4418
|
+
log(`Maven \u672C\u5730\u4ED3\u5E93: ${mavenRepo}`);
|
|
4562
4419
|
}
|
|
4563
|
-
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4420
|
+
log(`\u6784\u5EFA\u76EE\u5F55: ${projectRoot}`);
|
|
4421
|
+
const result = spawnSync5(command, {
|
|
4422
|
+
cwd: projectRoot,
|
|
4423
|
+
stdio: "inherit",
|
|
4424
|
+
shell: true,
|
|
4425
|
+
env: process.env
|
|
4426
|
+
});
|
|
4427
|
+
if (result.status !== 0) {
|
|
4428
|
+
fail(`Maven \u6784\u5EFA\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${result.status ?? 1}`);
|
|
4568
4429
|
}
|
|
4569
4430
|
}
|
|
4570
|
-
function
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
}
|
|
4431
|
+
function locateLibDir(projectRoot) {
|
|
4432
|
+
const targetDir = getTargetDir(projectRoot);
|
|
4433
|
+
if (!existsSync14(targetDir)) {
|
|
4434
|
+
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4435
|
+
}
|
|
4436
|
+
const libDir = path3.join(targetDir, "lib");
|
|
4437
|
+
if (!existsSync14(libDir) || !statSync6(libDir).isDirectory()) {
|
|
4438
|
+
fail(`lib \u76EE\u5F55\u4E0D\u5B58\u5728: ${libDir}`);
|
|
4439
|
+
}
|
|
4440
|
+
const libJars = readdirSync5(libDir).filter((name) => name.endsWith(".jar"));
|
|
4441
|
+
if (libJars.length === 0) {
|
|
4442
|
+
fail(`lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u4F9D\u8D56 JAR: ${libDir}`);
|
|
4443
|
+
}
|
|
4444
|
+
log(`\u5B9A\u4F4D lib \u4EA7\u7269: ${libJars.length} \u4E2A`);
|
|
4445
|
+
return libDir;
|
|
4575
4446
|
}
|
|
4576
|
-
function
|
|
4577
|
-
const
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
if (server) {
|
|
4581
|
-
args.push("--server", server);
|
|
4447
|
+
function locateMainJar(projectRoot) {
|
|
4448
|
+
const targetDir = getTargetDir(projectRoot);
|
|
4449
|
+
if (!existsSync14(targetDir)) {
|
|
4450
|
+
fail(`\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\u4E0D\u5B58\u5728: ${targetDir}`);
|
|
4582
4451
|
}
|
|
4583
|
-
const
|
|
4584
|
-
if (
|
|
4585
|
-
|
|
4586
|
-
process.exit(1);
|
|
4452
|
+
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);
|
|
4453
|
+
if (jarFiles.length === 0) {
|
|
4454
|
+
fail(`target \u76EE\u5F55\u4E0B\u6CA1\u6709\u4E3B JAR: ${targetDir}`);
|
|
4587
4455
|
}
|
|
4588
|
-
|
|
4456
|
+
const mainJar = jarFiles[0];
|
|
4457
|
+
log(`\u5B9A\u4F4D\u4E3B JAR \u4EA7\u7269: ${path3.basename(mainJar)}`);
|
|
4458
|
+
return mainJar;
|
|
4589
4459
|
}
|
|
4590
|
-
async function
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4460
|
+
async function connectSsh(config) {
|
|
4461
|
+
const client = new Client2();
|
|
4462
|
+
log(`\u8FDE\u63A5\u670D\u52A1\u5668 ${config.username}@${config.host}:${config.port}`);
|
|
4463
|
+
await new Promise((resolve5, reject) => {
|
|
4464
|
+
client.on("ready", () => resolve5()).on("error", (err) => reject(err)).connect({
|
|
4465
|
+
host: config.host,
|
|
4466
|
+
port: config.port,
|
|
4467
|
+
username: config.username,
|
|
4468
|
+
password: config.password,
|
|
4469
|
+
readyTimeout: 3e4,
|
|
4470
|
+
tryKeyboard: true
|
|
4471
|
+
});
|
|
4472
|
+
});
|
|
4473
|
+
const sftp = new SftpClient2();
|
|
4474
|
+
await sftp.connect({
|
|
4475
|
+
host: config.host,
|
|
4476
|
+
port: config.port,
|
|
4477
|
+
username: config.username,
|
|
4478
|
+
password: config.password,
|
|
4479
|
+
readyTimeout: 3e4,
|
|
4480
|
+
tryKeyboard: true
|
|
4481
|
+
});
|
|
4482
|
+
return { client, sftp };
|
|
4483
|
+
}
|
|
4484
|
+
async function closeSsh(conn) {
|
|
4485
|
+
try {
|
|
4486
|
+
await conn.sftp.end();
|
|
4487
|
+
} catch {
|
|
4597
4488
|
}
|
|
4598
|
-
|
|
4489
|
+
conn.client.end();
|
|
4599
4490
|
}
|
|
4600
|
-
async function
|
|
4601
|
-
|
|
4602
|
-
|
|
4491
|
+
async function getRemoteFileStats(sftp, remoteDir) {
|
|
4492
|
+
const stats = /* @__PURE__ */ new Map();
|
|
4493
|
+
try {
|
|
4494
|
+
const listing = await sftp.list(remoteDir);
|
|
4495
|
+
for (const item of listing) {
|
|
4496
|
+
if (item.name.endsWith(".jar")) {
|
|
4497
|
+
stats.set(item.name, { filename: item.name, size: item.size });
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
} catch {
|
|
4603
4501
|
}
|
|
4604
|
-
|
|
4605
|
-
return cfg?.baseUrl;
|
|
4502
|
+
return stats;
|
|
4606
4503
|
}
|
|
4607
|
-
function
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4504
|
+
async function uploadUpdatePackage(sftp, zipPath, config) {
|
|
4505
|
+
const remoteDir = config.remoteVueDistDir.replace(/\/$/, "");
|
|
4506
|
+
const remotePath = `${remoteDir}/${path3.basename(zipPath)}`;
|
|
4507
|
+
log(`\u4E0A\u4F20\u66F4\u65B0\u5305 -> ${remotePath}`);
|
|
4508
|
+
try {
|
|
4509
|
+
await sftp.fastPut(zipPath, remotePath);
|
|
4510
|
+
log("\u66F4\u65B0\u5305\u4E0A\u4F20\u6210\u529F");
|
|
4511
|
+
} catch (err) {
|
|
4512
|
+
fail(`\u66F4\u65B0\u5305\u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
4616
4513
|
}
|
|
4617
|
-
|
|
4618
|
-
nodePath: options.nodePath,
|
|
4619
|
-
apmScript: options.apmScript,
|
|
4620
|
-
connectArgs: options.connectArgs,
|
|
4621
|
-
cwd: options.cwd,
|
|
4622
|
-
env
|
|
4623
|
-
};
|
|
4624
|
-
writeFileSync12(
|
|
4625
|
-
PM2_CONNECT_LAUNCH_PATH,
|
|
4626
|
-
JSON.stringify(launch, null, 2) + "\n",
|
|
4627
|
-
"utf8"
|
|
4628
|
-
);
|
|
4514
|
+
return remotePath;
|
|
4629
4515
|
}
|
|
4630
|
-
function
|
|
4631
|
-
|
|
4632
|
-
const
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
);
|
|
4641
|
-
if (existsSync14(LEGACY_PM2_ECOSYSTEM_PATH)) {
|
|
4642
|
-
try {
|
|
4643
|
-
unlinkSync2(LEGACY_PM2_ECOSYSTEM_PATH);
|
|
4644
|
-
} catch {
|
|
4645
|
-
}
|
|
4516
|
+
async function uploadFullJar(sftp, localJarPath, config) {
|
|
4517
|
+
const remoteDir = config.remoteAppDir.replace(/\/$/, "");
|
|
4518
|
+
const remotePath = `${remoteDir}/${config.startupJar}`;
|
|
4519
|
+
log(`\u4E0A\u4F20\u5168\u91CF JAR (${path3.basename(localJarPath)}) -> ${remotePath}`);
|
|
4520
|
+
try {
|
|
4521
|
+
await sftp.mkdir(remoteDir, true);
|
|
4522
|
+
await sftp.fastPut(localJarPath, remotePath);
|
|
4523
|
+
log("\u5168\u91CF JAR \u4E0A\u4F20\u6210\u529F");
|
|
4524
|
+
} catch (err) {
|
|
4525
|
+
fail(`\u5168\u91CF JAR \u4E0A\u4F20\u5931\u8D25: ${err instanceof Error ? err.message : err}`);
|
|
4646
4526
|
}
|
|
4647
4527
|
}
|
|
4648
|
-
async function
|
|
4649
|
-
|
|
4650
|
-
const
|
|
4651
|
-
const
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4528
|
+
async function runRemoteCommand(client, command, options) {
|
|
4529
|
+
const check = options?.check ?? true;
|
|
4530
|
+
const stream = options?.stream ?? false;
|
|
4531
|
+
const label = options?.label ?? "\u8FDC\u7A0B\u547D\u4EE4";
|
|
4532
|
+
return new Promise((resolve5, reject) => {
|
|
4533
|
+
client.exec(command, (err, execStream) => {
|
|
4534
|
+
if (err) {
|
|
4535
|
+
reject(err);
|
|
4536
|
+
return;
|
|
4537
|
+
}
|
|
4538
|
+
let out = "";
|
|
4539
|
+
let errText = "";
|
|
4540
|
+
execStream.on("data", (data) => {
|
|
4541
|
+
const text = data.toString();
|
|
4542
|
+
out += text;
|
|
4543
|
+
if (stream) {
|
|
4544
|
+
process.stdout.write(text);
|
|
4545
|
+
}
|
|
4546
|
+
});
|
|
4547
|
+
execStream.stderr.on("data", (data) => {
|
|
4548
|
+
errText += data.toString();
|
|
4549
|
+
});
|
|
4550
|
+
execStream.on("close", (code) => {
|
|
4551
|
+
if (stream && out && !out.endsWith("\n")) {
|
|
4552
|
+
process.stdout.write("\n");
|
|
4553
|
+
}
|
|
4554
|
+
if (check && code !== 0) {
|
|
4555
|
+
const combined = `${out}
|
|
4556
|
+
${errText}`.trim();
|
|
4557
|
+
fail(
|
|
4558
|
+
`${label}\u5931\u8D25 (exit ${code})` + (combined ? `
|
|
4559
|
+
\u8F93\u51FA: ${combined}` : "")
|
|
4560
|
+
);
|
|
4561
|
+
}
|
|
4562
|
+
resolve5({ exitCode: code, out: out.trim(), err: errText.trim() });
|
|
4563
|
+
});
|
|
4564
|
+
});
|
|
4660
4565
|
});
|
|
4661
|
-
return cfg;
|
|
4662
4566
|
}
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
}
|
|
4686
|
-
console.log("[apm] \u67E5\u770B\u72B6\u6001: apm daemon status");
|
|
4687
|
-
console.log("[apm] \u67E5\u770B\u65E5\u5FD7: apm daemon logs -f");
|
|
4688
|
-
console.log("[apm] \u505C\u6B62: apm daemon stop");
|
|
4567
|
+
function buildExtractUpdatePackageScript(remoteZipPath, remoteLibDir) {
|
|
4568
|
+
const quotedZip = shellSingleQuote(remoteZipPath);
|
|
4569
|
+
const quotedLib = shellSingleQuote(remoteLibDir);
|
|
4570
|
+
return `
|
|
4571
|
+
set -e
|
|
4572
|
+
TMP=$(mktemp -d)
|
|
4573
|
+
trap 'rm -rf "$TMP"' EXIT
|
|
4574
|
+
unzip -oq ${quotedZip} -d "$TMP"
|
|
4575
|
+
updated=0
|
|
4576
|
+
while IFS= read -r -d '' src; do
|
|
4577
|
+
name=$(basename "$src")
|
|
4578
|
+
dest=${quotedLib}/"$name"
|
|
4579
|
+
if [ -f "$dest" ]; then
|
|
4580
|
+
cp -f "$src" "$dest"
|
|
4581
|
+
echo "\u8986\u76D6: $name"
|
|
4582
|
+
updated=$((updated + 1))
|
|
4583
|
+
else
|
|
4584
|
+
echo "\u8DF3\u8FC7(\u8FDC\u7A0B\u4E0D\u5B58\u5728): $name"
|
|
4585
|
+
fi
|
|
4586
|
+
done < <(find "$TMP" -name '*.jar' -type f -print0)
|
|
4587
|
+
echo "UPDATED_COUNT=$updated"
|
|
4588
|
+
`.trim();
|
|
4689
4589
|
}
|
|
4690
|
-
async function
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
|
|
4696
|
-
|
|
4697
|
-
}
|
|
4698
|
-
runPm2(["stop", resolvePm2ConnectName(findPm2ConnectProcess())], {
|
|
4699
|
-
inherit: true
|
|
4590
|
+
async function extractUpdatePackageOnRemote(client, config, remoteZipPath) {
|
|
4591
|
+
const script = buildExtractUpdatePackageScript(
|
|
4592
|
+
remoteZipPath,
|
|
4593
|
+
config.remoteLibDir
|
|
4594
|
+
);
|
|
4595
|
+
const { out } = await runRemoteCommand(client, script, {
|
|
4596
|
+
label: "\u8FDC\u7A0B\u89E3\u538B"
|
|
4700
4597
|
});
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
if (isPm2ConnectOnline()) {
|
|
4706
|
-
console.log(`[apm] \u4F18\u96C5\u505C\u6B62\u8D85\u65F6\uFF0C\u5F3A\u5236\u79FB\u9664 ${PM2_APP_NAME}\u2026`);
|
|
4707
|
-
runPm2(["delete", resolvePm2ConnectName(findPm2ConnectProcess())], {
|
|
4708
|
-
inherit: true
|
|
4709
|
-
});
|
|
4598
|
+
const match = out.match(/UPDATED_COUNT=(\d+)/);
|
|
4599
|
+
if (!match) {
|
|
4600
|
+
fail(`\u8FDC\u7A0B\u89E3\u538B\u5931\u8D25\uFF0C\u672A\u83B7\u53D6\u66F4\u65B0\u6570\u91CF
|
|
4601
|
+
\u8F93\u51FA: ${out || "(\u7A7A)"}`);
|
|
4710
4602
|
}
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4603
|
+
const updated = Number.parseInt(match[1], 10);
|
|
4604
|
+
log(`lib \u89E3\u538B\u5B8C\u6210: \u8986\u76D6 ${updated} \u4E2A`);
|
|
4605
|
+
return updated;
|
|
4714
4606
|
}
|
|
4715
|
-
function
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
try {
|
|
4720
|
-
process.kill(lock.pid, "SIGTERM");
|
|
4721
|
-
} catch {
|
|
4722
|
-
forceReleaseConnectLock();
|
|
4723
|
-
return;
|
|
4607
|
+
function springbootOutputIndicatesSuccess(action, combined) {
|
|
4608
|
+
const lower = combined.toLowerCase();
|
|
4609
|
+
if (action === "health") {
|
|
4610
|
+
return combined.includes("\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7");
|
|
4724
4611
|
}
|
|
4725
|
-
if (
|
|
4726
|
-
|
|
4727
|
-
process.kill(lock.pid, "SIGKILL");
|
|
4728
|
-
} catch {
|
|
4729
|
-
}
|
|
4612
|
+
if (action === "start" || action === "restart") {
|
|
4613
|
+
return combined.includes("is starting") || lower.includes("is running");
|
|
4730
4614
|
}
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
async function runDaemonRestart(options) {
|
|
4734
|
-
pruneStaleConnectLock();
|
|
4735
|
-
const lock = readConnectLock();
|
|
4736
|
-
if (lock && isProcessAlive(lock.pid) && !isPm2ConnectOnline()) {
|
|
4737
|
-
console.error(
|
|
4738
|
-
`[apm] \u5DF2\u6709\u524D\u53F0 apm connect \u5728\u8FD0\u884C (pid=${lock.pid})\uFF0C\u8BF7\u5148\u505C\u6B62\u540E\u518D\u542F\u52A8\u5B88\u62A4\u8FDB\u7A0B`
|
|
4739
|
-
);
|
|
4740
|
-
process.exit(1);
|
|
4615
|
+
if (action === "stop") {
|
|
4616
|
+
return combined.includes("is stopping") || lower.includes("not running") || lower.includes("please check it");
|
|
4741
4617
|
}
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
removePm2ConnectProcesses();
|
|
4745
|
-
runPm2(["start", PM2_ECOSYSTEM_PATH, "--update-env"], { inherit: true });
|
|
4746
|
-
console.log(`[apm] ${PM2_APP_NAME} \u5DF2\u91CD\u542F`);
|
|
4747
|
-
}
|
|
4748
|
-
async function runDaemonDelete() {
|
|
4749
|
-
ensureGlobalPm2();
|
|
4750
|
-
const app = findPm2ConnectProcess();
|
|
4751
|
-
if (!app?.name) {
|
|
4752
|
-
console.log(`[apm] ${PM2_APP_NAME} \u672A\u5728 PM2 \u4E2D\u6CE8\u518C`);
|
|
4753
|
-
return;
|
|
4618
|
+
if (action === "status") {
|
|
4619
|
+
return lower.includes("running") || lower.includes("not running");
|
|
4754
4620
|
}
|
|
4755
|
-
|
|
4756
|
-
forceReleaseConnectLock();
|
|
4757
|
-
console.log(`[apm] ${app.name} \u5DF2\u4ECE PM2 \u79FB\u9664`);
|
|
4621
|
+
return true;
|
|
4758
4622
|
}
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
const
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4623
|
+
function buildRemoteStatusScript(remoteAppDir, appName) {
|
|
4624
|
+
const dir = shellSingleQuote(remoteAppDir);
|
|
4625
|
+
const jar = shellSingleQuote(appName);
|
|
4626
|
+
return `
|
|
4627
|
+
set -e
|
|
4628
|
+
cd ${dir}
|
|
4629
|
+
appName=${jar}
|
|
4630
|
+
appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
|
|
4631
|
+
if [ -z "$appIds" ]; then
|
|
4632
|
+
echo -e "\\033[31m Not running \\033[0m"
|
|
4633
|
+
else
|
|
4634
|
+
echo -e "\\033[32m Running [$appIds] \\033[0m"
|
|
4635
|
+
fi
|
|
4636
|
+
`.trim();
|
|
4767
4637
|
}
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
const
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4638
|
+
function buildRemoteRestartScript(remoteAppDir) {
|
|
4639
|
+
const dir = shellSingleQuote(remoteAppDir);
|
|
4640
|
+
const javaOpts = shellSingleQuote(SPRINGBOOT_JAVA_OPTS);
|
|
4641
|
+
return `
|
|
4642
|
+
set -e
|
|
4643
|
+
cd ${dir}
|
|
4644
|
+
releaseApp=$(ls -t | grep '.jar$' | head -n1)
|
|
4645
|
+
lastVersionApp=$(ls -t | grep '.jar$' | head -n2 | tail -n1)
|
|
4646
|
+
appName=$lastVersionApp
|
|
4647
|
+
appIds=$(ps -ef | grep java | grep "$appName" | awk '{print $2}')
|
|
4648
|
+
if [ -z "$appIds" ]; then
|
|
4649
|
+
echo "Maybe $appName not running, please check it..."
|
|
4650
|
+
else
|
|
4651
|
+
echo "The $appName is stopping..."
|
|
4652
|
+
echo "$appIds" | xargs kill
|
|
4653
|
+
fi
|
|
4654
|
+
for i in $(seq 15 -1 1); do
|
|
4655
|
+
echo -n "$i "
|
|
4656
|
+
sleep 1
|
|
4657
|
+
done
|
|
4658
|
+
echo 0
|
|
4659
|
+
if [ ! -d "backup" ]; then
|
|
4660
|
+
mkdir backup
|
|
4661
|
+
fi
|
|
4662
|
+
for i in $(ls | grep '.jar$' | grep -vFx "$releaseApp"); do
|
|
4663
|
+
echo "backup $i"
|
|
4664
|
+
mv "$i" backup/
|
|
4665
|
+
done
|
|
4666
|
+
appName=$releaseApp
|
|
4667
|
+
count=$(ps -ef | grep java | grep "$appName" | wc -l)
|
|
4668
|
+
if [ "$count" != "0" ]; then
|
|
4669
|
+
echo "Maybe $appName is running, please check it..."
|
|
4670
|
+
else
|
|
4671
|
+
echo "The $appName is starting..."
|
|
4672
|
+
nohup java -jar "./$appName" ${javaOpts} > nohup.out 2>&1 &
|
|
4673
|
+
fi
|
|
4674
|
+
`.trim();
|
|
4675
|
+
}
|
|
4676
|
+
function normalizeHealthContext(context) {
|
|
4677
|
+
let normalized = context.trim() || "/";
|
|
4678
|
+
if (!normalized.startsWith("/")) {
|
|
4679
|
+
normalized = `/${normalized}`;
|
|
4774
4680
|
}
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
args.push("--nostream");
|
|
4681
|
+
if (!normalized.endsWith("/")) {
|
|
4682
|
+
normalized = `${normalized}/`;
|
|
4778
4683
|
}
|
|
4779
|
-
|
|
4684
|
+
return normalized;
|
|
4780
4685
|
}
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4686
|
+
function buildRemoteHealthScript(port, context, timeoutSecs) {
|
|
4687
|
+
const normalizedContext = normalizeHealthContext(context);
|
|
4688
|
+
const portStr = String(port);
|
|
4689
|
+
const timeoutStr = String(timeoutSecs);
|
|
4690
|
+
return `
|
|
4691
|
+
set -e
|
|
4692
|
+
port=${shellSingleQuote(portStr)}
|
|
4693
|
+
context=${shellSingleQuote(normalizedContext)}
|
|
4694
|
+
timeout=${shellSingleQuote(timeoutStr)}
|
|
4695
|
+
check_url="http://127.0.0.1:${portStr}${normalizedContext}"
|
|
4696
|
+
echo "\u5065\u5EB7\u68C0\u67E5: \${check_url} (\u8D85\u65F6 \${timeout}s)"
|
|
4697
|
+
deadline=$(($(date +%s) + timeout))
|
|
4698
|
+
attempt=0
|
|
4699
|
+
while [ $(date +%s) -lt $deadline ]; do
|
|
4700
|
+
attempt=$((attempt + 1))
|
|
4701
|
+
code=$(curl -s -o /dev/null -w "%{http_code}" "$check_url" 2>/dev/null || echo "000")
|
|
4702
|
+
code=$(echo "$code" | tail -n1 | tr -d '[:space:]')
|
|
4703
|
+
if [ \${#code} -ge 3 ]; then
|
|
4704
|
+
status=\${code:0:3}
|
|
4705
|
+
if echo "$status" | grep -qE '^[0-9]+$' && [ "$status" -ge 200 ] && [ "$status" -lt 500 ]; then
|
|
4706
|
+
echo -e "\\033[32m\u5065\u5EB7\u68C0\u67E5\u901A\u8FC7 (HTTP \${status})\\033[0m"
|
|
4707
|
+
exit 0
|
|
4708
|
+
fi
|
|
4709
|
+
fi
|
|
4710
|
+
remaining=$((deadline - $(date +%s)))
|
|
4711
|
+
if [ $remaining -lt 0 ]; then
|
|
4712
|
+
remaining=0
|
|
4713
|
+
fi
|
|
4714
|
+
echo "\u7B49\u5F85\u670D\u52A1\u542F\u52A8... \u7B2C \${attempt} \u6B21\uFF0C\u5269\u4F59 \${remaining}s"
|
|
4715
|
+
sleep 5
|
|
4716
|
+
done
|
|
4717
|
+
echo -e "\\033[31m\u5065\u5EB7\u68C0\u67E5\u8D85\u65F6 (\${timeout}s): \${check_url}\\033[0m"
|
|
4718
|
+
exit 1
|
|
4719
|
+
`.trim();
|
|
4720
|
+
}
|
|
4721
|
+
async function runRemoteServiceScript(client, script, action) {
|
|
4722
|
+
const { exitCode, out, err } = await runRemoteCommand(client, script, {
|
|
4723
|
+
check: false,
|
|
4724
|
+
label: action === "status" ? "\u8FDC\u7A0B status" : action === "restart" ? "\u8FDC\u7A0B restart" : "\u5065\u5EB7\u68C0\u67E5"
|
|
4790
4725
|
});
|
|
4791
|
-
|
|
4792
|
-
|
|
4726
|
+
const combined = `${out}
|
|
4727
|
+
${err}`.trim();
|
|
4728
|
+
const outputOk = springbootOutputIndicatesSuccess(action, combined);
|
|
4729
|
+
if (action === "health") {
|
|
4730
|
+
if (exitCode !== 0 || !outputOk) {
|
|
4731
|
+
fail(`\u5065\u5EB7\u68C0\u67E5\u5931\u8D25
|
|
4732
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`);
|
|
4733
|
+
}
|
|
4734
|
+
return combined;
|
|
4793
4735
|
}
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
}
|
|
4797
|
-
function prependPath(pathValue, segment) {
|
|
4798
|
-
if (!segment) {
|
|
4799
|
-
return pathValue ?? "";
|
|
4736
|
+
if (exitCode !== 0 && !outputOk) {
|
|
4737
|
+
fail(`\u8FDC\u7A0B ${action} \u5931\u8D25
|
|
4738
|
+
${combined}`);
|
|
4800
4739
|
}
|
|
4801
|
-
if (!
|
|
4802
|
-
|
|
4740
|
+
if (action === "restart" && !outputOk) {
|
|
4741
|
+
fail(`\u8FDC\u7A0B restart \u672A\u6210\u529F
|
|
4742
|
+
\u8F93\u51FA: ${combined || "(\u7A7A)"}`);
|
|
4743
|
+
}
|
|
4744
|
+
return combined;
|
|
4745
|
+
}
|
|
4746
|
+
function stripAnsi(text) {
|
|
4747
|
+
return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, "");
|
|
4748
|
+
}
|
|
4749
|
+
async function getRunningJar(client, config) {
|
|
4750
|
+
const script = buildRemoteStatusScript(
|
|
4751
|
+
config.remoteAppDir,
|
|
4752
|
+
config.startupJar
|
|
4753
|
+
);
|
|
4754
|
+
const combined = await runRemoteServiceScript(client, script, "status");
|
|
4755
|
+
const text = stripAnsi(combined).trim().toLowerCase();
|
|
4756
|
+
if (text.includes("not running")) {
|
|
4757
|
+
return null;
|
|
4803
4758
|
}
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
return pathValue;
|
|
4759
|
+
if (text.includes("running")) {
|
|
4760
|
+
return config.startupJar;
|
|
4807
4761
|
}
|
|
4808
|
-
return
|
|
4762
|
+
return null;
|
|
4809
4763
|
}
|
|
4810
|
-
function
|
|
4811
|
-
|
|
4812
|
-
const
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4764
|
+
async function healthCheckService(client, config) {
|
|
4765
|
+
log("\u5065\u5EB7\u68C0\u67E5...");
|
|
4766
|
+
const script = buildRemoteHealthScript(
|
|
4767
|
+
config.healthCheckPort,
|
|
4768
|
+
config.healthCheckContext,
|
|
4769
|
+
config.healthCheckTimeout
|
|
4770
|
+
);
|
|
4771
|
+
await runRemoteServiceScript(client, script, "health");
|
|
4772
|
+
}
|
|
4773
|
+
async function restartRemoteService(client, config) {
|
|
4774
|
+
const script = buildRemoteRestartScript(config.remoteAppDir);
|
|
4775
|
+
await runRemoteServiceScript(client, script, "restart");
|
|
4776
|
+
}
|
|
4777
|
+
function listAllLibFilesForArchive(libDir) {
|
|
4778
|
+
return readdirSync5(libDir).filter((name) => name.endsWith(".jar")).sort().map((jarName) => ({
|
|
4779
|
+
path: path3.join(libDir, jarName),
|
|
4780
|
+
arcname: jarName,
|
|
4781
|
+
reason: "\u4EC5\u6253\u5305\u5F52\u6863"
|
|
4782
|
+
}));
|
|
4783
|
+
}
|
|
4784
|
+
async function runWisdomBackendDeploy(options) {
|
|
4785
|
+
const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
|
|
4786
|
+
const config = options.config;
|
|
4787
|
+
const packOnly = Boolean(options.packOnly);
|
|
4788
|
+
log(`=== \u81EA\u52A8\u90E8\u7F72: ${config.projectName} ===`);
|
|
4789
|
+
log(`\u914D\u7F6E\u6587\u4EF6: ${path3.join(workspaceApmDir(), "apm.config.json")}`);
|
|
4790
|
+
log(`\u9879\u76EE\u6839\u76EE\u5F55: ${projectRoot}`);
|
|
4791
|
+
log(
|
|
4792
|
+
`\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"}${packOnly ? "\uFF1B\u4EC5\u6253\u5305\uFF08\u4E0D\u4E0A\u4F20\u8FDC\u7A0B\uFF09" : ""}`
|
|
4793
|
+
);
|
|
4794
|
+
runMavenBuild(projectRoot, config.mavenLocalRepo, {
|
|
4795
|
+
source: config.mavenLocalRepoSource,
|
|
4796
|
+
sourceDetail: config.mavenLocalRepoSourceDetail
|
|
4797
|
+
});
|
|
4798
|
+
if (packOnly) {
|
|
4799
|
+
const uploadWithoutRunId = !resolveDeploymentRunIdFromEnv();
|
|
4800
|
+
if (config.mode === "full") {
|
|
4801
|
+
const mainJar = locateMainJar(projectRoot);
|
|
4802
|
+
const archiveZipPath = await createUpdatePackage(
|
|
4803
|
+
[
|
|
4804
|
+
{
|
|
4805
|
+
path: mainJar,
|
|
4806
|
+
arcname: path3.basename(mainJar),
|
|
4807
|
+
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
4808
|
+
}
|
|
4809
|
+
],
|
|
4810
|
+
`.deploy-archive-${config.projectName}.jar.zip`
|
|
4811
|
+
);
|
|
4812
|
+
await uploadDeployArtifactZip({
|
|
4813
|
+
zipPath: archiveZipPath,
|
|
4814
|
+
projectName: config.projectName,
|
|
4815
|
+
kind: "backend",
|
|
4816
|
+
uploadWithoutRunId
|
|
4817
|
+
});
|
|
4818
|
+
} else {
|
|
4819
|
+
const libDir = locateLibDir(projectRoot);
|
|
4820
|
+
const entries = listAllLibFilesForArchive(libDir);
|
|
4821
|
+
if (entries.length === 0) {
|
|
4822
|
+
fail("lib \u76EE\u5F55\u4E0B\u6CA1\u6709\u53EF\u5F52\u6863\u7684 JAR");
|
|
4823
|
+
}
|
|
4824
|
+
const zipPath = await createUpdatePackage(entries, config.packageName);
|
|
4825
|
+
await uploadDeployArtifactZip({
|
|
4826
|
+
zipPath,
|
|
4827
|
+
projectName: config.projectName,
|
|
4828
|
+
kind: "backend",
|
|
4829
|
+
uploadWithoutRunId
|
|
4830
|
+
});
|
|
4831
|
+
}
|
|
4832
|
+
log("\u4EC5\u6253\u5305\u5B8C\u6210\uFF08\u672A\u4E0A\u4F20\u8FDC\u7A0B\u670D\u52A1\u5668\uFF09");
|
|
4833
|
+
return;
|
|
4816
4834
|
}
|
|
4835
|
+
const conn = await connectSsh(config);
|
|
4817
4836
|
try {
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4837
|
+
if (config.mode === "full") {
|
|
4838
|
+
const mainJar = locateMainJar(projectRoot);
|
|
4839
|
+
await uploadFullJar(conn.sftp, mainJar, config);
|
|
4840
|
+
const archiveZipPath = await createUpdatePackage(
|
|
4841
|
+
[
|
|
4842
|
+
{
|
|
4843
|
+
path: mainJar,
|
|
4844
|
+
arcname: path3.basename(mainJar),
|
|
4845
|
+
reason: "\u5168\u91CF JAR \u5F52\u6863"
|
|
4846
|
+
}
|
|
4847
|
+
],
|
|
4848
|
+
`.deploy-archive-${config.projectName}.jar.zip`
|
|
4849
|
+
);
|
|
4850
|
+
await uploadDeployArtifactZip({
|
|
4851
|
+
zipPath: archiveZipPath,
|
|
4852
|
+
projectName: config.projectName,
|
|
4853
|
+
kind: "backend"
|
|
4854
|
+
});
|
|
4855
|
+
log("\u91CD\u542F\u670D\u52A1...");
|
|
4856
|
+
await restartRemoteService(conn.client, config);
|
|
4857
|
+
await healthCheckService(conn.client, config);
|
|
4858
|
+
log("\u90E8\u7F72\u5B8C\u6210");
|
|
4859
|
+
return;
|
|
4860
|
+
}
|
|
4861
|
+
const libDir = locateLibDir(projectRoot);
|
|
4862
|
+
let manifest = loadManifest3();
|
|
4863
|
+
const remoteLibStats = await getRemoteFileStats(
|
|
4864
|
+
conn.sftp,
|
|
4865
|
+
config.remoteLibDir
|
|
4866
|
+
);
|
|
4867
|
+
log("\u6536\u96C6 JAR \u66F4\u65B0...");
|
|
4868
|
+
const libUploadEntries = listLibFilesToUpload(
|
|
4869
|
+
libDir,
|
|
4870
|
+
remoteLibStats,
|
|
4871
|
+
projectRoot,
|
|
4872
|
+
manifest
|
|
4873
|
+
);
|
|
4874
|
+
let updated = 0;
|
|
4875
|
+
if (libUploadEntries.length > 0) {
|
|
4876
|
+
const zipPath = await createUpdatePackage(
|
|
4877
|
+
libUploadEntries,
|
|
4878
|
+
config.packageName
|
|
4879
|
+
);
|
|
4880
|
+
const remoteZipPath = await uploadUpdatePackage(
|
|
4881
|
+
conn.sftp,
|
|
4882
|
+
zipPath,
|
|
4883
|
+
config
|
|
4884
|
+
);
|
|
4885
|
+
await uploadDeployArtifactZip({
|
|
4886
|
+
zipPath,
|
|
4887
|
+
projectName: config.projectName,
|
|
4888
|
+
kind: "backend"
|
|
4889
|
+
});
|
|
4890
|
+
log("\u8FDC\u7A0B\u89E3\u538B lib \u76EE\u5F55\uFF08\u4EC5\u8986\u76D6\u5DF2\u6709 JAR\uFF09...");
|
|
4891
|
+
updated = await extractUpdatePackageOnRemote(
|
|
4892
|
+
conn.client,
|
|
4893
|
+
config,
|
|
4894
|
+
remoteZipPath
|
|
4895
|
+
);
|
|
4896
|
+
} else {
|
|
4897
|
+
log("\u65E0 JAR \u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u66F4\u65B0\u5305\u4E0A\u4F20");
|
|
4898
|
+
}
|
|
4899
|
+
const runningJar = await getRunningJar(conn.client, config);
|
|
4900
|
+
let needRestart = updated > 0;
|
|
4901
|
+
if (!needRestart && !runningJar) {
|
|
4902
|
+
log("\u670D\u52A1\u672A\u8FD0\u884C\uFF0C\u9700\u8981\u542F\u52A8");
|
|
4903
|
+
needRestart = true;
|
|
4904
|
+
} else if (!needRestart) {
|
|
4905
|
+
log("\u6CA1\u6709\u6587\u4EF6\u9700\u8981\u66F4\u65B0\uFF0C\u8DF3\u8FC7\u91CD\u542F");
|
|
4906
|
+
}
|
|
4907
|
+
if (needRestart) {
|
|
4908
|
+
log("\u91CD\u542F\u670D\u52A1...");
|
|
4909
|
+
await restartRemoteService(conn.client, config);
|
|
4910
|
+
}
|
|
4911
|
+
await healthCheckService(conn.client, config);
|
|
4912
|
+
if (libUploadEntries.length > 0) {
|
|
4913
|
+
manifest = updateManifestEntries(manifest, libUploadEntries, projectRoot);
|
|
4914
|
+
saveManifest3(manifest);
|
|
4915
|
+
}
|
|
4916
|
+
} finally {
|
|
4917
|
+
await closeSsh(conn);
|
|
4823
4918
|
}
|
|
4824
|
-
|
|
4919
|
+
log("\u90E8\u7F72\u5B8C\u6210");
|
|
4825
4920
|
}
|
|
4826
4921
|
|
|
4827
4922
|
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
@@ -4866,30 +4961,6 @@ function resolveFrontendBuildCommand(env, cwd) {
|
|
|
4866
4961
|
}
|
|
4867
4962
|
return null;
|
|
4868
4963
|
}
|
|
4869
|
-
function runShellCommand(command, cwd, captureOutput) {
|
|
4870
|
-
const result = spawnSync5(command, {
|
|
4871
|
-
cwd,
|
|
4872
|
-
shell: true,
|
|
4873
|
-
env: buildDeployShellEnv(),
|
|
4874
|
-
encoding: "utf8",
|
|
4875
|
-
stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
|
|
4876
|
-
});
|
|
4877
|
-
const stdout = captureOutput ? String(result.stdout ?? "") : "";
|
|
4878
|
-
const stderr = captureOutput ? String(result.stderr ?? "") : "";
|
|
4879
|
-
const output = [stdout, stderr].filter(Boolean).join("\n");
|
|
4880
|
-
if (captureOutput) {
|
|
4881
|
-
if (stdout) process.stdout.write(stdout);
|
|
4882
|
-
if (stderr) process.stderr.write(stderr);
|
|
4883
|
-
}
|
|
4884
|
-
if (result.status !== 0) {
|
|
4885
|
-
throw new DeployExecutionError(
|
|
4886
|
-
`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
|
|
4887
|
-
result.status ?? 1,
|
|
4888
|
-
output
|
|
4889
|
-
);
|
|
4890
|
-
}
|
|
4891
|
-
return output;
|
|
4892
|
-
}
|
|
4893
4964
|
function resolveFrontendDistDir(cwd) {
|
|
4894
4965
|
const candidates = ["dist", "apps/web/dist"];
|
|
4895
4966
|
for (const rel of candidates) {
|
|
@@ -4924,7 +4995,7 @@ async function runWisdomAutoDeploy(options) {
|
|
|
4924
4995
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
4925
4996
|
}
|
|
4926
4997
|
console.error(`[apm] \u524D\u7AEF\u4EC5\u6253\u5305\u6A21\u5F0F\uFF1A\u6267\u884C ${buildCmd}`);
|
|
4927
|
-
|
|
4998
|
+
runDeployShellCommand(buildCmd, cwd, captureOutput);
|
|
4928
4999
|
const projectName = (cfg.name ?? "").trim();
|
|
4929
5000
|
if (!projectName) {
|
|
4930
5001
|
throw new DeployExecutionError(
|
|
@@ -4950,7 +5021,7 @@ async function runWisdomAutoDeploy(options) {
|
|
|
4950
5021
|
);
|
|
4951
5022
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
4952
5023
|
}
|
|
4953
|
-
|
|
5024
|
+
runDeployShellCommand(deployCmd, cwd, captureOutput);
|
|
4954
5025
|
return { projectType };
|
|
4955
5026
|
}
|
|
4956
5027
|
const settings = resolveWisdomBackendDeployFromApmConfig(cfg);
|
|
@@ -5114,30 +5185,6 @@ async function syncDeployBaselineBeforeDeploy(cwd) {
|
|
|
5114
5185
|
}
|
|
5115
5186
|
|
|
5116
5187
|
// src/commands/deploy/deploy-execute.ts
|
|
5117
|
-
function runShellCommand2(command, cwd, captureOutput) {
|
|
5118
|
-
const result = spawnSync6(command, {
|
|
5119
|
-
cwd,
|
|
5120
|
-
shell: true,
|
|
5121
|
-
env: buildDeployShellEnv(),
|
|
5122
|
-
encoding: "utf8",
|
|
5123
|
-
stdio: captureOutput ? ["inherit", "pipe", "pipe"] : "inherit"
|
|
5124
|
-
});
|
|
5125
|
-
const stdout = captureOutput ? String(result.stdout ?? "") : "";
|
|
5126
|
-
const stderr = captureOutput ? String(result.stderr ?? "") : "";
|
|
5127
|
-
const output = [stdout, stderr].filter(Boolean).join("\n");
|
|
5128
|
-
if (captureOutput) {
|
|
5129
|
-
if (stdout) process.stdout.write(stdout);
|
|
5130
|
-
if (stderr) process.stderr.write(stderr);
|
|
5131
|
-
}
|
|
5132
|
-
if (result.status !== 0) {
|
|
5133
|
-
throw new DeployExecutionError(
|
|
5134
|
-
`\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${result.status ?? "unknown"}: ${command}`,
|
|
5135
|
-
result.status ?? 1,
|
|
5136
|
-
output
|
|
5137
|
-
);
|
|
5138
|
-
}
|
|
5139
|
-
return output;
|
|
5140
|
-
}
|
|
5141
5188
|
async function executeDeploy(options) {
|
|
5142
5189
|
const cwd = resolveWorkdirPath(options.cwd ?? process.cwd());
|
|
5143
5190
|
const apmConfigPath = options.configPath ?? path5.join(workspaceApmDir(cwd), "apm.config.json");
|
|
@@ -5190,7 +5237,7 @@ async function executeDeploy(options) {
|
|
|
5190
5237
|
const lines = formatDeployNotConfiguredLines(options.env, deployCommands);
|
|
5191
5238
|
throw new DeployExecutionError(lines.join("\n"), 1, lines.join("\n"));
|
|
5192
5239
|
}
|
|
5193
|
-
const commandOutput =
|
|
5240
|
+
const commandOutput = runDeployShellCommand(command, cwd, captureOutput);
|
|
5194
5241
|
if (commandOutput.trim()) {
|
|
5195
5242
|
outputParts.push(commandOutput);
|
|
5196
5243
|
}
|
|
@@ -5209,9 +5256,32 @@ var DEPLOY_LOG_SYNC_INTERVAL_MS = 3e4;
|
|
|
5209
5256
|
function resolveDeployCommand(environment, packOnly) {
|
|
5210
5257
|
return packOnly ? `apm deploy ${environment} --pack-only` : `apm deploy ${environment}`;
|
|
5211
5258
|
}
|
|
5259
|
+
function logDeploySyncFailure(error) {
|
|
5260
|
+
console.error(
|
|
5261
|
+
"[apm] deploy log sync failed:",
|
|
5262
|
+
error instanceof Error ? error.message : String(error)
|
|
5263
|
+
);
|
|
5264
|
+
}
|
|
5212
5265
|
function createDeployLogSyncer(api, deploymentRunId) {
|
|
5213
5266
|
let lastSyncedLog = "";
|
|
5214
5267
|
let latestLog = "";
|
|
5268
|
+
let timer = null;
|
|
5269
|
+
const syncDelta = async (delta, append) => {
|
|
5270
|
+
const chunks = splitUtf8StringByMaxBytes(
|
|
5271
|
+
delta,
|
|
5272
|
+
DEPLOY_LOG_SYNC_MAX_CHUNK_BYTES
|
|
5273
|
+
);
|
|
5274
|
+
for (let index = 0; index < chunks.length; index += 1) {
|
|
5275
|
+
const chunk = chunks[index];
|
|
5276
|
+
await retryDeployLogSync(
|
|
5277
|
+
() => api.cli.syncTaskDeploymentLog({
|
|
5278
|
+
id: deploymentRunId,
|
|
5279
|
+
log: chunk,
|
|
5280
|
+
append: append || index > 0
|
|
5281
|
+
})
|
|
5282
|
+
);
|
|
5283
|
+
}
|
|
5284
|
+
};
|
|
5215
5285
|
const syncIfChanged = async () => {
|
|
5216
5286
|
if (!latestLog || latestLog === lastSyncedLog) {
|
|
5217
5287
|
return;
|
|
@@ -5220,34 +5290,58 @@ function createDeployLogSyncer(api, deploymentRunId) {
|
|
|
5220
5290
|
if (!delta) {
|
|
5221
5291
|
return;
|
|
5222
5292
|
}
|
|
5223
|
-
await
|
|
5224
|
-
id: deploymentRunId,
|
|
5225
|
-
log: delta,
|
|
5226
|
-
append: lastSyncedLog.length > 0
|
|
5227
|
-
});
|
|
5293
|
+
await syncDelta(delta, lastSyncedLog.length > 0);
|
|
5228
5294
|
lastSyncedLog = latestLog;
|
|
5229
5295
|
};
|
|
5230
|
-
|
|
5231
|
-
void syncIfChanged().catch(
|
|
5232
|
-
console.error(
|
|
5233
|
-
"[apm] deploy log sync failed:",
|
|
5234
|
-
error instanceof Error ? error.message : String(error)
|
|
5235
|
-
);
|
|
5236
|
-
});
|
|
5296
|
+
timer = setInterval(() => {
|
|
5297
|
+
void syncIfChanged().catch(logDeploySyncFailure);
|
|
5237
5298
|
}, DEPLOY_LOG_SYNC_INTERVAL_MS);
|
|
5238
5299
|
return {
|
|
5239
5300
|
updateLog(log2) {
|
|
5240
5301
|
latestLog = log2;
|
|
5241
5302
|
},
|
|
5242
5303
|
async flush() {
|
|
5243
|
-
|
|
5304
|
+
if (timer) {
|
|
5305
|
+
clearInterval(timer);
|
|
5306
|
+
timer = null;
|
|
5307
|
+
}
|
|
5244
5308
|
await syncIfChanged();
|
|
5245
5309
|
},
|
|
5310
|
+
async flushSafe() {
|
|
5311
|
+
if (timer) {
|
|
5312
|
+
clearInterval(timer);
|
|
5313
|
+
timer = null;
|
|
5314
|
+
}
|
|
5315
|
+
try {
|
|
5316
|
+
await syncIfChanged();
|
|
5317
|
+
return true;
|
|
5318
|
+
} catch (error) {
|
|
5319
|
+
logDeploySyncFailure(error);
|
|
5320
|
+
return false;
|
|
5321
|
+
}
|
|
5322
|
+
},
|
|
5246
5323
|
dispose() {
|
|
5247
|
-
|
|
5324
|
+
if (timer) {
|
|
5325
|
+
clearInterval(timer);
|
|
5326
|
+
timer = null;
|
|
5327
|
+
}
|
|
5248
5328
|
}
|
|
5249
5329
|
};
|
|
5250
5330
|
}
|
|
5331
|
+
async function finalizeTaskDeployment(api, deploymentRunId, logSyncer, input) {
|
|
5332
|
+
const synced = await logSyncer.flushSafe();
|
|
5333
|
+
const payload = {
|
|
5334
|
+
id: deploymentRunId,
|
|
5335
|
+
status: input.status
|
|
5336
|
+
};
|
|
5337
|
+
if (input.error) {
|
|
5338
|
+
payload.error = input.error;
|
|
5339
|
+
}
|
|
5340
|
+
if (!synced) {
|
|
5341
|
+
payload.log = truncateDeployLogForComplete(input.fullLog).log;
|
|
5342
|
+
}
|
|
5343
|
+
await retryDeployLogSync(() => api.cli.completeTaskDeployment(payload));
|
|
5344
|
+
}
|
|
5251
5345
|
function captureDeployConsole(logSyncer) {
|
|
5252
5346
|
const chunks = [];
|
|
5253
5347
|
const appendLog = (line) => {
|
|
@@ -5304,11 +5398,9 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
5304
5398
|
if (output.trim()) {
|
|
5305
5399
|
capture.appendLog(output);
|
|
5306
5400
|
}
|
|
5307
|
-
await logSyncer
|
|
5308
|
-
await api.cli.completeTaskDeployment({
|
|
5309
|
-
id: deploymentRunId,
|
|
5401
|
+
await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
|
|
5310
5402
|
status: "SUCCESS",
|
|
5311
|
-
|
|
5403
|
+
fullLog: capture.getLog()
|
|
5312
5404
|
});
|
|
5313
5405
|
console.log(`[apm] deploy success id=${deploymentRunId}`);
|
|
5314
5406
|
} catch (error) {
|
|
@@ -5321,13 +5413,18 @@ async function handleInboundDeploy(cfg, msg, signal) {
|
|
|
5321
5413
|
} else {
|
|
5322
5414
|
capture.appendLog(deployError.message);
|
|
5323
5415
|
}
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
})
|
|
5416
|
+
try {
|
|
5417
|
+
await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
|
|
5418
|
+
status: "FAILED",
|
|
5419
|
+
fullLog: capture.getLog(),
|
|
5420
|
+
error: deployError.message
|
|
5421
|
+
});
|
|
5422
|
+
} catch (finalizeError) {
|
|
5423
|
+
console.error(
|
|
5424
|
+
`[apm] deploy finalize failed id=${deploymentRunId}:`,
|
|
5425
|
+
finalizeError instanceof Error ? finalizeError.message : String(finalizeError)
|
|
5426
|
+
);
|
|
5427
|
+
}
|
|
5331
5428
|
console.error(
|
|
5332
5429
|
`[apm] deploy failed id=${deploymentRunId}: ${deployError.message}`
|
|
5333
5430
|
);
|
|
@@ -6599,7 +6696,7 @@ async function runConnect(options) {
|
|
|
6599
6696
|
shutdownAbort.abort();
|
|
6600
6697
|
logAbortSignalStats(shutdownAbort.signal, "connect:shutdown-after-abort");
|
|
6601
6698
|
try {
|
|
6602
|
-
await Promise.race([Promise.all(activeTasks),
|
|
6699
|
+
await Promise.race([Promise.all(activeTasks), delay3(drainMs)]);
|
|
6603
6700
|
} catch {
|
|
6604
6701
|
}
|
|
6605
6702
|
releaseConnectLock();
|
|
@@ -6797,10 +6894,9 @@ function normalizeDeployEnvironment(env) {
|
|
|
6797
6894
|
}
|
|
6798
6895
|
async function completeTrackedDeploy(api, deploymentRunId, logSyncer, input) {
|
|
6799
6896
|
logSyncer.updateLog(input.log);
|
|
6800
|
-
await logSyncer
|
|
6801
|
-
await api.cli.completeTaskDeployment({
|
|
6802
|
-
id: deploymentRunId,
|
|
6897
|
+
await finalizeTaskDeployment(api, deploymentRunId, logSyncer, {
|
|
6803
6898
|
status: input.status,
|
|
6899
|
+
fullLog: input.log,
|
|
6804
6900
|
error: input.error
|
|
6805
6901
|
});
|
|
6806
6902
|
}
|