listpage_cli 0.0.296 → 0.0.298

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.
@@ -134,18 +134,46 @@ async function askOverwrite() {
134
134
  }
135
135
  return (0, interaction_result_1.interactionValue)(result.value.ok);
136
136
  }
137
+ const HELP_ANSI = {
138
+ reset: "\u001b[0m",
139
+ dim: "\u001b[2m",
140
+ cyan: "\u001b[36m",
141
+ green: "\u001b[32m",
142
+ yellow: "\u001b[33m",
143
+ };
144
+ const HELP_USE_COLOR = process.env.NO_COLOR === undefined;
145
+ function helpColor(text, color) {
146
+ if (!HELP_USE_COLOR) {
147
+ return text;
148
+ }
149
+ return `${HELP_ANSI[color]}${text}${HELP_ANSI.reset}`;
150
+ }
137
151
  function printHelp() {
138
152
  const h = [
139
- "用法: listpage_cli init",
140
- "说明: 进入中文引导式交互,按提示填写即可",
141
- "用法: listpage_cli install-skill [skillName] [--project]",
142
- "说明: 安装技能到 Cursor。默认 skillName 为 test,安装到当前命令执行目录的 .cursor/skills/",
143
- "用法: listpage_cli build-project",
144
- "说明: 非交互校验当前目录是否为有效项目根(需存在 listpage.config.json)",
145
- "用法: listpage_cli release-project [tag] [--profile dev] [--platform linux/amd64] [--env KEY=VALUE]",
146
- "说明: 先校验 .listpage/output 产物,再按 login/build/tag/push 执行 Docker 发布;参数优先级: CLI > profile > base",
147
- "用法: listpage_cli deploy-project [tag] [--profile dev] [--platform linux/amd64] [--env KEY=VALUE]",
148
- "说明: 使用 docker.remote + docker.container 执行部署,支持 ports[] 与 envFile/env 合并;参数优先级: CLI > profile > base",
153
+ helpColor("listpage_cli 命令说明", "cyan"),
154
+ "",
155
+ ` ${helpColor("init", "green")}`,
156
+ ` 用法: ${helpColor("listpage_cli init", "dim")}`,
157
+ " 说明: 进入中文引导式交互,按提示填写即可",
158
+ "",
159
+ ` ${helpColor("install-skill", "green")}`,
160
+ ` 用法: ${helpColor("listpage_cli install-skill [skillName] [--project]", "dim")}`,
161
+ " 说明: 安装技能到 Cursor;默认 skillName test,安装到当前命令执行目录的 .cursor/skills/",
162
+ "",
163
+ ` ${helpColor("build-project", "green")}`,
164
+ ` 用法: ${helpColor("listpage_cli build-project", "dim")}`,
165
+ " 说明: 非交互校验当前目录是否为有效项目根(需存在 listpage.config.json)",
166
+ "",
167
+ ` ${helpColor("release-project", "green")}`,
168
+ ` 用法: ${helpColor("listpage_cli release-project [tag] [--profile dev] [--platform linux/amd64] [--env KEY=VALUE]", "dim")}`,
169
+ " 说明: 先校验 .listpage/output 产物,再按 login/build/tag/push 执行 Docker 发布",
170
+ ` 备注: ${helpColor("参数优先级为 CLI > profile > base", "yellow")}`,
171
+ "",
172
+ ` ${helpColor("deploy-project", "green")}`,
173
+ ` 用法: ${helpColor("listpage_cli deploy-project [tag] [--profile dev] [--platform linux/amd64] [--env KEY=VALUE] [--skip-image]", "dim")}`,
174
+ " 说明: 使用 docker.remote + docker.container 执行部署,支持 ports[] 与 envFile/env 合并",
175
+ ` 备注: ${helpColor("默认会清理并拉取镜像;传入 --skip-image 时只执行容器相关步骤(假定镜像已是最新且已存在)", "yellow")}`,
176
+ ` ${helpColor("参数优先级为 CLI > profile > base", "yellow")}`,
149
177
  ].join("\n");
150
178
  console.log(h);
151
179
  }
@@ -46,10 +46,12 @@ function parseCommandOptions(rawArgs) {
46
46
  const platform = readSingleOption(args, "platform");
47
47
  const envEntries = readMultiOption(args, "env");
48
48
  const env = parseEnvEntries(envEntries);
49
+ const skipImage = args.includes("--skip-image");
49
50
  return {
50
51
  profile,
51
52
  platform,
52
53
  env: Object.keys(env).length > 0 ? env : undefined,
54
+ skipImage: skipImage || undefined,
53
55
  };
54
56
  }
55
57
  function parseCommandPositionals(rawArgs) {
@@ -19,6 +19,7 @@ function createDeployProjectCommandHandler(deps) {
19
19
  return (0, deploy_project_service_1.runDeployProjectFlow)({
20
20
  ...deps,
21
21
  cliOverrides,
22
+ skipImage: options.skipImage === true,
22
23
  });
23
24
  };
24
25
  }
@@ -55,11 +55,19 @@ function readConfigText(reader, configPath) {
55
55
  }
56
56
  }
57
57
  function validateRequiredFields(config, configPath) {
58
- const frontend = config.frontend;
59
- if (!Array.isArray(frontend) || frontend.length === 0) {
58
+ const artifacts = config.artifacts;
59
+ // v1.1 配置:存在 artifacts 时,不再强制校验顶层 frontend/backend 必填,
60
+ // 仅对 artifacts.copyFiles 做结构校验,避免对新配置形态误报。
61
+ if (artifacts && typeof artifacts === "object" && !Array.isArray(artifacts)) {
62
+ validateCopyFiles(artifacts, configPath);
63
+ return;
64
+ }
65
+ // 旧配置形态(无 artifacts):保持原有必填校验以兼容历史行为
66
+ const legacyFrontend = config.frontend;
67
+ if (!Array.isArray(legacyFrontend) || legacyFrontend.length === 0) {
60
68
  throw new ConfigLoaderError("CONFIG_REQUIRED_FIELD_MISSING", configPath, `[${exports.CONFIG_LOADER_STAGE}][CONFIG_REQUIRED_FIELD_MISSING] 配置缺少必填字段: frontend (至少包含一个构建项)`);
61
69
  }
62
- frontend.forEach((item, index) => {
70
+ legacyFrontend.forEach((item, index) => {
63
71
  const projectDir = getNestedString({ frontend: item }, "frontend.projectDir");
64
72
  if (!projectDir) {
65
73
  throw new ConfigLoaderError("CONFIG_REQUIRED_FIELD_MISSING", configPath, `[${exports.CONFIG_LOADER_STAGE}][CONFIG_REQUIRED_FIELD_MISSING] 配置缺少必填字段: frontend[${index}].projectDir`);
@@ -90,6 +90,7 @@ async function runDeployProjectFlow(deps) {
90
90
  executionInput,
91
91
  resolve: deps.fs.resolve,
92
92
  readText: deps.fs.readText,
93
+ skipImage: deps.skipImage === true,
93
94
  });
94
95
  }
95
96
  function resolveRuntimeConfig(config, cliOverrides) {
@@ -142,28 +143,6 @@ async function runDockerDeploy(input) {
142
143
  const { client, executionInput } = input;
143
144
  const remoteImage = executionInput.remoteImage;
144
145
  logDeployStep("run", `开始部署镜像: ${remoteImage}`);
145
- try {
146
- logDeployStep("run", `检查本地镜像是否存在: ${remoteImage}`);
147
- const exists = await client.imageExists(remoteImage);
148
- logDeployStep("run", `本地镜像检查结果: exists=${exists ? "yes" : "no"}`);
149
- if (exists) {
150
- logDeployStep("run", `删除旧镜像: ${remoteImage}`);
151
- await client.removeImage(remoteImage);
152
- logDeployStep("run", `旧镜像删除完成: ${remoteImage}`);
153
- }
154
- }
155
- catch (error) {
156
- return (0, command_result_1.commandError)(`[${DEPLOY_STAGE}][run] 清理镜像缓存失败: ${toErrorMessage(error)}`, command_result_1.COMMAND_ERROR_CODES.executionFailed, 1);
157
- }
158
- try {
159
- const auth = buildPullAuth(executionInput.docker);
160
- logDeployStep("run", `开始拉取镜像: ${remoteImage} (auth=${auth ? "yes" : "no"})`);
161
- await client.pullImage(remoteImage, auth);
162
- logDeployStep("run", `镜像拉取完成: ${remoteImage}`);
163
- }
164
- catch (error) {
165
- return (0, command_result_1.commandError)(`[${DEPLOY_STAGE}][run] 拉取镜像失败: ${toErrorMessage(error)}`, command_result_1.COMMAND_ERROR_CODES.executionFailed, 1);
166
- }
167
146
  try {
168
147
  logDeployStep("run", `检查已有容器: ${executionInput.runtime.containerName}`);
169
148
  const existingContainerId = await client.findContainerIdByName(executionInput.runtime.containerName);
@@ -179,6 +158,33 @@ async function runDockerDeploy(input) {
179
158
  catch (error) {
180
159
  return (0, command_result_1.commandError)(`[${DEPLOY_STAGE}][run] 替换容器失败: ${toErrorMessage(error)}`, command_result_1.COMMAND_ERROR_CODES.executionFailed, 1);
181
160
  }
161
+ if (input.skipImage !== true) {
162
+ try {
163
+ logDeployStep("run", `检查本地镜像是否存在: ${remoteImage}`);
164
+ const exists = await client.imageExists(remoteImage);
165
+ logDeployStep("run", `本地镜像检查结果: exists=${exists ? "yes" : "no"}`);
166
+ if (exists) {
167
+ logDeployStep("run", `删除旧镜像: ${remoteImage}`);
168
+ await client.removeImage(remoteImage);
169
+ logDeployStep("run", `旧镜像删除完成: ${remoteImage}`);
170
+ }
171
+ }
172
+ catch (error) {
173
+ return (0, command_result_1.commandError)(`[${DEPLOY_STAGE}][run] 清理镜像缓存失败: ${toErrorMessage(error)}`, command_result_1.COMMAND_ERROR_CODES.executionFailed, 1);
174
+ }
175
+ try {
176
+ const auth = buildPullAuth(executionInput.docker);
177
+ logDeployStep("run", `开始拉取镜像: ${remoteImage} (auth=${auth ? "yes" : "no"})`);
178
+ await client.pullImage(remoteImage, auth);
179
+ logDeployStep("run", `镜像拉取完成: ${remoteImage}`);
180
+ }
181
+ catch (error) {
182
+ return (0, command_result_1.commandError)(`[${DEPLOY_STAGE}][run] 拉取镜像失败: ${toErrorMessage(error)}`, command_result_1.COMMAND_ERROR_CODES.executionFailed, 1);
183
+ }
184
+ }
185
+ else {
186
+ logDeployStep("run", `跳过镜像处理: 按照 --skip-image 假定镜像已存在且为最新`);
187
+ }
182
188
  let createContainerInput;
183
189
  try {
184
190
  createContainerInput = (0, dockerode_client_1.toRuntimeContainerInput)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "listpage_cli",
3
- "version": "0.0.296",
3
+ "version": "0.0.298",
4
4
  "private": false,
5
5
  "bin": {
6
6
  "listpage_cli": "bin/cli.js"
@@ -25,7 +25,7 @@
25
25
  "class-transformer": "^0.5.1",
26
26
  "class-validator": "~0.14.2",
27
27
  "rxjs": "^7.8.1",
28
- "listpage-next-nest": "~0.0.296"
28
+ "listpage-next-nest": "~0.0.298"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@nestjs/schematics": "^11.0.0",
@@ -12,7 +12,7 @@
12
12
  "dependencies": {
13
13
  "react": "^19.2.0",
14
14
  "react-dom": "^19.2.0",
15
- "listpage-next": "~0.0.296",
15
+ "listpage-next": "~0.0.298",
16
16
  "react-router-dom": ">=6.0.0",
17
17
  "@ant-design/v5-patch-for-react-19": "~1.0.3",
18
18
  "ahooks": "^3.9.5",
@@ -23,7 +23,7 @@
23
23
  "styled-components": "^6.1.19",
24
24
  "mobx": "~6.15.0",
25
25
  "@ant-design/icons": "~6.0.2",
26
- "listpage-components": "~0.0.296",
26
+ "listpage-components": "~0.0.298",
27
27
  "lucide-react": "~0.575.0"
28
28
  "mobx-react-lite": "~4.1.1"
29
29
  },