脚本式语法上传资源包部署完整版
2026-05-17 大约 3 分钟
jenkins版本2.555.1; 此配置适配大多数前端项目;
# 语法示例
适用场景:手动上传前端资源压缩包,利用
jenkins实现资源替换和备份以及代码回滚;
// 复制此部署脚本请修改以下内容;
// 变量:WWWROOT、targetFileName
// 插件: 需要安装插件 file-parameters
node {
properties([
buildDiscarder(logRotator(
numToKeepStr: '20', // 最多保留20个构建 【artifactDaysToKeepStr: '7' 归档产物保留7天(核心参数!)】
artifactNumToKeepStr: '10' // 最多保留10个构建的归档产物 【daysToKeepStr: '30' 保留30天内的构建(含日志和归档)】
)),
parameters([
stashedFile(
name: 'DEPLOY_PACKAGE',
description: '请上传部署压缩包(.gz)! Win中执行此命令进行压缩(tar -czvf uni.tar.gz -C web .)'
),
choice(
name: 'ROLLBACK_VERSION',
choices: getAvailableVersions().toList(),
description: '选择要回滚到的版本(仅在 ROLLBACK=true 时生效)'
),
booleanParam(
name: 'Rollback',
defaultValue: false,
description: '是否回滚,谨慎勾选!'
)
])
])
// 部署新版本时自动生成版本号 v20260517_1500,并去除首尾空格
def VERSION = "v${sh(script: 'date +%Y%m%d_%H%M', returnStdout: true).trim()}";
def backupDir = "/data/jenkins_backup/${env.JOB_NAME}";
def WWWROOT = "/www/wwwroot/uni.yahweh.top";
def workspaceDir = pwd();
def targetFileName = "uni-${VERSION}.tar.gz";
def isRollback = params.Rollback;
if (isRollback) {
echo "========== 执行回滚模式 =========="
} else {
echo "========== 执行正常构建模式 =========="
}
stage('Init') {
// 清理当前工作空间,使其恢复为空目录
deleteDir();
}
// 正常构建时执行
if (!isRollback) {
stage('Upload Package') {
// 需要安装插件 file-parameters
withFileParameter('DEPLOY_PACKAGE') {
// 获取原始文件名和临时文件路径
def tempFilePath = "${DEPLOY_PACKAGE}"
def originalFileName = "${env.DEPLOY_PACKAGE_FILENAME}"
// 检查是否上传了文件(检查文件内容是否为空)
sh """
if [ ! -s "${tempFilePath}" ]; then
echo "错误:文件内容为空,请上传有效的部署压缩包"
exit 1
fi
"""
// 检查原始文件名是否为 .gz
if (!(originalFileName ==~ /.*\.gz/)) {
error("文件格式不正确,请上传 .gz 文件,当前上传的文件名是: ${originalFileName}")
}
// 将上传的文件移动到工作空间并重命名
sh "mv ${tempFilePath} ${targetFileName}";
echo "===========上传文件信息==========="
echo " 临时文件路径: ${tempFilePath}"
echo " 原始文件名: ${originalFileName}"
echo " 新文件名: ${targetFileName}"
echo "=================================="
}
}
stage('Archive Dist') {
// 归档到Jenkins ${JENKINS_HOME}/jobs/${JOB_NAME}/builds/${BUILD_NUMBER}/archive/
archiveArtifacts artifacts: "${targetFileName}", fingerprint: true;
echo "归档完成 END";
}
stage('Save Version Info') {
// 保存版本信息
writeFile file: 'version.txt', text: """
BUILD_VERSION=${VERSION}
BUILD_NUMBER=${env.BUILD_NUMBER}
BUILD_ID=${env.BUILD_ID}
JOB_NAME=${env.JOB_NAME}
BUILD_TIMESTAMP=${new Date().format('yyyy-MM-dd HH:mm:ss')}
"""
archiveArtifacts artifacts: 'version.txt', fingerprint: true;
echo "保存版本信息 END";
}
stage('Backup') {
// 复制到备份目录
sh("""
mkdir -p ${backupDir}/${VERSION};
cp ${targetFileName} ${backupDir}/${VERSION}/;
cp version.txt ${backupDir}/${VERSION}/;
cd ${backupDir}/${VERSION};
ls -a;
# 保留最近10个版本,删除v开头的旧版本
cd ${backupDir};
ls -t1d -- v* | tail -n +11 | xargs -r rm -rf;
echo "备份完成 END:${backupDir}/${VERSION}";
""")
}
stage('Deploy') {
sh("""
# 删除网站根目录中的所有前端资源
rm -rf ${WWWROOT}/*;
# 移动当前版本包并解压
cp ${targetFileName} ${WWWROOT};
cd ${WWWROOT};
tar -zxf ${targetFileName};
rm -rf ${targetFileName};
ls -a;
echo "部署成功 END"
""")
}
}
// 回滚模式执行
if (isRollback) {
stage('Rollback') {
sh("""
# 检查备份是否存在
if [ ! -d "${backupDir}/${ROLLBACK_VERSION}" ]; then
echo "错误:版本 ${ROLLBACK_VERSION} 不存在于备份目录"
exit 1
fi
# 从备份目录获取文件
cp ${backupDir}/${ROLLBACK_VERSION}/uni-${ROLLBACK_VERSION}.tar.gz .
# 删除网站根目录中的所有前端资源
rm -rf ${WWWROOT}/*;
# 移动备份版本包并解压
mv uni-${ROLLBACK_VERSION}.tar.gz ${WWWROOT};
cd ${WWWROOT};
tar -xzf uni-${ROLLBACK_VERSION}.tar.gz;
rm -rf uni-${ROLLBACK_VERSION}.tar.gz;
ls -a;
echo "回滚完成:${ROLLBACK_VERSION}"
""")
// 读取版本元数据
def metadata = readPropertiesManual("${backupDir}/${ROLLBACK_VERSION}/version.txt");
// 记录回滚信息
sh """echo "${ROLLBACK_VERSION}" > ${WWWROOT}/version.txt"""
echo "原始构建: ${metadata.BUILD_NUMBER}"
echo "回滚版本到: ${metadata.BUILD_VERSION}"
}
stage('Verify') {
// 验证回滚是否成功
sh """
# 替换成自己的网站
# curl -s https://xxxxxh.top/version.txt
"""
}
}
}
// 辅助函数:获取可用版本列表
def getAvailableVersions() {
def versions = ['1', '2']
def backupDir = "/data/jenkins_backup/${env.JOB_NAME}";
def result = sh(script: "ls -t ${backupDir} 2>/dev/null || echo ''", returnStdout: true).trim();
if (result) {
versions = result.split('\n')
}
echo "历史版本: ${versions}";
return versions
}
// 辅助函数:解析key=value
def readPropertiesManual(String filePath) {
def props = [:]
def lines = readFile(filePath).split('\n')
lines.each { line ->
line = line.trim()
if (line && !line.startsWith('#')) {
def parts = line.split('=', 2)
if (parts.length == 2) {
props[parts[0].trim()] = parts[1].trim()
}
}
}
return props
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212