likes
comments
collection
share

教你如何帅气地启动jenkins打包任务🤣

作者站长头像
站长
· 阅读数 24

背景

在日常开发中经常会有需要构建多个打包任务的情况,每次都要打开jenkins客户端,然后逐个输入打包,非常繁琐。特别是公司又安装了监控软件,每次打开浏览器都要卡顿几秒。于是便萌生了通过node命令行来执行jenkins任务构建的想法。

基本实现

安装

由于我们的服务依赖jenkins包,所以需要在项目初始化之后对jenkins进行安装,执行如下命令行:

npm i jenkins

jenkins依赖包的API文档在这里,大家可以参考官方文档:github.com/silas/node-…

获取jenkins服务信息

然后我们在根目录下创建index.js文件,通过写入jenkins服务地址与jenkins账号,进行第一次登录尝试:

// index.js
const Jenkins = require("jenkins")
// 账号
const account = 'admin'
// 密码
const password = 'admin'
// jenkins服务地址
const host = 'localhost:8080'
// 用来存放jenkins实例
let jenkins

// 初始化jenkins实例
function initJenkins({ account, password } = {}){
    // 拼接baseUrl
    const baseUrl = `http://${account}:${password}@${host}`;
    // 创建Jenkins实例
    jenkins = new Jenkins({ baseUrl, crumbIssuer: true })
    // 获取实例信息
    jenkins.info().then(res=>{
        console.log(res)
    })
}

initJenkins({account, password})

在根目录终端中执行node脚本文件:

node index.js

此时只要我们输入正确的账号密码和服务地址,应该就能看到控制台输出jenkins信息:

{
  "assignedLabels": [{}],
  "description": null,
  "jobs": [
    {
      "color": "blue",
      "name": "example",
      "url": "http://localhost:8080/job/example/"
    }
  ],
  "mode": "NORMAL",
  "nodeDescription": "the master Jenkins node",
  "nodeName": "",
  "numExecutors": 2,
  "overallLoad": {},
  "primaryView": {
    "name": "All",
    "url": "http://localhost:8080/"
  },
  "quietingDown": false,
  "slaveAgentPort": 12345,
  "unlabeledLoad": {},
  "useCrumbs": false,
  "useSecurity": false,
  "views": [
    {
      "name": "All",
      "url": "http://localhost:8080/"
    }
  ]
}

构建打包任务

至此我们已经成功连接上了jenkins服务,是不是非常简单!此时我们快速构建jenkins任务只需要调用build方法。

// index.js
const Jenkins = require("jenkins")
// 账号
const account = 'admin'
// 密码
const password = 'admin'
// jenkins服务地址
const host = 'localhost:8080'
// 用来存放jenkins实例
let jenkins
// 打包任务的名称,这里需要修改成你自己的任务名称哦!
let jobName = 'JOB_NAME'

// 初始化jenkins实例
async function initJenkins({ account, password } = {}){
    // 拼接baseUrl
    const baseUrl = `http://${account}:${password}@${host}`;
    // 创建Jenkins实例
    jenkins = new Jenkins({ baseUrl, crumbIssuer: true })
}

// 构建打包任务
async function hdBuild(){
    await jenkins.job.build(jobName);
    console.log('打包任务开始了!')
}
// 主入口函数
async function main (){
    await initJenkins({account, password})
    hdBuild()
}

main()

现在我们打开浏览器,访问jenkins就可以看到打包任务已经开始了!

教你如何帅气地启动jenkins打包任务🤣

交互式命令行工具 Inquirer

虽然我们通过node命令轻轻松松构建了jenkins打包任务,但是细究下来,这其中还是有很多小细节需要优化。比如:

  1. 这个脚本工具是打算给我们项目组成员使用的,应该支持大家登录自己的账号进行打包
  2. jenkins服务上面的任务有多个,应该支持选择job进行打包
  3. 打包任务应该支持调整参数

我们可以使用inquirer来实现这些问答形式的交互,通过inquirer我们可以根据用户的输入来执行对应的操作,执行依赖安装:

npm i inquirer

基本用法

const inquirer = require('inquirer')

inquirer
  .prompt([
    /* 输入问题 */
  ])
  .then((answers) => {
    // 通过用户输入执行操作!!
  })
  .catch((error) => {
    // 捕获报错
  })

关于inquirer的使用方法这里不再赘述,大家可以参考官方文档:github.com/SBoudrias/I…

定义账号密码prompt配置

此处我们开始构建自己的prompt数组,由于项目组使用的jenkins服务地址是固定的,账号密码则通过问答的方式由用户自己键入:

// 账号密码prompt

const prompt = [
  {
    type: 'input',
    name: 'account',
    message: '请输入jenkins账号',
    validate(input) {
      if (!input || input.length === 0) {
        return '请输入jenkins账号!'
      }
      return true
    }
  },
  {
    type: 'password',
    name: 'password',
    message: '请输入jenkins密码',
    validate(input) {
      if (!input || input.length === 0) {
        return '请输入jenkins密码!'
      }
      return true
    }
  }
]

定义打包仓库与打包参数prompt配置

// 打包仓库和打包参数prompt
// 需要获取jenkins的job列表来动态生成prompt数组,因此封装成函数生成

function getJobPrompt(list) {
    return [
        {
            // 这里的打包仓库支持多选
            type: 'list',
            name: 'repository',
            message: '请选择需要打包的仓库(空格选择)',
            choices: list?.map(it => it.name) ?? [],
            validate(input) {
                if (!input || input.length === 0) {
                    return '请选择至少一个仓库!'
                }
                return true
            }
        },
        // 如果job构建任务配置了参数
        {
            type: 'input',
            name: 'params',
            message: '请输入需要构建参数Demo-params',
            validate(input) {
                if (!input || input.length === 0) {
                    return '请输入需要构建参数Demo-params!'
                }
                return true
            },
        }
    ]
}

完整代码

// index.js
const Jenkins = require("jenkins")
const inquirer = require('inquirer')

// jenkins服务地址,TODO
const host = 'localhost:8080'

let jenkins;

let jobList = [];

// 问题数组
const prompt = [
    {
        type: 'input',
        name: 'account',
        message: '请输入jenkins账号',
        validate(input) {
            if (!input || input.length === 0) {
                return '请输入jenkins账号!'
            }
            return true
        }
    },
    {
        type: 'password',
        name: 'password',
        message: '请输入jenkins密码',
        validate(input) {
            if (!input || input.length === 0) {
                return '请输入jenkins密码!'
            }
            return true
        }
    }
]
// 可以根据自己项目需要设计对应的prompt,TODU
function getJobPrompt(list) {
    return [
        {
            // 这里的打包仓库支持多选
            type: 'list',
            name: 'repository',
            message: '请选择需要打包的仓库(空格选择)',
            choices: list?.map(it => it.name) ?? [],
            validate(input) {
                if (!input || input.length === 0) {
                    return '请选择至少一个仓库!'
                }
                return true
            }
        },
        // 如果job构建任务配置了参数
        {
            type: 'input',
            name: 'params',
            message: '请输入需要构建参数Demo-params',
            validate(input) {
                if (!input || input.length === 0) {
                    return '请输入需要构建参数Demo-params!'
                }
                return true
            },
        }
    ]
}

// 初始化jenkins实例
async function initJenkins({ account, password } = {}) {
    try {
        // 拼接baseUrl
        const baseUrl = `http://${account}:${password}@${host}`;
        // 创建Jenkins实例
        jenkins = new Jenkins({ baseUrl, crumbIssuer: true })
        // 通过获取jenkins信息验证登录结果
        await jenkins.info() 
        // 获取jenkins服务的job任务列表
        const res = await jenkins.job.list()
        // 动态生成prompt
        jobListPrompt = getJobPrompt(res)
        return true
    } catch (error) {
        console.log('获取jenkins服务信息出错了!')
        return false
    }
}

async function hdBuild({ repository, params }) {
    await jenkins.job.build(repository,
        // 这里传入job构建时需要的参数信息
        {
            parameters: {
                // 替换成自己job任务的配置参数
                'Demo-params': params,  
            }
        });
    console.log('打包任务已经开始了!')
}

async function main() {
    // 引导用户输入密码
    const answers = await inquirer.prompt(prompt)
    // 初始化jenkins
    const isLogin = await initJenkins(answers)
    // 登录状态
    if(isLogin){
        // 引导用户输入打包仓库和构建参数
        const buildInfo = await inquirer.prompt(jobListPrompt)
        // 执行打包任务
        hdBuild(buildInfo)
    }
}

main()

目前为止,我们已经完成了大部分的功能代码,大家可以将代码复制粘贴到本地,执行以下代码调整(上面注释中TODO部分代码):

  1. 将代码中的jenkins服务地址改成自己真实项目的服务地址(host
  2. 根据自己项目需要设计对应的prompt,也可以按照前面的代码进行调整(getJobPrompt

运行脚本,便能够在本地进行功能验证了!

全局安装命令行

现在基本功能已经实现,我们还希望能够在随意的终端运行指令来实现打包功能,修改一下package.json的代码:

// package.json
{
  "name": "jk-build",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  // 新增
  "bin": {
    "jk-build": "index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "inquirer": "^7.1.4",
    "jenkins": "^1.0.1"
  }
}

修改index.js文件,指定脚本应该使用node.js解释器来运行:

// 新增
#!/usr/bin/env node
const Jenkins = require("jenkins")
const inquirer = require('inquirer')
...

保存文件后执行npm link,这样我们就能在任意地方执行jk-build命令行,启动脚本服务了。

教你如何帅气地启动jenkins打包任务🤣

功能优化

现在我们已经几乎可以抛弃jenkins客户端,直接使用node命令行调用jenkins服务了。把脚本 但是还是有一些功能优化可以去完善:

  1. 记住账号密码信息
  2. 输出当前任务的构建队列情况

目前只附上缓存账号密码信息的功能代码,输出当前任务的构建队列情况就只提供实现思路啦!

记住账号密码信息

使用node服务缓存信息可以使用node-localstorage插件来完成:

安装

npm i node-localstorage

完整代码



#!/usr/bin/env node
const Jenkins = require("jenkins")
const inquirer = require('inquirer')

// 加载缓存包
if (typeof localStorage === "undefined" || localStorage === null) {
    var LocalStorage = require('node-localstorage').LocalStorage;
    // 如果该目录不存在则自动生成
    localStorage = new LocalStorage('D:\/jk-build\/scratch');
}

// jenkins服务地址
const host = 'localhost:8080'

let jenkins;

let jobListPrompt = [];


// 问题数组
const prompt = [
    {
        type: 'input',
        name: 'account',
        message: '请输入jenkins账号',
        validate(input) {
            if (!input || input.length === 0) {
                return '请输入jenkins账号!'
            }
            return true
        }
    },
    {
        type: 'password',
        name: 'password',
        message: '请输入jenkins密码',
        validate(input) {
            if (!input || input.length === 0) {
                return '请输入jenkins密码!'
            }
            return true
        }
    }
]

function getJobPrompt(list) {
    return [
        {
            // 这里的打包仓库支持多选
            type: 'list',
            name: 'repository',
            message: '请选择需要打包的仓库(空格选择)',
            choices: list?.map(it => it.name) ?? [],
            validate(input) {
                if (!input || input.length === 0) {
                    return '请选择至少一个仓库!'
                }
                return true
            }
        },
        // 如果job构建任务配置了参数
        {
            type: 'input',
            name: 'branchs',
            message: '请输入需要构建参数Demo-params',
            validate(input) {
                if (!input || input.length === 0) {
                    return '请输入需要构建参数Demo-params!'
                }
                return true
            },
        }
    ]
}

// 初始化jenkins实例
async function initJenkins({ account, password } = {}) {
    try {
        // 拼接baseUrl
        const baseUrl = `http://${account}:${password}@${host}`;
        // 创建Jenkins实例
        jenkins = new Jenkins({ baseUrl, crumbIssuer: true })
        // 通过获取jenkins信息验证登录结果
        await jenkins.info() 
        // 获取jenkins服务的job任务列表
        const res = await jenkins.job.list()
        // 动态生成prompt
        jobListPrompt = getJobPrompt(res)
        return true
    } catch (error) {
        console.log('获取jenkins服务信息出错了!')
        return false
    }
}

async function hdBuild({ repository, params }) {
    await jenkins.job.build(repository,
        // 这里传入job构建时需要的参数信息
        {
            parameters: {
                // 替换成自己job任务的配置参数
                'Demo-params': params,
            }
        });
    console.log('打包任务已经开始了!')
}

function setLocalStorage({ account, password }){
    localStorage.setItem('account',account)
    localStorage.setItem('password',password)
}

function removeLocalStorage(){
    localStorage.removeItem('account')
    localStorage.removeItem('password')
}


async function main() {
    // 读取缓存信息
    const account = localStorage.getItem('account')
    const password = localStorage.getItem('password')
    let answers = {}
    if(account && password){
        // 使用缓存的账号密码
        answers = {
            account,
            password
        }
    }else{
        // 引导用户输入密码
        answers = await inquirer.prompt(prompt)
    }
    // 初始化jenkins
    const isLogin = await initJenkins(answers)
    // 登录状态
    if(isLogin){
        // 缓存登录信息
        setLocalStorage(answers)
        // 引导用户输入打包仓库和构建参数
        const buildInfo = await inquirer.prompt(jobListPrompt)
        // 执行打包任务
        hdBuild(buildInfo)
    }else{
        // 删除缓存信息
        removeLocalStorage()
    }
}

main()

输出当前任务的构建队列情况

开启打包任务之后在控制台打印出当前任务的构建队列情况,可以直接调用jenkins官网提供的/queue/api/json/api/json获取指定任务的构建等待队列和构建历史,配合ora做一些样式优化再进行输出即可。

写在最后

最后附上源码地址,大家可以根据自己平时实际项目需要,对代码进行一些调整,重点是分享一些简化开发工作流程的小思路哈,大家可以在评论区一起交流。

转载自:https://juejin.cn/post/7214735181171949627
评论
请登录