ai-project-manage-cli 6.0.80 → 6.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.js +106 -40
- package/package.json +1 -1
- package/template/skills/apm-deploy/SKILL.md +8 -2
- package/template/skills/apm-dev/SKILL.md +1 -1
package/README.md
CHANGED
package/dist/index.js
CHANGED
|
@@ -1180,22 +1180,47 @@ async function ensureGitRepo2(cwd) {
|
|
|
1180
1180
|
async function getCurrentBranch2(cwd) {
|
|
1181
1181
|
return (await execGit3(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
|
|
1182
1182
|
}
|
|
1183
|
-
async function
|
|
1183
|
+
async function resolveBaselineBranch(cwd, api) {
|
|
1184
|
+
const workdirPath = resolveWorkdirPath(cwd);
|
|
1185
|
+
const baseline = await api.cli.workspaceBaseline({ workdirPath });
|
|
1186
|
+
if (!baseline.repositoryId) {
|
|
1187
|
+
const detail = baseline.diagnostic?.message ?? `\u672A\u5728\u5E73\u53F0\u627E\u5230\u4E0E\u5F53\u524D\u76EE\u5F55\u5339\u914D\u7684\u5DE5\u4F5C\u76EE\u5F55\u767B\u8BB0\uFF1A${workdirPath}\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09
|
|
1188
|
+
\u8BF7\u5148\u5728\u5E73\u53F0\u767B\u8BB0\u8BE5\u8DEF\u5F84\u5BF9\u5E94\u7684\u5DE5\u4F5C\u76EE\u5F55\u4E0E\u4ED3\u5E93\u3002`;
|
|
1189
|
+
throw new Error(`[apm] ${detail}`);
|
|
1190
|
+
}
|
|
1191
|
+
const baselineBranch = (baseline.defaultBranch ?? "").trim();
|
|
1192
|
+
if (!baselineBranch) {
|
|
1193
|
+
throw new Error("[apm] \u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
|
|
1194
|
+
}
|
|
1195
|
+
await execGit3(cwd, ["fetch", "origin", baselineBranch], true);
|
|
1196
|
+
if (!await remoteBranchExists(cwd, baselineBranch)) {
|
|
1197
|
+
throw new Error(
|
|
1198
|
+
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
return baselineBranch;
|
|
1202
|
+
}
|
|
1203
|
+
async function isBranchBasedOnBaseline(cwd, branch, baselineBranch) {
|
|
1204
|
+
const ref = await localBranchExists2(cwd, branch) ? branch : `origin/${branch}`;
|
|
1184
1205
|
try {
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1206
|
+
await execGit3(
|
|
1207
|
+
cwd,
|
|
1208
|
+
["merge-base", "--is-ancestor", `origin/${baselineBranch}`, ref],
|
|
1209
|
+
true
|
|
1210
|
+
);
|
|
1211
|
+
return true;
|
|
1190
1212
|
} catch {
|
|
1213
|
+
return false;
|
|
1191
1214
|
}
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
const
|
|
1195
|
-
if (
|
|
1196
|
-
|
|
1215
|
+
}
|
|
1216
|
+
async function listCandidateSessionBranches(cwd, includeRemote) {
|
|
1217
|
+
const names = new Set(await listLocalSessionBranches(cwd));
|
|
1218
|
+
if (includeRemote) {
|
|
1219
|
+
for (const branch of await listRemoteSessionBranches(cwd)) {
|
|
1220
|
+
names.add(branch);
|
|
1221
|
+
}
|
|
1197
1222
|
}
|
|
1198
|
-
|
|
1223
|
+
return [...names].sort((a, b) => a.localeCompare(b));
|
|
1199
1224
|
}
|
|
1200
1225
|
async function listLocalSessionBranches(cwd) {
|
|
1201
1226
|
const out = await execGit3(
|
|
@@ -1268,20 +1293,27 @@ async function isBranchMergedIntoDefault(cwd, branch, defaultBranch) {
|
|
|
1268
1293
|
async function runCleanBranches(options = {}) {
|
|
1269
1294
|
const cwd = options.cwd ?? process.cwd();
|
|
1270
1295
|
const dryRun = options.dryRun ?? false;
|
|
1296
|
+
const includeRemote = options.includeRemote ?? false;
|
|
1271
1297
|
await ensureGitRepo2(cwd);
|
|
1272
1298
|
await execGit3(cwd, ["fetch", "--prune", "origin"], true);
|
|
1273
1299
|
const cfg = await ensureLoggedConfig();
|
|
1274
1300
|
const api = createApmApiClient(cfg);
|
|
1301
|
+
const baselineBranch = await resolveBaselineBranch(cwd, api);
|
|
1275
1302
|
const { sessions } = await api.cli.listSessionsForBranchCleanup({});
|
|
1276
1303
|
const sessionStatusById = new Map(
|
|
1277
1304
|
sessions.map((item) => [item.sessionId, item.taskStatus])
|
|
1278
1305
|
);
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1306
|
+
const candidates = await listCandidateSessionBranches(cwd, includeRemote);
|
|
1307
|
+
const branchNames = [];
|
|
1308
|
+
for (const branch of candidates) {
|
|
1309
|
+
if (await isBranchBasedOnBaseline(cwd, branch, baselineBranch)) {
|
|
1310
|
+
branchNames.push(branch);
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (branchNames.length === 0) {
|
|
1314
|
+
console.log(
|
|
1315
|
+
`[apm] \u672A\u53D1\u73B0\u4ECE ${baselineBranch} \u884D\u751F\u7684\u672C\u5730 feat/session-* \u5206\u652F${includeRemote ? "\uFF08\u542B\u8FDC\u7A0B\uFF09" : ""}`
|
|
1316
|
+
);
|
|
1285
1317
|
return;
|
|
1286
1318
|
}
|
|
1287
1319
|
const toDelete = [...branchNames].map((branch) => {
|
|
@@ -1300,10 +1332,6 @@ async function runCleanBranches(options = {}) {
|
|
|
1300
1332
|
return;
|
|
1301
1333
|
}
|
|
1302
1334
|
let currentBranch = await getCurrentBranch2(cwd);
|
|
1303
|
-
let defaultBranch = null;
|
|
1304
|
-
if (dryRun) {
|
|
1305
|
-
defaultBranch = await resolveDefaultBranch(cwd);
|
|
1306
|
-
}
|
|
1307
1335
|
for (const item of toDelete) {
|
|
1308
1336
|
const { branch, sessionId, reason } = item;
|
|
1309
1337
|
const label = `${branch} (${sessionId}: ${reason})`;
|
|
@@ -1311,16 +1339,15 @@ async function runCleanBranches(options = {}) {
|
|
|
1311
1339
|
const merged = await isBranchMergedIntoDefault(
|
|
1312
1340
|
cwd,
|
|
1313
1341
|
branch,
|
|
1314
|
-
|
|
1342
|
+
baselineBranch
|
|
1315
1343
|
);
|
|
1316
1344
|
const mergeTag = merged ? "\u5DF2\u5408\u5E76" : "\u672A\u5408\u5E76";
|
|
1317
1345
|
console.log(`[apm] [dry-run] \u5C06\u5220\u9664 ${label} [${mergeTag}]`);
|
|
1318
1346
|
continue;
|
|
1319
1347
|
}
|
|
1320
1348
|
if (currentBranch === branch) {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
currentBranch = defaultBranch;
|
|
1349
|
+
await execGit3(cwd, ["checkout", baselineBranch], true);
|
|
1350
|
+
currentBranch = baselineBranch;
|
|
1324
1351
|
}
|
|
1325
1352
|
if (await localBranchExists2(cwd, branch)) {
|
|
1326
1353
|
await execGit3(cwd, ["branch", "-D", branch], true);
|
|
@@ -4238,6 +4265,42 @@ function posixBasename(p) {
|
|
|
4238
4265
|
return idx >= 0 ? normalized.slice(idx + 1) : normalized;
|
|
4239
4266
|
}
|
|
4240
4267
|
|
|
4268
|
+
// src/commands/deploy/internal/deploy-hints.ts
|
|
4269
|
+
function formatDeployNotConfiguredLines(env, deployCommands) {
|
|
4270
|
+
const configuredEnvs = Object.keys(deployCommands).filter(
|
|
4271
|
+
(key) => deployCommands[key]?.trim()
|
|
4272
|
+
);
|
|
4273
|
+
if (configuredEnvs.length > 0) {
|
|
4274
|
+
return [
|
|
4275
|
+
`[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1Aapm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}`,
|
|
4276
|
+
`[apm] \u5F53\u524D\u5DF2\u914D\u7F6E\u7684\u73AF\u5883: ${configuredEnvs.join(", ")}`
|
|
4277
|
+
];
|
|
4278
|
+
}
|
|
4279
|
+
return [
|
|
4280
|
+
"[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A\u5F53\u524D\u9879\u76EE\u672A\u914D\u7F6E\u81EA\u52A8\u5316\u90E8\u7F72\uFF08apm.config.json \u4E2D\u65E0 deploy \u6BB5\uFF09",
|
|
4281
|
+
"[apm] \u90E8\u5206\u9879\u76EE\u4E0D\u652F\u6301\u6216\u672A\u542F\u7528\u81EA\u52A8\u5316\u90E8\u7F72\uFF0C\u5C5E\u6B63\u5E38\u60C5\u51B5\uFF0C\u53EF\u8DF3\u8FC7\u90E8\u7F72"
|
|
4282
|
+
];
|
|
4283
|
+
}
|
|
4284
|
+
function formatWisdomFrontendDeployNotConfiguredLines(env, packageJsonPath) {
|
|
4285
|
+
return [
|
|
4286
|
+
`[apm] \u65E0\u6CD5\u6267\u884C\u90E8\u7F72\uFF1A${packageJsonPath} \u4E2D\u672A\u627E\u5230 deploy:${env} \u811A\u672C`,
|
|
4287
|
+
"[apm] \u82E5\u672C\u9879\u76EE\u4E0D\u652F\u6301\u81EA\u52A8\u5316\u90E8\u7F72\uFF0C\u5C5E\u6B63\u5E38\u60C5\u51B5\uFF0C\u53EF\u8DF3\u8FC7\u90E8\u7F72"
|
|
4288
|
+
];
|
|
4289
|
+
}
|
|
4290
|
+
function printDeployNotConfiguredHint(env, deployCommands) {
|
|
4291
|
+
for (const line of formatDeployNotConfiguredLines(env, deployCommands)) {
|
|
4292
|
+
console.error(line);
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
function printWisdomFrontendDeployNotConfiguredHint(env, packageJsonPath) {
|
|
4296
|
+
for (const line of formatWisdomFrontendDeployNotConfiguredLines(
|
|
4297
|
+
env,
|
|
4298
|
+
packageJsonPath
|
|
4299
|
+
)) {
|
|
4300
|
+
console.error(line);
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
|
|
4241
4304
|
// src/commands/deploy/internal/wisdom-auto-deploy.ts
|
|
4242
4305
|
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
4243
4306
|
import path3 from "node:path";
|
|
@@ -5062,8 +5125,9 @@ async function runWisdomAutoDeploy(options) {
|
|
|
5062
5125
|
if (projectType === "frontend") {
|
|
5063
5126
|
const deployCmd = resolveFrontendDeployCommand(options.env, cwd);
|
|
5064
5127
|
if (!deployCmd) {
|
|
5065
|
-
|
|
5066
|
-
|
|
5128
|
+
printWisdomFrontendDeployNotConfiguredHint(
|
|
5129
|
+
options.env,
|
|
5130
|
+
path3.join(cwd, "package.json")
|
|
5067
5131
|
);
|
|
5068
5132
|
process.exit(1);
|
|
5069
5133
|
}
|
|
@@ -5115,12 +5179,7 @@ function registerDeployMainCommand(program) {
|
|
|
5115
5179
|
const deployCommands = cfg.deploy ?? {};
|
|
5116
5180
|
const command = deployCommands[env]?.trim();
|
|
5117
5181
|
if (!command) {
|
|
5118
|
-
|
|
5119
|
-
console.error(
|
|
5120
|
-
keys.length > 0 ? `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy.${env}\uFF0C\u53EF\u7528\u73AF\u5883: ${keys.join(
|
|
5121
|
-
", "
|
|
5122
|
-
)}` : `apm.config.json \u4E2D\u672A\u914D\u7F6E deploy \u6BB5`
|
|
5123
|
-
);
|
|
5182
|
+
printDeployNotConfiguredHint(env, deployCommands);
|
|
5124
5183
|
process.exit(1);
|
|
5125
5184
|
}
|
|
5126
5185
|
runShellCommand3(command, cwd);
|
|
@@ -6147,17 +6206,24 @@ function buildProgram() {
|
|
|
6147
6206
|
lines: Number.isFinite(lines) && lines > 0 ? lines : 100
|
|
6148
6207
|
});
|
|
6149
6208
|
});
|
|
6150
|
-
program.command("branch").description("\
|
|
6209
|
+
const branch = program.command("branch").description("\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>");
|
|
6210
|
+
branch.command("prune").description(
|
|
6211
|
+
"\u6E05\u7406\u672C\u5730\u4E0E\u8FDC\u7A0B feat/session-* \u5206\u652F\uFF08\u7C7B\u4F3C git fetch --prune\uFF09\uFF1A\u6C9F\u901A\u7FA4\u4E0D\u5728\u4EFB\u52A1\u5217\u8868\u4E2D\uFF0C\u6216\u5173\u8054\u4EFB\u52A1\u5DF2\u5B8C\u6210\u65F6\u5220\u9664"
|
|
6212
|
+
).option("--dry-run", "\u4EC5\u5217\u51FA\u5C06\u88AB\u5220\u9664\u7684\u5206\u652F\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").option(
|
|
6213
|
+
"-A, --all",
|
|
6214
|
+
"\u5305\u542B\u4EC5\u5B58\u5728\u4E8E\u8FDC\u7A0B\u3001\u672C\u5730\u672A checkout \u7684 feat/session-* \u5206\u652F\uFF08\u9ED8\u8BA4\u4EC5\u5904\u7406\u672C\u5730\u5206\u652F\uFF09"
|
|
6215
|
+
).action(async (opts) => {
|
|
6216
|
+
await runCleanBranches({
|
|
6217
|
+
dryRun: opts.dryRun,
|
|
6218
|
+
includeRemote: opts.all
|
|
6219
|
+
});
|
|
6220
|
+
});
|
|
6221
|
+
branch.description("\u5207\u6362\u6216\u521B\u5EFA\u4F1A\u8BDD\u5206\u652F feat/session-<sessionId>").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option(
|
|
6151
6222
|
"-m, --message <text>",
|
|
6152
6223
|
"\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E"
|
|
6153
6224
|
).action(async (sessionId, opts) => {
|
|
6154
6225
|
await runBranch(sessionId, { message: opts.message });
|
|
6155
6226
|
});
|
|
6156
|
-
program.command("clean-branches").description(
|
|
6157
|
-
"\u6E05\u7406\u672C\u5730\u4E0E\u8FDC\u7A0B feat/session-* \u5206\u652F\uFF1A\u6C9F\u901A\u7FA4\u4E0D\u5728\u4EFB\u52A1\u5217\u8868\u4E2D\uFF0C\u6216\u5173\u8054\u4EFB\u52A1\u5DF2\u5B8C\u6210\u65F6\u5220\u9664"
|
|
6158
|
-
).option("--dry-run", "\u4EC5\u5217\u51FA\u5C06\u88AB\u5220\u9664\u7684\u5206\u652F\uFF0C\u4E0D\u5B9E\u9645\u6267\u884C").action(async (opts) => {
|
|
6159
|
-
await runCleanBranches({ dryRun: opts.dryRun });
|
|
6160
|
-
});
|
|
6161
6227
|
program.command("create-pr").description(
|
|
6162
6228
|
"\u4E3A\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u7684\u4F1A\u8BDD\u7279\u6027\u5206\u652F\u521B\u5EFA PR\uFF08\u6807\u9898\u81EA\u52A8\u52A0 [AI] \u6807\u8BC6\uFF1B\u8FDC\u7A0B\u521B\u5EFA\u5931\u8D25\u65F6\u5E73\u53F0\u6570\u636E\u56DE\u6EDA\uFF09"
|
|
6163
6229
|
).requiredOption("--session <sessionId>", "\u6C9F\u901A\u7FA4 ID").requiredOption("--title <title>", "PR \u6807\u9898").option("--content <content>", "PR \u6B63\u6587\uFF08Markdown\uFF09", "").action(
|
package/package.json
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
# apm-deploy:自动化部署
|
|
2
|
+
|
|
1
3
|
## 工作流程
|
|
2
4
|
|
|
3
|
-
在项目根目录执行 `apm deploy
|
|
5
|
+
在项目根目录执行 `apm deploy <环境名称>`,**以命令执行结果为准**:
|
|
6
|
+
|
|
7
|
+
- **成功**:部署完成,在回信中说明测试环境地址或部署结果。
|
|
8
|
+
- **失败且输出含「未配置自动化部署」或「可跳过部署」**:本项目不支持自动化部署,属正常情况;放弃部署,在回信说明已跳过即可,无需修复配置或重试。
|
|
9
|
+
- **其他失败**:按输出排查并修复后重试。
|
|
4
10
|
|
|
5
11
|
## 示例
|
|
6
12
|
|
|
7
13
|
1. 部署测试环境:`apm deploy test`
|
|
8
|
-
2.
|
|
14
|
+
2. 部署正式环境:`apm deploy online`
|
|
@@ -86,7 +86,7 @@ ALTER TABLE inspection_class ADD COLUMN is_project_add VARCHAR(1) DEFAULT '0';
|
|
|
86
86
|
开发完成的定义是以下三项**全部满足**,缺一不可:
|
|
87
87
|
|
|
88
88
|
1. **构建通过**:执行本仓库的构建/检查命令(见 AGENTS.md 或部署文档),失败必须修复后重试。
|
|
89
|
-
2. **发布测试环境**:**Read** `.apm/skills/apm-deploy/SKILL.md`
|
|
89
|
+
2. **发布测试环境**:**Read** `.apm/skills/apm-deploy/SKILL.md` 并按其流程执行 `apm deploy`(以命令结果判断是否跳过);**后端**涉及 SQL 变更时,须在回复中引用 `docs/SQL.md`,并写明待执行的 SQL 文件名或执行顺序。
|
|
90
90
|
3. **白名单对账**:在回复中逐文件列出本次改动与计划白名单的对应关系。
|
|
91
91
|
|
|
92
92
|
**注意:不做联调。** 前后端各自按 `API.md` 交付,接口对不上属于契约或实现问题,由 diff 评审与人工验收暴露后打回修复;禁止自行发起「联调」「接口实测」类的开放式动作。
|