2025-06-20
编程与技术
00

目录

利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像
概述
适用场景
主要适用场景
其他可应用场景
核心架构设计
分支策略
实施步骤
第一步:创建数据库专用分支
第二步:配置Prisma Schema
第三步:处理Prisma Migration脚本(关键步骤)
3.1 Migration管理策略
3.2 Schema修改工作流程
3.3 自动化Migration生成工作流
3.4 冲突处理策略
3.5 最佳实践
第四步:配置自动分支同步
第五步:配置Docker构建工作流
第六步:Docker镜像标签策略
核心优势
1. 自动化管理
2. 版本一致性
3. 部署便利性
4. 维护成本低
其他优化建议(自行决定是否采用)
1. Migration管理的高级策略
1.1 Migration冲突预防
1.2 Migration验证工作流
1.3 自动检测Schema不一致
2. 数据库差异处理
2. 环境变量配置
3. 分支冲突处理
4. 版本发布流程
总结

副标题:利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像

参考仓库: https://github.com/dingdangdog/cashbook

利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像

概述

在现代应用开发中,支持多种数据库是一个常见需求。特别是对于基于 Prisma ORMNode.js 应用,用户可能需要在不同环境下使用 PostgreSQLMySQLSQLiteSQL Server 等不同数据库。本教程将详细介绍如何利用 GitHub ActionsGit分支 策略,实现一套代码自动构建多个数据库版本的 Docker 镜像。

适用场景

主要适用场景

  1. 多数据库支持的应用

    • 需要同时支持 PostgreSQLMySQLSQLiteSQL Server等多种数据库
    • 使用 Prisma ORM 进行数据库操作
    • 需要为不同客户提供不同数据库版本
  2. SaaS产品

    • 企业客户可能有特定的数据库要求
    • 需要提供多种部署选项
    • 降低客户迁移成本
  3. 开源项目

    • 让用户根据现有基础设施选择合适的数据库
    • 提供更好的兼容性和易用性
    • 降低部署门槛

其他可应用场景

  1. 多环境配置

    • 开发环境使用 SQLite ,生产环境使用 PostgreSQL
    • 测试环境和生产环境使用不同数据库
  2. 微服务架构

    • 不同服务使用不同数据库
    • 需要维护多个数据库版本的镜像
  3. 数据库迁移项目

    • 从一种数据库迁移到另一种数据库
    • 需要同时维护新旧版本

核心架构设计

分支策略

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

第二步:配置Prisma Schema

每个分支需要配置对应的数据库提供商:

PostgreSQL版本 (main/main-pgsql):

prisma
datasource db { provider = "postgresql" url = env("DATABASE_URL") }

MySQL版本 (main-mysql):

prisma
datasource db { provider = "mysql" url = env("DATABASE_URL") }

SQLite版本 (main-sqlite):

prisma
datasource db { provider = "sqlite" url = env("DATABASE_URL") }

SQL Server版本 (main-sqlserver):

prisma
datasource db { provider = "sqlserver" url = env("DATABASE_URL") }

第三步:处理Prisma Migration脚本(关键步骤)

这是整个方案中最复杂和最关键的步骤!

由于不同数据库生成的Prisma migration脚本不同,每次修改数据库结构时,需要在每个分支上独立生成migration脚本。这个过程容易产生冲突,需要特别小心处理。

3.1 Migration管理策略

原则:每个数据库分支独立管理自己的migration文件

3.2 Schema修改工作流程

当需要修改数据库结构时,按以下顺序操作:

  1. 在main分支修改schema.prisma
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
  1. 为每个数据库分支生成专用migration
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

3.3 自动化Migration生成工作流

创建 .github/workflows/generate-migrations.yaml

yaml
name: 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

3.4 冲突处理策略

常见冲突场景及解决方案:

  1. Migration文件冲突
bash
# 当合并时出现migration冲突 git checkout main-mysql git merge main # 如果有冲突,手动解决 git status # 查看冲突文件 # 编辑冲突文件,保留正确的数据库配置 git add . git commit -m "resolve: merge conflicts in schema"
  1. Migration顺序冲突
bash
# 如果migration时间戳冲突,重新生成 npx prisma migrate reset # 重置migrations npx prisma migrate dev --name fresh_migration
  1. 数据库特定的语法冲突
prisma
// 在schema.prisma中使用条件配置 model User { id Int @id @default(autoincrement()) // MySQL不支持某些PostgreSQL特性 // 需要在各分支中适配 data Json? // 某些数据库可能不支持 }

3.5 最佳实践

  1. 永远不要直接合并migration文件
  2. 每个分支的migration文件应该独立管理
  3. 使用工作流程自动化生成migration
  4. 定期检查各分支的migration一致性
  5. 在合并前先测试所有数据库版本

第四步:配置自动分支同步

注意:这个同步工作流需要排除migration文件

创建 .github/workflows/auto-sync-branches.yaml

yaml
name: 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 # 重复其他数据库分支的同步步骤...

第五步:配置Docker构建工作流

为每种数据库创建独立的构建工作流:

默认版本 (release-docker.yaml):

yaml
name: 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):

yaml
name: 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 .

第六步:Docker镜像标签策略

采用以下标签命名规范:

  • 默认版本(PostgreSQL): latest, {version}
  • MySQL版本: latest-mysql, {version}-mysql
  • SQLite版本: latest-sqlite, {version}-sqlite
  • SQL Server版本: latest-sqlserver, {version}-sqlserver

核心优势

1. 自动化管理

  • 代码只需要在main分支维护
  • 自动同步到所有数据库分支
  • 自动构建所有版本的Docker镜像

2. 版本一致性

  • 所有数据库版本使用相同的应用代码
  • 只有数据库配置不同
  • 确保功能特性的一致性

3. 部署便利性

  • 用户可以根据需求选择合适的数据库版本
  • Docker镜像标签清晰明确
  • 支持多架构(x86/ARM64)

4. 维护成本低

  • 单一代码库维护
  • 分支自动同步
  • 减少重复配置

其他优化建议(自行决定是否采用)

1. Migration管理的高级策略

由于这是整个方案的核心难点,这里提供更多实践建议:

1.1 Migration冲突预防

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!"

1.2 Migration验证工作流

创建 .github/workflows/validate-migrations.yaml

yaml
name: 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

1.3 自动检测Schema不一致

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

2. 数据库差异处理

不同数据库可能存在语法差异,需要注意:

prisma
// 在Prisma schema中使用条件配置 model User { id Int @id @default(autoincrement()) username String @unique // SQLite不支持某些数据类型,需要特殊处理 createDate DateTime @default(now()) }

2. 环境变量配置

确保Docker镜像支持不同数据库的环境变量:

dockerfile
# 在Dockerfile中设置默认数据库连接 ENV DATABASE_URL="postgresql://postgres:123456@localhost:5432/cashbook?schema=public"

3. 分支冲突处理

当自动同步出现冲突时:

yaml
git merge main --no-edit || { echo "Merge conflict detected, manual intervention required" exit 1 }

4. 版本发布流程

  1. 在main分支完成功能开发
  2. 创建版本标签:git tag v1.0.0
  3. 推送标签:git push origin v1.0.0
  4. 自动触发所有数据库版本的构建

总结

通过GitHub Actions和Git分支策略实现多数据库版本Docker镜像构建,是一个高效、可维护的解决方案。这种方法特别适合于:

  1. 使用Prisma ORM的Node.js应用
  2. 需要支持多种数据库的项目
  3. 追求自动化和标准化的团队
  4. 面向多样化客户需求的产品

这个方案不仅降低了维护成本,还提高了部署的灵活性,是现代应用开发中值得推广的最佳实践。通过合理的分支管理和自动化流程,开发团队可以专注于核心功能开发,而无需为多数据库支持投入过多的维护精力。

本文作者:DingDangDog

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!