ci: 添加 Forgejo Actions 工作流配置
Some checks are pending
CI / 代码检查和类型检查 (push) Waiting to run
CI / 构建应用 (push) Blocked by required conditions
CI / 安全扫描 (push) Waiting to run
Deploy / 构建并推送 Docker 镜像 (push) Waiting to run
Deploy / 部署到生产环境 (push) Blocked by required conditions
Deploy / 部署到测试环境 (push) Blocked by required conditions
Deploy / 部署通知 (push) Blocked by required conditions

添加 CI/CD 工作流配置文件,包括:
- ci.yml: 代码检查、构建和安全扫描
- deploy.yml: 构建 Docker 镜像和部署到不同环境
- database.yml: 数据库迁移、备份和回滚操作
- README.md: 工作流使用说明和配置指南
This commit is contained in:
bo.yu 2025-09-17 17:18:03 +08:00
parent 6e50bee438
commit 1c1c82ae07
4 changed files with 500 additions and 0 deletions

View file

@ -0,0 +1,129 @@
# Forgejo Actions 工作流配置

本项目使用 Forgejo Actions 进行 CI/CD 自动化,包含以下工作流:

## 工作流文件

### 1. CI 工作流 (`ci.yml`)
- **触发条件**: 推送到 `main`、`develop` 分支或创建 Pull Request
- **功能**:
- 代码风格检查 (`yarn lint`)
- TypeScript 类型检查 (`yarn typecheck`)
- 应用构建 (`yarn build`)
- 安全漏洞扫描 (`yarn audit`)

### 2. 部署工作流 (`deploy.yml`)
- **触发条件**: 推送到 `main` 分支、创建标签或手动触发
- **功能**:
- 构建并推送 Docker 镜像
- 部署到生产环境
- 部署到测试环境(手动触发)
- 部署结果通知

### 3. 数据库工作流 (`database.yml`)
- **触发条件**: 手动触发
- **功能**:
- 数据库迁移
- 数据库备份
- 数据库回滚

## 必需的环境变量和密钥

### Repository Secrets
在 Forgejo 仓库设置中配置以下密钥:

#### Docker 相关
```
DOCKER_USERNAME=your_docker_username
DOCKER_PASSWORD=your_docker_password
```

#### 数据库相关
```
DATABASE_URL=postgresql://user:password@host:port/database
POSTGRES_HOST=your_postgres_host
POSTGRES_PORT=5432
POSTGRES_DB=your_database_name
POSTGRES_USER=your_postgres_user
POSTGRES_PASSWORD=your_postgres_password
```

#### 部署相关
```
DEPLOY_USER=your_deploy_user
DEPLOY_HOST=your_server_host
DEPLOY_SSH_KEY=your_private_ssh_key
```

#### 通知相关(可选)
```
WEBHOOK_URL=your_notification_webhook_url
```

### Environment Variables
在工作流中使用的环境变量:

```yaml
env:
NODE_VERSION: '20'
REGISTRY: docker.io
IMAGE_NAME: windfonts
```

## 环境配置

### Production Environment
- 用于生产环境部署
- 需要配置所有必需的密钥
- 自动触发(推送到 main 分支)

### Staging Environment
- 用于测试环境部署
- 手动触发
- 可以使用测试数据库

### Development Environment
- 用于开发环境数据库操作
- 仅用于数据库工作流

## 使用指南

### 1. 首次设置
1. 在 Forgejo 仓库设置中添加所有必需的 Secrets
2. 确保 Docker Registry 访问权限正确
3. 配置服务器 SSH 访问权限

### 2. 日常开发
- 推送代码到 `develop` 分支会触发 CI 检查
- 创建 Pull Request 会自动运行 CI 流程
- 合并到 `main` 分支会触发部署流程

### 3. 数据库操作
- 在 Actions 页面手动触发数据库工作流
- 选择操作类型migrate/backup/rollback
- 选择目标环境

### 4. 手动部署
- 在 Actions 页面手动触发部署工作流
- 选择部署环境production/staging

## 注意事项

1. **安全性**: 确保所有敏感信息都通过 Secrets 配置,不要在代码中硬编码
2. **权限**: 确保 Forgejo Actions 有足够的权限访问 Docker Registry 和部署服务器
3. **备份**: 在执行数据库迁移前建议先创建备份
4. **监控**: 关注工作流执行结果,及时处理失败的任务

## 故障排除

### 常见问题
1. **Docker 推送失败**: 检查 Docker 凭证是否正确
2. **部署失败**: 检查 SSH 密钥和服务器访问权限
3. **数据库连接失败**: 检查数据库连接字符串和网络访问
4. **构建失败**: 检查依赖安装和环境变量配置

### 调试方法
1. 查看 Actions 执行日志
2. 检查环境变量和密钥配置
3. 在本地环境复现问题
4. 使用 `workflow_dispatch` 手动触发测试

105
.forgejo/workflows/ci.yml Normal file
View file

@ -0,0 +1,105 @@
name: CI

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]

env:
NODE_VERSION: '20'

jobs:
lint-and-typecheck:
name: 代码检查和类型检查
runs-on: docker
container:
image: node:20-alpine
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4
with:
fetch-depth: 0

- name: 设置 Node.js 缓存
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
~/.yarn/cache
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-

- name: 安装依赖
run: |
yarn config set registry https://registry.npmmirror.com
yarn install --frozen-lockfile

- name: 代码风格检查
run: yarn lint

- name: TypeScript 类型检查
run: yarn typecheck

build:
name: 构建应用
runs-on: docker
container:
image: node:20-alpine
needs: lint-and-typecheck
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4
with:
fetch-depth: 0

- name: 设置 Node.js 缓存
uses: https://code.forgejo.org/actions/cache@v4
with:
path: |
~/.yarn/cache
node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-

- name: 安装依赖
run: |
yarn config set registry https://registry.npmmirror.com
yarn install --frozen-lockfile

- name: 构建应用
run: yarn build
env:
NODE_ENV: production

- name: 上传构建产物
uses: https://code.forgejo.org/actions/upload-artifact@v4
with:
name: build-files
path: |
.next/
public/
retention-days: 7

security-scan:
name: 安全扫描
runs-on: docker
container:
image: node:20-alpine
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4

- name: 安装依赖
run: |
yarn config set registry https://registry.npmmirror.com
yarn install --frozen-lockfile

- name: 安全漏洞扫描
run: yarn audit --audit-level moderate
continue-on-error: true

View file

@ -0,0 +1,134 @@
name: Database Migration

on:
workflow_dispatch:
inputs:
action:
description: '数据库操作'
required: true
default: 'migrate'
type: choice
options:
- migrate
- backup
- rollback
environment:
description: '目标环境'
required: true
default: 'production'
type: choice
options:
- production
- staging
- development

env:
NODE_VERSION: '20'

jobs:
database-migrate:
name: 数据库迁移
runs-on: docker
container:
image: node:20-alpine
if: github.event.inputs.action == 'migrate'
environment: ${{ github.event.inputs.environment }}
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4

- name: 安装依赖
run: |
yarn config set registry https://registry.npmmirror.com
yarn install --frozen-lockfile

- name: 数据库迁移
run: |
echo "执行数据库迁移..."
yarn drizzle-kit migrate
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
POSTGRES_HOST: ${{ secrets.POSTGRES_HOST }}
POSTGRES_PORT: ${{ secrets.POSTGRES_PORT }}
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}

- name: 验证迁移结果
run: |
echo "验证数据库迁移结果..."
# 可以添加数据库连接测试
node -e "
const { drizzle } = require('drizzle-orm/postgres-js');
const postgres = require('postgres');
const sql = postgres(process.env.DATABASE_URL);
const db = drizzle(sql);
console.log('数据库连接成功');
sql.end();
"

database-backup:
name: 数据库备份
runs-on: docker
container:
image: postgres:15-alpine
if: github.event.inputs.action == 'backup'
environment: ${{ github.event.inputs.environment }}
steps:
- name: 创建数据库备份
run: |
echo "创建数据库备份..."
BACKUP_FILE="backup_$(date +%Y%m%d_%H%M%S).sql"
pg_dump $DATABASE_URL > $BACKUP_FILE
echo "备份文件: $BACKUP_FILE"
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}

- name: 上传备份文件
uses: https://code.forgejo.org/actions/upload-artifact@v4
with:
name: database-backup-${{ github.event.inputs.environment }}
path: "*.sql"
retention-days: 30

database-rollback:
name: 数据库回滚
runs-on: docker
container:
image: node:20-alpine
if: github.event.inputs.action == 'rollback'
environment: ${{ github.event.inputs.environment }}
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4

- name: 安装依赖
run: |
yarn config set registry https://registry.npmmirror.com
yarn install --frozen-lockfile

- name: 数据库回滚
run: |
echo "执行数据库回滚..."
# 注意drizzle-kit 可能不直接支持回滚,需要手动处理
echo "请手动执行回滚操作或使用备份恢复"
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}

notify-result:
name: 通知结果
runs-on: docker
container:
image: alpine:latest
needs: [database-migrate, database-backup, database-rollback]
if: always()
steps:
- name: 发送通知
run: |
echo "数据库操作完成: ${{ github.event.inputs.action }}"
echo "环境: ${{ github.event.inputs.environment }}"
# 可以集成通知服务

View file

@ -0,0 +1,132 @@
name: Deploy

on:
push:
branches: [ main ]
tags: [ 'v*' ]
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'production'
type: choice
options:
- production
- staging

env:
REGISTRY: docker.io
IMAGE_NAME: windfonts

jobs:
build-and-push:
name: 构建并推送 Docker 镜像
runs-on: docker
container:
image: docker:24-dind
steps:
- name: 检出代码
uses: https://code.forgejo.org/actions/checkout@v4
with:
fetch-depth: 0

- name: 设置 Docker Buildx
uses: https://github.com/docker/setup-buildx-action@v3

- name: 登录到 Docker Registry
uses: https://github.com/docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: 提取元数据
id: meta
uses: https://github.com/docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-

- name: 构建并推送 Docker 镜像
uses: https://github.com/docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64,linux/arm64

deploy-production:
name: 部署到生产环境
runs-on: docker
container:
image: alpine:latest
needs: build-and-push
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')
environment: production
steps:
- name: 安装必要工具
run: |
apk add --no-cache curl openssh-client

- name: 部署到服务器
run: |
echo "部署到生产环境..."
# 这里可以添加具体的部署脚本
# 例如:通过 SSH 连接到服务器并更新容器
# ssh -o StrictHostKeyChecking=no ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
# "docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main && \
# docker-compose up -d --no-deps app"

- name: 健康检查
run: |
echo "执行健康检查..."
# 可以添加健康检查逻辑
# curl -f https://app.windfonts.com/api/health || exit 1

deploy-staging:
name: 部署到测试环境
runs-on: docker
container:
image: alpine:latest
needs: build-and-push
if: github.event.inputs.environment == 'staging'
environment: staging
steps:
- name: 安装必要工具
run: |
apk add --no-cache curl openssh-client

- name: 部署到测试环境
run: |
echo "部署到测试环境..."
# 测试环境部署逻辑

notify:
name: 部署通知
runs-on: docker
container:
image: alpine:latest
needs: [deploy-production, deploy-staging]
if: always()
steps:
- name: 发送通知
run: |
echo "发送部署结果通知..."
# 可以集成钉钉、企业微信等通知服务
# curl -X POST ${{ secrets.WEBHOOK_URL }} \
# -H 'Content-Type: application/json' \
# -d '{"text": "部署完成: ${{ github.ref }}"}'