91 lines
2.7 KiB
JavaScript
91 lines
2.7 KiB
JavaScript
import { NodeSSH } from 'node-ssh';
|
|
import path from 'node:path';
|
|
import fs from 'node:fs';
|
|
import process from 'node:process';
|
|
|
|
// 动态导入配置文件(支持文件不存在的情况)
|
|
const loadConfig = async () => {
|
|
try {
|
|
const { default: config } = await import('./.deployrc.js');
|
|
return config;
|
|
} catch (error) {
|
|
if (error.code !== 'ERR_MODULE_NOT_FOUND') throw error;
|
|
|
|
return {
|
|
host: process.env.DEPLOY_HOST,
|
|
port: process.env.DEPLOY_PORT || 22,
|
|
username: process.env.DEPLOY_USER,
|
|
password: process.env.DEPLOY_PASSWORD,
|
|
privateKey: process.env.DEPLOY_PRIVATE_KEY_PATH ? fs.readFileSync(path.resolve(process.env.DEPLOY_PRIVATE_KEY_PATH)) : null,
|
|
remotePath: process.env.DEPLOY_REMOTE_PATH,
|
|
cleanRemote: process.env.DEPLOY_CLEAN === 'true'
|
|
};
|
|
}
|
|
};
|
|
|
|
const ssh = new NodeSSH();
|
|
|
|
async function deploy() {
|
|
try {
|
|
// 加载配置
|
|
const config = await loadConfig();
|
|
validateConfig(config);
|
|
|
|
// 连接服务器
|
|
console.log('🔄 Connecting to server...');
|
|
await ssh.connect({
|
|
host: config.host,
|
|
port: config.port,
|
|
username: config.username,
|
|
password: config.password,
|
|
privateKey: config.privateKey
|
|
});
|
|
|
|
// 确保目录存在
|
|
console.log('📂 Ensuring remote directory exists...');
|
|
await ssh.execCommand(`mkdir -p ${config.remotePath}`);
|
|
|
|
// 清理目录
|
|
if (config.cleanRemote) {
|
|
console.log('🧹 Cleaning remote directory...');
|
|
await ssh.execCommand(`rm -rf ${config.remotePath}/*`);
|
|
}
|
|
|
|
// 上传文件
|
|
console.log('🚀 Uploading files...');
|
|
const uploadResult = await ssh.putDirectory('./dist', config.remotePath, {
|
|
recursive: true,
|
|
concurrency: 10,
|
|
tick: (localPath, remotePath, error) => {
|
|
const relativePath = path.relative(process.cwd(), localPath);
|
|
console[error ? 'error' : 'log'](`${error ? '❌' : '✅'} ${error ? 'Failed' : 'Uploaded'}: ${relativePath}`);
|
|
}
|
|
});
|
|
|
|
if (!uploadResult) throw new Error('Upload failed without specific error');
|
|
|
|
console.log('🎉 Deployment completed successfully!');
|
|
} catch (error) {
|
|
console.error('🔥 Deployment failed:', error.message);
|
|
process.exit(1);
|
|
} finally {
|
|
ssh.dispose();
|
|
}
|
|
}
|
|
|
|
function validateConfig(config) {
|
|
const requiredFields = ['host', 'username', 'remotePath'];
|
|
const missing = requiredFields.filter((field) => !config[field]);
|
|
|
|
if (missing.length) {
|
|
throw new Error(`Missing required config fields: ${missing.join(', ')}`);
|
|
}
|
|
|
|
if (!config.password && !config.privateKey) {
|
|
throw new Error('Either password or privateKey must be provided');
|
|
}
|
|
}
|
|
|
|
// 执行部署
|
|
deploy();
|