提交代码自动触发 Jenkins 构建(Github、Gitlab、Bitbucket)

Published: 2022-09-22

Tags: Jenkins CI/CD

本文总阅读量

在上篇 《使用 Jenkins 部署 Golang 程序》 整理了如何手动触发 Jenkins 从 Github 上同步代码并构建,本篇将记录在提交代码时自动触发构建。

这里借助插件 Generic Webhook Trigger 实现,它支持多个 Git 托管服务的 WebHook,当仓库有提交时会发送请求到 Jenkins,插件解析请求,触发 Jenkins 任务执行。

插件简介

安装:Jenkins 系统管理 - 插件管理 - 搜索安装 “Generic Webhook Trigger”,重启 Jenkins 后生效。

插件安装后,访问 Jenkins 上的 /generic-webhook-trigger/invoke URL 即可触发项目构建。

插件配置

以自由软件风格项目为例,流水线和多分支流水线大同小异。

在 Github 仓库设置 WebHook

  1. 访问 Github 仓库设置(Settings)页面。
  2. 点击选择 “Webhooks”,填写 Payload URL,地址即为你的 Jenkins 地址 “http://JENKINS_URL/generic-webhook-trigger/invoke?token=iou53fgg38o6ua24m49x89k57fh8k697”(替换为自己的 Token)
  3. Content type 维持默认的 “application/x-www-form-urlencoded” 即可。
  4. 选择 Trigger 时机,这里选择的 “Just the push event.” (仅当分支 push 时触发)
  5. 勾选 Active 后保存。

在 Gitlab 仓库设置 WebHook

  1. 访问 Gitlab 仓库设置(Settings)页面。
  2. 点击选择 “Webhooks”,填写 Payload URL,地址即为你的 Jenkins 地址 “http://JENKINS_URL/generic-webhook-trigger/invoke”
  3. Secret token 处填写 Token。
  4. 勾选 Trigger 时机,这里选择的 “Just the push event.” (仅当分支 push 时触发)
  5. 点击保存。

在 Bitbucket 仓库设置 WebHook

  1. 访问 Bitbucket 仓库设置(Settings)页面。
  2. 点击选择 “Webhooks”,Title 填写 WebHook 的名称,便于记忆。
  3. 填写 Payload URL,地址即为你的 Jenkins 地址 “http://JENKINS_URL/generic-webhook-trigger/invoke?token=iou53fgg38o6ua24m49x89k57fh8k697”(替换为自己的 Token)
  4. 选择 Trigger 时机,这里选择的 “Push” (仅当分支 push 时触发)
  5. 点击保存。

Gitlab 的 WebHook 相较 Github 和 Bitbucket 设置选项更多,支持 Test 可以模拟相应事件,这点在调试时更加易用。 设置好代码仓库的 WebHook,接下来就可以使用 Jenkins 创建任务。


Jenkins 任务的简要配置

示例一:自由软件风格项目

创建自由软件风格项目时,设置如下:

  1. 源码管理,选择 Git,粘贴仓库地址。
  2. 指定分支,填写 “*/main”(根据你的仓库实际情况,默认的 master 就是 main)
  3. 勾选 “Generic Webhook Trigger”,Token 填写在仓库中自定义的 Token。
  4. 勾选 “Set up Go programming language tools”,如果你看过上篇配置过 Golang 工具,那么这里会默认选择 “go1.18”
  5. 构建 - “增加构建步骤”,选择 “执行 Shell”,填写如下内容,保存即可。
ls
pwd
go version
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -v -o bin/simple-web-linux

示例二:流水线

创建流水线项目,设置如下:

  1. 勾选 “Generic Webhook Trigger”。
  2. 流水线定义选择 “Pipeline script from SCM”,SCM 选择 Git,Repository URL 处再次填写仓库地址。
  3. 指定分支,填写 “*/main”(根据你的仓库实际情况,默认的 master 就是 main)
  4. 脚本路径,填写 “Jenkinsfile”,这个 Jenkisfile 已在上篇 添加到仓库的根目录下,保存即可。

示例三:多分支流水线

多分支流水线相比前两者比较特殊,如果你在构建 Jenkins 的时候使用推荐的方式安装,那么默认推荐安装的 “GitHub ” 县官插件会在 Github 分支更新时自动触发构建,不需要依赖 “Generic Webhook Trigger” 插件。

如果你使用 Gitlab 或 BitBucket,可以通过下文了解基于 “Generic Webhook Trigger” 插件的触发方式。


Generic Webhook Trigger 触发多分支流水线

再次回顾一下 Generic Webhook Trigger 的原理,我们提交代码到指定分支,SCM 通过 Webhooks 通知到 Generic Webhook Trigger,插件通过规则判断是否匹配到分支,来决定是否触发 Jenkins 构建。

自由软件或流水线很好理解,因为处理的是指定分支,而在多分支流水线,SCM 发送通知后,就需要有明确的规则来告诉 Generic Webhook Trigger 插件触发哪个分支构建。

接下来修改 Jenkinsfile

pipeline {
    // install golang 1.18 on Jenkins node
    agent any
    tools {
        go 'go1.18'
    }
    environment {
        GO114MODULE = 'on'
        CGO_ENABLED = 0
        GOPATH = "${JENKINS_HOME}/jobs/${JOB_NAME}/builds/${BUILD_ID}"
    }
    triggers {
        GenericTrigger (
            // 构建时的标题
            causeString: 'Triggered by $ref',
            // 获取POST参数中的变量,key指的是变量名,通过$ref来访问对应的值,value指的是JSON匹配值(参考Jmeter的JSON提取器)
            // ref 指的是推送的分支,格式如:refs/heads/master
            genericVariables: [[key: 'ref', value: '$.ref']],
            // 打印获取的变量的 key-value,此处会打印如:ref=refs/heads/master
            printContributedVariables: true,
            // 打印POST传递的参数
            printPostContent: true,
            // regexpFilterExpression 与 regexpFilterExpression成对使用
            // 当两者相等时,会触发对应分支的构建
            regexpFilterExpression: '^refs/heads/(master|main|production)$',
            regexpFilterText: '$ref',
            // 与webhook 中配置的 token 参数值一致
            token: 'iou53fgg38o6ua24m49x89k57fh8k697'
        )
    }

    stages {
        stage("Build") {
            steps {
                echo 'BUILD EXECUTION STARTED'
                sh 'go version'
                // 执行 Makefile 中的命令
                sh 'make bl_linux'
                // 将 build 后的文件保存
                archiveArtifacts artifacts: 'bin/*', fingerprint: true
                sh 'sudo supervisorctl stop simple-web'
                sh 'sudo cp bin/simple-web-linux /opt/simple-web/simple-web-linux'
                sh 'sudo supervisorctl start simple-web'
            }
        }
    }
}

相比较于上文的 Jenkinsfile,这个里新增的 triggers 是 Jenkins 提供的配置段,GenericTrigger 是插件提供的。代码中已经注释了各项配置的含义。

假设这个 Jenkinsfile 在 main 分支下,根据以下配置,如果我们推送代码到 main 分支,那么 $ref 内容为 "refs/heads/main",能够和 regexpFilterExpression 表达式匹配上,所以在多分支流水线 Jenkins 会构建 main 分支。

同理,如果是 develop 分支的 Jenkinsfile,regexpFilterExpression 就应该为 ^refs/heads/develop$ 这样的表达式,regexpFilterText 依然为 $ref,就能实现推送代码到 main 分支会触发多分支流水线的 main 分支构建,推送代码到 develop 分支会触发多分支流水线的 develop 分支构建。

配置一

regexpFilterExpression: '^refs/heads/(master|main|production)$'
regexpFilterText: '$ref'

那如果我只想在 develop 分支推送时构建,而 main 分支不自动构建,手动构建,则在 main 分支的 Jenkinsfile 中,让其不匹配即可。例如:

配置二

regexpFilterExpression: 'refs/heads/develop'
regexpFilterText: '$ref'

不过这里的配置方式是有些奇怪的,有点儿不符合正常思维逻辑。如果有很多分支需要构建,总不能每个分支都修改一下 Jenkinsfile 适配分支名,这时,可以不通过 triggers 进行控制,在 stages 中添加 when 条件,也能实现类似控制。

以下示例,指定分支有 push 都会被单独触发(我们不需要关心分支名称),然后执行到具体的构建步骤时,进行判断,不满足不会执行后续的步骤,这样做的好处是 Jenkinsfile 能更加的统一,触发的逻辑是相同的。

regexpFilterExpression: 'refs/heads/' + BRANCH_NAME
regexpFilterText: '$ref'
pipeline {
    agent any
    stages {
        stage('Example Deploy') {
            when {
                branch 'main'
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}

如果是分支名不是 xx 分支,则可以结合更多的语法。

when {
       not {
          anyOf {
            branch 'master';
            branch 'staging'
       }
    }
}

补充:保存项目不自动构建

在修改多分支流水线保存后,默认会自动触发所有分支的构建,我们大多数时候会期望它只扫描分支,不要自动构建,这时可以在 Git 配置块进行如下设置,就不会自动触发了。

补充:修改 Jenkinsfile 上传后第二次构建时生效

因为多分支流水线使用的 Jenkinsfile 在 SCM 仓库中,Jenkins 是使用的是“上次”构建时用的 Jenkinsfile,而对 SCM 仓库中 Jenkinsfile 的修改,需要先让 Jenkins “重新加载” Jenkinsfile 后,再次构建才会生效。简单来说,需要再次扫描多分支流水线。

补充:多分支流水线其它插件

在 “Generic Webhook Trigger” 插件的文档页面,提到多分支流水线可用这个插件: Multibranch Scan Webhook Trigger Plugin

拥有不同的 Trigger 触发地址,如果没有比较定制化的需求,单个分支 Push 触发单个分支构建这种,推荐使用这一插件,勾选上 “Scan by webhook” 填写上 Token 就可以了

两个插件 —— “Generic Webhook Trigger” 的灵活性更高,可以实现更加定制化的触发规则,而 “Multibranch Scan Webhook Trigger ” 场景较为单一,更易于使用,根据自己的情况选择即可。

提交代码触发 Jenkins 构建,简单的用法到这里就差不多了,可以应付初级 CI/CD 的 Trigger 场景。

参考

  1. Jenkins:Webhook 触发多分支项目构建
  2. Jenkins 多分支流水线:Webhook 按分支触发自动构建
  3. Jenkins Multibranch Pipeline 集成 GitLab Webhook
  4. 使用 Generic Webhook Trigger 触发 Jenkins 多分支流水线自动化构建