Jenkins踩坑记录:SVN集成与TypeScript编译

Jenkins踩坑记录

去年我们团队引入Jenkins做持续集成,从安装到配置走了很多弯路,这里记录一下过程。

Jenkins基础安装与启动

Jenkins启动方式

Jenkins支持多种部署方式,最常用的方式是使用内置的Jetty服务器直接运行:

1
2
3
4
5
# 基础启动命令
java -jar jenkins.war --httpPort=8080 &

# 指定工作目录启动
java -jar jenkins.war --httpPort=8080 --webroot=/var/lib/jenkins/war &

常用启动参数:

参数 说明 示例
--httpPort HTTP服务端口 --httpPort=8080
--httpsPort HTTPS服务端口 --httpsPort=8443
--prefix URL前缀 --prefix=/jenkins
--webroot Web根目录 --webroot=/data/jenkins

后台运行配置

在生产环境中,建议使用systemd管理服务:

1
2
# 创建Jenkins服务文件
sudo vim /etc/systemd/system/jenkins.service

服务文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[Unit]
Description=Jenkins CI Server
After=network.target

[Service]
Type=simple
User=jenkins
Group=jenkins
ExecStart=/usr/bin/java -jar /opt/jenkins/jenkins.war --httpPort=8080
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

启动并启用服务:

1
2
3
sudo systemctl daemon-reload
sudo systemctl start jenkins
sudo systemctl enable jenkins

Jenkins插件管理

核心插件安装

Jenkins的功能主要通过插件扩展,以下是常用插件清单:

版本控制插件

插件名 用途 推荐场景
Subversion SVN版本控制支持 使用SVN的团队
Git Git版本控制支持 使用Git的团队
GitHub Branch Source GitHub多分支管理 GitHub项目

构建工具插件

插件名 用途 推荐场景
NodeJS Node.js环境管理 前端/Node项目
Gradle Gradle构建支持 Android/Gradle项目
Maven Integration Maven构建支持 Java Maven项目

部署与通知插件

插件名 用途 推荐场景
Publish Over SSH SSH远程部署 服务器部署
Slack Notification Slack消息通知 团队协作
Email Extension 邮件通知 传统通知方式

插件安装步骤

  1. 进入Jenkins管理界面
  2. 点击”Manage Jenkins” - “Manage Plugins”
  3. 选择”Available”标签页
  4. 搜索需要的插件并勾选
  5. 点击”Install without restart”

SVN集成配置

安装Subversion插件

1
2
Manage Jenkins → Manage Plugins → Available
搜索 "Subversion" → 勾选安装

创建SVN项目

新建自由风格项目

  1. 点击”New Item”
  2. 输入项目名称
  3. 选择”Freestyle project”
  4. 点击”OK”

源码管理配置

SVN配置参数:

1
2
3
4
Repository URL: svn://your-svn-server/project/trunk
Credentials: 添加SVN用户名密码
Local module directory: .(检出到工作目录根)
Repository depth: infinity(完整检出)

触发器配置

1
2
3
4
5
6
7
Build Triggers:
☑ Poll SCM
Schedule: H/5 * * * * (每5分钟轮询)



☑ Build when a change is pushed to SVN

SVN强制提交注释

为了保证代码提交质量,可以配置SVN提交前强制要求填写注释。

服务端配置

1. 准备钩子脚本

1
2
3
cd /path/to/svn/repo/hooks
cp pre-commit.tmpl pre-commit
chmod +x pre-commit

2. 编写pre-commit脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

REPOS="$1"
TXN="$2"

SVNLOOK=/usr/bin/svnlook

# 获取提交日志长度
LOGMSG=`$SVNLOOK log -t $TXN $REPOS | wc -m`

# 至少4个汉字(48个字符)
if [ "$LOGMSG" -lt 48 ]
then
echo "提交失败:至少输入4个汉字的描述信息" >&2
exit 1
fi

# 所有检查通过
exit 0

3. 设置可执行权限

1
chmod +x pre-commit

配置说明:

  • 一个汉字占用3个字节(UTF-8)
  • wc -m 统计字符数,4个汉字约为12个字符(不含换行)
  • 根据需要调整最小长度阈值

Node.js与TypeScript编译配置

安装NodeJS插件

1
2
Manage Jenkins → Manage Plugins → Available
搜索 "NodeJS" → 勾选安装

配置Node.js环境

全局工具配置:

1
2
3
4
5
Manage Jenkins → Global Tool Configuration → NodeJS

Name: NodeJS-16
Version: 16.x.x (选择需要的版本)
Global npm packages to install: typescript@latest

TypeScript项目构建

项目结构示例

1
2
3
4
5
6
7
my-project/
├── src/
│ ├── index.ts
│ └── utils/
├── dist/ # 编译输出目录
├── package.json
└── tsconfig.json

tsconfig.json配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

Jenkins构建配置

构建环境:

1
2
Provide Node & npm bin/ folder to PATH
NodeJS Installation: NodeJS-16

构建步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 安装依赖
npm ci

# 2. 编译TypeScript
./node_modules/.bin/tsc -p tsconfig.json

# 或简写为
npx tsc -p tsconfig.json

# 3. 运行测试(可选)
npm test

# 4. 打包部署(可选)
npm run build
cp -r dist/* /var/www/html/

多环境配置

对于需要构建多个环境(开发/测试/生产)的项目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用环境变量控制配置
case $BUILD_ENV in
"dev")
cp config/dev.config.ts src/config.ts
;;
"test")
cp config/test.config.ts src/config.ts
;;
"prod")
cp config/prod.config.ts src/config.ts
;;
esac

# 编译
npx tsc -p tsconfig.json

Jenkins Pipeline流水线

Pipeline基础概念

Jenkins Pipeline使用Groovy DSL定义完整的构建流程,支持:

  • 复杂的构建逻辑
  • 并行执行
  • 可视化流程
  • 持久化状态

声明式Pipeline示例

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
pipeline {
agent any

environment {
NODE_VERSION = '16'
DEPLOY_PATH = '/var/www/app'
}

stages {
stage('Checkout') {
steps {
checkout scm
}
}

stage('Install Dependencies') {
steps {
nodejs(nodeJSInstallationName: 'NodeJS-16') {
sh 'npm ci'
}
}
}

stage('Compile TypeScript') {
steps {
nodejs(nodeJSInstallationName: 'NodeJS-16') {
sh 'npx tsc -p tsconfig.json'
}
}
}

stage('Run Tests') {
steps {
nodejs(nodeJSInstallationName: 'NodeJS-16') {
sh 'npm test'
}
}
}

stage('Deploy') {
when {
branch 'main'
}
steps {
sh '''
rsync -avz --delete dist/ user@server:${DEPLOY_PATH}
'''
}
}
}

post {
always {
cleanWs()
}
success {
echo '构建成功!'
}
failure {
echo '构建失败!'
}
}
}

Pipeline脚本式示例

对于更灵活的场景,可以使用脚本式Pipeline:

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
node {
try {
stage('准备') {
checkout scm
}

stage('构建') {
nodejs(nodeJSInstallationName: 'NodeJS-16') {
sh 'npm ci'
sh 'npx tsc'
}
}

stage('测试') {
nodejs(nodeJSInstallationName: 'NodeJS-16') {
sh 'npm test'
}
}

if (env.BRANCH_NAME == 'main') {
stage('部署') {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'production-server',
transfers: [
sshTransfer(
sourceFiles: 'dist/**',
removePrefix: 'dist',
remoteDirectory: '/var/www/app'
)
]
)
]
)
}
}
} catch (e) {
currentBuild.result = 'FAILURE'
throw e
} finally {
cleanWs()
}
}

自动化部署策略

部署方式对比

方式 工具 适用场景 特点
SSH命令 ssh/sshPublisher 单服务器 简单直接
Rsync rsync 多服务器 增量同步
Docker docker/docker-compose 容器化部署 环境一致
Kubernetes kubectl/helm 大规模部署 自动扩缩容

SSH部署配置

Publish Over SSH插件配置:

1
2
3
4
5
6
7
8
9
10
11
Manage Jenkins → Configure System → Publish over SSH

SSH Servers:
Name: production-server
Hostname: 192.168.1.100
Username: deploy
Remote Directory: /var/www

Advanced:
☑ Use password authentication, or use a different key
Passphrase / Password: ********

构建后操作:

1
2
3
4
5
6
7
8
9
Send build artifacts over SSH
SSH Server: production-server
Source files: dist/**/*
Remove prefix: dist
Remote directory: app
Exec command: |
cd /var/www/app
npm install --production
pm2 restart app

Docker部署

Dockerfile示例:

1
2
3
4
5
6
7
8
9
10
11
12
FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY dist/ ./dist/

EXPOSE 3000

CMD ["node", "dist/index.js"]

Pipeline中的Docker构建:

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
stage('Docker Build & Push') {
steps {
script {
def image = docker.build("myapp:${BUILD_NUMBER}")
docker.withRegistry('https://registry.example.com', 'docker-credentials') {
image.push()
image.push('latest')
}
}
}
}

stage('Docker Deploy') {
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'docker-host',
execCommand: '''
docker pull registry.example.com/myapp:latest
docker-compose up -d
'''
)
]
)
}
}

最佳实践与优化

构建优化策略

依赖缓存

1
2
3
4
5
# 使用npm ci而不是npm install(更快、确定性更强)
npm ci

# 或使用yarn的离线模式
yarn install --offline

增量构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
stage('TypeScript Compile') {
steps {
// 检查文件变更
script {
def changed = sh(
script: 'git diff --name-only HEAD~1 | grep -E "\\.ts$"',
returnStatus: true
)
if (changed == 0) {
sh 'npx tsc'
} else {
echo 'No TypeScript files changed, skipping compile'
}
}
}
}

安全建议

  1. 凭证管理:使用Jenkins Credentials存储密码、密钥
  2. 权限控制:基于角色的访问控制(Role-based Authorization)
  3. 安全更新:定期更新Jenkins和插件版本
  4. 备份策略:定期备份Jenkins主目录

监控与告警

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
post {
failure {
emailext (
subject: "构建失败: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: """构建失败!
项目: ${env.JOB_NAME}
构建号: ${env.BUILD_NUMBER}
详情: ${env.BUILD_URL}
""",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
fixed {
slackSend(
color: 'good',
message: "✅ 构建恢复: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
}

总结

Jenkins配置虽然繁琐,但一旦跑起来能节省很多重复工作。建议从小规模项目开始,逐步完善自动化流程。


参考资源: