@zhipu/zp-cli 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cli.mjs +29 -22
- package/dist/{config-BZc9pg7J.mjs → config-DzcldbBT.mjs} +13 -4
- package/dist/index.d.mts +11 -3
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/templates/zpc.config.schema.json +9 -0
package/README.md
CHANGED
|
@@ -24,11 +24,13 @@ zpc check
|
|
|
24
24
|
`zpc deploy` 若发现无 `~/.zpc` 部署记录、但 api/web 部署目录非空或已有本项目 nginx 配置,会交互询问:
|
|
25
25
|
|
|
26
26
|
1. 清空目标文件夹并覆盖(全量,旧目录备份为带时间戳的 `.bak-*`)
|
|
27
|
-
2. 使用增量部署(api 保留 `public
|
|
27
|
+
2. 使用增量部署(api 保留 `excludeOnIncremental` 指定项,默认 `public`;web 全量覆盖)
|
|
28
28
|
3. 取消部署(默认;非交互环境同样默认取消)
|
|
29
29
|
|
|
30
30
|
可选环境变量:`DEPLOY_DIR`、`DEPLOY_FORCE`;`ZPC_HOME` 覆盖 `~/.zpc`(部署状态记录);测试可用 `ZPC_API_ROOT` / `ZPC_WEB_ROOT` / `ZPC_NGINX_DEFAULT_D` / `ZPC_DEPLOY_SKIP_RUNTIME`。
|
|
31
31
|
|
|
32
|
+
`api.excludeOnIncremental`:增量部署时排除、不覆盖的目录或文件名列表,默认 `["public"]`;仅对 api 生效,指定项在增量部署后保留服务器上的既有内容。
|
|
33
|
+
|
|
32
34
|
## 部署约定
|
|
33
35
|
|
|
34
36
|
`zpc.config` 中的 `projectName` 即下文 `<system-tag>`。
|
package/dist/cli.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as findConfigFile, c as loadConfig, l as templateFileName, n as SCHEMA_FILE_NAME, o as isConfigFormat, r as configFileName, s as listConfigFiles, t as CONFIG_FORMATS } from "./config-
|
|
1
|
+
import { a as findConfigFile, c as loadConfig, l as templateFileName, n as SCHEMA_FILE_NAME, o as isConfigFormat, r as configFileName, s as listConfigFiles, t as CONFIG_FORMATS } from "./config-DzcldbBT.mjs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import fs, { mkdtempSync, writeFileSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
@@ -8,7 +8,7 @@ import os, { tmpdir } from "node:os";
|
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import zlib from "node:zlib";
|
|
10
10
|
//#region package.json
|
|
11
|
-
var version = "0.0.
|
|
11
|
+
var version = "0.0.4";
|
|
12
12
|
var description = "内部单命令部署工具";
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/utils/deploy-paths.ts
|
|
@@ -351,8 +351,8 @@ function deployDir(src, dest, log) {
|
|
|
351
351
|
}
|
|
352
352
|
}
|
|
353
353
|
const API_INCREMENTAL_FILES = ["package.json", "bootstrap.js"];
|
|
354
|
-
/** api 增量部署:只替换 dist 与根部 package.json / bootstrap.js
|
|
355
|
-
function deployApiIncremental(src, dest, log) {
|
|
354
|
+
/** api 增量部署:只替换 dist 与根部 package.json / bootstrap.js,不覆盖 excludeOnIncremental 指定项。 */
|
|
355
|
+
function deployApiIncremental(src, dest, exclude, log) {
|
|
356
356
|
const distSrc = path.join(src, "dist");
|
|
357
357
|
if (!fs.existsSync(distSrc)) throw new Error(`[deploy] ${src} 缺少 dist,无法增量部署`);
|
|
358
358
|
fs.mkdirSync(dest, { recursive: true });
|
|
@@ -378,8 +378,15 @@ function deployApiIncremental(src, dest, log) {
|
|
|
378
378
|
if (!fs.existsSync(from)) throw new Error(`[deploy] ${src} 缺少 ${file},无法增量部署`);
|
|
379
379
|
fs.cpSync(from, path.join(dest, file));
|
|
380
380
|
}
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
for (const entry of exclude) {
|
|
382
|
+
const destEntry = path.join(dest, entry);
|
|
383
|
+
if (fs.existsSync(destEntry)) continue;
|
|
384
|
+
const srcEntry = path.join(src, entry);
|
|
385
|
+
if (fs.existsSync(srcEntry) && !fs.statSync(srcEntry).isDirectory()) continue;
|
|
386
|
+
fs.mkdirSync(destEntry, { recursive: true });
|
|
387
|
+
}
|
|
388
|
+
const skipText = exclude.length ? `,跳过 ${exclude.join("、")}` : "";
|
|
389
|
+
log(`增量部署 ${src} -> ${dest}(更新 dist、${API_INCREMENTAL_FILES.join("、")}${skipText})`);
|
|
383
390
|
}
|
|
384
391
|
async function installAndReloadApi(name, dest, item, log) {
|
|
385
392
|
if (!fs.existsSync(path.join(dest, "package.json"))) throw new Error(`[deploy] ${dest} 缺少 package.json,无法 pnpm i`);
|
|
@@ -696,7 +703,7 @@ async function promptDeployResolution() {
|
|
|
696
703
|
value: "full"
|
|
697
704
|
},
|
|
698
705
|
{
|
|
699
|
-
name: "使用增量部署(api
|
|
706
|
+
name: "使用增量部署(api 保留排除项,web 全量覆盖)",
|
|
700
707
|
value: "incremental"
|
|
701
708
|
},
|
|
702
709
|
{
|
|
@@ -844,12 +851,13 @@ async function deployKind(stagingDir, items, kind, destRoot, projectName, increm
|
|
|
844
851
|
if (!fs.existsSync(src)) throw new Error(`[deploy] 配置中有 ${kind}/${item.name},但压缩包中没有对应目录`);
|
|
845
852
|
const dest = path.join(destRoot, projectName, item.name);
|
|
846
853
|
const pm2Name = `${projectName}-${item.name}`;
|
|
847
|
-
|
|
854
|
+
const apiItem = kind === "api" ? item : null;
|
|
855
|
+
if (apiItem && incremental && fs.existsSync(dest)) deployApiIncremental(src, dest, apiItem.excludeOnIncremental ?? [], log$2.info);
|
|
848
856
|
else {
|
|
849
|
-
if (
|
|
857
|
+
if (apiItem && incremental && !fs.existsSync(dest)) log$2.warn(`${dest} 不存在,增量改为全量部署`);
|
|
850
858
|
deployDir(src, dest, log$2.info);
|
|
851
859
|
}
|
|
852
|
-
if (
|
|
860
|
+
if (apiItem && !shouldSkipDeployRuntime()) await installAndReloadApi(pm2Name, dest, apiItem, log$2.info);
|
|
853
861
|
}
|
|
854
862
|
}
|
|
855
863
|
async function runDeploy(dir, options, prompt = promptDeployResolution) {
|
|
@@ -898,12 +906,11 @@ async function runDeploy(dir, options, prompt = promptDeployResolution) {
|
|
|
898
906
|
} else incremental = zipIsIncremental;
|
|
899
907
|
}
|
|
900
908
|
try {
|
|
901
|
-
log$2.info(`模式: ${incremental ? "增量(api
|
|
909
|
+
log$2.info(`模式: ${incremental ? "增量(api 保留排除项,web 全量覆盖)" : "全量"}`);
|
|
902
910
|
if (!incremental && !hadRecord) log$2.info("首次部署该项目,完成后将记录到 ~/.zpc/deploy-state.json");
|
|
903
911
|
if (force) log$2.warn("已启用 --force(允许抢占冲突的 nginx proxy)");
|
|
904
912
|
await deployKind(stagingDir, config.api, "api", getApiRoot(), config.projectName, incremental);
|
|
905
|
-
|
|
906
|
-
else if (config.web?.length) log$2.warn("增量模式跳过 web 部署");
|
|
913
|
+
await deployKind(stagingDir, config.web, "web", getWebRoot(), config.projectName, false);
|
|
907
914
|
} finally {
|
|
908
915
|
fs.rmSync(stagingDir, {
|
|
909
916
|
recursive: true,
|
|
@@ -921,7 +928,7 @@ async function runDeploy(dir, options, prompt = promptDeployResolution) {
|
|
|
921
928
|
else if (zipIsIncremental) log$2.info("已记录部署状态,下次 deploy 将默认增量");
|
|
922
929
|
}
|
|
923
930
|
const apiCount = config.api?.length ?? 0;
|
|
924
|
-
const webCount =
|
|
931
|
+
const webCount = config.web?.length ?? 0;
|
|
925
932
|
const prefix = `${config.projectName}/<name>`;
|
|
926
933
|
log$2.success(`完成: api ${apiCount} 个 -> ${getApiRoot()}/${prefix},web ${webCount} 个 -> ${getWebRoot()}/${prefix}`);
|
|
927
934
|
}
|
|
@@ -1016,10 +1023,9 @@ function resolveIncrementalMode(projectRoot, config, options = {}) {
|
|
|
1016
1023
|
//#endregion
|
|
1017
1024
|
//#region src/commands/pack.ts
|
|
1018
1025
|
const log = createLogger("pack");
|
|
1019
|
-
function resolveProjects(root, config
|
|
1026
|
+
function resolveProjects(root, config) {
|
|
1020
1027
|
const projects = [];
|
|
1021
|
-
const
|
|
1022
|
-
for (const kind of kinds) {
|
|
1028
|
+
for (const kind of ["api", "web"]) {
|
|
1023
1029
|
const items = config[kind];
|
|
1024
1030
|
if (items == null) continue;
|
|
1025
1031
|
for (const item of items) {
|
|
@@ -1035,7 +1041,7 @@ function resolveProjects(root, config, incremental) {
|
|
|
1035
1041
|
});
|
|
1036
1042
|
}
|
|
1037
1043
|
}
|
|
1038
|
-
if (projects.length === 0) throw new Error(
|
|
1044
|
+
if (projects.length === 0) throw new Error("[pack] 配置中没有 api / web 项目");
|
|
1039
1045
|
return projects;
|
|
1040
1046
|
}
|
|
1041
1047
|
function resolveBuildEnv(project, config) {
|
|
@@ -1101,7 +1107,8 @@ function stageApi(project, dest, incremental) {
|
|
|
1101
1107
|
copyRequiredFile(path.join(project.absPath, "package.json"), dest, "package.json");
|
|
1102
1108
|
copyRequiredFile(path.join(project.absPath, "bootstrap.js"), dest, "bootstrap.js");
|
|
1103
1109
|
if (incremental) {
|
|
1104
|
-
|
|
1110
|
+
const excluded = project.excludeOnIncremental ?? [];
|
|
1111
|
+
log.warn(excluded.length ? `api/${project.name} 增量:跳过 ${excluded.join("、")}` : `api/${project.name} 增量:不排除任何目录/文件`);
|
|
1105
1112
|
return;
|
|
1106
1113
|
}
|
|
1107
1114
|
const publicDir = path.join(project.absPath, "public");
|
|
@@ -1136,13 +1143,13 @@ async function runPack(dir, options) {
|
|
|
1136
1143
|
log.info(`项目目录: ${projectRoot}`);
|
|
1137
1144
|
log.info(`配置: ${file}`);
|
|
1138
1145
|
log.info(`项目: ${config.projectName}`);
|
|
1139
|
-
log.info(`模式: ${incremental ? "
|
|
1146
|
+
log.info(`模式: ${incremental ? "增量(api 不含排除项,web 全量)" : "全量"}`);
|
|
1140
1147
|
if (packMode.existingDeployDirs.length > 0) {
|
|
1141
1148
|
const listed = packMode.existingDeployDirs.map((dir) => path.normalize(dir)).join(", ");
|
|
1142
|
-
log.warn(`无 ~/.zpc 部署记录,但检测到部署目录非空:${listed}。将按增量处理(api
|
|
1149
|
+
log.warn(`无 ~/.zpc 部署记录,但检测到部署目录非空:${listed}。将按增量处理(api 保留排除项,web 全量覆盖);若确需全量请清空部署目录并删除 deploy-state 中对应记录`);
|
|
1143
1150
|
}
|
|
1144
1151
|
if (options.concurrency !== void 0) log.info(`并行上限: ${options.concurrency}`);
|
|
1145
|
-
const projects = resolveProjects(projectRoot, config
|
|
1152
|
+
const projects = resolveProjects(projectRoot, config);
|
|
1146
1153
|
await buildAll(projects, config, options);
|
|
1147
1154
|
stageDist(stagingDir, projectRoot, projects, incremental);
|
|
1148
1155
|
try {
|
|
@@ -13,6 +13,8 @@ const CONFIG_FORMATS = [
|
|
|
13
13
|
"cjs",
|
|
14
14
|
"json"
|
|
15
15
|
];
|
|
16
|
+
/** 增量部署默认排除、不覆盖的目录/文件名。 */
|
|
17
|
+
const DEFAULT_EXCLUDE_ON_INCREMENTAL = ["public"];
|
|
16
18
|
function defineConfig(config) {
|
|
17
19
|
return config;
|
|
18
20
|
}
|
|
@@ -66,9 +68,9 @@ function validateConfig(config, source) {
|
|
|
66
68
|
function optionalAppList(value, kind, source) {
|
|
67
69
|
if (value == null) return [];
|
|
68
70
|
if (!Array.isArray(value)) throw new Error(`[config] ${source} 中 ${kind} 必须是数组`);
|
|
69
|
-
return value.map((item, index) => parseAppConfig(item, `${source} ${kind}[${index}]`));
|
|
71
|
+
return value.map((item, index) => parseAppConfig(item, kind, `${source} ${kind}[${index}]`));
|
|
70
72
|
}
|
|
71
|
-
function parseAppConfig(item, label) {
|
|
73
|
+
function parseAppConfig(item, kind, label) {
|
|
72
74
|
if (!isPlainObject(item)) throw new Error(`[config] ${label} 必须是对象`);
|
|
73
75
|
if (typeof item.path !== "string" || item.path.trim() === "") throw new Error(`[config] ${label} 缺少 path`);
|
|
74
76
|
if (!isSafeName(item.name)) throw new Error(`[config] ${label} 缺少合法 name`);
|
|
@@ -81,8 +83,15 @@ function parseAppConfig(item, label) {
|
|
|
81
83
|
parsed.proxy = item.proxy;
|
|
82
84
|
}
|
|
83
85
|
if (item.buildEnv != null) parsed.buildEnv = parseEnvMap(item.buildEnv, `${label} buildEnv`);
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
+
if (kind === "web") return parsed;
|
|
87
|
+
const api = { ...parsed };
|
|
88
|
+
if (item.env != null) api.env = parseEnvMap(item.env, `${label} env`);
|
|
89
|
+
api.excludeOnIncremental = item.excludeOnIncremental != null ? parseExcludeOnIncremental(item.excludeOnIncremental, `${label} excludeOnIncremental`) : [...DEFAULT_EXCLUDE_ON_INCREMENTAL];
|
|
90
|
+
return api;
|
|
91
|
+
}
|
|
92
|
+
function parseExcludeOnIncremental(value, label) {
|
|
93
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string" || !isSafeName(entry))) throw new Error(`[config] ${label} 必须是字符串数组(目录/文件名,不含路径分隔符)`);
|
|
94
|
+
return value;
|
|
86
95
|
}
|
|
87
96
|
function parseEnvMap(value, label) {
|
|
88
97
|
if (!isPlainObject(value)) throw new Error(`[config] ${label} 必须是对象`);
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
declare const CONFIG_FORMATS: readonly ["ts", "mts", "cts", "js", "mjs", "cjs", "json"];
|
|
3
3
|
type ConfigFormat = (typeof CONFIG_FORMATS)[number];
|
|
4
4
|
type ZpcEnvMap = Record<string, string | number | boolean>;
|
|
5
|
+
/** api / web 应用配置公共字段 */
|
|
5
6
|
interface ZpcAppConfig {
|
|
6
7
|
/** 相对项目根的源码目录 */
|
|
7
8
|
path: string;
|
|
@@ -11,14 +12,21 @@ interface ZpcAppConfig {
|
|
|
11
12
|
proxy?: string;
|
|
12
13
|
/** 仅构建期注入 */
|
|
13
14
|
buildEnv?: ZpcEnvMap;
|
|
15
|
+
}
|
|
16
|
+
/** api 应用配置(额外含运行时 env 与增量部署排除项) */
|
|
17
|
+
interface ZpcApiConfig extends ZpcAppConfig {
|
|
14
18
|
/** 仅运行时注入(pm2) */
|
|
15
19
|
env?: ZpcEnvMap;
|
|
20
|
+
/** 增量部署时排除、不覆盖的目录/文件名;默认 ["public"] */
|
|
21
|
+
excludeOnIncremental?: string[];
|
|
16
22
|
}
|
|
23
|
+
/** web 应用配置(静态站点,仅构建期 buildEnv,无运行时 env) */
|
|
24
|
+
interface ZpcWebConfig extends ZpcAppConfig {}
|
|
17
25
|
interface ZpcConfig {
|
|
18
26
|
projectName: string;
|
|
19
|
-
api?:
|
|
20
|
-
web?:
|
|
27
|
+
api?: ZpcApiConfig[];
|
|
28
|
+
web?: ZpcWebConfig[];
|
|
21
29
|
}
|
|
22
30
|
declare function defineConfig(config: ZpcConfig): ZpcConfig;
|
|
23
31
|
//#endregion
|
|
24
|
-
export { type ConfigFormat, type ZpcAppConfig, type ZpcConfig, type ZpcEnvMap, defineConfig };
|
|
32
|
+
export { type ConfigFormat, type ZpcApiConfig, type ZpcAppConfig, type ZpcConfig, type ZpcEnvMap, type ZpcWebConfig, defineConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as defineConfig } from "./config-
|
|
1
|
+
import { i as defineConfig } from "./config-DzcldbBT.mjs";
|
|
2
2
|
export { defineConfig };
|
package/package.json
CHANGED
|
@@ -54,6 +54,15 @@
|
|
|
54
54
|
"env": {
|
|
55
55
|
"$ref": "#/definitions/envMap",
|
|
56
56
|
"description": "仅运行时注入(pm2)"
|
|
57
|
+
},
|
|
58
|
+
"excludeOnIncremental": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"minLength": 1
|
|
63
|
+
},
|
|
64
|
+
"default": ["public"],
|
|
65
|
+
"description": "增量部署时排除、不覆盖的目录/文件名;仅 api 生效,默认 [\"public\"]"
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
},
|