如何集中管理多个项目的Jenkins Job 流程?

背景

现在前端有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,主体流程是:

  1. 项目环境变量初始化
  2. 拉取代码
  3. 设置构建面板信息
  4. 分支名和提交信息检查
  5. 打包web资源
  6. 生成docker镜像
  7. 部署docker镜像
  8. 发送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到了一门新技能,今天没有虚度,有收获,但愿每周,至少每月都能过得充实一些。

相关推荐
德育处主任4 分钟前
p5.js 正方形square的基础用法
前端·数据可视化·canvas
烛阴5 分钟前
Mix - Bilinear Interpolation
前端·webgl
90后的晨仔9 分钟前
Vue 3 应用实例详解:从 createApp 到 mount,你真正掌握了吗?
前端·vue.js
德育处主任16 分钟前
p5.js 矩形rect绘制教程
前端·数据可视化·canvas
前端工作日常36 分钟前
我学习到的babel插件移除Flow 类型注解效果
前端·babel·前端工程化
前端工作日常1 小时前
我学习到的 Babel 配置
前端·babel·前端工程化
xw52 小时前
uni-app项目跑APP报useStore报错
前端·uni-app
!win !2 小时前
uni-app项目跑APP报useStore报错
前端·uni-app
拾光拾趣录2 小时前
Flexbox 布局:从“垂直居中都搞不定”到写出响应式万能布局
前端·css
huxihua20063 小时前
各种前端框架界面
前端