背景
现在前端有20多个项目,从Gitlab-CI迁移到了Jenkins平台, 这20多个前端项目,构建任务采用的是Pipeline方式,构建流程几乎完全一致,每个项目只有下面五个参数不一样:
参数 | 含义 |
---|---|
PKG_MANAGER | node包管理器 |
CI_PROJECT_NAMESPACE | 项目大类 |
CI_PROJECT_NAME | 项目名称 |
GITLAB_PROJECT_ID | GitLab中项目ID |
GITLAB_URL | 项目Gitlab仓库地址 |
现在要对这些项目添加,删除,修改步骤,得一个一个去修改与验证,效率不高。于是就想着把这些项目的Jenkins构建路程集中进行管理,方便维护与提高更改效率。问了一下大模型,大模型给出的方案是采用Jenkins共享库,我一看到共享二字,感觉这个方案靠谱,于是决定践行一下。
Jenkins共享库如何使用
目录结构
典型的Jenkins共享库目录结构如下所示:先把目录结构搭建好,再往里面填充内容。
js
your-shared-library/
├── src/ # 核心类和工具类Java/Groovy 类文件
│ └── com/
│ └── your-company-name/
│ └── utils/
│ ├── GitUtils.groovy
│ └── BuildUtils.groovy
├── vars/ # 全局可调用的方法
│ ├── sayHello.groovy
│ └── sendNotification.groovy
├── resources/ # 静态资源文件(如 JSON、模板等)
│ └── templates/
│ └── email-template.html
└── lib/ # 可选:第三方依赖JAR文件
入口文件
入口文件是vars\jsCommonDeployPipeline.groovy,主体流程是:
- 项目环境变量初始化
- 拉取代码
- 设置构建面板信息
- 分支名和提交信息检查
- 打包web资源
- 生成docker镜像
- 部署docker镜像
- 发送Sonar+ESLint报告
每个流程要干的事情,与今天要讲的主题,关系不大,就不详细展开介绍这些构建步骤了。与共享库有关系的是主流程引入的一些工具方法和配置文件,先讲一下主流程中引入的工具方法。主流程中用到了src\com\xxx\下4个工具类。
- import com.xxx.GitUtils(用于获取git的分支名,提交信息)
- import com.xxx.SonarUtils(运行sonar并获取代码质量报告)
- import com.xxx.EmailUtils(用于给产研领导发送邮件)
- import com.xxx.ESLintUtils(前端ESLint代码质量检查)
js
import com.xxx.GitUtils
import com.xxx.SonarUtils
import com.xxx.EmailUtils
import com.xxx.ESLintUtils
def call(String projectName = '') {
node {
try {
// 环境变量
env.REGISTRY_HOST = 'reg.xxx.com:9088'
env.KUBECONFIG = '/etc/deploy/config'
env.KUBECONFIG2PROD = '/etc/deploy/kube2prod'
env.KUBECONFIG2CANARY = '/etc/deploy/kube2canary'
env.DOCKER_CONTEXT_DIR = 'docker-context'
env.SONAR_HOST = 'http://192.168.10.70:9090'
env.GITLAB_API = 'https://git.xxx.com/api/v4/projects'
stage('项目环境变量初始化') {
def yamlText = libraryResource("project-config-js/${env.JOB_BASE_NAME}.yaml")
def config = readYaml text: yamlText
// 项目业务类别
env.CI_PROJECT_NAMESPACE = config.CI_PROJECT_NAMESPACE ?: ''
// 项目名称
env.CI_PROJECT_NAME = config.CI_PROJECT_NAME ?: ''
// 项目Gitlab_ID
env.GITLAB_PROJECT_ID = config.GITLAB_PROJECT_ID
// 项目gitlab地址
env.GITLAB_URL = config.GITLAB_URL ?: ''
// 包管理器
env.PKG_MANAGER = config.PKG_MANAGER ?: 'npm'
}
stage('拉取代码') {
// ...
}
// ...
}
}
}
编写工具类方法
接下来说说工具类的实现语法:工具类要放在src目录下,这里以ESLintUtils为例,说说与写js的不同之处。

第一处不同是要在首行声明包的命名空间; 第二处不同是需要传入Jenkins上下文; 其它的差异主要是js和groove面向对象编程的差异。
js
package com.xxx
class ESLintUtils implements Serializable {
/**
* 运行 ESLint 并统计错误/警告数量
*/
static void runAndParseReport(script) {
script.timeout(time: 10, unit: 'MINUTES') {
script.sh """
set -e
bash -c "
source ~/.bashrc
nvm use 18.20.6
npx eslint 'src/**/*.{tsx,ts,vue}' --cache --format json --output-file eslint-report.json || true
"
"""
// 读取和解析 JSON 报告
def reportFile = script.readFile file: 'eslint-report.json'
def report = script.readJSON text: reportFile
int totalErrors = 0
int totalWarnings = 0
report.each { fileReport ->
fileReport.messages.each { message ->
if (message.severity == 2) totalErrors++
else if (message.severity == 1) totalWarnings++
}
}
// 写入环境变量供后续使用
script.env.totalErrors = totalErrors.toString()
script.env.totalWarnings = totalWarnings.toString()
script.echo "ESLint 总错误数: ${totalErrors}"
script.echo "ESLint 总警告数: ${totalWarnings}"
}
}
}
再看看调用方式: 先导入工具类,再调用工具类具体的方法,只要会一个,其它就可以依葫芦画瓢。不再赘述。
js
import com.xxx.ESLintUtils
def call(String projectName = '') {
node {
stage('发送Sonar+ESLint报告') {
ESLintUtils.runAndParseReport(this)
}
}
}
添加资源文件
资源文件目录如下所示: 分为js和project-config-js文件夹。先说说js文件夹,不同编程语言的Jenkins构建部署流程有差异,所以目录名按编程语言进行组织。每个编程语言下存放的配置文件类别都是相似的。第一类是Dockerfile配置文件,第二类是k8s-deployment配置文件,第三类是构建脚本文件,个别项目的构建脚本与大对数项目不同,所以拆分了两个文件。

再说一下project-config-js文件夹,里面放置的不同项目之间的差异配置,这几个参数的含义文章开头介绍过,其它的参数一看就知道干什么用,可是PKG_MANAGER这个参数的用途,不是很明了。这里说一下项目背景,由于历史原因,每个项目使用的node包管理器不太一样,有的用的是npm,有的用的是yarn,有的用的是pnpm,
yaml
PKG_MANAGER: pnpm
CI_PROJECT_NAMESPACE: data
CI_PROJECT_NAME: ai-platform-frontend
GITLAB_PROJECT_ID: 654
GITLAB_URL: https://git.xxx.com/research-and-development/data/ai-platform-frontend.git
所以在打包构建web资源的时候,需要区别对待。
js
if (env.PKG_MANAGER == 'pnpm') {
if (env.JOB_BASE_NAME in ['travel-ai', 'ai-platform-frontend']) {
buildCmd = 'pnpm build'
} else {
buildCmd = "pnpm build:${env.ManualDeployEnv}"
}
} else if (env.PKG_MANAGER == 'yarn') {
buildCmd = "yarn build:${env.ManualDeployEnv}"
} else {
buildCmd = "npm run build:${env.ManualDeployEnv}"
}
这些配置文件何时被加载呢?是在项目环境变量初始化步骤,使用ibraryResource api进行加载,路径是相对resoures目录而言的:
js
stage('项目环境变量初始化') {
def yamlText = libraryResource("project-config-js/${env.JOB_BASE_NAME}.yaml")
def config = readYaml text: yamlText
// 项目业务类别
env.CI_PROJECT_NAMESPACE = config.CI_PROJECT_NAMESPACE ?: ''
// 项目名称
env.CI_PROJECT_NAME = config.CI_PROJECT_NAME ?: ''
// 项目Gitlab_ID
env.GITLAB_PROJECT_ID = config.GITLAB_PROJECT_ID
// 项目gitlab地址
env.GITLAB_URL = config.GITLAB_URL ?: ''
// 包管理器
env.PKG_MANAGER = config.PKG_MANAGER ?: 'npm'
-// sh 'env'
}
配置共享库
上面完成了共享库的开发,开发完之后,需要在Jenkins中配置之后才能使用。共享库配置菜单入口: 系统管理==>系统配置,找到Global Trusted Pipeline Libraries配置项,添加共享库,配置共享库的名称和使用的git

Library Path (optional)这一项还可精确设置共享库所在目录,这对共享库按照目录划分时很有用。

使用共享库
在流水线配置项,选择流水线文件为Pipeline script, 在Groove脚本编辑区,导入前面编写的共享库,并指定要运行的流水线方法,

js
@Library('jenkins-js-shared-library') _
// 构建主流程, 配置的Jenkins流水线名称必须与项目名称一致
jsCommonDeployPipeline()
点击保存之后,执行构建,效果如下图所示,成功构建,说明共享库配置的没有任何问题。

最后
又get到了一门新技能,今天没有虚度,有收获,但愿每周,至少每月都能过得充实一些。