Files
test-alert/deploy.js
2026-04-07 14:57:26 +08:00

192 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const { execSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
// --- 【配置区】 ---
const CONFIG = {
// 1. 开发源码所在的根目录
devProjectRoot: path.join(__dirname, './'),
// 2. phpStudy 实际运行网站的目录 (目标位置)
phpStudyPath: 'D:/phpstudy_pro/WWW/alert',
deployRemote: false,
remote: {
host: '1.2.3.4', // 服务器IP
user: 'root', // 用户名
path: '/www/wwwroot/my-app', // 服务器项目根目录
},
};
// 预定义几个常用路径
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 log = {
info: (msg) => console.log(`\x1b[36m>>> ${msg}\x1b[0m`),
success: (msg) => console.log(`\x1b[32m✔ ${msg}\x1b[0m`),
warn: (msg) => console.log(`\x1b[33m${msg}\x1b[0m`),
error: (msg) => console.log(`\x1b[31m✘ ${msg}\x1b[0m`),
};
async function start() {
let version = '';
// 1. 获取 Git Tag
try {
version = execSync('git describe --tags --abbrev=0').toString().trim();
log.success(`读取到目标版本: ${version}`);
} catch (_e) {
log.error('无法获取 Git Tag。请确保本地有 tag (例如执行: git tag v1.0.0)');
process.exit(1);
}
// 2. 执行编译
try {
log.info('开始执行构建 (npm run build)...');
execSync('npm run build', {
stdio: 'inherit',
env: { ...process.env, UMI_APP_SENTRY_RELEASE: version },
});
log.success('项目构建成功');
} catch (_e) {
log.error('项目构建失败,请检查代码语法或 Umi/Webpack 配置');
process.exit(1);
}
// 3. 上传 SourceMap
try {
log.info('正在尝试上传 SourceMap 到 GlitchTip...');
// 如果这里报错,通常不中断部署,因为可能是网络波动,以后可以补传
execSync(
`sentry-cli releases files "${version}" upload-sourcemaps ./dist --url-prefix "~/"`,
{ stdio: 'inherit' },
);
log.success('SourceMap 上传成功');
} catch (_e) {
log.warn(
'SourceMap 上传失败原因可能是sentry-cli 未安装、环境变量未配置、.sentryclirc 文件未配置或网络不通。',
);
log.warn('您可以稍后手动补传,部署将继续进行...');
}
// 4. 清理并本地归档
try {
log.info('正在清理.map 文件并执行本地归档...');
cleanSourceMaps(DEV_DIST);
deployToLocal(version);
log.success('本地归档与软链接更新完成');
} catch (_e) {
log.error(`本地归档失败: ${_e.message}`);
process.exit(1);
}
// 5. 远程部署
if (CONFIG.deployRemote) {
try {
log.info(`准备向远程服务器部署: ${CONFIG.remote.host}...`);
deployToRemote(version);
log.success('远程部署任务全部完成');
} catch (_e) {
log.error(`远程部署失败!错误详情: ${_e.message}`);
log.error(
'请检查1.服务器IP/路径是否正确2.是否配置了 SSH 免密登录3.服务器磁盘空间是否充足。',
);
process.exit(1);
}
}
log.info(`\n🎉 所有任务执行完毕 [Version: ${version}]`);
}
// --- 逻辑封装函数 ---
function deployToLocal(version) {
const targetDir = path.join(PHPSTUDY_RELEASES, version);
// A. 确保 phpStudy 下的 releases 文件夹存在
if (!fs.existsSync(PHPSTUDY_RELEASES)) {
fs.mkdirSync(PHPSTUDY_RELEASES, { recursive: true });
}
// 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)) {
try {
// Windows 下删除目录链接
fs.rmSync(PHPSTUDY_CURRENT, { recursive: true, force: true });
} catch (_e) {
throw new Error(`无法删除 phpStudy 旧的软链接: ${_e.message}`);
}
}
// 在 phpStudy 目录下创建链接,指向刚复制过去的版本
const linkType = process.platform === 'win32' ? 'junction' : 'dir';
fs.symlinkSync(targetDir, PHPSTUDY_CURRENT, linkType);
}
function deployToRemote(version) {
const { host, user, path: remotePath } = CONFIG.remote;
const localVersionDir = path.join(CONFIG.localReleasesDir, version);
const remoteVersionDir = `${remotePath}/releases/${version}`;
const remoteCurrentLink = `${remotePath}/current`;
// A. 在服务器创建目录
try {
execSync(`ssh ${user}@${host} "mkdir -p ${remotePath}/releases"`, {
timeout: 10000,
});
} catch (_e) {
throw new Error(`无法连接服务器或创建目录: ${_e.message}`);
}
// B. 使用 scp 上传文件夹
try {
log.info(` 正在上传至: ${remoteVersionDir} (请耐心等待...)`);
execSync(
`scp -r "${localVersionDir}" ${user}@${host}:${remotePath}/releases/`,
{ timeout: 60000 },
);
} catch (_e) {
throw new Error(`文件传输失败 (SCP): ${_e.message}`);
}
// C. 更新远程软链接
try {
const sshCmd = `ln -snf ${remoteVersionDir} ${remoteCurrentLink}`;
execSync(`ssh ${user}@${host} "${sshCmd}"`);
} catch (_e) {
throw new Error(`远程软链接更新失败: ${_e.message}`);
}
}
function cleanSourceMaps(dir) {
if (!fs.existsSync(dir)) return;
const files = fs.readdirSync(dir);
for (const file of files) {
const fullPath = path.join(dir, file);
if (fs.statSync(fullPath).isDirectory()) {
cleanSourceMaps(fullPath);
} else if (file.endsWith('.map')) {
fs.unlinkSync(fullPath);
}
}
}
// 启动执行
start();