副标题:利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像
参考仓库: https://github.com/dingdangdog/cashbook
在现代应用开发中,支持多种数据库是一个常见需求。特别是对于基于 Prisma ORM
的 Node.js
应用,用户可能需要在不同环境下使用 PostgreSQL
、MySQL
、SQLite
或 SQL Server
等不同数据库。本教程将详细介绍如何利用 GitHub Actions
和 Git分支
策略,实现一套代码自动构建多个数据库版本的 Docker
镜像。
多数据库支持的应用
PostgreSQL
、MySQL
、SQLite
、SQL Server
等多种数据库Prisma ORM
进行数据库操作SaaS产品
开源项目
多环境配置
SQLite
,生产环境使用 PostgreSQL
微服务架构
数据库迁移项目
main (PostgreSQL - 默认版本) ├── main-mysql (MySQL版本) ├── main-pgsql (PostgreSQL版本) ├── main-sqlite (SQLite版本) └── main-sqlserver (SQL Server版本)
首先需要为每种数据库创建专用分支:
bash# 创建并切换到MySQL分支
git checkout -b main-mysql
# 修改Prisma schema为MySQL配置
# 提交并推送
# 创建其他数据库分支
git checkout main
git checkout -b main-pgsql
git checkout -b main-sqlite
git checkout -b main-sqlserver
每个分支需要配置对应的数据库提供商:
PostgreSQL版本 (main/main-pgsql):
prismadatasource db { provider = "postgresql" url = env("DATABASE_URL") }
MySQL版本 (main-mysql):
prismadatasource db { provider = "mysql" url = env("DATABASE_URL") }
SQLite版本 (main-sqlite):
prismadatasource db { provider = "sqlite" url = env("DATABASE_URL") }
SQL Server版本 (main-sqlserver):
prismadatasource db { provider = "sqlserver" url = env("DATABASE_URL") }
这是整个方案中最复杂和最关键的步骤!
由于不同数据库生成的Prisma migration脚本不同,每次修改数据库结构时,需要在每个分支上独立生成migration脚本。这个过程容易产生冲突,需要特别小心处理。
原则:每个数据库分支独立管理自己的migration文件
当需要修改数据库结构时,按以下顺序操作:
bash# 在main分支修改Prisma schema
git checkout main
# 修改 prisma/schema.prisma
# 提交更改但不生成migration
git add prisma/schema.prisma
git commit -m "update: modify database schema"
git push origin main
bash# MySQL分支
git checkout main-mysql
git merge main # 合并schema变更
# 修改datasource为mysql
npm run prisma:migrate dev --name update_schema_mysql
# PostgreSQL分支
git checkout main-pgsql
git merge main
# 修改datasource为postgresql
npm run prisma:migrate dev --name update_schema_pgsql
# SQLite分支
git checkout main-sqlite
git merge main
# 修改datasource为sqlite
npm run prisma:migrate dev --name update_schema_sqlite
# SQL Server分支
git checkout main-sqlserver
git merge main
# 修改datasource为sqlserver
npm run prisma:migrate dev --name update_schema_sqlserver
创建 .github/workflows/generate-migrations.yaml
:
yamlname: Generate Database Migrations
on:
workflow_dispatch:
inputs:
migration_name:
description: 'Migration name'
required: true
default: 'update_schema'
jobs:
generate-migrations:
runs-on: ubuntu-latest
strategy:
matrix:
database: [mysql, pgsql, sqlite, sqlserver]
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main-${{ matrix.database }}
token: ${{ secrets.MERGE_TOKEN }}
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Configure database URL for ${{ matrix.database }}
run: |
case "${{ matrix.database }}" in
mysql)
echo "DATABASE_URL=mysql://user:password@localhost:3306/testdb" >> $GITHUB_ENV
;;
pgsql)
echo "DATABASE_URL=postgresql://user:password@localhost:5432/testdb" >> $GITHUB_ENV
;;
sqlite)
echo "DATABASE_URL=file:./dev.db" >> $GITHUB_ENV
;;
sqlserver)
echo "DATABASE_URL=sqlserver://localhost:1433;database=testdb;user=sa;password=Password123" >> $GITHUB_ENV
;;
esac
- name: Generate Prisma Client
run: npx prisma generate
- name: Create migration
run: |
npx prisma migrate dev --name ${{ github.event.inputs.migration_name }}_${{ matrix.database }} --create-only
continue-on-error: true
- name: Commit and push migration
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
if [ -n "$(git status --porcelain prisma/migrations/)" ]; then
git add prisma/migrations/
git commit -m "migration: generate ${{ github.event.inputs.migration_name }} for ${{ matrix.database }}"
git push origin main-${{ matrix.database }}
echo "Migration generated and pushed for ${{ matrix.database }}"
else
echo "No migration changes for ${{ matrix.database }}"
fi
常见冲突场景及解决方案:
bash# 当合并时出现migration冲突
git checkout main-mysql
git merge main
# 如果有冲突,手动解决
git status # 查看冲突文件
# 编辑冲突文件,保留正确的数据库配置
git add .
git commit -m "resolve: merge conflicts in schema"
bash# 如果migration时间戳冲突,重新生成
npx prisma migrate reset # 重置migrations
npx prisma migrate dev --name fresh_migration
prisma// 在schema.prisma中使用条件配置 model User { id Int @id @default(autoincrement()) // MySQL不支持某些PostgreSQL特性 // 需要在各分支中适配 data Json? // 某些数据库可能不支持 }
注意:这个同步工作流需要排除migration文件
创建 .github/workflows/auto-sync-branches.yaml
:
yamlname: Auto Sync Branches
on:
push:
branches:
- main
paths-ignore:
- 'prisma/migrations/**' # 排除migration文件
permissions:
contents: write
actions: read
jobs:
sync-branches:
runs-on: ubuntu-latest
steps:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.MERGE_TOKEN }}
- name: Configure Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Sync to main-mysql branch
run: |
echo "Syncing to main-mysql branch..."
git checkout main-mysql || git checkout -b main-mysql
# 只合并非migration文件的更改
git merge main --no-edit -X ours prisma/migrations/ || {
echo "Merge conflict detected for main-mysql, manual intervention required"
exit 1
}
git push origin main-mysql
# 重复其他数据库分支的同步步骤...
为每种数据库创建独立的构建工作流:
默认版本 (release-docker.yaml):
yamlname: Release-Docker
on:
push:
tags:
- v*.*.*
jobs:
docker_build:
environment: docker_hub
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get version
id: get_version
run: |
VERSION=$(git describe --tags --always --match 'v*' | sed -n 's|^v\([0-9]*\.[0-9]*\.[0-9]*\)\(-.*\)\{0,1\}|\1|p')
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push multi-platform image
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag dingdangdog/cashbook:${VERSION} \
--tag dingdangdog/cashbook:latest \
--push .
MySQL版本 (release-mysql-docker.yaml):
yamlname: Release-MySQL-Docker
on:
push:
tags:
- v*.*.*
jobs:
docker_build:
environment: docker_hub
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main-mysql # 关键:checkout到MySQL分支
- name: Get version
id: get_version
run: |
VERSION="${GITHUB_REF#refs/tags/v}"
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Build and push multi-platform image
run: |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag dingdangdog/cashbook:${VERSION}-mysql \
--tag dingdangdog/cashbook:latest-mysql \
--push .
采用以下标签命名规范:
latest
, {version}
latest-mysql
, {version}-mysql
latest-sqlite
, {version}-sqlite
latest-sqlserver
, {version}-sqlserver
由于这是整个方案的核心难点,这里提供更多实践建议:
bash# 创建一个专门的migration管理脚本
#!/bin/bash
# scripts/manage-migrations.sh
MIGRATION_NAME=$1
if [ -z "$MIGRATION_NAME" ]; then
echo "Usage: $0 <migration_name>"
exit 1
fi
DATABASES=("mysql" "pgsql" "sqlite" "sqlserver")
echo "🚀 Starting migration generation for all databases..."
for db in "${DATABASES[@]}"; do
echo "📦 Processing $db database..."
# 切换到对应分支
git checkout main-$db
# 合并最新的schema变更(排除migrations)
git merge main --no-edit || {
echo "❌ Merge conflict in main-$db. Please resolve manually."
continue
}
# 设置对应的数据库URL
case $db in
mysql)
export DATABASE_URL="mysql://user:password@localhost:3306/testdb"
;;
pgsql)
export DATABASE_URL="postgresql://user:password@localhost:5432/testdb"
;;
sqlite)
export DATABASE_URL="file:./dev.db"
;;
sqlserver)
export DATABASE_URL="sqlserver://localhost:1433;database=testdb;user=sa;password=Password123"
;;
esac
# 生成migration
echo "🔄 Generating migration for $db..."
npx prisma migrate dev --name "${MIGRATION_NAME}_${db}" --create-only
# 提交变更
if [ -n "$(git status --porcelain prisma/migrations/)" ]; then
git add prisma/migrations/
git commit -m "migration: ${MIGRATION_NAME} for $db"
git push origin main-$db
echo "✅ Migration created and pushed for $db"
else
echo "ℹ️ No migration changes for $db"
fi
done
echo "🎉 Migration generation completed for all databases!"
创建 .github/workflows/validate-migrations.yaml
:
yamlname: Validate Migrations
on:
pull_request:
paths:
- 'prisma/schema.prisma'
- 'prisma/migrations/**'
jobs:
validate-migrations:
runs-on: ubuntu-latest
strategy:
matrix:
database: [mysql, pgsql, sqlite, sqlserver]
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: password
POSTGRES_USER: user
POSTGRES_DB: testdb
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
mysql:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testdb
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: main-${{ matrix.database }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Configure database URL
run: |
case "${{ matrix.database }}" in
mysql)
echo "DATABASE_URL=mysql://user:password@localhost:3306/testdb" >> $GITHUB_ENV
;;
pgsql)
echo "DATABASE_URL=postgresql://user:password@localhost:5432/testdb" >> $GITHUB_ENV
;;
sqlite)
echo "DATABASE_URL=file:./test.db" >> $GITHUB_ENV
;;
sqlserver)
echo "DATABASE_URL=sqlserver://localhost:1433;database=testdb;user=sa;password=Password123" >> $GITHUB_ENV
;;
esac
- name: Run migrations
run: |
npx prisma migrate deploy
npx prisma generate
- name: Test database connection
run: |
npx prisma db execute --stdin <<< "SELECT 1 as test;"
continue-on-error: true
bash#!/bin/bash
# scripts/check-schema-consistency.sh
echo "🔍 Checking schema consistency across branches..."
DATABASES=("mysql" "pgsql" "sqlite" "sqlserver")
TEMP_DIR="/tmp/schema-check"
mkdir -p $TEMP_DIR
# 提取每个分支的schema文件
for db in "${DATABASES[@]}"; do
git show main-$db:prisma/schema.prisma > $TEMP_DIR/schema-$db.prisma
done
# 比较schema文件(排除datasource差异)
echo "📊 Schema comparison results:"
for db in "${DATABASES[@]}"; do
# 移除datasource部分,只比较model定义
grep -v "datasource\|provider\|url" $TEMP_DIR/schema-$db.prisma > $TEMP_DIR/schema-$db-clean.prisma
done
# 检查是否所有clean schema都一致
FIRST_FILE=$TEMP_DIR/schema-${DATABASES[0]}-clean.prisma
CONSISTENT=true
for db in "${DATABASES[@]:1}"; do
if ! diff -q $FIRST_FILE $TEMP_DIR/schema-$db-clean.prisma > /dev/null; then
echo "❌ Schema inconsistency detected between ${DATABASES[0]} and $db"
CONSISTENT=false
fi
done
if [ "$CONSISTENT" = true ]; then
echo "✅ All schemas are consistent"
else
echo "⚠️ Manual review required for schema inconsistencies"
fi
# 清理临时文件
rm -rf $TEMP_DIR
不同数据库可能存在语法差异,需要注意:
prisma// 在Prisma schema中使用条件配置 model User { id Int @id @default(autoincrement()) username String @unique // SQLite不支持某些数据类型,需要特殊处理 createDate DateTime @default(now()) }
确保Docker镜像支持不同数据库的环境变量:
dockerfile# 在Dockerfile中设置默认数据库连接 ENV DATABASE_URL="postgresql://postgres:123456@localhost:5432/cashbook?schema=public"
当自动同步出现冲突时:
yamlgit merge main --no-edit || {
echo "Merge conflict detected, manual intervention required"
exit 1
}
git tag v1.0.0
git push origin v1.0.0
通过GitHub Actions和Git分支策略实现多数据库版本Docker镜像构建,是一个高效、可维护的解决方案。这种方法特别适合于:
这个方案不仅降低了维护成本,还提高了部署的灵活性,是现代应用开发中值得推广的最佳实践。通过合理的分支管理和自动化流程,开发团队可以专注于核心功能开发,而无需为多数据库支持投入过多的维护精力。
本文作者:DingDangDog
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!