如何利用 Github Actions + branch 实现 Prisma(伪)多数据源?
> 副标题:利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)
渲染中...
> 副标题:利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像
参考仓库: https://github.com/dingdangdog/cashbook
# 利用GitHub Actions + Git分支实现一套代码构建多数据库(Prisma)版本Docker镜像
## 概述
在现代应用开发中,支持多种数据库是一个常见需求。特别是对于基于 `Prisma ORM` 的 `Node.js` 应用,用户可能需要在不同环境下使用 `PostgreSQL`、`MySQL`、`SQLite` 或 `SQL Server` 等不同数据库。本教程将详细介绍如何利用 `GitHub Actions` 和 `Git分支` 策略,实现一套代码自动构建多个数据库版本的 `Docker` 镜像。
<!-- more -->
## 适用场景
### 主要适用场景
1. **多数据库支持的应用**
- 需要同时支持 `PostgreSQL`、`MySQL`、`SQLite`、`SQL 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"
```
2. **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. **面向多样化客户需求的产品**
这个方案不仅降低了维护成本,还提高了部署的灵活性,是现代应用开发中值得推广的最佳实践。通过合理的分支管理和自动化流程,开发团队可以专注于核心功能开发,而无需为多数据库支持投入过多的维护精力。
END
评论
登录后查看和发表评论
前往登录