建议将本篇的文字修改为jenkins的参数化构建和前端缓存优化
第二个考虑需要将nodejs动态设置版本的功能加进来
这个功能之前已经整理成为具体的文档了
加缓存
groovy
pipeline {
agent any
tools {
nodejs 'Node20.16.0' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
environment {
GIT_URL = 'https://gitlab.cghbh.com/open-source/npm-jenkins-cache.git'
GIT_BRANCH = 'master'
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${env.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${env.GIT_URL}"
]]
])
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码拉取阶段耗时: ${duration} 秒"
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
// 设置 npm 缓存目录
sh 'npm config set cache ~/.npm-cache'
// 安装依赖
sh 'npm install'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "安装依赖阶段耗时: ${duration} 秒"
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
// 构建项目
sh 'npm run build'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码构建阶段耗时: ${duration} 秒"
}
}
}
}
}
不使用缓存的方法1
请注意:使用npm ci清除构建时候的缓存,需要项目必须有一个现有的
package-lock.json
,如果没有的话则不能命中这个规则。此命令与
npm install
类似,不同之处在于它旨在用于自动化环境,例如测试平台、持续集成和部署——或任何您希望确保对依赖项进行全新安装的情况。
npm ci
在以下情况下会明显更快:
- 有一个
package-lock.json
或npm-shrinkwrap.json
文件。node_modules
文件夹丢失或为空。简而言之,使用
npm install
和npm ci
的主要区别在于:
- 项目必须有一个现有的
package-lock.json
或npm-shrinkwrap.json
。- 如果包锁中的依赖项与
package.json
中的依赖项不匹配,npm ci
将退出并出错,而不是更新包锁。npm ci
一次只能安装整个项目:无法使用此命令添加单个依赖项。- 如果
node_modules
已经存在,它将在npm ci
开始安装之前自动删除。- 它永远不会写入
package.json
或任何包锁:安装基本上是冻结的。
groovy
pipeline {
agent any
tools {
nodejs 'Node20.16.0' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
environment {
GIT_URL = 'https://gitlab.cghbh.com/open-source/npm-jenkins-cache.git'
GIT_BRANCH = 'master'
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${env.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${env.GIT_URL}"
]]
])
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码拉取阶段耗时: ${duration} 秒"
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
// 清空并重新安装依赖
sh 'npm ci'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "安装依赖阶段耗时: ${duration} 秒"
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
// 构建项目
sh 'npm run build'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码构建阶段耗时: ${duration} 秒"
}
}
}
}
}
不使用缓存的方法2
groovy
pipeline {
agent any
tools {
nodejs 'Node20.16.0' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
environment {
GIT_URL = 'https://gitlab.cghbh.com/open-source/npm-jenkins-cache.git'
GIT_BRANCH = 'master'
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${env.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${env.GIT_URL}",
credentialsId: "${env.GIT_CREDENTIALS_ID}"
]]
])
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码拉取阶段耗时: ${duration} 秒"
}
}
}
stage('Clean Node Modules') {
steps {
script {
def startTime = System.currentTimeMillis()
// 删除 node_modules 目录
sh 'rm -rf node_modules'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "删除缓存阶段耗时: ${duration} 秒"
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
// 安装依赖
sh 'npm install'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "下载依赖阶段耗时: ${duration} 秒"
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
// 构建项目
sh 'npm run build'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "项目构建阶段耗时: ${duration} 秒"
}
}
}
}
}
将以上两种方法聚合的方法3
groovy
pipeline {
agent any
tools {
nodejs 'Node20.16.0' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
environment {
GIT_URL = 'https://gitlab.cghbh.com/open-source/npm-jenkins-cache.git'
GIT_BRANCH = 'master'
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${env.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${env.GIT_URL}",
credentialsId: "${env.GIT_CREDENTIALS_ID}"
]]
])
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码拉取阶段耗时: ${duration} 秒"
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
// 判断是否存在 package-lock.json 文件
def hasPackageLock = fileExists('package-lock.json')
if (hasPackageLock) {
echo 'package-lock.json 文件存在,使用 npm ci重新安装依赖'
// 使用 npm ci 清除缓存并重新安装依赖
sh 'npm ci'
} else {
echo 'package-lock.json 文件不存在,将删除 node_modules 并重新安装依赖'
// 删除 node_modules 目录并重新安装依赖
sh 'rm -rf node_modules'
sh 'npm install'
}
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "安装依赖阶段耗时: ${duration} 秒"
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
// 构建项目
sh 'npm run build'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "项目构建阶段耗时: ${duration} 秒"
}
}
}
}
}
参数化决定缓存的构建
groovy
pipeline {
agent any
tools {
nodejs 'Node20.16.0' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
parameters {
booleanParam(name: 'ENABLE_CACHE', defaultValue: true, description: '是否使用开启node_modules缓存')
}
environment {
GIT_URL = 'https://gitlab.cghbh.com/open-source/npm-jenkins-cache.git'
GIT_BRANCH = 'master'
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${env.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${env.GIT_URL}",
credentialsId: "${env.GIT_CREDENTIALS_ID}"
]]
])
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "代码拉取阶段耗时: ${duration} 秒"
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
// 判断是否存在 package-lock.json 文件
def hasPackageLock = fileExists('package-lock.json')
if (params.ENABLE_CACHE) {
// 设置 npm 缓存目录
sh 'npm config set cache ~/.npm-cache'
// 继续安装可能更新的依赖
if (hasPackageLock) {
sh 'npm install'
} else {
// 如果本来就没有package-lock.json的文件,则阻止这个文件的生成,避免对后续的操作造成干扰
sh 'npm install --no-package-lock'
}
} else {
// 不开启缓存的话,则根据不同的方案进行依赖的重新下载
if (hasPackageLock) {
echo 'package-lock.json 文件存在,使用 npm ci重新安装依赖'
// 使用 npm ci 清除缓存并重新安装依赖
sh 'npm ci'
} else {
echo 'package-lock.json 文件不存在,将删除 node_modules 并重新安装依赖'
// 删除 node_modules 目录并重新安装依赖
sh 'rm -rf node_modules'
// 如果本来就没有package-lock.json的文件,则阻止这个文件的生成,避免对后续的操作造成干扰
sh 'npm install --no-package-lock'
}
}
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "安装依赖阶段耗时: ${duration} 秒"
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
// 构建项目
sh 'npm run build'
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "项目构建阶段耗时: ${duration} 秒"
}
}
}
}
}
jenkins的参数化配置
groovy
pipeline {
agent any
parameters {
string(name: 'BUILD_VERSION', defaultValue: '1.0', description: 'Version number to build')
booleanParam(name: 'DEPLOY_TO_PROD', defaultValue: false, description: 'Deploy to production server?')
choice(name: 'ENVIRONMENT', choices: ['dev', 'qa', 'prod'], description: 'Select environment for deployment')
}
stages {
stage('Build') {
steps {
echo "Building version ${params.BUILD_VERSION}"
}
}
stage('Test') {
steps {
echo "Running tests on version ${params.BUILD_VERSION}"
}
}
stage('Deploy') {
when {
expression { params.DEPLOY_TO_PROD == true }
}
steps {
echo "Deploying to ${params.ENVIRONMENT}"
}
}
}
}
在这个示例中,我们定义了三个参数:
- BUILD_VERSION:一个字符串类型的参数,用于指定要构建的版本号。如果用户没有提供该参数,则默认为 1.0。
- DEPLOY_TO_PROD:一个布尔类型的参数,用于指示是否要将构建结果部署到生产环境。如果用户没有提供该参数,则默认为 false。
- ENVIRONMENT:一个选择类型的参数,用于指定要部署到的环境。选择项为 dev、qa 和 prod。如果用户没有提供该参数,则不会有默认值。
- 在Pipeline的不同阶段中,我们使用了这些参数。例如,在 Build 阶段中,我们使用了 $params.BUILD_VERSION 来打印要构建的版本号。在 Deploy 阶段中,我们使用了 $params.DEPLOY_TO_PROD 和 $params.ENVIRONMENT 来决定是否将构建结果部署到生产环境以及要部署到哪个环境。
- 当用户运行Pipeline时,他们将被要求提供这些参数的值。用户可以在Jenkins界面中输入这些值,也可以通过Jenkins API来动态地提供这些值。
jenkins的api参考
js
const Jenkins = require('jenkins');
const jenkins = Jenkins({ baseUrl: 'http://your-username:your-api-token@your-jenkins-url', crumbIssuer: true });
const jobName = 'your-job-name';
const parameters = {
ENVIRONMENT: 'production',
GIT_URL: 'https://your-git-repo-url.git',
GIT_BRANCH: 'main',
GIT_CREDENTIALS_ID: 'your-credentials-id'
};
// Trigger the Jenkins job with parameters
jenkins.job.build({ name: jobName, parameters }, (err, data) => {
if (err) {
return console.error('Error triggering Jenkins job:', err);
}
console.log('Jenkins job triggered successfully:', data);
});
// 上面的参数相当于下面流水线的参数如下
parameters {
choice(name: 'ENVIRONMENT', choices: ['development', 'staging', 'production'], description: '选择部署环境')
string(name: 'GIT_URL', defaultValue: 'https://your-git-repo-url.git', description: 'Git 仓库 URL')
string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Git 分支')
string(name: 'GIT_CREDENTIALS_ID', defaultValue: 'your-credentials-id', description: 'Git 凭据 ID')
}
前端静态资源全部参考如下:
groovy
pipeline {
agent any
tools {
nodejs 'NodeJS_14' // 这里的 'NodeJS_14' 是你在 Jenkins 中配置的 Node.js 名称
}
options {
timestamps() // 启用时间戳
}
parameters {
choice(name: 'ENVIRONMENT', choices: ['development', 'staging', 'production'], description: '选择部署环境')
string(name: 'GIT_URL', defaultValue: 'https://your-git-repo-url.git', description: 'Git 仓库 URL')
string(name: 'GIT_BRANCH', defaultValue: 'main', description: 'Git 分支')
string(name: 'GIT_CREDENTIALS_ID', defaultValue: 'your-credentials-id', description: 'Git 凭据 ID')
booleanParam(name: 'UPLOAD_TO_OSS', defaultValue: false, description: '是否上传到 OSS')
}
environment {
API_URL = 'https://your-api-endpoint.com/report' // 你的接口地址
OSS_BUCKET = 'your-oss-bucket-name' // 你的 OSS 桶名称
OSS_ENDPOINT = 'your-oss-endpoint' // 你的 OSS 终端
OSS_ACCESS_KEY_ID = 'your-oss-access-key-id' // 你的 OSS 访问密钥 ID
OSS_ACCESS_KEY_SECRET = 'your-oss-access-key-secret' // 你的 OSS 访问密钥
}
stages {
stage('Checkout Code') {
steps {
script {
def startTime = System.currentTimeMillis()
def status = 'SUCCESS'
try {
// 输出参数到控制台
echo "ENVIRONMENT: ${params.ENVIRONMENT}"
echo "GIT_URL: ${params.GIT_URL}"
echo "GIT_BRANCH: ${params.GIT_BRANCH}"
echo "GIT_CREDENTIALS_ID: ${params.GIT_CREDENTIALS_ID}"
// 拉取代码
checkout([
$class: 'GitSCM',
branches: [[name: "*/${params.GIT_BRANCH}"]],
doGenerateSubmoduleConfigurations: false,
extensions: [],
submoduleCfg: [],
userRemoteConfigs: [[
url: "${params.GIT_URL}",
credentialsId: "${params.GIT_CREDENTIALS_ID}"
]]
])
} catch (Exception e) {
status = 'FAILURE'
throw e
} finally {
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "Checkout Code 阶段耗时: ${duration} 秒"
// 发送结果和执行时间到指定接口
sh """
curl -X POST -H "Content-Type: application/json" -d '{
"stage": "Checkout Code",
"status": "${status}",
"duration": "${duration} 秒"
}' ${env.API_URL}
"""
}
}
}
}
stage('Install Dependencies') {
steps {
script {
def startTime = System.currentTimeMillis()
def status = 'SUCCESS'
try {
// 判断是否存在 package-lock.json 文件
def hasPackageLock = fileExists('package-lock.json')
if (hasPackageLock) {
echo 'package-lock.json 文件存在,使用 npm ci'
// 使用 npm ci 清除缓存并重新安装依赖
sh 'npm ci'
} else {
echo 'package-lock.json 文件不存在,删除 node_modules 并重新安装依赖'
// 删除 node_modules 目录并重新安装依赖
sh 'rm -rf node_modules'
sh 'npm install --no-package-lock'
}
} catch (Exception e) {
status = 'FAILURE'
throw e
} finally {
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "Install Dependencies 阶段耗时: ${duration} 秒"
// 发送结果和执行时间到指定接口
sh """
curl -X POST -H "Content-Type: application/json" -d '{
"stage": "Install Dependencies",
"status": "${status}",
"duration": "${duration} 秒"
}' ${env.API_URL}
"""
}
}
}
}
stage('Build') {
steps {
script {
def startTime = System.currentTimeMillis()
def status = 'SUCCESS'
try {
// 根据选择的环境设置不同的构建命令
if (params.ENVIRONMENT == 'development') {
sh 'npm run build:dev'
} else if (params.ENVIRONMENT == 'staging') {
sh 'npm run build:staging'
} else if (params.ENVIRONMENT == 'production') {
sh 'npm run build:prod'
}
} catch (Exception e) {
status = 'FAILURE'
throw e
} finally {
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "Build 阶段耗时: ${duration} 秒"
// 发送结果和执行时间到指定接口
sh """
curl -X POST -H "Content-Type: application/json" -d '{
"stage": "Build",
"status": "${status}",
"duration": "${duration} 秒"
}' ${env.API_URL}
"""
}
}
}
}
stage('Upload to OSS') {
when {
expression { return params.UPLOAD_TO_OSS }
}
steps {
script {
def startTime = System.currentTimeMillis()
def status = 'SUCCESS'
try {
// 上传静态资源到 OSS
sh """
ossutil cp -r ./dist oss://${env.OSS_BUCKET}/path/to/upload --endpoint=${env.OSS_ENDPOINT} --access-key-id=${env.OSS_ACCESS_KEY_ID} --access-key-secret=${env.OSS_ACCESS_KEY_SECRET}
"""
} catch (Exception e) {
status = 'FAILURE'
throw e
} finally {
def endTime = System.currentTimeMillis()
def duration = (endTime - startTime) / 1000
echo "Upload to OSS 阶段耗时: ${duration} 秒"
// 发送结果和执行时间到指定接口
sh """
curl -X POST -H "Content-Type: application/json" -d '{
"stage": "Upload to OSS",
"status": "${status}",
"duration": "${duration} 秒"
}' ${env.API_URL}
"""
}
}
}
}
}
}
在这个示例中:
parameters
块中添加了一个布尔参数UPLOAD_TO_OSS
,用户可以选择是否上传到 OSS。Upload to OSS
阶段使用when
块来判断是否执行该阶段。只有当UPLOAD_TO_OSS
参数为true
时,才会执行上传到 OSS 的操作。- 每个阶段都使用
try-catch-finally
块来捕获异常,并在finally
块中发送结果和执行时间到指定的接口。
通过这种方式,你可以在 Jenkins 流水线中动态控制是否执行 Upload to OSS
阶段,并在每个阶段完成后将结果和执行时间发送到指定的接口,同时在执行失败时也发送结果。
你可以通过在 Jenkins Pipeline 中添加一个布尔参数来控制是否执行 Upload to OSS
这一步。用户可以在运行时选择是否需要发布到 OSS。以下是一个更新后的示例,展示了如何通过布尔参数动态控制 Upload to OSS
阶段的执行:
1. 配置 Jenkins Pipeline 脚本
jenkins需要考虑的问题如下
- 配置发送构建钉钉消息
- 配置发送邮件构建信息