Jenkins 读取 Spring Boot 的 application.yaml 并执行 SQL 脚本到数据库
下面是一个完整的 Jenkins Pipeline 实现方案,用于读取 Spring Boot 项目的 application.yaml
中的 app.version
,并根据版本号执行 db
目录下对应的 SQL 脚本,数据库连接信息从服务器环境变量获取。
TIPS:为了保证连接数据库信息的安全性,建议把数据库信息的url、用户名、密码配置到jenkins服务器的环境变量中。
1. 项目结构假设
your-springboot-project/
├── src/main/resources/
│ └── application.yaml
├── db/
│ ├── 1.0.0/
│ │ ├── create_tables.sql
│ │ └── initial_data.sql
│ ├── 1.1.0/
│ │ └── add_new_columns.sql
│ └── 1.2.0/
│ └── update_views.sql
└── Jenkinsfile
2. application.yaml 示例
app:
version: 1.2.0
# 其他配置...
3. Jenkins Pipeline 实现
pipeline {
agent any
stages {
stage('Checkout') {
steps {
// 仓库地址
git branch: 'main', url: 'https://your-git-repo.git'
}
}
stage('Read app.version') {
steps {
script {
// 读取Spring Boot的application.yaml
// 注意路径是相对于项目根目录的
def yamlContent = readYaml file: 'src/main/resources/application.yaml'
// 获取版本号
currentVersion = yamlContent.app.version
echo "Current app version: ${currentVersion}"
// 验证版本号格式
if (!(currentVersion =~ /^\d+\.\d+\.\d+$/)) {
error "Invalid version format: ${currentVersion}"
}
}
}
}
stage('Execute SQL Scripts') {
environment {
// 从服务器环境变量获取数据库配置
// 这些变量需要在Jenkins服务器或agent上设置
DB_URL = credentials('DB_URL') // 或者直接使用 env.DB_URL
DB_USER = credentials('DB_USER') // 或者直接使用 env.DB_USER
DB_PASSWORD = credentials('DB_PASSWORD') // 或者直接使用 env.DB_PASSWORD
DB_NAME = credentials('DB_NAME') // 可选
}
steps {
script {
// 检查SQL目录是否存在
def sqlDir = "db/${currentVersion}"
if (!fileExists(sqlDir)) {
error "SQL directory for version ${currentVersion} does not exist!"
}
// 获取所有.sql文件并按名称排序
def sqlFiles = findFiles glob: "${sqlDir}/*.sql"
if (sqlFiles.isEmpty()) {
echo "No SQL files found for version ${currentVersion}"
} else {
echo "Found ${sqlFiles.size()} SQL files for version ${currentVersion}"
// 按文件名排序执行
sqlFiles.sort { it.name }.each { file ->
def fileName = file.name
echo "Executing SQL script: ${fileName}"
// 根据数据库类型选择执行方式
if (env.DB_TYPE == 'mysql' || env.DB_TYPE == null) {
// MySQL执行方式
sh """
mysql -h ${DB_URL} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} \
< ${file.path}
"""
} else if (env.DB_TYPE == 'postgresql') {
// PostgreSQL执行方式
withEnv(["PGPASSWORD=${DB_PASSWORD}"]) {
sh """
psql -h ${DB_URL} -U ${DB_USER} -d ${DB_NAME} \
-f ${file.path}
"""
}
} else {
error "Unsupported database type: ${env.DB_TYPE}"
}
echo "Successfully executed ${fileName}"
}
}
}
}
}
}
post {
success {
echo "Database update completed successfully for version ${currentVersion}"
}
failure {
echo "Database update failed for version ${currentVersion}"
// 可以添加通知逻辑
}
}
}
4. 配置说明
环境变量设置
-
在服务器上设置环境变量(以Linux为例):
# 编辑/etc/environment或用户profile文件 export DB_URL=your-db-host export DB_USER=your-db-user export DB_PASSWORD=your-db-password export DB_NAME=your-db-name export DB_TYPE=mysql # 或 postgresql
-
或者在Jenkins中配置:
- 进入 Jenkins → Manage Jenkins → Configure System → Global properties
- 勾选 "Environment variables" 并添加键值对
安全建议
-
使用Jenkins凭据管理敏感信息:
environment { DB_URL = credentials('db-prod-url') DB_USER = credentials('db-prod-user') DB_PASSWORD = credentials('db-prod-password') }
-
添加执行前确认(特别是生产环境):
stage('Confirm Deployment') { when { expression { env.BRANCH_NAME == 'main' || env.BRANCH_NAME == 'master' } } steps { timeout(time: 1, unit: 'MINUTES') { input message: "Confirm deployment to PROD for version ${currentVersion}?", ok: "Deploy" } } }
5. 高级改进建议
-
添加版本验证逻辑:
script { // 获取当前数据库版本(需要实现查询逻辑) def currentDbVersion = sh(script: 'mysql -N -h ${DB_URL} -u ${DB_USER} -p${DB_PASSWORD} -e "SELECT version FROM db_version"', returnStdout: true).trim() if (compareVersions(currentVersion, currentDbVersion) <= 0) { error "Target version ${currentVersion} is not newer than current DB version ${currentDbVersion}" } }
-
实现版本比较函数(放在Pipeline开头):
def compareVersions(String v1, String v2) { def parts1 = v1.tokenize('.').collect { it as int } def parts2 = v2.tokenize('.').collect { it as int } for (int i = 0; i < Math.max(parts1.size(), parts2.size()); i++) { def p1 = i < parts1.size() ? parts1[i] : 0 def p2 = i < parts2.size() ? parts2[i] : 0 if (p1 != p2) { return p1 <=> p2 } } return 0 }
-
添加事务支持(复杂场景):
- 考虑将所有SQL脚本合并为一个事务,可能造成执行性能问题
- 或者使用专业的数据库迁移工具(Flyway/Liquibase)
-
添加SQL重复执行判断(复杂场景)
可以根据你的具体需求进一步调整。