简单的node服务自动更新脚本
# 图例

# 实现思路
设置需要监听的 node 服务的 js 文件,服务器上 node 服务的文件路径,以及更新重启服务的命令
根据最近一次的 git-commit 记录,判断 node 服务的 js 代码文件,是否有提交记录
- 通过
git diff --name-only HEAD^ HEAD
查询最近一次的 commit 记录中的文件变更列表
- 通过
如果有的话,将全量的 node 服务文件 push 到服务器端的指定路径(也可以只 push 更改的 js 服务文件),push 成功后,执行重启重启服务的命令
通过 ssh2 的 sftp 子模块, 连接到服务器并且推送文件到服务器
通过 ssh2 的 exec 方法,执行 shell 命令,可以启动在服务器上的 node 服务
如果没有,结束
# 实现代码
点击 展开/收起 jsx 代码
import fs from 'fs';
import { Client } from 'ssh2';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
// 获取当前模块的文件路径
const __filename = fileURLToPath(import.meta.url);
// 获取当前模块的目录路径
const __dirname = path.dirname(__filename);
// 检查最近一次 commit 是否有变更指定文件
const checkCommitChanges = (filepaths) => {
try {
// 获取最近一次 commit 的文件变更列表
const changes = execSync('git diff --name-only HEAD^ HEAD')
.toString()
.trim()
.split('\n');
// 检查变更的文件列表中是否包含指定文件
const hasChanges = changes.some((file) => filepaths.includes(file));
return hasChanges;
} catch (error) {
console.error('Error checking commit changes:', error);
return false;
}
};
const sqlServicePaths = [
'scripts/consts.mjs',
'scripts/mysql.mjs',
'scripts/batchRunTask.mjs',
'scripts/publishMessage.mjs',
'scripts/swRoute.mjs',
// ...
];
const remoteSqlPath = '/etc/nginx/service/SqlService';
const remoteSqlExecute = 'sudo systemctl restart myService.service';
// 上传文件 并 执行命令
const uploadAndExecute = async (localPaths, remotePath, sshConfig, execute) => {
return new Promise((resolve, reject) => {
const conn = new Client();
conn
.on('ready', () => {
console.log('连接成功,开始上传文件...');
conn.sftp((err, sftp) => {
if (err) {
// console.error('SFTP 错误:', err);
conn.end();
return;
}
let uploadCount = 0;
localPaths.forEach((pathItem) => {
const localPath = path.join(__dirname, pathItem);
const remoteFilePath = `${remotePath}/${path.basename(pathItem)}`;
// 获取本地文件大小
const fileSize = (fs.statSync(localPath).size / 1024).toFixed(1);
// 获取本地文件行数
fs.readFile(localPath, 'utf8', (readErr, readData) => {
if (readErr) {
console.error('读取文件错误:', readErr);
conn.end();
return reject(readErr);
}
const lines = readData.split(/\r\n|\r|\n/).length;
sftp.fastPut(localPath, remoteFilePath, {}, (err) => {
if (err) {
// console.error('上传错误:', err);
conn.end();
return;
}
console.log(
`文件上传成功: ${pathItem.padEnd(30, ' ')}大小: ${fileSize
.toString()
.padEnd(6, ' ')}KB 行数: ${lines}`,
);
uploadCount++;
// 如果所有文件上传完成,执行指定命令
if (uploadCount === localPaths.length) {
console.log(`开始执行服务重启命令: ${execute} ...`);
conn.exec(`${execute}`, (err, stream) => {
if (err) {
// console.error('执行命令错误:', err);
conn.end();
return;
}
stream
.on('close', (code, sinal) => {
console.log('服务重启完成,关闭连接...');
conn.end();
resolve();
})
.on('data', (data) => {
// console.log('STDOUT', data.toString())
})
.stderr.on('data', (errData) => {
// console.error('STDERR', errData.toString())
reject(errData.toString());
});
});
}
});
});
});
});
})
.connect(sshConfig);
});
};
const main = async () => {
try {
console.log('正在检测后端文件是否变更...');
const hasSqlChange = checkCommitChanges(sqlServicePaths);
if (hasSqlChange) {
console.log('存在 sqlService 文件变更...');
await uploadAndExecute(
sqlServicePaths,
remoteSqlPath,
sshConfig,
remoteSqlExecute,
);
console.log('上传文件并执行 sqlService 重启命令完成\n');
} else {
console.log('无 sqlService 文件变更!!\n');
}
console.log('全部任务执行完成!!!');
console.log(
'--------------------------------------------------------------------------',
);
} catch (error) {
console.error('操作失败:', error);
process.exit(1); // 停止构建过程
}
};
main();
← DNS预解析脚本