Commit d06263b8 by lijiabin

【需求 25171】feat: 测试分支导航增加更新时间,新增 npm run remove:test-branch 主动删除已部署分支

parent f176c1d6
...@@ -14,6 +14,15 @@ const deploySlug = getDeploySlug(branchName); ...@@ -14,6 +14,15 @@ const deploySlug = getDeploySlug(branchName);
// 最多部署的分支个数 // 最多部署的分支个数
const MAX_DEPLOY_BRANCH_COUNT = 5; const MAX_DEPLOY_BRANCH_COUNT = 5;
// 加 --remove 参数时只删除已部署的分支,不构建不部署(npm run remove:test-branch)
const isRemoveMode = process.argv.includes("--remove");
// 本次部署时间,写进 .deploy-meta.json,也用于导航页展示“更新时间”
const deployedAt = new Date().toLocaleString("zh-CN", {
hour12: false,
timeZone: "Asia/Shanghai",
});
const connectInfo = { const connectInfo = {
host: process.env.DEPLOY_TEST_HOST, host: process.env.DEPLOY_TEST_HOST,
port: process.env.DEPLOY_TEST_PORT, port: process.env.DEPLOY_TEST_PORT,
...@@ -128,12 +137,14 @@ async function getDeployEntries(folderList) { ...@@ -128,12 +137,14 @@ async function getDeployEntries(folderList) {
} }
// 写入index.html文件 // 写入index.html文件
async function writeIndexHtml(entries) { // includeCurrent: 是否把当前分支也放进导航(删除模式下当前分支不一定已部署,不能加)
async function writeIndexHtml(entries, { includeCurrent = true } = {}) {
// 剔除掉已经部署的 // 剔除掉已经部署的
const dedupedEntries = []; const dedupedEntries = [];
const seenSlugs = new Set(); const seenSlugs = new Set();
const currentEntry = includeCurrent ? [{ slug: deploySlug, branchName, deployedAt }] : [];
for (const entry of [{ slug: deploySlug, branchName }, ...entries]) { for (const entry of [...currentEntry, ...entries]) {
if (seenSlugs.has(entry.slug)) { if (seenSlugs.has(entry.slug)) {
continue; continue;
} }
...@@ -338,6 +349,7 @@ async function writeIndexHtml(entries) { ...@@ -338,6 +349,7 @@ async function writeIndexHtml(entries) {
<section class="branch-card"> <section class="branch-card">
<h2 class="branch-name">分支「${item.branchName}」</h2> <h2 class="branch-name">分支「${item.branchName}」</h2>
<p class="branch-slug">访问路径:/${item.slug}/</p> <p class="branch-slug">访问路径:/${item.slug}/</p>
${item.deployedAt ? `<p class="branch-slug">更新时间:${item.deployedAt}</p>` : ""}
<button class="branch-button" onclick="goToBranch('${item.slug}')"> <button class="branch-button" onclick="goToBranch('${item.slug}')">
进入当前分支 进入当前分支
</button> </button>
...@@ -418,10 +430,7 @@ async function writeDeployMeta() { ...@@ -418,10 +430,7 @@ async function writeDeployMeta() {
const meta = { const meta = {
branchName, branchName,
deploySlug, deploySlug,
deployedAt: new Date().toLocaleString("zh-CN", { deployedAt,
hour12: false,
timeZone: "Asia/Shanghai",
}),
}; };
const metaContent = JSON.stringify(meta, null, 2); const metaContent = JSON.stringify(meta, null, 2);
...@@ -431,12 +440,12 @@ ${metaContent} ...@@ -431,12 +440,12 @@ ${metaContent}
EOF`); EOF`);
} }
// 已部署分支数量达到上限时,直接在控制台勾选要删除的分支,删完继续部署。 // 在控制台勾选要删除的分支并删除:
// 返回删除后剩余的分支列表;用户取消 / 不是交互终端 / 删的不够时返回 null,调用方终止部署 // - 部署时分支数量达到上限:至少删 needRemoveCount 个才有空位,删完继续部署
async function cleanUpBranches(deployEntries) { // - --remove 模式:主动删任意个
const needRemoveCount = deployEntries.length - MAX_DEPLOY_BRANCH_COUNT + 1; // 返回删除后剩余的分支列表;用户取消 / 不是交互终端 / 删的不够时返回 null,调用方终止
async function removeBranches(deployEntries, { minCount, intro, cancelText }) {
console.log(`当前已部署分支数量已达到上限 ${MAX_DEPLOY_BRANCH_COUNT},需要先删除至少 ${needRemoveCount} 个:`); console.log(intro);
deployEntries.forEach((item, index) => { deployEntries.forEach((item, index) => {
console.log(`${index + 1}. ${item.branchName} (${item.slug}) ${item.deployedAt}`); console.log(`${index + 1}. ${item.branchName} (${item.slug}) ${item.deployedAt}`);
}); });
...@@ -450,20 +459,20 @@ async function cleanUpBranches(deployEntries) { ...@@ -450,20 +459,20 @@ async function cleanUpBranches(deployEntries) {
{ {
type: "checkbox", type: "checkbox",
name: "slugs", name: "slugs",
message: `请选择要删除的分支(空格选中,回车确认,至少选 ${needRemoveCount} 个,不选直接回车则取消部署)`, message: `请选择要删除的分支(空格选中,回车确认,至少选 ${minCount} 个,不选直接回车则${cancelText})`,
choices: deployEntries.map((item) => ({ choices: deployEntries.map((item) => ({
name: `${item.branchName} (${item.slug}) ${item.deployedAt}`, name: `${item.branchName} (${item.slug}) ${item.deployedAt}`,
value: item.slug, value: item.slug,
})), })),
validate: (value) => validate: (value) =>
value.length === 0 || value.length >= needRemoveCount value.length === 0 || value.length >= minCount
? true ? true
: `至少要选 ${needRemoveCount} 个才有空位`, : `至少要选 ${minCount} 个`,
}, },
]); ]);
if (slugs.length === 0) { if (slugs.length === 0) {
console.log("已取消部署"); console.log(`已${cancelText}`);
return null; return null;
} }
...@@ -477,7 +486,7 @@ async function cleanUpBranches(deployEntries) { ...@@ -477,7 +486,7 @@ async function cleanUpBranches(deployEntries) {
]); ]);
if (!confirmed) { if (!confirmed) {
console.log("已取消部署"); console.log(`已${cancelText}`);
return null; return null;
} }
...@@ -493,16 +502,45 @@ async function cleanUpBranches(deployEntries) { ...@@ -493,16 +502,45 @@ async function cleanUpBranches(deployEntries) {
return deployEntries.filter((item) => !slugs.includes(item.slug)); return deployEntries.filter((item) => !slugs.includes(item.slug));
} }
// 主动删除已部署的分支(需求做完不需要留着了),删完刷新导航页
async function removeOnly() {
const deployEntries = await getDeployEntries(await getFolderList());
if (deployEntries.length === 0) {
console.log("服务器上没有已部署的分支");
return;
}
const remaining = await removeBranches(deployEntries, {
minCount: 1,
intro: `当前已部署 ${deployEntries.length} 个分支:`,
cancelText: "取消删除",
});
if (!remaining) return;
await writeIndexHtml(remaining, { includeCurrent: false });
}
async function start() { async function start() {
try { try {
await connectServer(); await connectServer();
if (isRemoveMode) {
await removeOnly();
return;
}
const folderList = await getFolderList(); const folderList = await getFolderList();
let deployEntries = await getDeployEntries(folderList); let deployEntries = await getDeployEntries(folderList);
const alreadyDeployed = deployEntries.some((item) => item.slug === deploySlug); const alreadyDeployed = deployEntries.some((item) => item.slug === deploySlug);
if (!alreadyDeployed && deployEntries.length >= MAX_DEPLOY_BRANCH_COUNT) { if (!alreadyDeployed && deployEntries.length >= MAX_DEPLOY_BRANCH_COUNT) {
deployEntries = await cleanUpBranches(deployEntries); deployEntries = await removeBranches(deployEntries, {
minCount: deployEntries.length - MAX_DEPLOY_BRANCH_COUNT + 1,
intro: `当前已部署分支数量已达到上限 ${MAX_DEPLOY_BRANCH_COUNT},需要先删除至少 ${
deployEntries.length - MAX_DEPLOY_BRANCH_COUNT + 1
} 个:`,
cancelText: "取消部署",
});
if (!deployEntries) { if (!deployEntries) {
return; return;
} }
...@@ -522,7 +560,7 @@ async function start() { ...@@ -522,7 +560,7 @@ async function start() {
if (conn) { if (conn) {
conn.end(); conn.end();
} }
delZip(); if (!isRemoveMode) delZip();
} }
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
"build:dev": "nvm use 20 && vite build --mode development", "build:dev": "nvm use 20 && vite build --mode development",
"build:test": "nvm use 20 && vite build --mode test", "build:test": "nvm use 20 && vite build --mode test",
"deploy:test": "node --env-file=.env.local deploy/deploytest.js", "deploy:test": "node --env-file=.env.local deploy/deploytest.js",
"remove:test-branch": "node --env-file=.env.local deploy/deploytest.js --remove",
"build:prod": "nvm use 20 && vite build --mode production", "build:prod": "nvm use 20 && vite build --mode production",
"deploy:prod": "node --env-file=.env.local deploy/deployprod.js", "deploy:prod": "node --env-file=.env.local deploy/deployprod.js",
"deploy:prod:update-info": "node --env-file=.env.local deploy/deployprod.js --update-info" "deploy:prod:update-info": "node --env-file=.env.local deploy/deployprod.js --update-info"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment