本文主要讲解如何使用nodejs调用Jenkins的API进行自动化部署。
应用场景
目前团队中前端项目的部署都基于Jenkins,每次发布时候前端需要登录Jenkins的管理后台,找到对应的JOB然后手动执行任务。
在测试期间,会频繁的做合并分支和手动执行部署Jenkins的任务的操作。
本着重复的低价值的工作应该交给工具的理念,我们期望简化这一系列的操作,最好能自动化。
我们有两个选择:
- 通过设置Jenkins任务的定期运行解决;
- 将部署前的所有操作都集成到命令行工具中,实现本地一行命令完成部署的所有操作;
我们选择了第2种方案,因为本地命令行工具基于Nodejs开发,所以需要在完成分支合并后调用Jenkins API进行项目的打包部署。
思路
- 通过API触发打包任务;
- 获取日志流输出到当前控制台;
- 打包任务完成通过API获取是否执行成功;
实施
我们找到了Jenkins的NPM包 www.npmjs.com/package/jen…
我们主要使用以下几个API:
jenkins.job.build发起打包任务;
jenkins.build.logStream 获取日志流;
jenkins.build.get 任务结束时候获取任务执行结果;
但是有个问题,jenkins.job.build执行后只能获取到 queueId,但是获取日志流需要用到 buildId,因此我们需要用 queueId换 buildId,所以我们找到了另外一个API:
jenkins.queue.item 通过 queueId查询当前任务的详情;
但是我们又要面临另外一个问题,jenkins.job.build发起打包任务是一个异步操作,什么时候创建任务成功并加入队列,这个时间是不确定的。但是要通过 queueId查询当前任务的详情必须是在任务创建完成才可以查下到。
我们的处理办法,做一个轮询去查询任务的详情,查询到任务详情之后再去获取日志流。
核心脚本如下:
const jenkinsApi = require('jenkins');
...
async function publish(options) {
const { jenkinsHost, jenkinsUser, jenkinsToken, jenkinsJob, jenkinsParameters, buildBranch, globalConfig, config, prod } = options;
if (!config.jenkinsUser || !config.jenkinsToken) {
log.error('Jenkins账号和密码输入异常,打包终止!');
return
}
const baseUrl = `http://${config.jenkinsUser}:${config.jenkinsToken}@${config.jenkinsHost}`;
// crumbIssuer 默认false, true 启用CSRF保护支持
const jenkins = jenkinsApi({ baseUrl, crumbIssuer: true });
const jobName = config.jenkinsJob
let count = 1;
return new Promise((resolve, reject) => {
const params = {
name: jobName
};
// 启动Jenkins打包任务
jenkins.job.build(
params,
function (err, queueId) {
if (err) {
reject(err);
log.error(err.message);
return;
};
log.info(`============ Jenkins任务[${jobName}]开始 ============`)
log.notice('当前任务的队列ID:' + queueId);
// 2秒之后再进行log的获取,防止buildId还未生成的情况
setTimeout(() => { exceLog(queueId) }, 2000);
});
// 轮询 通过queueId获取任务详情
function exceLog(queueId) {
jenkins.queue.item(queueId, function (err, data) {
if (err) {
reject(err);
throw err
};
// 获取任务ID次数超过10次则终止
if (count > 10) {
log.error(`Jenkins任务[${jobName}]获取buildId请求超时!`)
reject(`Jenkins任务[${jobName}]获取buildId请求超时!`);
return
}
if (!data.executable) {
count++;
// 2秒之后再进行log的获取,防止buildId还未生成的情况
setTimeout(() => { exceLog(queueId) }, 2000);
return;
}
const lastBuildNumber = data.executable.number
log.notice('打包任务ID:' + lastBuildNumber);
//获取到 lastBuildNumber 则进行下一步的日志输出
logStream(lastBuildNumber)
});
}
// 日志流处理
function logStream(buildNumber) {
var logAction = jenkins.build.logStream({
name: jobName,
number: buildNumber
});
logAction.on('data', function (text) {
process.stdout.write(text);
});
logAction.on('error', function (err) {
log.error(err);
reject(err);
});
logAction.on('end', async () => {
log.info('============ Jenkins任务结束 ============')
// 任务结束获取任务是否执行成功
jenkins.build.get(jobName, buildNumber, async (err, data) => {
if (err) {
reject(err);
throw err
};
log.verbose("build result data", data);
const result = data.result || '';
resolve(result.toUpperCase() == 'SUCCESS' ? true : false);
});
});
}
});
}
以上,即可完成Nodejs调用Jenkins任务完成自动化部署。