326 lines
9.1 KiB
Markdown
326 lines
9.1 KiB
Markdown
# GitHub Actions CI/CD 工作流配置说明
|
||
|
||
## 工作流列表
|
||
|
||
| 工作流 | 说明 | 文档 |
|
||
|--------|------|------|
|
||
| Docker Build and Deploy | API 镜像构建与部署 | 见下文 |
|
||
| Android Release Build | Android APK 构建与发布 | 见下文 |
|
||
| App Expo Deploy | app-expo Web 构建与发布 | [docs/app-expo-deploy.md](../../docs/app-expo-deploy.md) |
|
||
|
||
---
|
||
|
||
## 概述
|
||
|
||
本工作流实现了自动化的 Docker 镜像构建和部署流程:
|
||
|
||
1. **构建阶段**:当代码推送到指定分支时,自动构建 Docker 镜像
|
||
2. **推送阶段**:将构建好的镜像推送到阿里云容器镜像服务(ACR)
|
||
3. **部署阶段**:通过 SSH 连接到远程服务器,拉取最新镜像并重建容器
|
||
|
||
## 工作流触发条件
|
||
|
||
- 推送到 `main`、`master` 或 `develop` 分支
|
||
- 仅当 `api/` 目录或 `.github/workflows/` 目录有变更时触发
|
||
- 支持手动触发(workflow_dispatch)
|
||
|
||
## 配置步骤
|
||
|
||
### 1. 配置 GitHub Secrets
|
||
|
||
在 GitHub 仓库设置中添加以下 Secrets:
|
||
|
||
#### 必需配置
|
||
|
||
- **SSH_PRIVATE_KEY**: SSH 私钥,用于连接到远程服务器
|
||
```bash
|
||
# 生成 SSH 密钥对(如果还没有)
|
||
ssh-keygen -t ed25519 -C "github-actions"
|
||
|
||
# 将私钥添加到 GitHub Secrets
|
||
cat ~/.ssh/id_ed25519 # 复制内容到 SSH_PRIVATE_KEY
|
||
|
||
# 将公钥添加到远程服务器的 ~/.ssh/authorized_keys
|
||
cat ~/.ssh/id_ed25519.pub | ssh user@your-server "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
|
||
```
|
||
|
||
- **SSH_USER**: SSH 用户名(例如:`root`、`ubuntu`、`deploy`)
|
||
- **SSH_HOST**: 远程服务器 IP 地址或域名(例如:`192.168.1.100`、`example.com`)
|
||
|
||
#### 阿里云容器镜像服务配置(必需)
|
||
|
||
- **ALIYUN_CR_USERNAME**: 阿里云容器镜像服务用户名(例如:`zaikunxu`)
|
||
- **ALIYUN_CR_PASSWORD**: 阿里云容器镜像服务密码
|
||
|
||
#### 可选配置
|
||
|
||
- **SSH_PORT**: SSH 端口(默认:`22`)
|
||
- **DEPLOY_PATH**: 远程服务器上的部署目录(默认:`/opt/life-echo`)
|
||
|
||
### 2. 容器镜像仓库配置
|
||
|
||
本工作流使用**阿里云容器镜像服务(ACR)**作为镜像仓库。
|
||
|
||
#### 镜像地址
|
||
|
||
- 镜像仓库地址:`crpi-u2903xccyzd6nqnc.cn-shanghai.personal.cr.aliyuncs.com`
|
||
- 命名空间:`huaga`
|
||
- 镜像名称:`lifecho-api`
|
||
- 完整镜像路径:`crpi-u2903xccyzd6nqnc.cn-shanghai.personal.cr.aliyuncs.com/huaga/lifecho-api:latest`
|
||
|
||
#### 配置说明
|
||
|
||
工作流已配置为使用阿里云容器镜像服务,无需修改工作流文件。只需在 GitHub Secrets 中配置:
|
||
|
||
- `ALIYUN_CR_USERNAME`: 阿里云容器镜像服务用户名
|
||
- `ALIYUN_CR_PASSWORD`: 阿里云容器镜像服务密码
|
||
|
||
#### 在远程服务器上配置 Docker 登录
|
||
|
||
确保远程服务器可以拉取私有镜像,需要在远程服务器上执行:
|
||
|
||
```bash
|
||
docker login crpi-u2903xccyzd6nqnc.cn-shanghai.personal.cr.aliyuncs.com \
|
||
--username=zaikunxu \
|
||
--password=57ucV,g4LF2cqm8
|
||
```
|
||
|
||
或者将登录信息添加到工作流的部署步骤中(已自动配置)。
|
||
|
||
### 3. 远程服务器准备
|
||
|
||
确保远程服务器已安装:
|
||
|
||
- Docker
|
||
- Docker Compose
|
||
|
||
```bash
|
||
# 安装 Docker(Ubuntu/Debian)
|
||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||
sudo sh get-docker.sh
|
||
|
||
# 安装 Docker Compose
|
||
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
||
sudo chmod +x /usr/local/bin/docker-compose
|
||
```
|
||
|
||
### 4. 首次部署准备
|
||
|
||
在远程服务器上创建部署目录:
|
||
|
||
```bash
|
||
mkdir -p /opt/life-echo/api
|
||
```
|
||
|
||
确保 SSH 用户可以访问该目录并执行 Docker 命令。
|
||
|
||
## 工作流执行流程
|
||
|
||
### 构建阶段(build-and-push)
|
||
|
||
1. 检出代码
|
||
2. 设置 Docker Buildx
|
||
3. 登录到容器镜像仓库
|
||
4. 提取元数据(标签、标签)
|
||
5. 构建并推送 Docker 镜像
|
||
|
||
### 部署阶段(deploy)
|
||
|
||
1. 设置 SSH 连接
|
||
2. 登录到容器镜像仓库(在远程服务器上)
|
||
3. 拉取最新镜像
|
||
4. 停止现有容器
|
||
5. 更新 docker-compose.yml 中的镜像标签
|
||
6. 启动新容器
|
||
7. 清理旧镜像
|
||
8. 验证部署状态
|
||
|
||
## 镜像标签规则
|
||
|
||
- `main`/`master` 分支 → `latest`
|
||
- 其他分支 → 使用分支名作为标签
|
||
- 同时会生成基于 commit SHA 的标签
|
||
|
||
## 故障排查
|
||
|
||
### 1. SSH 连接失败
|
||
|
||
- 检查 `SSH_PRIVATE_KEY` 是否正确配置
|
||
- 确认远程服务器的 SSH 服务正在运行
|
||
- 验证 SSH 公钥已添加到远程服务器的 `~/.ssh/authorized_keys`
|
||
|
||
### 2. Docker 登录失败
|
||
|
||
- 检查 `ALIYUN_CR_USERNAME` 和 `ALIYUN_CR_PASSWORD` 是否正确配置
|
||
- 确认阿里云容器镜像服务的账号密码是否正确
|
||
- 检查远程服务器是否可以访问阿里云容器镜像服务
|
||
|
||
### 3. 镜像拉取失败
|
||
|
||
- 确认远程服务器可以访问容器镜像仓库
|
||
- 检查镜像标签是否正确
|
||
- 验证网络连接
|
||
|
||
### 4. 容器启动失败
|
||
|
||
- 检查 docker-compose.yml 文件是否正确
|
||
- 查看容器日志:`docker-compose logs`
|
||
- 确认环境变量配置正确
|
||
|
||
## 手动触发
|
||
|
||
可以通过 GitHub Actions 界面手动触发工作流:
|
||
|
||
1. 进入仓库的 Actions 标签页
|
||
2. 选择 "Docker Build and Deploy" 工作流
|
||
3. 点击 "Run workflow" 按钮
|
||
|
||
## 自定义配置
|
||
|
||
### 修改触发分支
|
||
|
||
编辑 `.github/workflows/docker-build-deploy.yml`:
|
||
|
||
```yaml
|
||
on:
|
||
push:
|
||
branches:
|
||
- your-branch-name
|
||
```
|
||
|
||
### 修改镜像名称
|
||
|
||
编辑工作流文件中的 `IMAGE_NAME` 和 `REGISTRY_NAMESPACE` 环境变量:
|
||
|
||
```yaml
|
||
env:
|
||
IMAGE_NAME: your-image-name
|
||
REGISTRY_NAMESPACE: your-namespace
|
||
```
|
||
|
||
### 修改部署路径
|
||
|
||
在 GitHub Secrets 中设置 `DEPLOY_PATH`,或直接修改工作流文件中的默认值。
|
||
|
||
## 注意事项
|
||
|
||
1. **安全性**:确保 SSH 私钥和密码等敏感信息只存储在 GitHub Secrets 中,不要提交到代码仓库
|
||
2. **权限**:确保 SSH 用户在远程服务器上有执行 Docker 命令的权限
|
||
3. **备份**:部署前会自动备份 docker-compose.yml 文件(`.bak` 后缀)
|
||
4. **回滚**:如果部署失败,可以使用备份文件恢复
|
||
|
||
## 相关文件
|
||
|
||
- 工作流文件:`.github/workflows/docker-build-deploy.yml`
|
||
- Dockerfile:`api/Dockerfile`
|
||
- Docker Compose:`api/docker-compose.yml`
|
||
|
||
---
|
||
|
||
# Android Release 工作流
|
||
|
||
## 概述
|
||
|
||
自动构建生产环境签名 APK 并发布到 GitHub Releases:
|
||
|
||
1. **构建阶段**:编译 Release APK,使用生产签名配置
|
||
2. **发布阶段**:创建 GitHub Release,将 APK 作为附件上传
|
||
|
||
## 工作流触发条件
|
||
|
||
- **Tag 推送**:推送 `v*` 格式的标签时自动触发(如 `v1.0.0`、`v1.2.3-beta`)
|
||
- **手动触发**:通过 GitHub Actions 界面手动触发,可选指定版本号
|
||
|
||
## 配置步骤
|
||
|
||
### 1. 配置 GitHub Secrets
|
||
|
||
在 GitHub 仓库设置中添加以下 Secrets:
|
||
|
||
| Secret | 说明 | 示例 |
|
||
|--------|------|------|
|
||
| `ANDROID_KEYSTORE_BASE64` | keystore 文件的 Base64 编码 | 见下方生成方法 |
|
||
| `ANDROID_KEY_ALIAS` | 密钥别名 | `suiyueshishu` |
|
||
| `ANDROID_KEY_PASSWORD` | 密钥密码 | |
|
||
| `ANDROID_STORE_PASSWORD` | keystore 密码 | |
|
||
|
||
#### 生成 ANDROID_KEYSTORE_BASE64
|
||
|
||
```bash
|
||
# 在本地项目 app-android 目录下执行
|
||
base64 -i release-keystore.jks | pbcopy # macOS,结果已复制到剪贴板
|
||
|
||
# Linux
|
||
base64 -w 0 release-keystore.jks
|
||
```
|
||
|
||
将输出内容粘贴到 GitHub Secrets 的 `ANDROID_KEYSTORE_BASE64` 中。
|
||
|
||
### 2. 版本号管理
|
||
|
||
- **Tag 触发**:从 tag 名自动提取版本号(如 `v1.0.0` → `versionName = "1.0.0"`)
|
||
- **手动触发**:使用输入的 `version_name`,或使用 `build.gradle.kts` 中的默认值
|
||
- **versionCode**:自动使用 `GITHUB_RUN_NUMBER` 递增
|
||
|
||
### 3. 发布流程
|
||
|
||
#### 方式一:通过 Tag 自动发布
|
||
|
||
```bash
|
||
# 打标签并推送
|
||
git tag v1.0.0
|
||
git push origin v1.0.0
|
||
|
||
# 或者一步完成
|
||
git tag v1.0.0 && git push origin v1.0.0
|
||
```
|
||
|
||
工作流将自动:
|
||
1. 构建签名 Release APK
|
||
2. 上传 APK 为 GitHub Artifact
|
||
3. 创建 GitHub Release(附带 APK 和自动生成的更新日志)
|
||
|
||
#### 方式二:手动触发
|
||
|
||
1. 进入仓库的 Actions 标签页
|
||
2. 选择 "Android Release Build" 工作流
|
||
3. 点击 "Run workflow" 按钮
|
||
4. (可选)填写版本号
|
||
5. 点击 "Run workflow"
|
||
|
||
手动触发时仅构建 APK 并上传为 Artifact,不会创建 GitHub Release。
|
||
|
||
## 工作流执行流程
|
||
|
||
1. 检出代码(完整历史)
|
||
2. 确定版本号(Tag / 手动输入 / build.gradle.kts 默认值)
|
||
3. 设置 JDK 17 + Gradle 缓存
|
||
4. 解码签名 keystore + 生成 keystore.properties
|
||
5. 覆盖 build.gradle.kts 中的版本号
|
||
6. 执行 `./gradlew assembleRelease`
|
||
7. 上传 APK 为 Artifact(保留 30 天)
|
||
8. (Tag 触发时)生成 Release Notes 并创建 GitHub Release
|
||
|
||
## APK 命名规则
|
||
|
||
APK 文件命名格式:`岁月时书_v{版本号}_release.apk`
|
||
|
||
示例:`岁月时书_v1.0.0_release.apk`
|
||
|
||
## 故障排查
|
||
|
||
### 1. 签名失败
|
||
|
||
- 检查 `ANDROID_KEYSTORE_BASE64` 是否正确生成(不能有换行或空格)
|
||
- 确认 `ANDROID_KEY_ALIAS`、`ANDROID_KEY_PASSWORD`、`ANDROID_STORE_PASSWORD` 与 keystore 匹配
|
||
|
||
### 2. 构建失败
|
||
|
||
- 检查 Actions 日志中的 Gradle 错误输出
|
||
- 确认本地 `./gradlew assembleRelease` 可以成功
|
||
|
||
### 3. Release 创建失败
|
||
|
||
- 确认工作流有 `contents: write` 权限
|
||
- 检查 Tag 名称是否以 `v` 开头
|