This commit is contained in:
2026-04-07 20:16:40 +08:00
parent 978821fe19
commit 51f9ef04ba
10 changed files with 244 additions and 259 deletions

167
deploy.js
View File

@@ -6,10 +6,11 @@ const path = require('node:path');
const CONFIG = {
// 1. 开发源码所在的根目录
devProjectRoot: path.join(__dirname, './'),
// 2. phpStudy 实际运行网站的目录 (目标位置)
phpStudyPath: 'D:/phpstudy_pro/WWW/alert',
// 2. 实际运行网站的目录 (目标位置)
prodProjectPath: 'D:\\phpstudy_pro\\WWW\\alert',
deployRemote: false,
// 需要配置ssh密钥对否则会提示输入密码
remote: {
host: '1.2.3.4', // 服务器IP
user: 'root', // 用户名
@@ -19,8 +20,8 @@ const CONFIG = {
// 预定义几个常用路径
const DEV_DIST = path.join(CONFIG.devProjectRoot, 'dist');
const PHPSTUDY_RELEASES = path.join(CONFIG.phpStudyPath, 'releases');
const PHPSTUDY_CURRENT = path.join(CONFIG.phpStudyPath, 'current');
const PROD_RELEASES = path.join(CONFIG.prodProjectPath, 'releases');
const PROD_CURRENT = path.join(CONFIG.prodProjectPath, 'current');
// 颜色工具
const log = {
@@ -30,9 +31,68 @@ const log = {
error: (msg) => console.log(`\x1b[31m✘ ${msg}\x1b[0m`),
};
async function start() {
let version = '';
async function main() {
// 解析参数: node scripts/deploy.js --rollback v1.0.0
const args = process.argv.slice(2);
const isStatus = args[0] === '--status' || args[0] === '-s';
const isRollback = args[0] === '--rollback' || args[0] === '-r';
const targetVersion = isRollback ? args[1] : '';
if (isStatus) {
showStatus();
return; // 结束执行
}
if (isRollback) {
if (!targetVersion) {
log.error(
'请指定要切换的版本号,例如: node scripts/deploy.js --rollback v1.0.0',
);
process.exit(1);
}
await performRollback(targetVersion);
} else {
await performFullDeploy();
}
}
// --- 【逻辑函数:秒级回退/切换】 ---
async function performRollback(version) {
log.info(`开始秒级切换至版本: ${version}...`);
// 1. 检查本地是否存在该版本
const localTarget = path.join(PROD_RELEASES, version);
if (!fs.existsSync(localTarget)) {
log.error(
`本地 releases 目录中未找到版本 ${version},请检查路径: ${localTarget}`,
);
process.exit(1);
}
// 2. 切换本地软链接
try {
updateLocalLink(localTarget);
log.success(`本地 Prod项目 已成功切换至 ${version}`);
} catch (e) {
log.error(`本地切换失败: ${e.message}`);
}
// 3. 切换远程软链接
if (CONFIG.deployRemote) {
try {
updateRemoteLink(version);
log.success(`远程服务器已成功切换至 ${version}`);
} catch (e) {
log.error(`远程切换失败: ${e.message}`);
}
}
log.info('切换任务结束。');
}
// --- 【逻辑函数:全流程发布】 ---
async function performFullDeploy() {
let version = '';
// 1. 获取 Git Tag
try {
version = execSync('git describe --tags --abbrev=0').toString().trim();
@@ -42,6 +102,8 @@ async function start() {
process.exit(1);
}
log.info(`开始全流程发布版本: ${version}`);
// 2. 执行编译
try {
log.info('开始执行构建 (npm run build)...');
@@ -71,14 +133,27 @@ async function start() {
log.warn('您可以稍后手动补传,部署将继续进行...');
}
// 4. 清理并本地归档
// 4. 清理 Map
try {
log.info('正在清理.map 文件并执行本地归档...');
log.info('正在尝试清理 SourceMap...');
cleanSourceMaps(DEV_DIST);
deployToLocal(version);
log.success('本地归档与软链接更新完成');
log.success('SourceMap 清理成功');
} catch (_e) {
log.error(`本地归档失败: ${_e.message}`);
log.error(`SourceMap 清理失败: ${_e.message}`);
}
// D. 归档到本地 Prod项目
try {
const targetDir = path.join(PROD_RELEASES, version);
if (fs.existsSync(targetDir))
fs.rmSync(targetDir, { recursive: true, force: true });
if (!fs.existsSync(PROD_RELEASES))
fs.mkdirSync(PROD_RELEASES, { recursive: true });
fs.cpSync(DEV_DIST, targetDir, { recursive: true });
updateLocalLink(targetDir);
log.success('本地部署成功');
} catch (e) {
log.error(`本地归档失败: ${e.message}`);
process.exit(1);
}
@@ -100,43 +175,55 @@ async function start() {
log.info(`\n🎉 所有任务执行完毕 [Version: ${version}]`);
}
// --- 逻辑封装函数 ---
// --- 新增状态查看函数 ---
function showStatus() {
log.info('正在查询当前版本状态...');
function deployToLocal(version) {
const targetDir = path.join(PHPSTUDY_RELEASES, version);
// A. 确保 phpStudy 下的 releases 文件夹存在
if (!fs.existsSync(PHPSTUDY_RELEASES)) {
fs.mkdirSync(PHPSTUDY_RELEASES, { recursive: true });
// 1. 查看本地版本 (Node.js 获取软链接指向的真实路径)
if (fs.existsSync(PROD_CURRENT)) {
// realpathSync 可以获取软链接指向的真实绝对路径
const realPath = fs.realpathSync(PROD_CURRENT);
const version = path.basename(realPath);
log.success(`本地 Prod 当前版本: ${version}`);
log.info(` 路径: ${realPath}`);
} else {
log.warn('本地尚未创建 current 软链接。');
}
// B. 如果该版本已存在,先删除旧的
if (fs.existsSync(targetDir)) {
fs.rmSync(targetDir, { recursive: true, force: true });
}
// C. 【关键】将开发目录下的 dist 复制到 phpStudy 对应的版本目录
// fs.cpSync 需要 Node.js v16.7.0+
try {
fs.cpSync(DEV_DIST, targetDir, { recursive: true });
log.success(`已将产物复制到: ${targetDir}`);
} catch (_e) {
throw new Error(`复制产物到 phpStudy 目录失败: ${_e.message}`);
}
// D. 更新 phpStudy 目录下的软链接 (Junction)
if (fs.existsSync(PHPSTUDY_CURRENT)) {
// 2. 查看远程版本 (通过 SSH 执行 readlink)
if (CONFIG.deployRemote) {
try {
// Windows 下删除目录链接
fs.rmSync(PHPSTUDY_CURRENT, { recursive: true, force: true });
const { host, user, path: remotePath } = CONFIG.remote;
const remoteCurrentLink = `${remotePath}/current`;
// readlink -f 可以获取链接指向的完整路径
const cmd = `ssh ${user}@${host} "readlink -f ${remoteCurrentLink}"`;
const remoteRealPath = execSync(cmd).toString().trim();
const remoteVersion = path.basename(remoteRealPath);
log.success(`远程服务器当前版本: ${remoteVersion}`);
} catch (_e) {
throw new Error(`无法删除 phpStudy 旧的软链接: ${_e.message}`);
log.error('无法获取远程版本状态,请检查 SSH 连接。');
}
}
}
// 在 phpStudy 目录下创建链接,指向刚复制过去的版本
// --- 【通用工具函数】 ---
function updateLocalLink(targetDir) {
if (fs.existsSync(PROD_CURRENT)) {
fs.rmSync(PROD_CURRENT, { recursive: true, force: true });
}
const linkType = process.platform === 'win32' ? 'junction' : 'dir';
fs.symlinkSync(targetDir, PHPSTUDY_CURRENT, linkType);
fs.symlinkSync(targetDir, PROD_CURRENT, linkType);
}
function updateRemoteLink(version) {
const { host, user, path: remotePath } = CONFIG.remote;
const remoteVersionDir = `${remotePath}/releases/${version}`;
const remoteCurrentLink = `${remotePath}/current`;
// 直接发送 ln 命令,前提是该目录已经在服务器 releases 里了
execSync(
`ssh ${user}@${host} "ln -snf ${remoteVersionDir} ${remoteCurrentLink}"`,
);
}
function deployToRemote(version) {
@@ -188,4 +275,4 @@ function cleanSourceMaps(dir) {
}
// 启动执行
start();
main();