Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95d131eb51 | ||
|
|
8fa9df03a0 | ||
|
|
330aea4d16 | ||
|
|
bb93a6bc6c | ||
|
|
3cb55db864 | ||
|
|
f62146b655 | ||
|
|
425ca5b5fd | ||
|
|
f36b0159bd | ||
|
|
740ff4e1d8 | ||
|
|
135ea99deb | ||
|
|
6bc2506842 | ||
|
|
c0b5f15895 | ||
|
|
32d674a4c0 | ||
|
|
02d870a2d6 | ||
|
|
417f3e6d2b | ||
|
|
ece54efc14 |
@@ -1,11 +1,21 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://opencode.ai/config.json",
|
"$schema": "https://opencode.ai/config.json",
|
||||||
"mcp": {},
|
"model": "moonshotai-cn/kimi-k2.5",
|
||||||
"permission": "allow",
|
"permission": "allow",
|
||||||
"plugin": ["@mohak34/opencode-notifier@latest"],
|
"plugin": [
|
||||||
|
"@mohak34/opencode-notifier@latest"
|
||||||
|
],
|
||||||
"provider": {
|
"provider": {
|
||||||
"opencode": {
|
"opencode": {
|
||||||
"models": {
|
"models": {
|
||||||
|
"claude-sonnet-4-5": {
|
||||||
|
"options": {
|
||||||
|
"thinking": {
|
||||||
|
"type": "enabled",
|
||||||
|
"budgetTokens": 16000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"claude-opus-4-5": {
|
"claude-opus-4-5": {
|
||||||
"options": {
|
"options": {
|
||||||
"thinking": {
|
"thinking": {
|
||||||
@@ -15,11 +25,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"zhipuai-coding-plan": {
|
|
||||||
"options": {
|
|
||||||
"apiKey": "0f76aea86295476dbfa98724013b0fe8.o2EaJVqcl4Cf7WLP"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
"mcp": {}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ You are an expert in Git version control and repository management.
|
|||||||
|------|------|
|
|------|------|
|
||||||
| [Commit Workflow](./commit-workflow.md) | 提交暂存文件,自动生成提交信息并创建版本标签 |
|
| [Commit Workflow](./commit-workflow.md) | 提交暂存文件,自动生成提交信息并创建版本标签 |
|
||||||
| [Push Workflow](./push-workflow.md) | 提交并推送到远程仓库的完整工作流 |
|
| [Push Workflow](./push-workflow.md) | 提交并推送到远程仓库的完整工作流 |
|
||||||
|
| [SSH Keychain](./ssh-keychain.md) | SSH 密钥管理与 macOS Keychain 自动解锁配置 |
|
||||||
|
|
||||||
## Core Principles
|
## Core Principles
|
||||||
|
|
||||||
|
|||||||
209
skill/git/ssh-keychain.md
Normal file
209
skill/git/ssh-keychain.md
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
---
|
||||||
|
name: ssh-keychain
|
||||||
|
description: SSH key management with macOS Keychain for password-protected keys
|
||||||
|
---
|
||||||
|
|
||||||
|
# SSH 密钥与 macOS Keychain 管理
|
||||||
|
|
||||||
|
处理带密码的 SSH 密钥,配置自动解锁避免重复输入密码。
|
||||||
|
|
||||||
|
## 问题场景
|
||||||
|
|
||||||
|
生成带密码的 SSH 密钥后,每次使用都需要输入密码:
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
# Enter passphrase for /Users/xxx/.ssh/id_xxx:
|
||||||
|
```
|
||||||
|
|
||||||
|
## 解决方案
|
||||||
|
|
||||||
|
### 1. 生成带密码的 SSH 密钥
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 生成密钥(推荐使用 ed25519)
|
||||||
|
ssh-keygen -t ed25519 -f ~/.ssh/id_custom -N "your-passphrase"
|
||||||
|
|
||||||
|
# 或使用随机密码
|
||||||
|
PASSPHRASE=$(openssl rand -base64 32)
|
||||||
|
ssh-keygen -t ed25519 -f ~/.ssh/id_custom -N "$PASSPHRASE"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 配置 SSH 使用 Keychain(关键步骤)
|
||||||
|
|
||||||
|
编辑 `~/.ssh/config`:
|
||||||
|
|
||||||
|
```ssh-config
|
||||||
|
Host gitea.example.com
|
||||||
|
HostName gitea.example.com
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_custom
|
||||||
|
IdentitiesOnly yes
|
||||||
|
AddKeysToAgent yes
|
||||||
|
UseKeychain yes
|
||||||
|
```
|
||||||
|
|
||||||
|
**重要配置项:**
|
||||||
|
- `AddKeysToAgent yes` - 自动添加到 ssh-agent
|
||||||
|
- `UseKeychain yes` - 使用 macOS Keychain 存储密码
|
||||||
|
- **不要**使用 `IdentityAgent none`(会禁用 Keychain)
|
||||||
|
|
||||||
|
### 3. 添加密钥到 ssh-agent 并保存到 Keychain
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方法 1:使用 --apple-use-keychain (macOS 12+)
|
||||||
|
ssh-add --apple-use-keychain ~/.ssh/id_custom
|
||||||
|
|
||||||
|
# 方法 2:使用 -K 选项 (macOS 11 及更早)
|
||||||
|
ssh-add -K ~/.ssh/id_custom
|
||||||
|
```
|
||||||
|
|
||||||
|
输入密码后,系统会保存到 Keychain,以后自动解锁。
|
||||||
|
|
||||||
|
### 4. 验证配置
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查已加载的密钥
|
||||||
|
ssh-add -l
|
||||||
|
|
||||||
|
# 测试 SSH 连接
|
||||||
|
ssh -T git@gitea.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
如果显示 `Hi there, xxx! You've successfully authenticated` 且**没有提示输入密码**,则配置成功。
|
||||||
|
|
||||||
|
## 完整工作流程示例
|
||||||
|
|
||||||
|
### 场景:配置 Gitea SSH 访问
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 生成带密码的密钥
|
||||||
|
PASSPHRASE="bkt/52MFsLVSRSHvIXv2WTCKEUaPhD0btDghUY6RnQI="
|
||||||
|
ssh-keygen -t ed25519 -f ~/.ssh/id_gitea_new -N "$PASSPHRASE"
|
||||||
|
|
||||||
|
# 2. 配置 SSH
|
||||||
|
# 编辑 ~/.ssh/config,添加:
|
||||||
|
cat >> ~/.ssh/config << 'EOF'
|
||||||
|
Host gitea.refining.dev
|
||||||
|
HostName gitea.refining.dev
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_gitea_new
|
||||||
|
IdentitiesOnly yes
|
||||||
|
AddKeysToAgent yes
|
||||||
|
UseKeychain yes
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 3. 添加到 Keychain
|
||||||
|
ssh-add --apple-use-keychain ~/.ssh/id_gitea_new
|
||||||
|
# 输入密码: bkt/52MFsLVSRSHvIXv2WTCKEUaPhD0btDghUY6RnQI=
|
||||||
|
|
||||||
|
# 4. 验证
|
||||||
|
ssh -T git@gitea.refining.dev
|
||||||
|
|
||||||
|
# 5. 后续使用 git 无需输入密码
|
||||||
|
git pull
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见错误与解决
|
||||||
|
|
||||||
|
### 错误 1:仍然提示输入密码
|
||||||
|
|
||||||
|
**原因:** `~/.ssh/config` 中配置了 `IdentityAgent none`
|
||||||
|
|
||||||
|
**解决:** 移除 `IdentityAgent none`,添加 `UseKeychain yes`
|
||||||
|
|
||||||
|
```ssh-config
|
||||||
|
# ❌ 错误配置
|
||||||
|
Host gitea.example.com
|
||||||
|
IdentityFile ~/.ssh/id_custom
|
||||||
|
IdentityAgent none # 这会禁用 Keychain
|
||||||
|
|
||||||
|
# ✅ 正确配置
|
||||||
|
Host gitea.example.com
|
||||||
|
IdentityFile ~/.ssh/id_custom
|
||||||
|
AddKeysToAgent yes
|
||||||
|
UseKeychain yes
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误 2:1Password SSH agent 冲突
|
||||||
|
|
||||||
|
如果系统使用 1Password 的 SSH agent(`IdentityAgent "~/Library/Group Containers/.../agent.sock"`):
|
||||||
|
|
||||||
|
```ssh-config
|
||||||
|
Host *
|
||||||
|
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
|
||||||
|
|
||||||
|
Host gitea.example.com
|
||||||
|
HostName gitea.example.com
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/id_custom
|
||||||
|
IdentitiesOnly yes
|
||||||
|
AddKeysToAgent yes
|
||||||
|
UseKeychain yes
|
||||||
|
# 不使用 1Password agent,直接使用系统 Keychain
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误 3:ssh-add 命令不存在
|
||||||
|
|
||||||
|
某些 macOS 版本可能不支持 `--apple-use-keychain`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建或编辑 ~/.ssh/config,添加全局配置
|
||||||
|
Host *
|
||||||
|
UseKeychain yes
|
||||||
|
AddKeysToAgent yes
|
||||||
|
|
||||||
|
# 然后直接使用
|
||||||
|
ssh-add ~/.ssh/id_custom
|
||||||
|
```
|
||||||
|
|
||||||
|
## 密钥管理最佳实践
|
||||||
|
|
||||||
|
### 文件保存位置
|
||||||
|
|
||||||
|
将密钥备份到安全位置(如 iCloud):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 复制到 iCloud Key 文件夹
|
||||||
|
KEY_FOLDER="$HOME/Library/Mobile Documents/com~apple~CloudDocs/key"
|
||||||
|
cp ~/.ssh/id_custom "$KEY_FOLDER/id_custom_$(date +%Y%m%d)"
|
||||||
|
cp ~/.ssh/id_custom.pub "$KEY_FOLDER/id_custom_$(date +%Y%m%d).pub"
|
||||||
|
|
||||||
|
# 创建密码说明文件
|
||||||
|
cat > "$KEY_FOLDER/id_custom_$(date +%Y%m%d)_info.txt" << EOF
|
||||||
|
SSH Key: id_custom
|
||||||
|
Generated: $(date)
|
||||||
|
Passphrase: $PASSPHRASE
|
||||||
|
Host: gitea.example.com
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 定期检查
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 列出所有已加载的密钥
|
||||||
|
ssh-add -l
|
||||||
|
|
||||||
|
# 检查 SSH 配置
|
||||||
|
ssh -G gitea.example.com
|
||||||
|
|
||||||
|
# 测试连接
|
||||||
|
ssh -vT git@gitea.example.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安全建议
|
||||||
|
|
||||||
|
1. **始终使用带密码的密钥** - 防止私钥泄露后被直接使用
|
||||||
|
2. **不要将私钥提交到 Git** - 添加到 `.gitignore`
|
||||||
|
3. **定期轮换密钥** - 建议每 6-12 个月
|
||||||
|
4. **为不同服务使用不同密钥** - 隔离风险
|
||||||
|
|
||||||
|
## 快速参考
|
||||||
|
|
||||||
|
| 命令 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `ssh-add --apple-use-keychain ~/.ssh/id_custom` | 添加密钥到 Keychain |
|
||||||
|
| `ssh-add -l` | 列出已加载的密钥 |
|
||||||
|
| `ssh-add -D` | 删除所有已加载的密钥 |
|
||||||
|
| `ssh -T git@host` | 测试 SSH 连接 |
|
||||||
|
| `ssh -G host` | 查看 SSH 配置 |
|
||||||
@@ -7,15 +7,43 @@ description: Comprehensive Gitea management tool for creating runners, workflows
|
|||||||
|
|
||||||
完整的 Gitea 管理工具,提供 Runner、Workflow、仓库的创建和管理功能。
|
完整的 Gitea 管理工具,提供 Runner、Workflow、仓库的创建和管理功能。
|
||||||
|
|
||||||
|
## 最新改进 (2026-01-23)
|
||||||
|
|
||||||
|
### 仓库创建功能增强(简洁高效版)
|
||||||
|
1. **智能解析**:自动识别 `组织/仓库` 格式,优先使用指定组织而非默认组织
|
||||||
|
2. **简化验证**:默认假设组织存在,API创建失败时清晰提示如何创建组织
|
||||||
|
3. **默认私有**:除非明确指定,所有仓库默认创建为私有
|
||||||
|
4. **Git集成**:自动检查当前目录是否为Git仓库,提供一键初始化、提交、推送
|
||||||
|
5. **错误处理**:详细的Token验证、权限检查,API错误时给出具体解决方案
|
||||||
|
6. **工作目录澄清**:仓库操作可在任何目录执行,配置操作在 `~/.config/gitea/`
|
||||||
|
7. **流程优化**:减少不必要的预先验证,API失败时再提示,更简洁高效
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
```bash
|
||||||
|
# 智能解析组织/仓库格式
|
||||||
|
/gitea-create-repo shigongcao/shigongcao
|
||||||
|
|
||||||
|
# 创建公开仓库
|
||||||
|
/gitea-create-repo org/project public
|
||||||
|
|
||||||
|
# 自动初始化并推送
|
||||||
|
/gitea-create-repo org/project --init
|
||||||
|
```
|
||||||
|
|
||||||
## 工作目录
|
## 工作目录
|
||||||
|
|
||||||
**重要:该技能和所有相关命令的工作目录统一为:**
|
**重要:Gitea 配置和 Runner 相关操作的工作目录为 `~/.config/gitea/`,但仓库操作可以在任何目录执行。**
|
||||||
|
|
||||||
### macOS / Linux
|
### 配置和 Runner 目录
|
||||||
```bash
|
```bash
|
||||||
~/.config/gitea/
|
~/.config/gitea/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 仓库操作目录
|
||||||
|
可以在任何项目目录执行,技能会自动:
|
||||||
|
1. 从 `~/.config/gitea/config.env` 读取配置
|
||||||
|
2. 在当前目录创建仓库、初始化 Git 等
|
||||||
|
|
||||||
### Windows
|
### Windows
|
||||||
```powershell
|
```powershell
|
||||||
# PowerShell
|
# PowerShell
|
||||||
@@ -28,7 +56,9 @@ $env:USERPROFILE\.config\gitea\
|
|||||||
C:\Users\YourUsername\.config\gitea\
|
C:\Users\YourUsername\.config\gitea\
|
||||||
```
|
```
|
||||||
|
|
||||||
所有配置文件、Runner 目录、日志文件都存储在此目录下。请确保该目录具有适当的读写权限。
|
**重要区分**:
|
||||||
|
- **配置/Runner 操作**:在 `~/.config/gitea/` 目录执行
|
||||||
|
- **仓库操作**:可在任何项目目录执行(自动加载配置)
|
||||||
|
|
||||||
**目录结构(所有平台通用):**
|
**目录结构(所有平台通用):**
|
||||||
```
|
```
|
||||||
@@ -40,9 +70,9 @@ C:\Users\YourUsername\.config\gitea\
|
|||||||
```
|
```
|
||||||
|
|
||||||
**平台兼容性:**
|
**平台兼容性:**
|
||||||
- **macOS**: `~/.config/gitea/`(完全支持)
|
- **macOS**: `~/.config/gitea/`(配置目录),任意目录(仓库操作)
|
||||||
- **Linux**: `~/.config/gitea/`(完全支持)
|
- **Linux**: `~/.config/gitea/`(配置目录),任意目录(仓库操作)
|
||||||
- **Windows**: `%USERPROFILE%\.config\gitea\`(Act Runner 支持,但该技能的命令和脚本需要适配)
|
- **Windows**: `%USERPROFILE%\.config\gitea\`(配置目录),任意目录(仓库操作)
|
||||||
|
|
||||||
**Windows 用户注意事项:**
|
**Windows 用户注意事项:**
|
||||||
- Gitea Act Runner 官方支持 Windows 平台(包括 Host 模式)
|
- Gitea Act Runner 官方支持 Windows 平台(包括 Host 模式)
|
||||||
@@ -57,6 +87,7 @@ C:\Users\YourUsername\.config\gitea\
|
|||||||
| 功能模块 | 文档 | 说明 |
|
| 功能模块 | 文档 | 说明 |
|
||||||
|---------|------|------|
|
|---------|------|------|
|
||||||
| 环境配置 | [setup-guide.md](./setup-guide.md) | 首次使用引导,配置 Gitea URL 和 Token |
|
| 环境配置 | [setup-guide.md](./setup-guide.md) | 首次使用引导,配置 Gitea URL 和 Token |
|
||||||
|
| SSH 密钥管理 | [ssh-key-management.md](./ssh-key-management.md) | 创建、部署 SSH 密钥,配置本地客户端 |
|
||||||
| Runner 管理 | [runner-management.md](./runner-management.md) | 创建、注册、管理 Gitea Act Runner |
|
| Runner 管理 | [runner-management.md](./runner-management.md) | 创建、注册、管理 Gitea Act Runner |
|
||||||
| 自动创建脚本 | [create-runner.md](./create-runner.md) | 包含完整的 Runner 创建 Bash 脚本 |
|
| 自动创建脚本 | [create-runner.md](./create-runner.md) | 包含完整的 Runner 创建 Bash 脚本 |
|
||||||
| Workflow 生成 | [workflow-generator.md](./workflow-generator.md) | 根据项目类型生成 CI/CD workflow |
|
| Workflow 生成 | [workflow-generator.md](./workflow-generator.md) | 根据项目类型生成 CI/CD workflow |
|
||||||
@@ -72,6 +103,7 @@ C:\Users\YourUsername\.config\gitea\
|
|||||||
- "生成 workflow"、"CI/CD"
|
- "生成 workflow"、"CI/CD"
|
||||||
- "创建仓库"、"gitea 仓库"
|
- "创建仓库"、"gitea 仓库"
|
||||||
- "gitea 配置"、"gitea token"
|
- "gitea 配置"、"gitea token"
|
||||||
|
- "ssh key"、"ssh密钥"、"gitea ssh"、"部署密钥"
|
||||||
|
|
||||||
## 首次使用
|
## 首次使用
|
||||||
|
|
||||||
@@ -89,7 +121,13 @@ C:\Users\YourUsername\.config\gitea\
|
|||||||
- 可在所有个人仓库的 Actions workflow 中使用
|
- 可在所有个人仓库的 Actions workflow 中使用
|
||||||
- 使用方式:`${{ vars.USERNAME }}`、`${{ vars.WEBHOOK_URL }}`
|
- 使用方式:`${{ vars.USERNAME }}`、`${{ vars.WEBHOOK_URL }}`
|
||||||
|
|
||||||
**重要**:Gitea 不允许 variable 名称以 `GITEA_` 或 `GITHUB_` 开头
|
**重要:变量命名规范**
|
||||||
|
- Gitea Actions 不允许变量和密钥名称以 `GITEA_` 或 `GITHUB_` 开头
|
||||||
|
- 推荐命名:
|
||||||
|
- `REGISTRY_TOKEN`(替代 `GITEA_TOKEN`)
|
||||||
|
- `REGISTRY_URL` → 使用内置 `${{ env.registry }}`
|
||||||
|
- `USERNAME` → 使用内置 `${{ github.actor }}`
|
||||||
|
- `WEBHOOK_URL`(已自动注册为 variable)
|
||||||
|
|
||||||
详见:[环境配置指南](./setup-guide.md)
|
详见:[环境配置指南](./setup-guide.md)
|
||||||
|
|
||||||
@@ -100,6 +138,7 @@ C:\Users\YourUsername\.config\gitea\
|
|||||||
| `/gitea-config` | 查看当前 Gitea 配置和 Runner 状态 |
|
| `/gitea-config` | 查看当前 Gitea 配置和 Runner 状态 |
|
||||||
| `/gitea-reset` | 重置 Gitea 配置(交互式向导) |
|
| `/gitea-reset` | 重置 Gitea 配置(交互式向导) |
|
||||||
| `/gitea-switch-org` | 切换默认组织 |
|
| `/gitea-switch-org` | 切换默认组织 |
|
||||||
|
| `/gitea-setup-ssh` | 创建 SSH 密钥并部署到 Gitea 服务器 |
|
||||||
| `/gitea-create-runner` | 创建并启动新 Runner(默认 host 模式) |
|
| `/gitea-create-runner` | 创建并启动新 Runner(默认 host 模式) |
|
||||||
| `/gitea-list-runners` | 列出所有 Runner 及其状态 |
|
| `/gitea-list-runners` | 列出所有 Runner 及其状态 |
|
||||||
| `/gitea-delete-runner` | 删除指定 Runner |
|
| `/gitea-delete-runner` | 删除指定 Runner |
|
||||||
@@ -183,12 +222,30 @@ AI: 检测到 Go 项目,服务目录: ./backend
|
|||||||
[自动生成 .gitea/workflows/backend.yml]
|
[自动生成 .gitea/workflows/backend.yml]
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. 创建仓库
|
### 4. 创建仓库(智能解析,默认私有)
|
||||||
|
|
||||||
|
**智能解析特性**:
|
||||||
|
- 自动识别 `组织/仓库` 格式,优先使用指定组织
|
||||||
|
- 默认创建私有仓库(除非明确指定公开)
|
||||||
|
- 支持自动初始化 Git 仓库并推送代码
|
||||||
|
|
||||||
|
**示例**:
|
||||||
```
|
```
|
||||||
用户: /gitea-create-repo my-project
|
用户: /gitea-create-repo shigongcao/shigongcao
|
||||||
AI: [使用配置的 Gitea URL 和默认组织创建仓库]
|
AI: [智能解析为 shigongcao 组织下的 shigongcao 仓库]
|
||||||
✓ 仓库创建成功: ai/my-project
|
✓ 仓库创建成功: shigongcao/shigongcao (私有)
|
||||||
|
✓ 组织验证通过
|
||||||
|
✓ 可选的 Git 初始化流程...
|
||||||
|
|
||||||
|
用户: /gitea-create-repo my-project public
|
||||||
|
AI: [使用默认组织创建公开仓库]
|
||||||
|
✓ 仓库创建成功: ai/my-project (公开)
|
||||||
|
|
||||||
|
用户: /gitea-create-repo org/project --init
|
||||||
|
AI: [创建仓库并自动初始化当前目录的 Git 仓库]
|
||||||
|
✓ 仓库创建成功: org/project
|
||||||
|
✓ Git 初始化完成
|
||||||
|
✓ 代码已推送到远程仓库
|
||||||
```
|
```
|
||||||
|
|
||||||
## 配置管理
|
## 配置管理
|
||||||
@@ -336,7 +393,9 @@ Runner 信息
|
|||||||
|
|
||||||
| 类型 | 模板文档 | 适用场景 |
|
| 类型 | 模板文档 | 适用场景 |
|
||||||
|------|---------|---------|
|
|------|---------|---------|
|
||||||
|
| **Rust 后端** | [rust-backend.md](./workflow-templates/rust-backend.md) | **Rust API 服务、微服务、CLI 工具** |
|
||||||
| Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 |
|
| Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 |
|
||||||
|
| Rust 后端 | [rust-backend.md](./workflow-templates/rust-backend.md) | Rust API 服务、异步服务、IoT 设备服务 |
|
||||||
| Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js |
|
| Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js |
|
||||||
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
|
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
|
||||||
| 微信小程序 | [wechat-miniprogram.md](./workflow-templates/wechat-miniprogram.md) | 微信小程序 CI/CD |
|
| 微信小程序 | [wechat-miniprogram.md](./workflow-templates/wechat-miniprogram.md) | 微信小程序 CI/CD |
|
||||||
@@ -349,6 +408,31 @@ AI 会自动:
|
|||||||
|
|
||||||
详见:[Workflow 生成器](./workflow-generator.md)
|
详见:[Workflow 生成器](./workflow-generator.md)
|
||||||
|
|
||||||
|
## Dockerfile 模板
|
||||||
|
|
||||||
|
提供多种 Dockerfile 模板,适配不同的构建场景:
|
||||||
|
|
||||||
|
| 类型 | 模板文档 | 适用场景 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| Go 服务 | [go-service.md](./dockerfile-templates/go-service.md) | Go 后端服务容器化(CI 构建、完整构建、代码生成) |
|
||||||
|
| Rust 服务 | [rust-service.md](./dockerfile-templates/rust-service.md) | Rust 后端服务容器化(CI 构建、完整构建、多平台) |
|
||||||
|
| Node.js 前端 | [nodejs-frontend.md](./dockerfile-templates/nodejs-frontend.md) | 前端静态文件服务(Nginx + SPA 路由) |
|
||||||
|
|
||||||
|
**模板特性对比**:
|
||||||
|
|
||||||
|
| 特性 | Go 服务 | Rust 服务 | Node.js 前端 |
|
||||||
|
|------|---------|-----------|-------------|
|
||||||
|
| CI 构建版 | ✅ | ✅ | ✅ |
|
||||||
|
| 完整构建版 | ✅ | ✅ | ✅ |
|
||||||
|
| 健康检查脚本 | ✅ | ✅ | ✅(Nginx) |
|
||||||
|
| 多阶段构建 | ✅ | ✅ | ✅ |
|
||||||
|
| 镜像大小 | ~15MB | ~20MB | ~20MB |
|
||||||
|
| 代码生成支持 | ✅ | ❌ | ❌ |
|
||||||
|
| 多平台构建 | ❌ | ✅ | ❌ |
|
||||||
|
| API 代理配置 | ❌ | ❌ | ✅(Nginx) |
|
||||||
|
|
||||||
|
详见各模板文档了解具体使用方法。
|
||||||
|
|
||||||
## API 调用
|
## API 调用
|
||||||
|
|
||||||
所有与 Gitea 服务器的交互都通过 API 完成,使用配置文件中的:
|
所有与 Gitea 服务器的交互都通过 API 完成,使用配置文件中的:
|
||||||
@@ -357,6 +441,38 @@ AI 会自动:
|
|||||||
|
|
||||||
详见:[API 参考](./api-reference.md)
|
详见:[API 参考](./api-reference.md)
|
||||||
|
|
||||||
|
## 变量命名规范
|
||||||
|
|
||||||
|
**⚠️ 重要限制**:Gitea Actions 不允许变量和密钥名称以 `GITEA_` 或 `GITHUB_` 开头
|
||||||
|
|
||||||
|
### 推荐命名约定
|
||||||
|
|
||||||
|
| 原名称 | 推荐替代 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| `GITEA_TOKEN` | `REGISTRY_TOKEN` | Container registry 访问令牌 |
|
||||||
|
| `GITEA_URL` | `${{ env.registry }}` | 使用内置变量自动获取当前实例地址 |
|
||||||
|
| `GITEA_USERNAME` | `${{ github.actor }}` | 使用内置变量获取当前用户 |
|
||||||
|
| `GITHUB_TOKEN` | `RELEASE_TOKEN` | 用于创建 Release 的 API 令牌 |
|
||||||
|
|
||||||
|
### 内置变量利用
|
||||||
|
|
||||||
|
Gitea Actions 提供的内置变量(推荐优先使用):
|
||||||
|
- `${{ env.registry }}`: 当前 Gitea 实例的 registry 地址
|
||||||
|
- `${{ github.actor }}`: 触发 workflow 的用户名
|
||||||
|
- `${{ github.repository }}`: 当前仓库的 `owner/repo` 格式
|
||||||
|
- `${{ github.server_url }}`: Gitea 实例的基础 URL
|
||||||
|
|
||||||
|
### Variables vs Secrets
|
||||||
|
|
||||||
|
**Variables**(明文存储,适用于非敏感配置):
|
||||||
|
- `USERNAME`: 用户名(自动从配置同步)
|
||||||
|
- `WEBHOOK_URL`: 通知 webhook 地址
|
||||||
|
- `DEFAULT_ORG`: 默认组织名
|
||||||
|
|
||||||
|
**Secrets**(加密存储,适用于敏感信息):
|
||||||
|
- `REGISTRY_TOKEN`: Container registry 密码
|
||||||
|
- `RELEASE_TOKEN`: Release 创建 API 令牌
|
||||||
|
|
||||||
## 安全性
|
## 安全性
|
||||||
|
|
||||||
- 配置文件权限:`600`(仅所有者可读写)
|
- 配置文件权限:`600`(仅所有者可读写)
|
||||||
@@ -376,10 +492,23 @@ AI 会自动:
|
|||||||
|
|
||||||
## 版本
|
## 版本
|
||||||
|
|
||||||
- **Skill Version**: 1.0
|
- **Skill Version**: 1.4
|
||||||
- **Last Updated**: 2026-01-12
|
- **Last Updated**: 2026-01-29
|
||||||
- **整合内容**: gitea-runner + gitea-workflow
|
- **整合内容**: gitea-runner + gitea-workflow + 增强仓库管理 + SSH 密钥管理 + 完整 Dockerfile 模板库
|
||||||
- **新增功能**: 统一配置管理、Runner CRUD、智能 labels 检测
|
- **主要改进**:
|
||||||
|
- 仓库创建智能解析(优先使用指定组织)
|
||||||
|
- **简化验证**:默认假设组织存在,API失败时提示创建组织
|
||||||
|
- 默认私有仓库策略(除非明确指定公开)
|
||||||
|
- **完整 Git 集成**:自动检查Git状态,提供一键初始化、提交、推送
|
||||||
|
- **简洁高效**:减少预先验证,API失败时给出具体解决方案
|
||||||
|
- 工作目录概念澄清(配置 vs 仓库操作)
|
||||||
|
- **SSH 密钥管理**:完整的密钥创建、部署和跨设备使用指南
|
||||||
|
- **Rust 项目支持**:新增 Rust workflow 和 Dockerfile 模板,支持交叉编译、musl 静态链接、多平台构建
|
||||||
|
- **完整 Dockerfile 模板库**:新增 Go 和 Node.js 前端 Dockerfile 模板,涵盖所有后端和前端容器化需求
|
||||||
|
|
||||||
|
## 快速参考
|
||||||
|
|
||||||
|
- [Rust 项目快速配置](./rust-quick-reference.md) - 3 步设置 Rust 项目 CI/CD
|
||||||
|
|
||||||
## 相关资源
|
## 相关资源
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,112 @@ agent: general
|
|||||||
|
|
||||||
本文档提供了多种创建 Gitea Actions Runner 的方式,支持 Host 模式和 Docker 模式。
|
本文档提供了多种创建 Gitea Actions Runner 的方式,支持 Host 模式和 Docker 模式。
|
||||||
|
|
||||||
|
## ⚠️ 重要:前置条件
|
||||||
|
|
||||||
|
**创建 Runner 前请确保满足以下条件:**
|
||||||
|
|
||||||
|
### 1. 网络访问要求
|
||||||
|
- **必须能够访问外网**(如 google.com)
|
||||||
|
- Runner 需要从 Docker Hub、GitHub 等拉取镜像和依赖
|
||||||
|
- 如果无法访问外网,workflow 执行会失败
|
||||||
|
|
||||||
|
### 2. 硬件配置要求
|
||||||
|
- **最低配置:2C4G**(2 核 CPU + 4GB 内存)
|
||||||
|
- **推荐配置:4C8G** 或更高
|
||||||
|
- 配置过低会导致 workflow 执行缓慢或失败
|
||||||
|
|
||||||
|
### 3. 网络模式要求
|
||||||
|
**两种模式都必须使用 host 网络模式,否则缓存功能无法正常工作!**
|
||||||
|
|
||||||
|
- **Docker 模式**:Runner 容器和 Job 容器都使用 host 网络
|
||||||
|
- **Self-host 模式**:Job 进程直接在宿主机运行,使用 127.0.0.1
|
||||||
|
|
||||||
|
原因:Gitea Actions 的缓存服务运行在 runner 本地,job 容器需要通过网络访问 runner 的缓存端口。只有在 host 网络模式下,job 容器才能访问到 runner 的缓存服务。
|
||||||
|
|
||||||
|
## 📋 Runner 标签说明
|
||||||
|
|
||||||
|
### Host 模式标签
|
||||||
|
|
||||||
|
Runner 会自动检测系统配置并生成以下标签:
|
||||||
|
|
||||||
|
**基础标签**:
|
||||||
|
- `host` - 运行模式标识
|
||||||
|
- `self-hosted` - 自托管标识
|
||||||
|
- `ubuntu-latest` - GitHub Actions 兼容标签(所有 Linux 系统)
|
||||||
|
- `x64` / `ARM64` - CPU 架构
|
||||||
|
- `linux-x86_64` / `darwin-arm64` - 系统-架构组合
|
||||||
|
|
||||||
|
**发行版标签**(自动检测):
|
||||||
|
- `debian` - Debian 系统
|
||||||
|
- `debian-12` - Debian 12 版本
|
||||||
|
- `ubuntu` - Ubuntu 系统
|
||||||
|
- `ubuntu-22` - Ubuntu 22.04 版本
|
||||||
|
- `centos` - CentOS 系统
|
||||||
|
- `macOS` - macOS 系统
|
||||||
|
- `linux` - 通用 Linux 标签
|
||||||
|
|
||||||
|
**设备配置标签**(自动检测):
|
||||||
|
- `4c8g` - CPU 核心数 + 内存大小(如 4核8G内存)
|
||||||
|
|
||||||
|
**标签示例(Debian 12 系统)**:
|
||||||
|
```
|
||||||
|
host, ubuntu-latest, self-hosted, x64, linux-x86_64,
|
||||||
|
debian, debian-12, linux, 4c3g
|
||||||
|
```
|
||||||
|
|
||||||
|
**标签示例(Ubuntu 22.04 系统)**:
|
||||||
|
```
|
||||||
|
host, ubuntu-latest, self-hosted, x64, linux-x86_64,
|
||||||
|
ubuntu, ubuntu-22, linux, 8c16g
|
||||||
|
```
|
||||||
|
|
||||||
|
**标签示例(macOS ARM64 系统)**:
|
||||||
|
```
|
||||||
|
host, ubuntu-latest, self-hosted, ARM64, darwin-arm64,
|
||||||
|
macOS, 8c16g
|
||||||
|
```
|
||||||
|
|
||||||
|
**在 Workflow 中使用**:
|
||||||
|
```yaml
|
||||||
|
# 兼容模式 - 匹配所有 Linux Host Runner
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
# 精确匹配 - 只在 Debian 系统上运行
|
||||||
|
runs-on: [self-hosted, debian]
|
||||||
|
|
||||||
|
# 更精确 - 指定 Debian 版本
|
||||||
|
runs-on: [self-hosted, debian-12]
|
||||||
|
|
||||||
|
# 根据配置筛选 - 需要 8核16G 的 runner
|
||||||
|
runs-on: [self-hosted, 8c16g]
|
||||||
|
|
||||||
|
# 组合条件 - Debian 系统且 8核16G
|
||||||
|
runs-on: [debian, 8c16g]
|
||||||
|
|
||||||
|
# macOS 环境
|
||||||
|
runs-on: [self-hosted, macOS]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker 模式标签
|
||||||
|
|
||||||
|
Docker 模式使用预定义的镜像映射:
|
||||||
|
- `ubuntu-latest:docker://catthehacker/ubuntu:act-latest`
|
||||||
|
- `ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04`
|
||||||
|
- `ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04`
|
||||||
|
- `linux:docker://catthehacker/ubuntu:act-latest`
|
||||||
|
|
||||||
|
**标签设计说明**:
|
||||||
|
1. **`ubuntu-latest` 是兼容标签**:所有 Linux Host Runner 都包含此标签,方便兼容 GitHub Actions workflow
|
||||||
|
2. **实际发行版标签提供精确控制**:如 `debian`、`ubuntu` 等,用于需要特定系统的场景
|
||||||
|
3. **`host` 标签**:用于区分 Host 模式和 Docker 模式的 runner
|
||||||
|
4. **配置标签**:如 `4c8g`,方便根据资源需求调度任务
|
||||||
|
|
||||||
## 📦 快速使用
|
## 📦 快速使用
|
||||||
|
|
||||||
|
**使用前请确认:**
|
||||||
|
1. ✅ 主机能访问外网(如 google.com)
|
||||||
|
2. ✅ 硬件配置满足 2C4G(推荐 4C8G)
|
||||||
|
|
||||||
### 方法一:直接执行(推荐)
|
### 方法一:直接执行(推荐)
|
||||||
复制以下命令到终端执行,可指定参数:
|
复制以下命令到终端执行,可指定参数:
|
||||||
|
|
||||||
@@ -38,7 +142,7 @@ gitea-create-runner() {
|
|||||||
local mode="${1:-docker}"
|
local mode="${1:-docker}"
|
||||||
local token="${2:-$GITEA_TOKEN}"
|
local token="${2:-$GITEA_TOKEN}"
|
||||||
local gitea_url="${3:-https://git.shigongcao.com}"
|
local gitea_url="${3:-https://git.shigongcao.com}"
|
||||||
local name="${4:-runner-$(hostname -s)-$mode}"
|
local name="${4:-$(hostname -s)}"
|
||||||
|
|
||||||
echo "创建 $mode runner: $name"
|
echo "创建 $mode runner: $name"
|
||||||
|
|
||||||
@@ -54,7 +158,7 @@ set -e
|
|||||||
MODE="docker"
|
MODE="docker"
|
||||||
TOKEN=""
|
TOKEN=""
|
||||||
GITEA_URL="https://git.shigongcao.com"
|
GITEA_URL="https://git.shigongcao.com"
|
||||||
RUNNER_NAME="runner-$(hostname -s)-docker"
|
RUNNER_NAME="$(hostname -s)"
|
||||||
ALL_MODE=false
|
ALL_MODE=false
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
while [[ $# -gt 0 ]]; do
|
||||||
@@ -77,7 +181,7 @@ fi
|
|||||||
|
|
||||||
create_runner() {
|
create_runner() {
|
||||||
local mode=$1
|
local mode=$1
|
||||||
local name=$2
|
local name="${2:-$(hostname -s)}"
|
||||||
echo "创建 $mode runner: $name"
|
echo "创建 $mode runner: $name"
|
||||||
|
|
||||||
if [ "$mode" = "docker" ]; then
|
if [ "$mode" = "docker" ]; then
|
||||||
@@ -108,6 +212,7 @@ cache:
|
|||||||
host: "host.docker.internal"
|
host: "host.docker.internal"
|
||||||
port: 9040
|
port: 9040
|
||||||
container:
|
container:
|
||||||
|
# 【关键】必须使用 host 网络!否则 job 容器无法访问 runner 的缓存服务
|
||||||
network: "host"
|
network: "host"
|
||||||
privileged: false
|
privileged: false
|
||||||
options:
|
options:
|
||||||
@@ -119,8 +224,11 @@ host:
|
|||||||
workdir_parent: /data/workspace
|
workdir_parent: /data/workspace
|
||||||
YAML
|
YAML
|
||||||
|
|
||||||
local labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
|
# 使用 catthehacker/ubuntu:act-* 镜像,内置 Docker CLI、Buildx 等 CI/CD 工具
|
||||||
|
# 注意:不要使用 node:16-bullseye 等纯运行时镜像,它们不包含 docker 命令
|
||||||
|
local labels="ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04,linux:docker://catthehacker/ubuntu:act-latest"
|
||||||
|
|
||||||
|
# 必须使用 --network host!这样 job 容器才能访问 runner 的缓存服务
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name "$name" \
|
--name "$name" \
|
||||||
--restart always \
|
--restart always \
|
||||||
@@ -176,6 +284,7 @@ runner:
|
|||||||
cache:
|
cache:
|
||||||
enabled: true
|
enabled: true
|
||||||
dir: "$runner_dir/cache"
|
dir: "$runner_dir/cache"
|
||||||
|
# Self-host 模式:job 进程直接在宿主机运行,使用 127.0.0.1 访问缓存
|
||||||
host: "127.0.0.1"
|
host: "127.0.0.1"
|
||||||
port: 0
|
port: 0
|
||||||
host:
|
host:
|
||||||
@@ -206,8 +315,8 @@ YAML
|
|||||||
}
|
}
|
||||||
|
|
||||||
if [ "$ALL_MODE" = true ]; then
|
if [ "$ALL_MODE" = true ]; then
|
||||||
create_runner "docker" "runner-$(hostname -s)-docker"
|
create_runner "docker" "$(hostname -s)-docker"
|
||||||
create_runner "host" "runner-$(hostname -s)-host"
|
create_runner "host" "$(hostname -s)-host"
|
||||||
else
|
else
|
||||||
create_runner "$MODE" "$RUNNER_NAME"
|
create_runner "$MODE" "$RUNNER_NAME"
|
||||||
fi
|
fi
|
||||||
@@ -224,6 +333,45 @@ EOF
|
|||||||
# gitea-create-runner all "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
|
# gitea-create-runner all "wibnIxvgeyYj3D7d53VTQvNxv0UVqArBwAtPFBWD"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 🔄 恢复离线 Runner
|
||||||
|
|
||||||
|
**场景**:Runner 在 Gitea 服务器上被删除,但本地配置文件仍在。需要重新注册并上线。
|
||||||
|
|
||||||
|
**快速恢复命令**(针对 runner-Mac-mini4-host):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/.config/gitea/runners/runner-Mac-mini4-host
|
||||||
|
|
||||||
|
# 停止旧进程
|
||||||
|
if [ -f pid ]; then kill $(cat pid) 2>/dev/null || true; fi
|
||||||
|
|
||||||
|
# 加载配置并重新注册
|
||||||
|
source ~/.config/gitea/config.env
|
||||||
|
|
||||||
|
# 获取令牌(需要 jq)
|
||||||
|
token=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"${GITEA_URL}/api/v1/admin/runners/registration-token" | jq -r '.token')
|
||||||
|
|
||||||
|
# 生成标签
|
||||||
|
OS=$(uname -s); ARCH=$(uname -m)
|
||||||
|
case "$OS" in Darwin) os="macOS";; Linux) os="ubuntu";; *) os="unknown";; esac
|
||||||
|
case "$ARCH" in arm64|aarch64) arch="ARM64";; x86_64) arch="x64";; *) arch="unknown";; esac
|
||||||
|
labels="self-hosted:host,${os}:host,${arch}:host,$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]'):host"
|
||||||
|
|
||||||
|
# 重新注册并启动
|
||||||
|
act_runner register --config config.yaml --instance "$GITEA_URL" \
|
||||||
|
--token "$token" --name "runner-Mac-mini4-host" --labels "$labels" --no-interactive
|
||||||
|
|
||||||
|
nohup act_runner daemon --config config.yaml > runner.log 2>&1 &
|
||||||
|
echo $! > pid
|
||||||
|
echo "✅ Runner 恢复完成 (PID: $(cat pid))"
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 确保 `config.yaml` 中的 `labels:` 配置为空(`labels: []`),注册时会使用命令行参数
|
||||||
|
- 如果全局令牌权限不足,需要获取组织令牌(参考 runner-management.md)
|
||||||
|
- 恢复后 runner 会获得新的 ID,但名称和 labels 保持不变
|
||||||
|
|
||||||
### 方法三:完整脚本
|
### 方法三:完整脚本
|
||||||
如果你需要更多自定义选项,可以使用下面的完整脚本:
|
如果你需要更多自定义选项,可以使用下面的完整脚本:
|
||||||
|
|
||||||
@@ -251,6 +399,8 @@ RUNNER_NAME=""
|
|||||||
ALL_MODE=false
|
ALL_MODE=false
|
||||||
INTERACTIVE=false
|
INTERACTIVE=false
|
||||||
SKIP_CHECKS=false
|
SKIP_CHECKS=false
|
||||||
|
SKIP_NETWORK_CHECK=false
|
||||||
|
SKIP_RESOURCE_CHECK=false
|
||||||
|
|
||||||
# 显示帮助
|
# 显示帮助
|
||||||
show_help() {
|
show_help() {
|
||||||
@@ -265,8 +415,10 @@ Gitea Actions Runner 创建脚本
|
|||||||
--all 同时创建 Docker 和 Host runner
|
--all 同时创建 Docker 和 Host runner
|
||||||
--token TOKEN Gitea 注册令牌 (必需)
|
--token TOKEN Gitea 注册令牌 (必需)
|
||||||
--gitea-url URL Gitea 实例地址 (默认: https://git.shigongcao.com)
|
--gitea-url URL Gitea 实例地址 (默认: https://git.shigongcao.com)
|
||||||
--name NAME Runner 名称 (默认: runner-\$(hostname)-模式)
|
--name NAME Runner 名称 (默认: hostname)
|
||||||
--skip-checks 跳过依赖检查
|
--skip-checks 跳过所有检查
|
||||||
|
--skip-network-check 跳过网络连接检查
|
||||||
|
--skip-resource-check 跳过硬件资源检查
|
||||||
--interactive 交互模式 (传统方式)
|
--interactive 交互模式 (传统方式)
|
||||||
--help 显示此帮助信息
|
--help 显示此帮助信息
|
||||||
|
|
||||||
@@ -292,7 +444,9 @@ while [[ $# -gt 0 ]]; do
|
|||||||
--token) GITEA_TOKEN="$2"; shift ;;
|
--token) GITEA_TOKEN="$2"; shift ;;
|
||||||
--gitea-url) GITEA_URL="$2"; shift ;;
|
--gitea-url) GITEA_URL="$2"; shift ;;
|
||||||
--name) RUNNER_NAME="$2"; shift ;;
|
--name) RUNNER_NAME="$2"; shift ;;
|
||||||
--skip-checks) SKIP_CHECKS=true ;;
|
--skip-checks) SKIP_CHECKS=true; SKIP_NETWORK_CHECK=true; SKIP_RESOURCE_CHECK=true ;;
|
||||||
|
--skip-network-check) SKIP_NETWORK_CHECK=true ;;
|
||||||
|
--skip-resource-check) SKIP_RESOURCE_CHECK=true ;;
|
||||||
--interactive) INTERACTIVE=true ;;
|
--interactive) INTERACTIVE=true ;;
|
||||||
--help) show_help ;;
|
--help) show_help ;;
|
||||||
*) echo "❌ 未知参数: $1"; echo "使用 --help 查看帮助"; exit 1 ;;
|
*) echo "❌ 未知参数: $1"; echo "使用 --help 查看帮助"; exit 1 ;;
|
||||||
@@ -308,7 +462,95 @@ if [ -z "$GITEA_TOKEN" ] && [ "$INTERACTIVE" = false ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 2. Interactive Mode (向后兼容)
|
# 2. Pre-flight Checks (前置条件检查)
|
||||||
|
# ==========================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "前置条件检查"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2.1 网络连接检查
|
||||||
|
if [ "$SKIP_NETWORK_CHECK" = false ]; then
|
||||||
|
echo "检查外网连接..."
|
||||||
|
|
||||||
|
# 尝试连接 Google(使用 curl 超时 5 秒)
|
||||||
|
if curl -s --connect-timeout 5 --max-time 10 https://www.google.com > /dev/null 2>&1; then
|
||||||
|
echo "✓ 外网连接正常 (google.com 可访问)"
|
||||||
|
else
|
||||||
|
echo "⚠️ 警告:无法访问 google.com"
|
||||||
|
echo " Runner 需要访问外网来拉取镜像和依赖"
|
||||||
|
echo " 如果你确认网络正常,可以使用 --skip-network-check 跳过检查"
|
||||||
|
read -p " 是否继续?[y/N]: " continue_choice
|
||||||
|
if [ "$continue_choice" != "y" ] && [ "$continue_choice" != "Y" ]; then
|
||||||
|
echo "❌ 已取消"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⊘ 已跳过网络连接检查"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2.2 硬件资源检查
|
||||||
|
if [ "$SKIP_RESOURCE_CHECK" = false ]; then
|
||||||
|
echo "检查硬件资源..."
|
||||||
|
|
||||||
|
# 检测 CPU 核心数
|
||||||
|
if command -v nproc > /dev/null 2>&1; then
|
||||||
|
cpu_cores=$(nproc)
|
||||||
|
elif command -v sysctl > /dev/null 2>&1; then
|
||||||
|
cpu_cores=$(sysctl -n hw.ncpu 2>/dev/null || echo "0")
|
||||||
|
else
|
||||||
|
cpu_cores=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检测内存大小(GB)
|
||||||
|
if command -v free > /dev/null 2>&1; then
|
||||||
|
mem_gb=$(free -g | awk '/^Mem:/{print $2}')
|
||||||
|
elif command -v sysctl > /dev/null 2>&1; then
|
||||||
|
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo "0")
|
||||||
|
mem_gb=$((mem_bytes / 1024 / 1024 / 1024))
|
||||||
|
else
|
||||||
|
mem_gb=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo " CPU: ${cpu_cores} 核"
|
||||||
|
echo " 内存: ${mem_gb}GB"
|
||||||
|
|
||||||
|
# 检查是否满足最低配置
|
||||||
|
config_ok=true
|
||||||
|
if [ "$cpu_cores" -lt 2 ]; then
|
||||||
|
echo "⚠️ 警告:CPU 核心数不足 (当前 ${cpu_cores} 核,建议至少 2 核)"
|
||||||
|
config_ok=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mem_gb" -lt 4 ]; then
|
||||||
|
echo "⚠️ 警告:内存不足 (当前 ${mem_gb}GB,建议至少 4GB)"
|
||||||
|
config_ok=false
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$config_ok" = false ]; then
|
||||||
|
echo ""
|
||||||
|
echo " 最低配置要求:2C4G (2 核 CPU + 4GB 内存)"
|
||||||
|
echo " 推荐配置:4C8G 或更高"
|
||||||
|
echo " 配置过低可能导致 workflow 执行失败或缓慢"
|
||||||
|
read -p " 是否继续?[y/N]: " continue_choice
|
||||||
|
if [ "$continue_choice" != "y" ] && [ "$continue_choice" != "Y" ]; then
|
||||||
|
echo "❌ 已取消"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✓ 硬件资源满足要求"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "⊘ 已跳过硬件资源检查"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ==========================================
|
||||||
|
# 3. Interactive Mode (向后兼容)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
if [ "$INTERACTIVE" = true ]; then
|
if [ "$INTERACTIVE" = true ]; then
|
||||||
@@ -329,7 +571,7 @@ if [ "$INTERACTIVE" = true ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 3. Load Gitea Configuration
|
# 4. Load Gitea Configuration
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
# 如果未通过命令行提供,尝试从配置文件加载
|
# 如果未通过命令行提供,尝试从配置文件加载
|
||||||
@@ -372,7 +614,7 @@ if [ -z "$RUNNER_NAME" ]; then
|
|||||||
# 批量模式会创建两个runner
|
# 批量模式会创建两个runner
|
||||||
echo "批量模式:将创建 docker 和 host runner"
|
echo "批量模式:将创建 docker 和 host runner"
|
||||||
else
|
else
|
||||||
RUNNER_NAME="runner-$hostname-$RUNNER_MODE"
|
RUNNER_NAME="$hostname"
|
||||||
echo "生成 Runner 名称: $RUNNER_NAME"
|
echo "生成 Runner 名称: $RUNNER_NAME"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -440,7 +682,7 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 3. Load Gitea Configuration
|
# 4. Load Gitea Configuration
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
# 如果未通过命令行提供,尝试从配置文件加载
|
# 如果未通过命令行提供,尝试从配置文件加载
|
||||||
@@ -473,7 +715,7 @@ echo " Token: ${GITEA_TOKEN:0:8}..."
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 4. Generate Runner Name
|
# 5. Generate Runner Name
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
# 如果未指定名称,生成默认名称
|
# 如果未指定名称,生成默认名称
|
||||||
@@ -483,7 +725,7 @@ if [ -z "$RUNNER_NAME" ]; then
|
|||||||
# 批量模式会创建两个runner
|
# 批量模式会创建两个runner
|
||||||
echo "批量模式:将创建 docker 和 host runner"
|
echo "批量模式:将创建 docker 和 host runner"
|
||||||
else
|
else
|
||||||
RUNNER_NAME="runner-$hostname-$RUNNER_MODE"
|
RUNNER_NAME="$hostname"
|
||||||
echo "生成 Runner 名称: $RUNNER_NAME"
|
echo "生成 Runner 名称: $RUNNER_NAME"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -498,7 +740,7 @@ fi
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5. Main Creation Function
|
# 6. Main Creation Function
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
create_runner() {
|
create_runner() {
|
||||||
@@ -533,7 +775,7 @@ create_runner() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5.1 Check Dependencies
|
# 6.1 Check Dependencies
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
if [ "$SKIP_CHECKS" = false ]; then
|
if [ "$SKIP_CHECKS" = false ]; then
|
||||||
@@ -562,33 +804,84 @@ create_runner() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5.2 Detect System Environment & Labels
|
# 6.2 Detect System Environment & Labels
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo "生成 Labels..."
|
echo "生成 Labels..."
|
||||||
|
|
||||||
if [ "$mode" = "host" ]; then
|
if [ "$mode" = "host" ]; then
|
||||||
OS=$(uname -s)
|
OS=$(uname -s)
|
||||||
case "$OS" in
|
|
||||||
Darwin) os_label="macOS" ;;
|
|
||||||
Linux) os_label="ubuntu" ;;
|
|
||||||
*) os_label="unknown" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
ARCH=$(uname -m)
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
# 检测架构
|
||||||
case "$ARCH" in
|
case "$ARCH" in
|
||||||
arm64|aarch64) arch_label="ARM64" ;;
|
arm64|aarch64) arch_label="ARM64" ;;
|
||||||
x86_64) arch_label="x64" ;;
|
x86_64) arch_label="x64" ;;
|
||||||
*) arch_label="unknown" ;;
|
*) arch_label="unknown" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# 检测操作系统和发行版
|
||||||
|
if [ "$OS" = "Darwin" ]; then
|
||||||
|
os_label="macOS"
|
||||||
|
distro_label=""
|
||||||
|
distro_version_label=""
|
||||||
|
elif [ "$OS" = "Linux" ]; then
|
||||||
|
# 检测 Linux 发行版
|
||||||
|
if [ -f /etc/os-release ]; then
|
||||||
|
. /etc/os-release
|
||||||
|
distro_label="$ID" # debian, ubuntu, centos, etc.
|
||||||
|
distro_version_label="${ID}-${VERSION_ID%%.*}" # debian-12, ubuntu-22, etc.
|
||||||
|
else
|
||||||
|
distro_label="linux"
|
||||||
|
distro_version_label=""
|
||||||
|
fi
|
||||||
|
os_label="$distro_label"
|
||||||
|
else
|
||||||
|
os_label="unknown"
|
||||||
|
distro_label=""
|
||||||
|
distro_version_label=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检测设备配置
|
||||||
|
cpu_cores=$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo "unknown")
|
||||||
|
mem_gb=$(free -g 2>/dev/null | awk '/^Mem:/{print $2}' || sysctl -n hw.memsize 2>/dev/null | awk '{print int($1/1024/1024/1024)}' || echo "unknown")
|
||||||
|
|
||||||
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
|
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
|
||||||
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
|
|
||||||
|
# 构建标签(移除 :host 后缀,统一添加 host 模式标签)
|
||||||
|
# 添加 ubuntu-latest 以兼容 GitHub Actions workflow
|
||||||
|
labels="host,ubuntu-latest,self-hosted,${arch_label},${combined}"
|
||||||
|
|
||||||
|
# 添加实际发行版标签
|
||||||
|
if [ -n "$distro_label" ] && [ "$distro_label" != "linux" ]; then
|
||||||
|
labels="${labels},${distro_label}"
|
||||||
|
if [ -n "$distro_version_label" ]; then
|
||||||
|
labels="${labels},${distro_version_label}"
|
||||||
|
fi
|
||||||
|
elif [ "$os_label" != "unknown" ]; then
|
||||||
|
labels="${labels},${os_label}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 添加通用 linux 标签(用于区分 macOS)
|
||||||
|
if [ "$OS" = "Linux" ]; then
|
||||||
|
labels="${labels},linux"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 添加设备配置标签
|
||||||
|
if [ "$cpu_cores" != "unknown" ] && [ "$mem_gb" != "unknown" ]; then
|
||||||
|
labels="${labels},${cpu_cores}c${mem_gb}g"
|
||||||
|
fi
|
||||||
|
|
||||||
else
|
else
|
||||||
# Docker mode uses standard labels mapping to images
|
# Docker mode uses standard labels mapping to images
|
||||||
# Format: label:docker://image
|
# Format: label:docker://image
|
||||||
labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
|
# 重要:必须使用包含 Docker CLI 的镜像,否则 docker/login-action 等 actions 会失败
|
||||||
|
# catthehacker/ubuntu:act-* 是专为 GitHub/Gitea Actions 设计的镜像,预装:
|
||||||
|
# - Docker CLI (docker 命令)
|
||||||
|
# - Docker Buildx
|
||||||
|
# - git, curl, jq 等常用工具
|
||||||
|
# 不要使用 node:16-bullseye 等纯运行时镜像!
|
||||||
|
labels="ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04,linux:docker://catthehacker/ubuntu:act-latest"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ Labels ($mode):"
|
echo "✓ Labels ($mode):"
|
||||||
@@ -596,7 +889,7 @@ create_runner() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5.3 Create Runner Directory
|
# 6.3 Create Runner Directory
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo "创建 Runner 目录..."
|
echo "创建 Runner 目录..."
|
||||||
@@ -609,7 +902,7 @@ create_runner() {
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5.4 Create Configuration File
|
# 6.4 Create Configuration File
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo "创建配置文件..."
|
echo "创建配置文件..."
|
||||||
@@ -700,6 +993,7 @@ EOF
|
|||||||
|
|
||||||
echo "启动 Docker 容器 (自动注册)..."
|
echo "启动 Docker 容器 (自动注册)..."
|
||||||
|
|
||||||
|
# 必须使用 --network host!这样 job 容器才能访问 runner 的缓存服务
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name "$name" \
|
--name "$name" \
|
||||||
--restart always \
|
--restart always \
|
||||||
@@ -726,7 +1020,7 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 5.5 Display Runner Info
|
# 6.5 Display Runner Info
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -747,7 +1041,7 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 6. Main Execution Logic
|
# 7. Main Execution Logic
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
@@ -766,8 +1060,8 @@ if [ "$ALL_MODE" = true ]; then
|
|||||||
host_name="${RUNNER_NAME}-host"
|
host_name="${RUNNER_NAME}-host"
|
||||||
else
|
else
|
||||||
# 使用默认名称
|
# 使用默认名称
|
||||||
docker_name="runner-${hostname}-docker"
|
docker_name="${hostname}-docker"
|
||||||
host_name="runner-${hostname}-host"
|
host_name="${hostname}-host"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "批量创建模式:"
|
echo "批量创建模式:"
|
||||||
@@ -849,7 +1143,13 @@ if [ "$RUNNER_MODE" = "host" ]; then
|
|||||||
else
|
else
|
||||||
# Docker mode uses standard labels mapping to images
|
# Docker mode uses standard labels mapping to images
|
||||||
# Format: label:docker://image
|
# Format: label:docker://image
|
||||||
labels="ubuntu-latest:docker://node:16-bullseye,ubuntu-22.04:docker://node:16-bullseye,ubuntu-20.04:docker://node:16-buster,linux:docker://node:16-bullseye"
|
# 重要:必须使用包含 Docker CLI 的镜像,否则 docker/login-action 等 actions 会失败
|
||||||
|
# catthehacker/ubuntu:act-* 是专为 GitHub/Gitea Actions 设计的镜像,预装:
|
||||||
|
# - Docker CLI (docker 命令)
|
||||||
|
# - Docker Buildx
|
||||||
|
# - git, curl, jq 等常用工具
|
||||||
|
# 不要使用 node:16-bullseye 等纯运行时镜像!
|
||||||
|
labels="ubuntu-latest:docker://catthehacker/ubuntu:act-latest,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04,ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04,linux:docker://catthehacker/ubuntu:act-latest"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✓ Labels ($RUNNER_MODE):"
|
echo "✓ Labels ($RUNNER_MODE):"
|
||||||
@@ -857,7 +1157,7 @@ echo " $labels"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 6. Create Runner Directory
|
# 7. Create Runner Directory
|
||||||
# ==========================================
|
# ==========================================
|
||||||
|
|
||||||
echo "创建 Runner 目录..."
|
echo "创建 Runner 目录..."
|
||||||
@@ -1021,7 +1321,7 @@ cache:
|
|||||||
host: "host.docker.internal"
|
host: "host.docker.internal"
|
||||||
port: 9040
|
port: 9040
|
||||||
container:
|
container:
|
||||||
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问
|
# 【关键】必须使用 host 网络!否则 job 容器无法访问 runner 的缓存服务
|
||||||
network: "host"
|
network: "host"
|
||||||
privileged: false
|
privileged: false
|
||||||
options:
|
options:
|
||||||
@@ -1035,7 +1335,7 @@ EOF
|
|||||||
|
|
||||||
echo "启动 Docker 容器 (自动注册)..."
|
echo "启动 Docker 容器 (自动注册)..."
|
||||||
|
|
||||||
# 使用 host 网络模式,确保 runner 和 job 容器共处一个网络,以便 cache 能够正常访问
|
# 【关键】必须使用 --network host!否则 job 容器无法访问 runner 的缓存服务
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name "$runner_name" \
|
--name "$runner_name" \
|
||||||
--restart always \
|
--restart always \
|
||||||
|
|||||||
@@ -69,10 +69,10 @@ LOCAL_MAP=$(mktemp)
|
|||||||
FINAL_LIST=$(mktemp)
|
FINAL_LIST=$(mktemp)
|
||||||
|
|
||||||
# 2.1 Fetch Remote Runners (Try Admin first, then Org)
|
# 2.1 Fetch Remote Runners (Try Admin first, then Org)
|
||||||
# Note: Admin endpoint /api/v1/admin/runners lists all runners
|
# Note: Admin endpoint /api/v1/admin/actions/runners lists all runners
|
||||||
HTTP_CODE=$(curl -s -w "%{http_code}" -o "$REMOTE_LIST" \
|
HTTP_CODE=$(curl -s -w "%{http_code}" -o "$REMOTE_LIST" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
"${GITEA_URL}/api/v1/admin/runners?page=1&limit=100")
|
"${GITEA_URL}/api/v1/admin/actions/runners?page=1&limit=100")
|
||||||
|
|
||||||
if [ "$HTTP_CODE" != "200" ]; then
|
if [ "$HTTP_CODE" != "200" ]; then
|
||||||
# Fallback to Org level if defined
|
# Fallback to Org level if defined
|
||||||
|
|||||||
550
skill/gitea/dockerfile-templates/go-service.md
Normal file
550
skill/gitea/dockerfile-templates/go-service.md
Normal file
@@ -0,0 +1,550 @@
|
|||||||
|
# Go 服务 Dockerfile 模板
|
||||||
|
|
||||||
|
Go 后端服务的 Docker 容器化模板,支持多种构建场景。
|
||||||
|
|
||||||
|
## 模板类型
|
||||||
|
|
||||||
|
### 1. CI 构建镜像(Dockerfile.ci)
|
||||||
|
|
||||||
|
**适用场景**:在 CI/CD 中使用,二进制文件已在外部构建完成。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 镜像体积最小(~15MB)
|
||||||
|
- 构建速度快
|
||||||
|
- 适合生产环境
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# CI 构建专用 Dockerfile
|
||||||
|
# 将不常变化的层放在前面,最大化利用缓存
|
||||||
|
# 使用固定版本避免 latest 导致的缓存失效和不确定性
|
||||||
|
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# 基础设置层 - 很少变化,可以长期缓存
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata wget netcat-openbsd && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
# 健康检查脚本 - 偶尔变化
|
||||||
|
COPY docker-healthcheck.sh .
|
||||||
|
RUN chmod +x docker-healthcheck.sh
|
||||||
|
|
||||||
|
# 应用二进制 - 每次构建都变化,放在最后
|
||||||
|
COPY your-binary-name .
|
||||||
|
|
||||||
|
# 健康检查(根据实际服务修改端口和端点)
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD ./docker-healthcheck.sh
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 将 `your-binary-name` 替换为实际的二进制文件名
|
||||||
|
2. 创建 `docker-healthcheck.sh` 健康检查脚本
|
||||||
|
3. 在 CI workflow 中先构建二进制,再构建镜像
|
||||||
|
|
||||||
|
### 2. 完整构建镜像(Dockerfile)
|
||||||
|
|
||||||
|
**适用场景**:本地开发、无 CI 环境、独立构建。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 自包含构建流程
|
||||||
|
- 可在任何环境构建
|
||||||
|
- 利用 Docker 缓存加速
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
# 安装构建依赖
|
||||||
|
RUN apk add --no-cache git make
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 设置 Go 代理(国内加速)
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
ENV GOOS=linux
|
||||||
|
ENV GOARCH=amd64
|
||||||
|
|
||||||
|
# 先复制依赖文件,利用 Docker 层缓存
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# 复制源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建应用(根据项目需求修改)
|
||||||
|
RUN go build -o app \
|
||||||
|
-ldflags '-s -w -X main.GitTag=dev' \
|
||||||
|
.
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# 安装运行时依赖
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata wget netcat-openbsd && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从构建阶段复制二进制文件
|
||||||
|
COPY --from=builder /build/app .
|
||||||
|
COPY --from=builder /build/docker-healthcheck.sh .
|
||||||
|
|
||||||
|
RUN chmod +x app docker-healthcheck.sh
|
||||||
|
|
||||||
|
# 暴露端口(根据实际服务修改)
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD ./docker-healthcheck.sh
|
||||||
|
|
||||||
|
CMD ["./app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 将构建命令修改为项目实际的构建命令
|
||||||
|
2. 调整 ldflags 参数(版本号注入、符号表去除等)
|
||||||
|
3. 修改端口号
|
||||||
|
|
||||||
|
### 3. 代码生成支持版(Dockerfile.codegen)
|
||||||
|
|
||||||
|
**适用场景**:需要在容器中生成代码(如 Ent、Wire、oapi-codegen)。
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
# 安装构建工具
|
||||||
|
RUN apk add --no-cache git make
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
ENV GOOS=linux
|
||||||
|
ENV GOARCH=amd64
|
||||||
|
|
||||||
|
# 复制依赖文件
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# 安装代码生成工具(根据项目需求选择)
|
||||||
|
RUN go install entgo.io/ent/cmd/ent@latest && \
|
||||||
|
go install github.com/google/wire/cmd/wire@latest && \
|
||||||
|
go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@latest && \
|
||||||
|
go install golang.org/x/tools/cmd/stringer@latest
|
||||||
|
|
||||||
|
# 复制源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 代码生成(根据项目需求修改)
|
||||||
|
RUN go generate ./...
|
||||||
|
# 或使用 go tool 调用
|
||||||
|
# RUN go tool ent generate ./ent/schema
|
||||||
|
# RUN go tool wire ./...
|
||||||
|
# RUN go tool oapi-codegen -config oapi.yaml api.yaml
|
||||||
|
|
||||||
|
# 测试(可选)
|
||||||
|
RUN go test ./...
|
||||||
|
RUN go vet ./...
|
||||||
|
|
||||||
|
# 构建
|
||||||
|
RUN go build -o app \
|
||||||
|
-ldflags '-s -w' \
|
||||||
|
.
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata wget netcat-openbsd && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /build/app .
|
||||||
|
COPY --from=builder /build/docker-healthcheck.sh .
|
||||||
|
|
||||||
|
RUN chmod +x app docker-healthcheck.sh
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||||
|
CMD ./docker-healthcheck.sh
|
||||||
|
|
||||||
|
CMD ["./app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 健康检查脚本
|
||||||
|
|
||||||
|
### docker-healthcheck.sh(基础版)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
# Docker 健康检查脚本
|
||||||
|
|
||||||
|
# 检查应用端口是否在监听
|
||||||
|
if nc -z localhost 8080 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-healthcheck.sh(HTTP 端点版)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
# Docker 健康检查脚本
|
||||||
|
# 检查 HTTP 端点是否可访问
|
||||||
|
|
||||||
|
# 使用 /health 或 /metrics 端点
|
||||||
|
if wget --spider --quiet --timeout=5 http://localhost:8080/health 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 备用:检查端口是否在监听
|
||||||
|
if nc -z localhost 8080 2>/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-healthcheck.sh(多端口版)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/bin/sh
|
||||||
|
# Docker 健康检查脚本
|
||||||
|
# 检查多个端口(适用于多个 API 服务)
|
||||||
|
|
||||||
|
# 检查端口 2020 (Platform API)
|
||||||
|
if ! nc -z localhost 2020 2>/dev/null; then
|
||||||
|
echo "Port 2020 is not responding"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查端口 2021 (WxMP API)
|
||||||
|
if ! nc -z localhost 2021 2>/dev/null; then
|
||||||
|
echo "Port 2021 is not responding"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 或使用 HTTP 端点
|
||||||
|
# if ! wget --spider --quiet --timeout=5 http://localhost:2020/metrics 2>/dev/null; then
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 高级配置
|
||||||
|
|
||||||
|
### 环境变量配置
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 在 Dockerfile 中设置默认环境变量
|
||||||
|
ENV MODE=production
|
||||||
|
ENV LOG_LEVEL=info
|
||||||
|
ENV GIN_MODE=release
|
||||||
|
|
||||||
|
# 运行时可覆盖
|
||||||
|
# docker run -e MODE=test -e LOG_LEVEL=debug your-image
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置文件挂载
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 创建配置目录
|
||||||
|
RUN mkdir -p /app/configs
|
||||||
|
|
||||||
|
# 运行时挂载配置文件
|
||||||
|
# docker run -v ./configs:/app/configs your-image
|
||||||
|
```
|
||||||
|
|
||||||
|
### 非 root 用户运行
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 创建非特权用户
|
||||||
|
RUN addgroup -g 1000 appuser && \
|
||||||
|
adduser -D -u 1000 -G appuser appuser
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制文件
|
||||||
|
COPY --from=builder --chown=appuser:appuser /build/app .
|
||||||
|
|
||||||
|
# 切换用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
CMD ["./app"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多阶段构建优化
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 依赖下载阶段(缓存 go.mod)
|
||||||
|
# ============================================
|
||||||
|
FROM golang:1.25-alpine AS deps
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
ENV GOPROXY=https://goproxy.cn,direct
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 从依赖阶段复制缓存
|
||||||
|
COPY --from=deps /go/pkg /go/pkg
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
ENV GOOS=linux
|
||||||
|
ENV GOARCH=amd64
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN go build -o app -ldflags '-s -w' .
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# ... 省略后续步骤
|
||||||
|
```
|
||||||
|
|
||||||
|
## 镜像优化技巧
|
||||||
|
|
||||||
|
### 1. 减小镜像体积
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 使用 Alpine 基础镜像
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# 去除符号表和调试信息
|
||||||
|
RUN go build -ldflags '-s -w' -o app
|
||||||
|
|
||||||
|
# 使用 upx 压缩(可选,启动时间会变长)
|
||||||
|
RUN apk add --no-cache upx && \
|
||||||
|
upx --best --lzma app && \
|
||||||
|
apk del upx
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 利用构建缓存
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 先复制 go.mod 和 go.sum,利用依赖缓存
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# 再复制源码(源码变化频繁)
|
||||||
|
COPY . .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 合并 RUN 命令
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 合并多个 RUN 命令减少层数
|
||||||
|
RUN apk add --no-cache tzdata wget netcat-openbsd && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||||
|
addgroup -g 1000 appuser
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 使用 .dockerignore
|
||||||
|
|
||||||
|
```
|
||||||
|
# .dockerignore
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
docs/
|
||||||
|
tests/
|
||||||
|
.github/
|
||||||
|
.gitea/
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose.yml
|
||||||
|
*.log
|
||||||
|
tmp/
|
||||||
|
vendor/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 构建命令示例
|
||||||
|
|
||||||
|
### 本地构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基础构建
|
||||||
|
docker build -t your-service:latest .
|
||||||
|
|
||||||
|
# 指定 Dockerfile
|
||||||
|
docker build -f Dockerfile.ci -t your-service:latest .
|
||||||
|
|
||||||
|
# 传递构建参数
|
||||||
|
docker build --build-arg GIT_TAG=v1.0.0 -t your-service:1.0.0 .
|
||||||
|
|
||||||
|
# 查看构建过程
|
||||||
|
docker build --progress=plain -t your-service:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD 构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 BuildKit 缓存
|
||||||
|
export DOCKER_BUILDKIT=1
|
||||||
|
|
||||||
|
# 使用注册表缓存
|
||||||
|
docker build \
|
||||||
|
--cache-from=type=registry,ref=your-service:buildcache \
|
||||||
|
--cache-to=type=registry,ref=your-service:buildcache,mode=max \
|
||||||
|
-t your-service:latest \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行容器示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基础运行
|
||||||
|
docker run -d -p 8080:8080 your-service:latest
|
||||||
|
|
||||||
|
# 挂载配置文件
|
||||||
|
docker run -d \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-v ./configs:/app/configs \
|
||||||
|
-v ./logs:/app/logs \
|
||||||
|
your-service:latest
|
||||||
|
|
||||||
|
# 设置环境变量
|
||||||
|
docker run -d \
|
||||||
|
-p 8080:8080 \
|
||||||
|
-e MODE=production \
|
||||||
|
-e DATABASE_URL=postgres://... \
|
||||||
|
your-service:latest
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker logs -f <container-id>
|
||||||
|
|
||||||
|
# 进入容器调试
|
||||||
|
docker exec -it <container-id> /bin/sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安全建议
|
||||||
|
|
||||||
|
### 1. 使用特定版本标签
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 不推荐:使用 latest
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# 推荐:使用特定版本
|
||||||
|
FROM alpine:3.21
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 定期更新依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 更新 Go 模块
|
||||||
|
go get -u ./...
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
# 更新基础镜像
|
||||||
|
docker pull alpine:3.21
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 扫描漏洞
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 Trivy 扫描
|
||||||
|
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
|
||||||
|
aquasec/trivy image your-service:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 最小权限原则
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 使用非 root 用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# 只读文件系统(如果适用)
|
||||||
|
# docker run --read-only your-service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 时区不正确
|
||||||
|
|
||||||
|
**原因**: 容器默认使用 UTC 时区
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```dockerfile
|
||||||
|
RUN apk add --no-cache tzdata && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q2: 网络连接超时
|
||||||
|
|
||||||
|
**原因**: DNS 解析问题或网络配置
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```bash
|
||||||
|
# 运行时指定 DNS
|
||||||
|
docker run --dns 8.8.8.8 your-service
|
||||||
|
|
||||||
|
# 或在 Dockerfile 中配置
|
||||||
|
RUN echo 'nameserver 8.8.8.8' > /etc/resolv.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: CGO 依赖问题
|
||||||
|
|
||||||
|
**错误信息**: `binary was compiled with 'CGO_ENABLED=1'`
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```dockerfile
|
||||||
|
# 禁用 CGO
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
|
# 或安装 gcc
|
||||||
|
RUN apk add --no-cache gcc musl-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 静态链接失败
|
||||||
|
|
||||||
|
**错误信息**: `dynamic executable`
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```dockerfile
|
||||||
|
# 确保 CGO_ENABLED=0
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
|
||||||
|
# 或使用 musl 交叉编译
|
||||||
|
RUN apk add --no-cache musl-dev
|
||||||
|
RUN go build -ldflags '-linkmode external -extldflags "-static"' -o app
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考资源
|
||||||
|
|
||||||
|
- [Go 官方 Docker 指南](https://docs.docker.com/language/golang/)
|
||||||
|
- [Alpine Linux 包搜索](https://pkgs.alpinelinux.org/packages)
|
||||||
|
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
|
||||||
|
- [Go 编译器标志](https://pkg.go.dev/cmd/go)
|
||||||
631
skill/gitea/dockerfile-templates/nodejs-frontend.md
Normal file
631
skill/gitea/dockerfile-templates/nodejs-frontend.md
Normal file
@@ -0,0 +1,631 @@
|
|||||||
|
# Node.js 前端 Dockerfile 模板
|
||||||
|
|
||||||
|
Node.js 前端项目的 Docker 容器化模板,使用 Nginx 提供静态文件服务。
|
||||||
|
|
||||||
|
## 模板类型
|
||||||
|
|
||||||
|
### 1. CI 构建镜像(Dockerfile.ci)
|
||||||
|
|
||||||
|
**适用场景**:在 CI/CD 中使用,静态文件已在外部构建完成。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 镜像体积最小(~20MB)
|
||||||
|
- 构建速度快
|
||||||
|
- 适合生产环境
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# CI 构建专用 Dockerfile
|
||||||
|
# 将不常变化的层放在前面,最大化利用缓存
|
||||||
|
# 使用固定版本避免 latest 导致的缓存失效和不确定性
|
||||||
|
|
||||||
|
FROM nginx:1.28.0-alpine
|
||||||
|
|
||||||
|
# 基础设置层 - 很少变化,可以长期缓存
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
# Nginx 配置文件 - 配置 SPA 路由处理
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# 应用静态文件 - 每次构建都变化,放在最后
|
||||||
|
COPY dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 创建 `nginx.conf` 配置文件
|
||||||
|
2. 在 CI workflow 中先构建前端(生成 dist 目录),再构建镜像
|
||||||
|
3. 根据实际需求修改 Nginx 配置
|
||||||
|
|
||||||
|
### 2. 完整构建镜像(Dockerfile)
|
||||||
|
|
||||||
|
**适用场景**:本地开发、无 CI 环境、独立构建。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 自包含构建流程
|
||||||
|
- 可在任何环境构建
|
||||||
|
- 利用 Docker 缓存加速
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
# 安装构建依赖
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 设置 npm 镜像源(国内加速)
|
||||||
|
RUN npm config set registry https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 安装 pnpm
|
||||||
|
RUN npm install -g pnpm@latest-10
|
||||||
|
|
||||||
|
# 先复制依赖文件,利用 Docker 层缓存
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
# 复制源码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建应用
|
||||||
|
# 根据项目使用的构建工具修改命令
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM nginx:1.28.0-alpine
|
||||||
|
|
||||||
|
# 安装运行时依赖
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
# 复制 Nginx 配置
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
|
||||||
|
# 从构建阶段复制静态文件
|
||||||
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 根据项目使用 npm、yarn 或 pnpm 修改安装命令
|
||||||
|
2. 根据构建工具(Vite、Webpack、Next.js 等)修改构建命令和输出目录
|
||||||
|
3. 调整 Nginx 配置
|
||||||
|
|
||||||
|
### 3. 多环境构建版(Dockerfile.multienv)
|
||||||
|
|
||||||
|
**适用场景**:需要根据不同环境(dev/test/prod)构建不同配置。
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
|
||||||
|
ARG BUILD_ENV=production
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
RUN npm config set registry https://registry.npmmirror.com && \
|
||||||
|
npm install -g pnpm@latest-10
|
||||||
|
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
RUN pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 根据 BUILD_ENV 设置不同的环境变量
|
||||||
|
ENV NODE_ENV=${BUILD_ENV}
|
||||||
|
ENV VITE_APP_ENV=${BUILD_ENV}
|
||||||
|
|
||||||
|
RUN pnpm run build
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM nginx:1.28.0-alpine
|
||||||
|
|
||||||
|
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||||
|
apk add --no-cache tzdata && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**构建命令**:
|
||||||
|
```bash
|
||||||
|
# 生产环境
|
||||||
|
docker build --build-arg BUILD_ENV=production -t app:prod .
|
||||||
|
|
||||||
|
# 测试环境
|
||||||
|
docker build --build-arg BUILD_ENV=test -t app:test .
|
||||||
|
|
||||||
|
# 开发环境
|
||||||
|
docker build --build-arg BUILD_ENV=development -t app:dev .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nginx 配置模板
|
||||||
|
|
||||||
|
### nginx.conf(基础 SPA 配置)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 启用 gzip 压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript
|
||||||
|
application/x-javascript application/xml+rss
|
||||||
|
application/json application/javascript;
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA 路由处理:所有请求都返回 index.html
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查端点
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### nginx.conf(带 API 代理)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 30M;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 启用 gzip 压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript
|
||||||
|
application/x-javascript application/xml+rss
|
||||||
|
application/json application/javascript;
|
||||||
|
|
||||||
|
# 静态资源缓存
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA 路由处理
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 代理(根据实际后端服务修改)
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend-service:8080/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host:$server_port;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header REMOTE-HOST $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 健康检查端点
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### nginx.conf(完整生产配置)
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# SPA 应用 Nginx 配置
|
||||||
|
# 所有路由都返回 index.html,由前端路由处理
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 30M;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# 启用 gzip 压缩
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_min_length 1024;
|
||||||
|
gzip_types text/plain text/css text/xml text/javascript
|
||||||
|
application/x-javascript application/xml+rss
|
||||||
|
application/json application/javascript;
|
||||||
|
|
||||||
|
# 静态资源缓存(带版本号的文件可以长期缓存)
|
||||||
|
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
|
||||||
|
# HTML 文件不缓存(确保用户总是获取最新版本)
|
||||||
|
location ~* \.html$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
# SPA 路由处理:所有请求都返回 index.html
|
||||||
|
location / {
|
||||||
|
# 用于配合 browserHistory 使用
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 代理:平台 API
|
||||||
|
location /platform/api/ {
|
||||||
|
proxy_pass http://backend-service:2020/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host:$server_port;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header REMOTE-HOST $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
# API 代理:微信小程序 API
|
||||||
|
location /wxmp/api/ {
|
||||||
|
proxy_pass http://backend-service:2021/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_read_timeout 300s;
|
||||||
|
proxy_redirect off;
|
||||||
|
proxy_set_header Host $host:$server_port;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header REMOTE-HOST $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 第三方服务代理(如高德地图)
|
||||||
|
location /_AMapService/ {
|
||||||
|
set $args "$args&jscode=YOUR_AMAP_KEY";
|
||||||
|
proxy_pass https://restapi.amap.com/;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 错误页面
|
||||||
|
error_page 404 /404.html;
|
||||||
|
error_page 500 502 503 504 /50x.html;
|
||||||
|
|
||||||
|
# 健康检查端点
|
||||||
|
location /health {
|
||||||
|
access_log off;
|
||||||
|
return 200 "healthy\n";
|
||||||
|
add_header Content-Type text/plain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 高级配置
|
||||||
|
|
||||||
|
### 支持 HTTPS
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name example.com;
|
||||||
|
return 301 https://$server_name$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
server_name example.com;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/ssl/cert.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/ssl/key.pem;
|
||||||
|
ssl_protocols TLSv1.2 TLSv1.3;
|
||||||
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
# ... 其他配置
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 防盗链
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location ~* \.(jpg|jpeg|png|gif|svg)$ {
|
||||||
|
valid_referers none blocked example.com *.example.com;
|
||||||
|
if ($invalid_referer) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
expires 1y;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 限流
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 在 http 块中定义限流区域
|
||||||
|
limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;
|
||||||
|
|
||||||
|
# 在 server 块中应用
|
||||||
|
location / {
|
||||||
|
limit_req zone=one burst=20 nodelay;
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安全头
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location / {
|
||||||
|
# 防止点击劫持
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
|
||||||
|
# 防止 MIME 类型嗅探
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
|
||||||
|
# 启用 XSS 保护
|
||||||
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
|
||||||
|
# CSP 策略
|
||||||
|
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
|
||||||
|
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 镜像优化技巧
|
||||||
|
|
||||||
|
### 1. 使用多阶段构建
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 分离构建和运行环境,只保留必要文件
|
||||||
|
FROM node:22-alpine AS builder
|
||||||
|
# ... 构建步骤
|
||||||
|
|
||||||
|
FROM nginx:1.28.0-alpine
|
||||||
|
# 只复制构建产物
|
||||||
|
COPY --from=builder /build/dist /usr/share/nginx/html
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 利用构建缓存
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 先复制依赖文件
|
||||||
|
COPY package.json pnpm-lock.yaml ./
|
||||||
|
RUN pnpm install
|
||||||
|
|
||||||
|
# 再复制源码(源码变化频繁)
|
||||||
|
COPY . .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 使用 .dockerignore
|
||||||
|
|
||||||
|
```
|
||||||
|
# .dockerignore
|
||||||
|
node_modules/
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
docs/
|
||||||
|
tests/
|
||||||
|
.github/
|
||||||
|
.gitea/
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose.yml
|
||||||
|
.env*
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
coverage/
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 优化 Nginx 配置
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
# 启用 sendfile
|
||||||
|
sendfile on;
|
||||||
|
|
||||||
|
# 启用 tcp_nopush
|
||||||
|
tcp_nopush on;
|
||||||
|
|
||||||
|
# 启用 tcp_nodelay
|
||||||
|
tcp_nodelay on;
|
||||||
|
|
||||||
|
# 设置 keepalive 超时
|
||||||
|
keepalive_timeout 65;
|
||||||
|
|
||||||
|
# 禁用访问日志(生产环境可选)
|
||||||
|
access_log off;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 构建命令示例
|
||||||
|
|
||||||
|
### 本地构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基础构建
|
||||||
|
docker build -t frontend:latest .
|
||||||
|
|
||||||
|
# 指定 Dockerfile
|
||||||
|
docker build -f Dockerfile.ci -t frontend:latest .
|
||||||
|
|
||||||
|
# 传递构建参数
|
||||||
|
docker build --build-arg BUILD_ENV=production -t frontend:1.0.0 .
|
||||||
|
```
|
||||||
|
|
||||||
|
### CI/CD 构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 BuildKit 缓存
|
||||||
|
export DOCKER_BUILDKIT=1
|
||||||
|
|
||||||
|
# 使用注册表缓存
|
||||||
|
docker build \
|
||||||
|
--cache-from=type=registry,ref=frontend:buildcache \
|
||||||
|
--cache-to=type=registry,ref=frontend:buildcache,mode=max \
|
||||||
|
-t frontend:latest \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行容器示例
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基础运行
|
||||||
|
docker run -d -p 80:80 frontend:latest
|
||||||
|
|
||||||
|
# 挂载自定义 Nginx 配置
|
||||||
|
docker run -d \
|
||||||
|
-p 80:80 \
|
||||||
|
-v ./nginx.conf:/etc/nginx/conf.d/default.conf \
|
||||||
|
frontend:latest
|
||||||
|
|
||||||
|
# 设置环境变量(如果 Nginx 配置需要)
|
||||||
|
docker run -d \
|
||||||
|
-p 80:80 \
|
||||||
|
-e BACKEND_HOST=api.example.com \
|
||||||
|
frontend:latest
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker logs -f <container-id>
|
||||||
|
|
||||||
|
# 进入容器调试
|
||||||
|
docker exec -it <container-id> /bin/sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Compose 示例
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
frontend:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
depends_on:
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
backend:
|
||||||
|
image: backend-service:latest
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
restart: unless-stopped
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: SPA 路由 404 错误
|
||||||
|
|
||||||
|
**原因**: Nginx 找不到对应的文件
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```nginx
|
||||||
|
# 配置 try_files,所有路由返回 index.html
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q2: API 代理跨域问题
|
||||||
|
|
||||||
|
**原因**: CORS 配置不正确
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```nginx
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://backend:8080/;
|
||||||
|
|
||||||
|
# 添加 CORS 头
|
||||||
|
add_header 'Access-Control-Allow-Origin' '*' always;
|
||||||
|
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
||||||
|
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type' always;
|
||||||
|
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: 静态资源缓存问题
|
||||||
|
|
||||||
|
**原因**: 浏览器缓存了旧版本
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```nginx
|
||||||
|
# HTML 文件不缓存
|
||||||
|
location ~* \.html$ {
|
||||||
|
expires -1;
|
||||||
|
add_header Cache-Control "no-store, no-cache, must-revalidate";
|
||||||
|
}
|
||||||
|
|
||||||
|
# JS/CSS 使用版本号或哈希,可以长期缓存
|
||||||
|
location ~* \.(js|css)$ {
|
||||||
|
expires 1y;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 容器启动后立即退出
|
||||||
|
|
||||||
|
**原因**: Nginx 配置文件语法错误
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```bash
|
||||||
|
# 测试配置文件
|
||||||
|
docker run --rm -v ./nginx.conf:/etc/nginx/conf.d/default.conf \
|
||||||
|
nginx:1.28.0-alpine nginx -t
|
||||||
|
|
||||||
|
# 查看容器日志
|
||||||
|
docker logs <container-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考资源
|
||||||
|
|
||||||
|
- [Node.js 官方 Docker 指南](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/)
|
||||||
|
- [Nginx 官方文档](https://nginx.org/en/docs/)
|
||||||
|
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
|
||||||
|
- [前端构建工具文档](https://vitejs.dev/guide/)
|
||||||
571
skill/gitea/dockerfile-templates/rust-service.md
Normal file
571
skill/gitea/dockerfile-templates/rust-service.md
Normal file
@@ -0,0 +1,571 @@
|
|||||||
|
# Rust 服务 Dockerfile 模板
|
||||||
|
|
||||||
|
Rust 项目的 Docker 容器化模板,支持多种构建场景。
|
||||||
|
|
||||||
|
## 模板类型
|
||||||
|
|
||||||
|
### 1. CI 构建镜像(Dockerfile.ci)
|
||||||
|
|
||||||
|
**适用场景**:在 CI/CD 中使用,二进制文件已在外部构建完成。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 镜像体积最小(~20MB)
|
||||||
|
- 构建速度快
|
||||||
|
- 适合生产环境
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# 安装运行时依赖
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制编译好的二进制文件(由 CI workflow 构建)
|
||||||
|
COPY your-binary-name .
|
||||||
|
|
||||||
|
# 环境变量
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
# 根据你的服务实际情况修改端点和端口
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 将 `your-binary-name` 替换为实际的二进制文件名
|
||||||
|
2. 修改 `EXPOSE` 端口号
|
||||||
|
3. 修改 `HEALTHCHECK` 的端点和端口
|
||||||
|
4. 在 CI workflow 中先构建二进制,再构建镜像
|
||||||
|
|
||||||
|
### 2. 完整构建镜像(Dockerfile)
|
||||||
|
|
||||||
|
**适用场景**:本地开发、无 CI 环境、独立构建。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 自包含构建流程
|
||||||
|
- 可在任何环境构建
|
||||||
|
- 利用 Docker 缓存加速
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM rust:1.83-alpine AS builder
|
||||||
|
|
||||||
|
# 安装构建依赖
|
||||||
|
# musl-dev: musl libc 开发包(静态链接)
|
||||||
|
# openssl-dev: OpenSSL 开发包(如果项目依赖 openssl)
|
||||||
|
# pkgconfig: pkg-config 工具
|
||||||
|
RUN apk add --no-cache musl-dev openssl-dev pkgconfig
|
||||||
|
|
||||||
|
# 配置 Cargo 镜像加速(使用 rsproxy.cn)
|
||||||
|
RUN mkdir -p /root/.cargo && \
|
||||||
|
echo '[source.crates-io]' > /root/.cargo/config.toml && \
|
||||||
|
echo 'replace-with = "rsproxy-sparse"' >> /root/.cargo/config.toml && \
|
||||||
|
echo '[source.rsproxy-sparse]' >> /root/.cargo/config.toml && \
|
||||||
|
echo 'registry = "sparse+https://rsproxy.cn/index/"' >> /root/.cargo/config.toml
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 先复制依赖文件,利用 Docker 层缓存
|
||||||
|
# 只要 Cargo.toml 和 Cargo.lock 不变,依赖层就会被缓存
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
|
||||||
|
# 创建虚拟 src 目录,预先下载和编译依赖
|
||||||
|
# 这样修改源码时不需要重新编译依赖
|
||||||
|
RUN mkdir src && \
|
||||||
|
echo "fn main() {}" > src/main.rs && \
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl && \
|
||||||
|
rm -rf src
|
||||||
|
|
||||||
|
# 复制实际源码
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# 构建 Release 版本(musl 静态链接)
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
# 安装运行时依赖
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从构建阶段复制二进制文件
|
||||||
|
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
|
||||||
|
|
||||||
|
# 环境变量
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 将 `your-binary-name` 替换为 `Cargo.toml` 中的 `[[bin]]` name
|
||||||
|
2. 如果不需要 OpenSSL,可移除 `openssl-dev`
|
||||||
|
3. 修改端口和健康检查配置
|
||||||
|
|
||||||
|
### 3. 依赖缓存优化版(Dockerfile.cache)
|
||||||
|
|
||||||
|
**适用场景**:本地开发,频繁构建。
|
||||||
|
|
||||||
|
**优点**:
|
||||||
|
- 最大化利用缓存
|
||||||
|
- 依赖只编译一次
|
||||||
|
- 适合迭代开发
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 依赖缓存阶段
|
||||||
|
# ============================================
|
||||||
|
FROM rust:1.83-alpine AS dependencies
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev openssl-dev pkgconfig
|
||||||
|
|
||||||
|
# 配置 Cargo 镜像
|
||||||
|
RUN mkdir -p /root/.cargo && \
|
||||||
|
echo '[source.crates-io]' > /root/.cargo/config.toml && \
|
||||||
|
echo 'replace-with = "rsproxy-sparse"' >> /root/.cargo/config.toml && \
|
||||||
|
echo '[source.rsproxy-sparse]' >> /root/.cargo/config.toml && \
|
||||||
|
echo 'registry = "sparse+https://rsproxy.cn/index/"' >> /root/.cargo/config.toml
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 只复制依赖文件
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
|
||||||
|
# 创建虚拟 main.rs,编译依赖
|
||||||
|
RUN mkdir -p src && \
|
||||||
|
echo "fn main() {}" > src/main.rs && \
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# 删除虚拟源码编译产物
|
||||||
|
RUN rm -f target/x86_64-unknown-linux-musl/release/deps/your_binary_name*
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段
|
||||||
|
# ============================================
|
||||||
|
FROM rust:1.83-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache musl-dev openssl-dev pkgconfig
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
# 从依赖阶段复制编译缓存
|
||||||
|
COPY --from=dependencies /root/.cargo /root/.cargo
|
||||||
|
COPY --from=dependencies /build/target target
|
||||||
|
|
||||||
|
# 复制所有文件
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 构建项目
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
1. 将 `your_binary_name` 替换为二进制文件名(下划线形式)
|
||||||
|
2. 适合本地 `docker build` 使用
|
||||||
|
|
||||||
|
### 4. 多平台构建版(Dockerfile.multiarch)
|
||||||
|
|
||||||
|
**适用场景**:需要支持 ARM64 和 AMD64。
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# ============================================
|
||||||
|
# 构建阶段(支持多平台)
|
||||||
|
# ============================================
|
||||||
|
FROM --platform=$BUILDPLATFORM rust:1.83-alpine AS builder
|
||||||
|
|
||||||
|
# 安装构建依赖
|
||||||
|
RUN apk add --no-cache musl-dev openssl-dev pkgconfig clang
|
||||||
|
|
||||||
|
# 配置 Cargo 镜像
|
||||||
|
RUN mkdir -p /root/.cargo && \
|
||||||
|
echo '[source.crates-io]' > /root/.cargo/config.toml && \
|
||||||
|
echo 'replace-with = "rsproxy-sparse"' >> /root/.cargo/config.toml && \
|
||||||
|
echo '[source.rsproxy-sparse]' >> /root/.cargo/config.toml && \
|
||||||
|
echo 'registry = "sparse+https://rsproxy.cn/index/"' >> /root/.cargo/config.toml
|
||||||
|
|
||||||
|
# 安装目标架构支持
|
||||||
|
ARG TARGETARCH
|
||||||
|
RUN case "$TARGETARCH" in \
|
||||||
|
"amd64") echo "x86_64-unknown-linux-musl" > /target.txt ;; \
|
||||||
|
"arm64") echo "aarch64-unknown-linux-musl" > /target.txt ;; \
|
||||||
|
*) echo "Unsupported architecture: $TARGETARCH" && exit 1 ;; \
|
||||||
|
esac && \
|
||||||
|
rustup target add $(cat /target.txt)
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY Cargo.toml Cargo.lock ./
|
||||||
|
COPY src ./src
|
||||||
|
|
||||||
|
# 构建对应架构
|
||||||
|
RUN cargo build --release --target $(cat /target.txt)
|
||||||
|
|
||||||
|
# 复制二进制到统一路径
|
||||||
|
RUN mkdir -p /app && \
|
||||||
|
cp target/$(cat /target.txt)/release/your-binary-name /app/
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# 运行阶段
|
||||||
|
# ============================================
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/your-binary-name .
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**使用说明**:
|
||||||
|
```bash
|
||||||
|
# 构建多平台镜像
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64 -t your-image:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
## 高级配置
|
||||||
|
|
||||||
|
### 健康检查配置
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 存活探针(Liveness Probe)
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
# 参数说明:
|
||||||
|
# --interval=30s 每 30 秒检查一次
|
||||||
|
# --timeout=5s 单次检查超时 5 秒
|
||||||
|
# --start-period=10s 容器启动后 10 秒开始检查
|
||||||
|
# --retries=3 失败 3 次后认为不健康
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多端点健康检查
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 使用脚本检查多个端点
|
||||||
|
RUN echo '#!/bin/sh' > /healthcheck.sh && \
|
||||||
|
echo 'curl -sf http://localhost:9090/healthz || exit 1' >> /healthcheck.sh && \
|
||||||
|
echo 'curl -sf http://localhost:9090/readyz || exit 1' >> /healthcheck.sh && \
|
||||||
|
chmod +x /healthcheck.sh
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD ["/healthcheck.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 环境变量配置
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 基础环境变量
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
ENV RUST_BACKTRACE=1
|
||||||
|
|
||||||
|
# 应用配置(可在 docker run 时覆盖)
|
||||||
|
ENV CONFIG_FILE=production.yaml
|
||||||
|
ENV HTTP_PORT=9090
|
||||||
|
ENV LOG_LEVEL=info
|
||||||
|
|
||||||
|
# 数据库配置(敏感信息应通过运行时注入)
|
||||||
|
# ENV DATABASE_URL=postgresql://...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 非 root 用户运行
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 创建非特权用户
|
||||||
|
RUN addgroup -g 1000 appuser && \
|
||||||
|
adduser -D -u 1000 -G appuser appuser
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制二进制文件
|
||||||
|
COPY --from=builder /build/target/x86_64-unknown-linux-musl/release/your-binary-name .
|
||||||
|
|
||||||
|
# 修改所有权
|
||||||
|
RUN chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
# 切换用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 配置文件挂载
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 在 Dockerfile 中创建配置目录
|
||||||
|
RUN mkdir -p /app/configs /app/logs
|
||||||
|
|
||||||
|
# 在 docker run 时挂载
|
||||||
|
# docker run -v ./configs:/app/configs -v ./logs:/app/logs your-image
|
||||||
|
```
|
||||||
|
|
||||||
|
## 构建命令示例
|
||||||
|
|
||||||
|
### 本地构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 基础构建
|
||||||
|
docker build -t your-service:latest .
|
||||||
|
|
||||||
|
# 指定 Dockerfile
|
||||||
|
docker build -f Dockerfile.ci -t your-service:latest .
|
||||||
|
|
||||||
|
# 传递构建参数
|
||||||
|
docker build --build-arg GIT_TAG=1.0.0 -t your-service:1.0.0 .
|
||||||
|
|
||||||
|
# 查看构建过程
|
||||||
|
docker build --progress=plain -t your-service:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 多平台构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建 buildx builder
|
||||||
|
docker buildx create --name mybuilder --use
|
||||||
|
|
||||||
|
# 构建并推送
|
||||||
|
docker buildx build \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
-t registry.example.com/your-service:latest \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 优化构建速度
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 使用 BuildKit 缓存
|
||||||
|
export DOCKER_BUILDKIT=1
|
||||||
|
|
||||||
|
# 使用外部缓存
|
||||||
|
docker build \
|
||||||
|
--cache-from=type=registry,ref=your-service:buildcache \
|
||||||
|
--cache-to=type=registry,ref=your-service:buildcache,mode=max \
|
||||||
|
-t your-service:latest \
|
||||||
|
.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 镜像大小优化
|
||||||
|
|
||||||
|
### 1. 使用 Alpine 基础镜像
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Alpine 镜像体积小(~5MB)
|
||||||
|
FROM alpine:3.21
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 静态链接(musl)
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 构建时使用 musl 目标,生成静态链接二进制
|
||||||
|
RUN cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Strip 二进制文件
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Cargo.toml
|
||||||
|
[profile.release]
|
||||||
|
strip = true # 去除符号表
|
||||||
|
```
|
||||||
|
|
||||||
|
或在 Dockerfile 中:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 手动 strip
|
||||||
|
RUN strip target/x86_64-unknown-linux-musl/release/your-binary-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 最小化层数
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 合并多个 RUN 命令
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||||
|
addgroup -g 1000 appuser
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 使用 .dockerignore
|
||||||
|
|
||||||
|
```
|
||||||
|
# .dockerignore
|
||||||
|
target/
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
tests/
|
||||||
|
benches/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 安全建议
|
||||||
|
|
||||||
|
### 1. 使用特定版本标签
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 不推荐:使用 latest
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# 推荐:使用特定版本
|
||||||
|
FROM alpine:3.21
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 定期更新依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 更新 Cargo 依赖
|
||||||
|
cargo update
|
||||||
|
|
||||||
|
# 检查安全漏洞
|
||||||
|
cargo audit
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 最小权限原则
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 使用非 root 用户
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
# 只读文件系统(根据需要)
|
||||||
|
# docker run --read-only your-service
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 不在镜像中包含敏感信息
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# 错误示例
|
||||||
|
# ENV DATABASE_PASSWORD=secret123
|
||||||
|
|
||||||
|
# 正确做法:通过运行时注入
|
||||||
|
# docker run -e DATABASE_PASSWORD=secret123 your-service
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 镜像体积过大
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
- 使用 `rust:latest` 作为运行镜像(>1GB)
|
||||||
|
- 没有使用多阶段构建
|
||||||
|
- 包含 debug 符号
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```dockerfile
|
||||||
|
# 使用多阶段构建 + Alpine
|
||||||
|
FROM rust:1.83-alpine AS builder
|
||||||
|
# ... 构建步骤 ...
|
||||||
|
|
||||||
|
FROM alpine:3.21 # 最终镜像只有 ~20MB
|
||||||
|
COPY --from=builder /build/target/.../your-binary .
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q2: openssl 链接错误
|
||||||
|
|
||||||
|
**错误信息**:
|
||||||
|
```
|
||||||
|
error: linking with `cc` failed
|
||||||
|
ld: library not found for -lssl
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```dockerfile
|
||||||
|
# 方案 1:安装 openssl-dev
|
||||||
|
RUN apk add --no-cache openssl-dev
|
||||||
|
|
||||||
|
# 方案 2:使用 rustls 替代 openssl
|
||||||
|
# 在 Cargo.toml 中使用 rustls feature
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q3: 交叉编译失败
|
||||||
|
|
||||||
|
**错误信息**:
|
||||||
|
```
|
||||||
|
error: linker `aarch64-linux-musl-gcc` not found
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决方案**:
|
||||||
|
```bash
|
||||||
|
# 安装 musl 交叉编译工具链
|
||||||
|
rustup target add aarch64-unknown-linux-musl
|
||||||
|
|
||||||
|
# 或使用 cargo-zigbuild
|
||||||
|
cargo install cargo-zigbuild
|
||||||
|
cargo zigbuild --target aarch64-unknown-linux-musl
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 容器启动失败
|
||||||
|
|
||||||
|
**检查步骤**:
|
||||||
|
```bash
|
||||||
|
# 查看日志
|
||||||
|
docker logs your-container
|
||||||
|
|
||||||
|
# 进入容器调试
|
||||||
|
docker run -it --entrypoint /bin/sh your-image
|
||||||
|
|
||||||
|
# 检查二进制是否可执行
|
||||||
|
docker run your-image ls -lh /app/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 参考资源
|
||||||
|
|
||||||
|
- [Rust 官方 Docker 指南](https://docs.docker.com/language/rust/)
|
||||||
|
- [Alpine Linux 包搜索](https://pkgs.alpinelinux.org/packages)
|
||||||
|
- [Docker 多阶段构建文档](https://docs.docker.com/build/building/multi-stage/)
|
||||||
|
- [cargo-zigbuild](https://github.com/rust-cross/cargo-zigbuild)
|
||||||
@@ -11,6 +11,34 @@
|
|||||||
- 管理 Secrets 和 Variables
|
- 管理 Secrets 和 Variables
|
||||||
- 设置 Webhooks
|
- 设置 Webhooks
|
||||||
|
|
||||||
|
## 快速使用
|
||||||
|
|
||||||
|
### 核心原则(简洁高效)
|
||||||
|
1. **智能解析**:自动识别 `组织/仓库` 格式,优先使用指定组织而非默认组织
|
||||||
|
2. **默认私有**:除非明确指定,所有仓库默认创建为私有
|
||||||
|
3. **简化验证**:默认假设组织存在,API创建失败时清晰提示解决方案
|
||||||
|
4. **Git集成**:自动检查Git仓库状态,提供一键初始化、提交、推送完整流程
|
||||||
|
5. **错误处理**:API失败时给出具体操作建议,而非预先复杂验证
|
||||||
|
|
||||||
|
### 常用命令(简洁高效)
|
||||||
|
```bash
|
||||||
|
# 智能解析组织/仓库格式(默认私有)
|
||||||
|
/gitea-create-repo shigongcao/shigongcao
|
||||||
|
|
||||||
|
# 使用默认组织创建公开仓库
|
||||||
|
/gitea-create-repo my-project public
|
||||||
|
|
||||||
|
# 自动检查Git状态,提供完整初始化流程
|
||||||
|
/gitea-create-repo org/project
|
||||||
|
|
||||||
|
# 特性说明:
|
||||||
|
# 1. 自动识别组织/仓库格式
|
||||||
|
# 2. 默认创建私有仓库(除非指定public)
|
||||||
|
# 3. 自动检查当前目录Git状态
|
||||||
|
# 4. 提供一键初始化、提交、推送选项
|
||||||
|
# 5. API失败时给出清晰解决方案(如组织不存在)
|
||||||
|
```
|
||||||
|
|
||||||
## 创建仓库
|
## 创建仓库
|
||||||
|
|
||||||
### 使用命令创建
|
### 使用命令创建
|
||||||
@@ -65,24 +93,28 @@ fi
|
|||||||
source "$config_file"
|
source "$config_file"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 步骤 2: 解析输入
|
#### 步骤 2: 智能解析输入(简洁高效版)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
input="$1"
|
input="$1"
|
||||||
|
|
||||||
# 解析 owner/repo
|
# 智能解析:优先使用用户指定的组织
|
||||||
if [[ "$input" =~ / ]]; then
|
if [[ "$input" =~ / ]]; then
|
||||||
owner=$(echo "$input" | cut -d'/' -f1)
|
owner=$(echo "$input" | cut -d'/' -f1)
|
||||||
repo=$(echo "$input" | cut -d'/' -f2)
|
repo=$(echo "$input" | cut -d'/' -f2)
|
||||||
|
echo "使用指定组织: $owner"
|
||||||
|
echo "提示:假设组织存在,如不存在会在API创建时提示"
|
||||||
else
|
else
|
||||||
# 使用默认组织或当前用户
|
# 未指定组织,使用默认组织或当前用户
|
||||||
if [ -z "$GITEA_DEFAULT_ORG" ]; then
|
if [ -z "$GITEA_DEFAULT_ORG" ]; then
|
||||||
# 获取当前用户
|
# 获取当前用户
|
||||||
|
echo "未指定组织,获取当前用户..."
|
||||||
owner=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
owner=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
"${GITEA_URL}/api/v1/user" | jq -r '.login')
|
"${GITEA_URL}/api/v1/user" | jq -r '.login')
|
||||||
|
|
||||||
if [ -z "$owner" ] || [ "$owner" = "null" ]; then
|
if [ -z "$owner" ] || [ "$owner" = "null" ]; then
|
||||||
echo "❌ 无法获取当前用户信息,请使用 owner/repo 格式"
|
echo "❌ 无法获取当前用户信息,请使用 组织/仓库 格式"
|
||||||
|
echo "例如:/gitea-create-repo shigongcao/shigongcao"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -94,9 +126,21 @@ else
|
|||||||
repo="$input"
|
repo="$input"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 解析可见性
|
# 解析可见性:默认私有仓库(除非明确指定公开)
|
||||||
visibility="${2:-private}"
|
visibility="${2:-private}"
|
||||||
|
if [[ "$visibility" != "private" && "$visibility" != "public" ]]; then
|
||||||
|
echo "⚠️ 可见性参数无效,使用默认值: private"
|
||||||
|
visibility="private"
|
||||||
|
fi
|
||||||
|
|
||||||
private_bool=$([ "$visibility" = "private" ] && echo "true" || echo "false")
|
private_bool=$([ "$visibility" = "private" ] && echo "true" || echo "false")
|
||||||
|
echo "仓库可见性: $visibility"
|
||||||
|
|
||||||
|
# 提前检查当前目录是否是 Git 仓库(为后续步骤做准备)
|
||||||
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
echo "⚠️ 当前目录不是 Git 仓库"
|
||||||
|
echo "提示:创建远程仓库后可以初始化本地 Git 仓库并推送代码"
|
||||||
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 步骤 3: 验证仓库名
|
#### 步骤 3: 验证仓库名
|
||||||
@@ -109,12 +153,29 @@ if ! [[ "$repo" =~ ^[a-zA-Z0-9_.-]+$ ]]; then
|
|||||||
fi
|
fi
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 步骤 4: 调用 API 创建
|
#### 步骤 4: 调用 API 创建(带详细错误处理)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
echo "正在创建仓库: $owner/$repo ($visibility)"
|
echo "正在创建仓库: $owner/$repo ($visibility)"
|
||||||
|
|
||||||
# 尝试组织仓库
|
# 检查 Token 权限
|
||||||
|
echo "检查 Token 权限..."
|
||||||
|
user_info=$(curl -s -H "Authorization: token $GITEA_TOKEN" "${GITEA_URL}/api/v1/user")
|
||||||
|
username=$(echo "$user_info" | jq -r '.login // empty')
|
||||||
|
|
||||||
|
if [ -z "$username" ] || [ "$username" = "null" ]; then
|
||||||
|
echo "❌ Token 无效或权限不足"
|
||||||
|
echo "请检查:"
|
||||||
|
echo "1. Token 是否有效"
|
||||||
|
echo "2. Token 是否有 'repo' 权限"
|
||||||
|
echo "3. GITEA_URL 是否正确"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "✓ Token 有效,当前用户: $username"
|
||||||
|
|
||||||
|
# 尝试创建组织仓库
|
||||||
|
echo "调用 Gitea API 创建仓库..."
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
@@ -122,42 +183,87 @@ response=$(curl -s -w "\n%{http_code}" -X POST \
|
|||||||
\"name\": \"${repo}\",
|
\"name\": \"${repo}\",
|
||||||
\"private\": ${private_bool},
|
\"private\": ${private_bool},
|
||||||
\"auto_init\": false,
|
\"auto_init\": false,
|
||||||
\"default_branch\": \"main\"
|
\"default_branch\": \"main\",
|
||||||
|
\"description\": \"\"
|
||||||
}" \
|
}" \
|
||||||
"${GITEA_URL}/api/v1/orgs/${owner}/repos")
|
"${GITEA_URL}/api/v1/orgs/${owner}/repos")
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
http_code=$(echo "$response" | tail -n1)
|
||||||
body=$(echo "$response" | sed '$d')
|
body=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
# 如果 404,可能是用户而非组织
|
|
||||||
if [ "$http_code" = "404" ]; then
|
|
||||||
echo "⚠️ 组织不存在,尝试创建用户仓库..."
|
|
||||||
|
|
||||||
response=$(curl -s -w "\n%{http_code}" -X POST \
|
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{
|
|
||||||
\"name\": \"${repo}\",
|
|
||||||
\"private\": ${private_bool}
|
|
||||||
}" \
|
|
||||||
"${GITEA_URL}/api/v1/user/repos")
|
|
||||||
|
|
||||||
http_code=$(echo "$response" | tail -n1)
|
|
||||||
body=$(echo "$response" | sed '$d')
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 处理响应
|
# 处理响应
|
||||||
case "$http_code" in
|
case "$http_code" in
|
||||||
201)
|
201)
|
||||||
echo "✓ 仓库创建成功"
|
echo "✓ 仓库创建成功"
|
||||||
;;
|
;;
|
||||||
|
400)
|
||||||
|
error_msg=$(echo "$body" | jq -r '.message // "未知错误"')
|
||||||
|
echo "❌ 请求参数错误: $error_msg"
|
||||||
|
echo "请检查:"
|
||||||
|
echo "1. 仓库名格式是否正确"
|
||||||
|
echo "2. 是否缺少必要参数"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
403)
|
||||||
|
echo "❌ 权限不足"
|
||||||
|
echo "请检查:"
|
||||||
|
echo "1. 是否有在组织 '$owner' 下创建仓库的权限"
|
||||||
|
echo "2. 是否是组织成员"
|
||||||
|
echo "3. Token 权限是否足够"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
404)
|
||||||
|
# 组织不存在(API 返回 404)
|
||||||
|
echo "❌ API 创建失败:组织 '$owner' 不存在"
|
||||||
|
|
||||||
|
# 检查是否为当前用户(可能用户输入的是自己的用户名)
|
||||||
|
if [ "$owner" = "$username" ]; then
|
||||||
|
echo "检测到 '$owner' 是当前用户,创建个人仓库..."
|
||||||
|
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"name\": \"${repo}\",
|
||||||
|
\"private\": ${private_bool},
|
||||||
|
\"auto_init\": false,
|
||||||
|
\"default_branch\": \"main\"
|
||||||
|
}" \
|
||||||
|
"${GITEA_URL}/api/v1/user/repos")
|
||||||
|
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
|
if [ "$http_code" = "201" ]; then
|
||||||
|
echo "✓ 个人仓库创建成功"
|
||||||
|
else
|
||||||
|
error_msg=$(echo "$body" | jq -r '.message // "未知错误"')
|
||||||
|
echo "❌ 个人仓库创建失败 (HTTP $http_code): $error_msg"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "组织不存在,请先创建组织"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
echo "您可以通过以下方式创建组织 '$owner':"
|
||||||
|
echo "1. 访问 ${GITEA_URL}/org/create"
|
||||||
|
echo "2. 使用 Gitea Web 界面创建组织"
|
||||||
|
echo "3. 或者使用个人仓库格式: $username/$repo"
|
||||||
|
echo ""
|
||||||
|
echo "创建组织后,重新运行此命令创建仓库。"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
409)
|
409)
|
||||||
echo "❌ 仓库已存在"
|
echo "❌ 仓库已存在: $owner/$repo"
|
||||||
|
echo "请使用不同的仓库名或删除现有仓库"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "❌ 创建失败 (HTTP $http_code)"
|
error_msg=$(echo "$body" | jq -r '.message // "未知错误"')
|
||||||
echo "$body" | jq -r '.message // empty'
|
echo "❌ 创建失败 (HTTP $http_code): $error_msg"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -183,45 +289,104 @@ echo " SSH URL: $ssh_url"
|
|||||||
echo ""
|
echo ""
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 步骤 6: 添加 Git Remote
|
#### 步骤 6: Git 仓库集成(简洁高效版)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
read -p "是否将此仓库添加为 Git remote? [Y/n] " add_remote
|
echo ""
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo "Git 仓库集成"
|
||||||
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||||
|
echo ""
|
||||||
|
|
||||||
if [[ ! "$add_remote" =~ ^[Nn]$ ]]; then
|
# 检查是否是 Git 仓库
|
||||||
# 检查是否是 Git 仓库
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
echo "当前目录不是 Git 仓库"
|
||||||
echo "当前目录不是 Git 仓库"
|
read -p "是否初始化 Git 仓库并添加 remote? [Y/n] " init_git
|
||||||
read -p "是否初始化 Git 仓库? [Y/n] " init_git
|
|
||||||
|
if [[ "$init_git" =~ ^[Nn]$ ]]; then
|
||||||
if [[ ! "$init_git" =~ ^[Nn]$ ]]; then
|
echo "跳过 Git 初始化,仅创建远程仓库。"
|
||||||
git init
|
echo "您可以在需要时手动执行:"
|
||||||
echo "✓ Git 仓库已初始化"
|
echo " git init"
|
||||||
else
|
echo " git remote add origin \"$clone_url\""
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 初始化 Git 仓库
|
||||||
|
echo "正在初始化 Git 仓库..."
|
||||||
|
git init
|
||||||
|
echo "✓ Git 仓库已初始化"
|
||||||
|
|
||||||
# 检查 origin 是否已存在
|
# 检查 origin 是否已存在
|
||||||
if git remote get-url origin >/dev/null 2>&1; then
|
if git remote get-url origin >/dev/null 2>&1; then
|
||||||
existing_url=$(git remote get-url origin)
|
existing_url=$(git remote get-url origin)
|
||||||
echo "⚠️ origin remote 已存在: $existing_url"
|
echo "⚠️ origin remote 已存在: $existing_url"
|
||||||
read -p "是否覆盖? [y/N] " overwrite
|
read -p "是否覆盖为新的仓库? [y/N] " overwrite
|
||||||
|
|
||||||
if [[ "$overwrite" =~ ^[Yy]$ ]]; then
|
if [[ "$overwrite" =~ ^[Yy]$ ]]; then
|
||||||
git remote set-url origin "$clone_url"
|
git remote set-url origin "$clone_url"
|
||||||
echo "✓ origin remote 已更新"
|
echo "✓ origin remote 已更新为: $clone_url"
|
||||||
|
else
|
||||||
|
echo "保持现有的 origin remote"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
git remote add origin "$clone_url"
|
git remote add origin "$clone_url"
|
||||||
echo "✓ origin remote 已添加"
|
echo "✓ origin remote 已添加: $clone_url"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 显示 remote 信息
|
# 可选:添加文件、提交并推送
|
||||||
echo ""
|
echo ""
|
||||||
echo "当前 remote:"
|
read -p "是否添加当前文件、提交并推送到远程仓库? [Y/n] " push_code
|
||||||
git remote -v
|
|
||||||
|
if [[ ! "$push_code" =~ ^[Nn]$ ]]; then
|
||||||
|
echo "添加所有文件到暂存区..."
|
||||||
|
git add .
|
||||||
|
|
||||||
|
echo "创建初始提交..."
|
||||||
|
git commit -m "Initial commit" || {
|
||||||
|
echo "⚠️ 提交失败(可能没有文件可提交)"
|
||||||
|
echo "请手动添加文件后提交"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "推送到远程仓库 (main 分支)..."
|
||||||
|
git branch -M main 2>/dev/null
|
||||||
|
git push -u origin main
|
||||||
|
echo "✓ 代码已推送到远程仓库"
|
||||||
|
else
|
||||||
|
echo "跳过推送,您可以在需要时手动推送代码。"
|
||||||
|
fi
|
||||||
|
|
||||||
|
else
|
||||||
|
# 已经是 Git 仓库,询问是否添加/更新 remote
|
||||||
|
echo "当前目录已是 Git 仓库"
|
||||||
|
read -p "是否添加/更新 origin remote 为此仓库? [Y/n] " add_remote
|
||||||
|
|
||||||
|
if [[ ! "$add_remote" =~ ^[Nn]$ ]]; then
|
||||||
|
# 检查 origin 是否已存在
|
||||||
|
if git remote get-url origin >/dev/null 2>&1; then
|
||||||
|
existing_url=$(git remote get-url origin)
|
||||||
|
echo "⚠️ origin remote 已存在: $existing_url"
|
||||||
|
read -p "是否覆盖? [y/N] " overwrite
|
||||||
|
|
||||||
|
if [[ "$overwrite" =~ ^[Yy]$ ]]; then
|
||||||
|
git remote set-url origin "$clone_url"
|
||||||
|
echo "✓ origin remote 已更新"
|
||||||
|
else
|
||||||
|
echo "保持现有的 origin remote"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
git remote add origin "$clone_url"
|
||||||
|
echo "✓ origin remote 已添加"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 显示当前状态
|
||||||
|
echo ""
|
||||||
|
echo "当前 Git 状态:"
|
||||||
|
git status --short 2>/dev/null || echo "(非 Git 仓库)"
|
||||||
|
echo ""
|
||||||
|
echo "Remote 配置:"
|
||||||
|
git remote -v 2>/dev/null || echo "(无 remote 配置)"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 仓库初始化
|
## 仓库初始化
|
||||||
@@ -689,5 +854,11 @@ curl -s -H "Authorization: token $GITEA_TOKEN" \
|
|||||||
|
|
||||||
## 版本
|
## 版本
|
||||||
|
|
||||||
- **文档版本**: 1.0
|
- **文档版本**: 1.1
|
||||||
- **最后更新**: 2026-01-12
|
- **最后更新**: 2026-01-23
|
||||||
|
- **主要改进**:
|
||||||
|
- 智能解析输入:优先使用指定组织而非默认组织
|
||||||
|
- 组织存在性验证和清晰错误提示
|
||||||
|
- 默认私有仓库策略(除非明确指定公开)
|
||||||
|
- 增强的错误处理:Token验证、权限检查、详细错误消息
|
||||||
|
- 完整的Git集成流程:可选自动初始化和推送
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ Gitea Act Runner 是 Gitea Actions 的 CI/CD 执行器,兼容 GitHub Actions w
|
|||||||
- **macOS ARM64**: 使用 **Host Mode** 以支持 Android SDK
|
- **macOS ARM64**: 使用 **Host Mode** 以支持 Android SDK
|
||||||
- **Windows**: 使用 **Host Mode**(需在 workflow 中指定 `shell: powershell`)
|
- **Windows**: 使用 **Host Mode**(需在 workflow 中指定 `shell: powershell`)
|
||||||
- **Linux**: 两种模式均可,Docker Mode 隔离性更好
|
- **Linux**: 两种模式均可,Docker Mode 隔离性更好
|
||||||
|
- **Docker Mode**: 必须使用 `catthehacker/ubuntu:act-*` 镜像(内置 Docker CLI)
|
||||||
|
|
||||||
**Windows Host Mode 注意事项**:
|
**Windows Host Mode 注意事项**:
|
||||||
- Bash 默认不可用,需在 workflow 中指定 shell:
|
- Bash 默认不可用,需在 workflow 中指定 shell:
|
||||||
@@ -577,9 +578,51 @@ labels:
|
|||||||
```yaml
|
```yaml
|
||||||
labels:
|
labels:
|
||||||
- "ubuntu-latest:docker://catthehacker/ubuntu:act-latest"
|
- "ubuntu-latest:docker://catthehacker/ubuntu:act-latest"
|
||||||
- "ubuntu-22.04:docker://catthehacker/ubuntu:act-latest"
|
- "ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04"
|
||||||
|
- "ubuntu-20.04:docker://catthehacker/ubuntu:act-20.04"
|
||||||
|
- "linux:docker://catthehacker/ubuntu:act-latest"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Docker Mode 镜像选择(重要)
|
||||||
|
|
||||||
|
**必须使用包含 Docker CLI 的镜像**,否则 `docker/login-action`、`docker/build-push-action` 等 actions 会失败。
|
||||||
|
|
||||||
|
| 镜像 | Docker CLI | 适用场景 |
|
||||||
|
|------|------------|---------|
|
||||||
|
| `catthehacker/ubuntu:act-latest` | ✅ 有 | **推荐**,CI/CD 专用镜像 |
|
||||||
|
| `catthehacker/ubuntu:act-22.04` | ✅ 有 | Ubuntu 22.04 兼容 |
|
||||||
|
| `node:16-bullseye` | ❌ 无 | **不推荐**,仅适合纯 Node.js 项目 |
|
||||||
|
| `golang:1.21` | ❌ 无 | **不推荐**,仅适合纯 Go 项目 |
|
||||||
|
|
||||||
|
**catthehacker/ubuntu:act-* 预装工具**:
|
||||||
|
- Docker CLI (`/usr/bin/docker`)
|
||||||
|
- Docker Buildx
|
||||||
|
- git, curl, wget, jq
|
||||||
|
- Node.js, Python
|
||||||
|
- 常用编译工具
|
||||||
|
|
||||||
|
**工作原理**:
|
||||||
|
```
|
||||||
|
workflow: runs-on: ubuntu-latest
|
||||||
|
↓
|
||||||
|
runner 查找 label: ubuntu-latest:docker://镜像名
|
||||||
|
↓
|
||||||
|
启动 job 容器(使用该镜像)
|
||||||
|
↓
|
||||||
|
在 job 容器内执行 steps
|
||||||
|
↓
|
||||||
|
docker/login-action 调用 `docker login` 命令
|
||||||
|
↓
|
||||||
|
需要 job 容器内有 docker CLI ← 关键!
|
||||||
|
```
|
||||||
|
|
||||||
|
**常见错误**:
|
||||||
|
```
|
||||||
|
Unable to locate executable file: docker. Please verify either the file path exists
|
||||||
|
or the file can be found within a directory specified by the PATH environment variable.
|
||||||
|
```
|
||||||
|
此错误表示 job 容器内缺少 docker 命令,需要更换为包含 Docker CLI 的镜像。
|
||||||
|
|
||||||
### Workflow 匹配
|
### Workflow 匹配
|
||||||
|
|
||||||
**方法 1: 组合 label(推荐,最精确)**
|
**方法 1: 组合 label(推荐,最精确)**
|
||||||
@@ -993,6 +1036,106 @@ nohup act_runner daemon --config ~/.config/gitea/runners/runner-macbook-pro/conf
|
|||||||
tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 8. 恢复被删除的 Runner(从服务器删除但本地文件仍在)
|
||||||
|
|
||||||
|
**场景**:Runner 在 Gitea 服务器上被删除,但本地目录和配置文件仍在。需要重新注册并上线。
|
||||||
|
|
||||||
|
**恢复步骤**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 进入 runner 目录
|
||||||
|
cd ~/.config/gitea/runners/runner-Mac-mini4-host
|
||||||
|
|
||||||
|
# 2. 停止旧进程(如果仍在运行)
|
||||||
|
if [ -f pid ]; then
|
||||||
|
kill $(cat pid) 2>/dev/null || true
|
||||||
|
rm -f pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. 加载 Gitea 配置
|
||||||
|
source ~/.config/gitea/config.env
|
||||||
|
|
||||||
|
# 4. 获取注册令牌(优先全局,失败则降级到组织)
|
||||||
|
echo "获取注册令牌..."
|
||||||
|
response=$(curl -s -w "\n%{http_code}" \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"${GITEA_URL}/api/v1/admin/runners/registration-token")
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
|
||||||
|
if [ "$http_code" != "200" ]; then
|
||||||
|
echo "全局令牌权限不足,尝试组织令牌..."
|
||||||
|
if [ -n "$GITEA_DEFAULT_ORG" ]; then
|
||||||
|
org_name="$GITEA_DEFAULT_ORG"
|
||||||
|
else
|
||||||
|
read -p "请输入组织名称: " org_input
|
||||||
|
org_name="$org_input"
|
||||||
|
fi
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"${GITEA_URL}/api/v1/orgs/$org_name/actions/runners/registration-token")
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | sed '$d')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$http_code" != "200" ]; then
|
||||||
|
echo "❌ 获取注册令牌失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
registration_token=$(echo "$body" | jq -r '.token')
|
||||||
|
|
||||||
|
# 5. 生成标签(基于系统环境)
|
||||||
|
OS=$(uname -s)
|
||||||
|
case "$OS" in
|
||||||
|
Darwin) os_label="macOS" ;;
|
||||||
|
Linux) os_label="ubuntu" ;;
|
||||||
|
*) os_label="unknown" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
arm64|aarch64) arch_label="ARM64" ;;
|
||||||
|
x86_64) arch_label="x64" ;;
|
||||||
|
*) arch_label="unknown" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
combined=$(echo "${OS}-${ARCH}" | tr '[:upper:]' '[:lower:]')
|
||||||
|
labels="self-hosted:host,${os_label}:host,${arch_label}:host,${combined}:host"
|
||||||
|
|
||||||
|
# 6. 重新注册 runner
|
||||||
|
act_runner register \
|
||||||
|
--config config.yaml \
|
||||||
|
--instance "$GITEA_URL" \
|
||||||
|
--token "$registration_token" \
|
||||||
|
--name "runner-Mac-mini4-host" \
|
||||||
|
--labels "$labels" \
|
||||||
|
--no-interactive
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "❌ 注册失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 7. 启动 runner daemon
|
||||||
|
nohup act_runner daemon --config config.yaml > runner.log 2>&1 &
|
||||||
|
echo $! > pid
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
if ps -p $(cat pid) > /dev/null 2>&1; then
|
||||||
|
echo "✅ Runner 恢复成功 (PID: $(cat pid))"
|
||||||
|
echo "日志: tail -f runner.log"
|
||||||
|
else
|
||||||
|
echo "❌ Runner 启动失败"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:
|
||||||
|
- 恢复后 runner 会获得新的 ID 和 token,但名称和 labels 保持不变
|
||||||
|
- 旧的 `.runner` 文件会被新的覆盖
|
||||||
|
- 确保 `config.yaml` 中的 labels 配置为空(`labels: []`),注册时会使用命令行参数
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
| 任务 | 命令 |
|
| 任务 | 命令 |
|
||||||
@@ -1001,6 +1144,7 @@ tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
|||||||
| 创建 runner | `/gitea-create-runner`(自动安装、配置、启动)|
|
| 创建 runner | `/gitea-create-runner`(自动安装、配置、启动)|
|
||||||
| 列出 runners | `/gitea-list-runners` |
|
| 列出 runners | `/gitea-list-runners` |
|
||||||
| 删除 runner | `/gitea-delete-runner` |
|
| 删除 runner | `/gitea-delete-runner` |
|
||||||
|
| 恢复 runner | 参考「恢复被删除的 Runner」章节 |
|
||||||
| 手动启动 | `nohup act_runner daemon --config <config.yaml> > <log> 2>&1 &` |
|
| 手动启动 | `nohup act_runner daemon --config <config.yaml> > <log> 2>&1 &` |
|
||||||
| 停止 runner | `pkill -f "act_runner daemon --config.*<name>"` |
|
| 停止 runner | `pkill -f "act_runner daemon --config.*<name>"` |
|
||||||
| 查看状态 | `ps aux \| grep act_runner` |
|
| 查看状态 | `ps aux \| grep act_runner` |
|
||||||
@@ -1016,6 +1160,9 @@ tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
|||||||
|
|
||||||
## 版本
|
## 版本
|
||||||
|
|
||||||
- **文档版本**: 1.0
|
- **文档版本**: 1.2
|
||||||
- **最后更新**: 2026-01-12
|
- **最后更新**: 2026-01-24
|
||||||
|
- **更新内容**:
|
||||||
|
- Docker Mode 镜像选择说明(必须使用包含 Docker CLI 的镜像)
|
||||||
|
- Runner 恢复流程
|
||||||
- **兼容性**: act_runner 0.2.13+, macOS ARM64, Linux
|
- **兼容性**: act_runner 0.2.13+, macOS ARM64, Linux
|
||||||
|
|||||||
443
skill/gitea/rust-quick-reference.md
Normal file
443
skill/gitea/rust-quick-reference.md
Normal file
@@ -0,0 +1,443 @@
|
|||||||
|
# Rust 项目 Gitea CI/CD 快速参考
|
||||||
|
|
||||||
|
快速设置 Rust 项目的 CI/CD 流程。
|
||||||
|
|
||||||
|
## 最小化配置(3 步)
|
||||||
|
|
||||||
|
### 1. 创建 Dockerfile.ci
|
||||||
|
|
||||||
|
在项目根目录创建 `Dockerfile.ci`:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
FROM alpine:3.21
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata curl && \
|
||||||
|
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY your-binary-name .
|
||||||
|
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
|
||||||
|
EXPOSE 9090
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD curl -sf http://localhost:9090/healthz || exit 1
|
||||||
|
|
||||||
|
CMD ["./your-binary-name"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改项**:
|
||||||
|
- `your-binary-name` → 你的二进制文件名(通常和 `Cargo.toml` 中的 `name` 一致)
|
||||||
|
- `9090` → 你的服务端口
|
||||||
|
|
||||||
|
### 2. 创建 workflow 文件
|
||||||
|
|
||||||
|
创建 `.gitea/workflows/your-service.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Your Service - Build & Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- '**' # 修改为实际监听路径
|
||||||
|
tags:
|
||||||
|
- 'v*' # 修改为实际 tag 格式
|
||||||
|
|
||||||
|
env:
|
||||||
|
SERVICE_PREFIX: your-service # 修改为实际服务名
|
||||||
|
CARGO_INCREMENTAL: 0
|
||||||
|
CARGO_NET_RETRY: 10
|
||||||
|
RUSTUP_DIST_SERVER: https://rsproxy.cn
|
||||||
|
RUSTUP_UPDATE_ROOT: https://rsproxy.cn/rustup
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-publish:
|
||||||
|
runs-on: ubuntu-latest # 修改为你的 Runner 标签
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://rsproxy.cn/rustup-init.sh | sh -s -- -y
|
||||||
|
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
|
||||||
|
source "$HOME/.cargo/env"
|
||||||
|
rustup target add x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
cp target/x86_64-unknown-linux-musl/release/your-binary-name .
|
||||||
|
|
||||||
|
- name: Docker Login
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ github.server_url }}
|
||||||
|
username: ${{ vars.REGISTRY_USERNAME }}
|
||||||
|
password: ${{ secrets.RELEASE_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build & Push Image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile.ci
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ github.server_url }}/${{ github.repository }}:latest
|
||||||
|
${{ github.server_url }}/${{ github.repository }}:${{ github.sha }}
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改项**:
|
||||||
|
- `your-service` → 你的服务名
|
||||||
|
- `your-binary-name` → 你的二进制文件名
|
||||||
|
- `ubuntu-latest` → 你的 Runner 标签
|
||||||
|
- `paths` → 触发构建的路径
|
||||||
|
- `tags` → 触发构建的 tag 格式
|
||||||
|
|
||||||
|
### 3. 推送代码并打 tag
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .gitea Dockerfile.ci
|
||||||
|
git commit -m "ci: 添加 CI/CD 配置"
|
||||||
|
git push
|
||||||
|
|
||||||
|
# 触发构建
|
||||||
|
git tag v0.1.0
|
||||||
|
git push origin v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 进阶配置
|
||||||
|
|
||||||
|
### 添加交叉编译支持
|
||||||
|
|
||||||
|
如果你的 Runner 是 ARM64,但需要构建 x86_64 镜像:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install Zig and cargo-zigbuild
|
||||||
|
run: |
|
||||||
|
wget -q https://ziglang.org/download/0.13.0/zig-linux-aarch64-0.13.0.tar.xz
|
||||||
|
tar -xf zig-linux-aarch64-0.13.0.tar.xz
|
||||||
|
mv zig-linux-aarch64-0.13.0 /opt/zig
|
||||||
|
echo "/opt/zig" >> $GITHUB_PATH
|
||||||
|
cargo install cargo-zigbuild
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
cargo zigbuild --release --target x86_64-unknown-linux-musl
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加 Rust 缓存
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Cache Rust dependencies
|
||||||
|
uses: https://github.com/Swatinem/rust-cache@v2
|
||||||
|
with:
|
||||||
|
workspaces: . -> target
|
||||||
|
cache-targets: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加版本号注入
|
||||||
|
|
||||||
|
**1. 创建 `build.rs`**:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn main() {
|
||||||
|
if let Ok(git_tag) = std::env::var("GIT_TAG") {
|
||||||
|
println!("cargo:rustc-env=GIT_TAG={}", git_tag);
|
||||||
|
}
|
||||||
|
println!("cargo:rerun-if-env-changed=GIT_TAG");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 在 workflow 中设置环境变量**:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GIT_TAG: ${{ github.ref_name }}
|
||||||
|
run: cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. 在代码中使用**:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let version = option_env!("GIT_TAG").unwrap_or("dev");
|
||||||
|
println!("Version: {}", version);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加 Release 创建
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
release:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build-and-publish
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
steps:
|
||||||
|
- name: Create Release
|
||||||
|
run: |
|
||||||
|
curl -X POST "${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases" \
|
||||||
|
-H "Authorization: token ${{ secrets.RELEASE_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{
|
||||||
|
\"tag_name\": \"${{ github.ref_name }}\",
|
||||||
|
\"name\": \"Release ${{ github.ref_name }}\",
|
||||||
|
\"body\": \"自动构建的 Release\"
|
||||||
|
}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 健康检查端点实现
|
||||||
|
|
||||||
|
如果你的服务还没有健康检查端点,快速添加:
|
||||||
|
|
||||||
|
### 使用 Axum
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use axum::{Router, routing::get};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let app = Router::new()
|
||||||
|
.route("/healthz", get(|| async { "OK" }));
|
||||||
|
|
||||||
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:9090")
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 Actix-web
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use actix_web::{web, App, HttpServer, HttpResponse};
|
||||||
|
|
||||||
|
#[actix_web::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
HttpServer::new(|| {
|
||||||
|
App::new()
|
||||||
|
.route("/healthz", web::get().to(|| async { HttpResponse::Ok().body("OK") }))
|
||||||
|
})
|
||||||
|
.bind("0.0.0.0:9090")?
|
||||||
|
.run()
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 Rocket
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
#[get("/healthz")]
|
||||||
|
fn healthz() -> &'static str {
|
||||||
|
"OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[launch]
|
||||||
|
fn rocket() -> _ {
|
||||||
|
rocket::build().mount("/", routes![healthz])
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cargo.toml 优化配置
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3 # 最高优化级别
|
||||||
|
lto = true # 链接时优化
|
||||||
|
codegen-units = 1 # 单个代码生成单元
|
||||||
|
strip = true # 去除符号表
|
||||||
|
panic = 'abort' # panic 时直接退出
|
||||||
|
```
|
||||||
|
|
||||||
|
## .dockerignore 示例
|
||||||
|
|
||||||
|
创建 `.dockerignore` 减小构建上下文:
|
||||||
|
|
||||||
|
```
|
||||||
|
target/
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
tests/
|
||||||
|
benches/
|
||||||
|
examples/
|
||||||
|
.github/
|
||||||
|
.gitea/workflows/
|
||||||
|
Dockerfile*
|
||||||
|
docker-compose.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## 环境变量配置
|
||||||
|
|
||||||
|
### 在 workflow 中
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
RUST_LOG: info # 日志级别
|
||||||
|
CONFIG_FILE: production.yaml # 配置文件
|
||||||
|
DATABASE_URL: ${{ secrets.DATABASE_URL }} # 数据库连接
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在 Dockerfile 中
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
ENV RUST_LOG=info
|
||||||
|
ENV CONFIG_FILE=production.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
### 在运行时
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -e RUST_LOG=debug -e CONFIG_FILE=custom.yaml your-image
|
||||||
|
```
|
||||||
|
|
||||||
|
## 常见 Runner 标签
|
||||||
|
|
||||||
|
| 标签 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `ubuntu-latest` | Ubuntu 最新版本 |
|
||||||
|
| `darwin-arm64` | macOS ARM64 |
|
||||||
|
| `darwin-amd64` | macOS x86_64 |
|
||||||
|
| `linux-amd64` | Linux x86_64 |
|
||||||
|
| `linux-arm64` | Linux ARM64 |
|
||||||
|
|
||||||
|
查看可用 Runner:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
/gitea-list-runners
|
||||||
|
```
|
||||||
|
|
||||||
|
## 触发条件参考
|
||||||
|
|
||||||
|
### 推送到特定分支
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- develop
|
||||||
|
```
|
||||||
|
|
||||||
|
### 推送特定路径
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'Cargo.toml'
|
||||||
|
- 'Cargo.lock'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 特定 tag 格式
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*' # v1.0.0, v2.1.3
|
||||||
|
- 'device-rs-*' # device-rs-1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pull Request
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
```
|
||||||
|
|
||||||
|
### 定时触发
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 2 * * *' # 每天凌晨 2 点
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试技巧
|
||||||
|
|
||||||
|
### 查看 workflow 日志
|
||||||
|
|
||||||
|
在 Gitea 仓库页面:
|
||||||
|
1. 点击 "Actions"
|
||||||
|
2. 选择对应的 workflow run
|
||||||
|
3. 查看每个 step 的日志
|
||||||
|
|
||||||
|
### 本地测试构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试 Rust 构建
|
||||||
|
cargo build --release --target x86_64-unknown-linux-musl
|
||||||
|
|
||||||
|
# 测试 Docker 构建
|
||||||
|
docker build -f Dockerfile.ci -t test:latest .
|
||||||
|
|
||||||
|
# 测试容器运行
|
||||||
|
docker run -p 9090:9090 test:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### 使用 act 本地运行 workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 安装 act(如果使用 Gitea Actions)
|
||||||
|
# 注意:act 主要支持 GitHub Actions,Gitea Actions 可能有兼容性问题
|
||||||
|
|
||||||
|
# 列出可用的 actions
|
||||||
|
act -l
|
||||||
|
|
||||||
|
# 运行特定 job
|
||||||
|
act -j build-and-publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整配置示例
|
||||||
|
|
||||||
|
参考 BMS 项目的 device-rs:
|
||||||
|
|
||||||
|
- Workflow: [device-rs.yml](https://git.voson.top/tianchu/bms/src/branch/main/.gitea/workflows/device-rs.yml)
|
||||||
|
- Dockerfile: [Dockerfile.ci](https://git.voson.top/tianchu/bms/src/branch/main/device-rs/Dockerfile.ci)
|
||||||
|
- 项目结构: [device-rs/](https://git.voson.top/tianchu/bms/src/branch/main/device-rs)
|
||||||
|
|
||||||
|
## 故障排查清单
|
||||||
|
|
||||||
|
### 构建失败
|
||||||
|
|
||||||
|
- [ ] 检查 Rust 版本是否满足要求
|
||||||
|
- [ ] 检查依赖是否正确(`Cargo.lock` 是否提交)
|
||||||
|
- [ ] 检查 musl 目标是否安装(`rustup target list --installed`)
|
||||||
|
- [ ] 查看完整构建日志
|
||||||
|
|
||||||
|
### Docker 推送失败
|
||||||
|
|
||||||
|
- [ ] 检查 Docker Registry 地址是否正确
|
||||||
|
- [ ] 检查 `REGISTRY_USERNAME` 变量是否设置
|
||||||
|
- [ ] 检查 `RELEASE_TOKEN` secret 是否正确
|
||||||
|
- [ ] 检查 Token 权限(需要 packages write 权限)
|
||||||
|
|
||||||
|
### 容器启动失败
|
||||||
|
|
||||||
|
- [ ] 检查二进制文件名是否正确
|
||||||
|
- [ ] 检查端口是否正确
|
||||||
|
- [ ] 检查健康检查端点是否存在
|
||||||
|
- [ ] 查看容器日志(`docker logs <container>`)
|
||||||
|
|
||||||
|
### Runner 无法连接
|
||||||
|
|
||||||
|
- [ ] 检查 Runner 是否在运行(`/gitea-list-runners`)
|
||||||
|
- [ ] 检查 Runner labels 是否匹配 workflow 中的 `runs-on`
|
||||||
|
- [ ] 检查网络连接
|
||||||
|
- [ ] 查看 Runner 日志(`~/.config/gitea/runners/<runner-name>/*.log`)
|
||||||
|
|
||||||
|
## 更多资源
|
||||||
|
|
||||||
|
- [完整 Workflow 模板](./workflow-templates/rust-backend.md)
|
||||||
|
- [Dockerfile 模板详解](./dockerfile-templates/rust-service.md)
|
||||||
|
- [Gitea Actions 文档](https://docs.gitea.com/usage/actions/quickstart)
|
||||||
|
- [Rust CI 最佳实践](https://doc.rust-lang.org/cargo/guide/continuous-integration.html)
|
||||||
254
skill/gitea/ssh-key-management.md
Normal file
254
skill/gitea/ssh-key-management.md
Normal file
@@ -0,0 +1,254 @@
|
|||||||
|
# SSH 密钥管理
|
||||||
|
|
||||||
|
完整的 SSH 密钥创建、部署和配置流程,支持跨设备使用。
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
### `/gitea-setup-ssh`
|
||||||
|
创建 SSH 密钥对,部署公钥到 Gitea 服务器,配置本地 SSH 客户端。
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
1. **生成 SSH 密钥对** (RSA 4096 位)
|
||||||
|
2. **部署公钥到 Gitea 服务器** (通过 API)
|
||||||
|
3. **配置本地 SSH 客户端**
|
||||||
|
4. **验证 SSH 连接**
|
||||||
|
|
||||||
|
## 详细流程
|
||||||
|
|
||||||
|
### 1. 生成 SSH 密钥对
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -t rsa -b 4096 -f ~/.ssh/gitea_server_key -N ""
|
||||||
|
```
|
||||||
|
|
||||||
|
**生成的密钥文件:**
|
||||||
|
- 私钥:`~/.ssh/gitea_server_key`
|
||||||
|
- 公钥:`~/.ssh/gitea_server_key.pub`
|
||||||
|
|
||||||
|
### 2. 部署公钥到 Gitea
|
||||||
|
|
||||||
|
通过 Gitea API 将公钥添加到用户账户:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s -X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d "{\"title\":\"server-key\",\"key\":\"$(cat ~/.ssh/gitea_server_key.pub)\"}" \
|
||||||
|
"$GITEA_URL/api/v1/user/keys"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置本地 SSH 客户端
|
||||||
|
|
||||||
|
在 `~/.ssh/config` 中添加配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat >> ~/.ssh/config << 'EOF'
|
||||||
|
|
||||||
|
Host git.refining.app
|
||||||
|
HostName git.refining.app
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/gitea_server_key
|
||||||
|
IdentitiesOnly yes
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 添加服务器主机密钥
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keyscan git.refining.app >> ~/.ssh/known_hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 验证连接
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -T git@git.refining.app
|
||||||
|
```
|
||||||
|
|
||||||
|
成功输出:
|
||||||
|
```
|
||||||
|
Hi there, username! You've successfully authenticated with the key named server-key...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 跨设备使用
|
||||||
|
|
||||||
|
### 复制密钥到新设备
|
||||||
|
|
||||||
|
1. **复制私钥文件**到新设备的 `~/.ssh/` 目录
|
||||||
|
```bash
|
||||||
|
# 在源设备上查看私钥内容
|
||||||
|
cat ~/.ssh/gitea_server_key
|
||||||
|
|
||||||
|
# 在新设备上创建文件
|
||||||
|
echo "-----BEGIN OPENSSH PRIVATE KEY-----" > ~/.ssh/gitea_server_key
|
||||||
|
echo "MIIEog..." >> ~/.ssh/gitea_server_key
|
||||||
|
echo "...cQ=" >> ~/.ssh/gitea_server_key
|
||||||
|
echo "-----END OPENSSH PRIVATE KEY-----" >> ~/.ssh/gitea_server_key
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **设置正确的权限**
|
||||||
|
```bash
|
||||||
|
chmod 600 ~/.ssh/gitea_server_key
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **配置 SSH 客户端**
|
||||||
|
```bash
|
||||||
|
# 在新设备上编辑 ~/.ssh/config
|
||||||
|
cat >> ~/.ssh/config << 'EOF'
|
||||||
|
|
||||||
|
Host git.refining.app
|
||||||
|
HostName git.refining.app
|
||||||
|
User git
|
||||||
|
IdentityFile ~/.ssh/gitea_server_key
|
||||||
|
IdentitiesOnly yes
|
||||||
|
EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **添加服务器主机密钥**
|
||||||
|
```bash
|
||||||
|
ssh-keyscan git.refining.app >> ~/.ssh/known_hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **测试连接**
|
||||||
|
```bash
|
||||||
|
ssh -T git@git.refining.app
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 密钥位置说明
|
||||||
|
|
||||||
|
### 源设备(当前已配置)
|
||||||
|
```
|
||||||
|
~/.ssh/
|
||||||
|
├── gitea_server_key # 私钥 (权限 600)
|
||||||
|
├── gitea_server_key.pub # 公钥 (已部署到 Gitea)
|
||||||
|
├── config # SSH 配置
|
||||||
|
└── known_hosts # 服务器主机密钥
|
||||||
|
```
|
||||||
|
|
||||||
|
### 新设备(需要配置)
|
||||||
|
需要从源设备复制:
|
||||||
|
1. `gitea_server_key` - **私钥**(核心文件)
|
||||||
|
2. `~/.ssh/config` - SSH 配置(相关部分)
|
||||||
|
3. `known_hosts` - 服务器主机密钥(可选,会自动获取)
|
||||||
|
|
||||||
|
## 安全注意事项
|
||||||
|
|
||||||
|
### ✅ 推荐做法
|
||||||
|
1. **私钥权限设置为 600**
|
||||||
|
```bash
|
||||||
|
chmod 600 ~/.ssh/gitea_server_key
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **不分享私钥**
|
||||||
|
- 私钥应保持在个人设备上
|
||||||
|
- 仅在有信任关系的设备间复制
|
||||||
|
|
||||||
|
3. **使用强密码保护**
|
||||||
|
- 生成密钥时可添加密码:`ssh-keygen -t rsa -b 4096 -f ~/.ssh/gitea_server_key`
|
||||||
|
- 每次使用时需要输入密码
|
||||||
|
|
||||||
|
### ❌ 禁止行为
|
||||||
|
1. 不要将私钥提交到 Git 仓库
|
||||||
|
2. 不要通过不安全渠道传输私钥
|
||||||
|
3. 不要设置过于宽松的权限(如 644、777)
|
||||||
|
|
||||||
|
## 故障排除
|
||||||
|
|
||||||
|
### 1. 权限错误
|
||||||
|
```
|
||||||
|
Permissions 0644 for '/Users/username/.ssh/gitea_server_key' are too open.
|
||||||
|
```
|
||||||
|
**解决方案**:
|
||||||
|
```bash
|
||||||
|
chmod 600 ~/.ssh/gitea_server_key
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 主机密钥验证失败
|
||||||
|
```
|
||||||
|
Host key verification failed.
|
||||||
|
```
|
||||||
|
**解决方案**:
|
||||||
|
```bash
|
||||||
|
ssh-keyscan git.refining.app >> ~/.ssh/known_hosts
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 认证失败
|
||||||
|
```
|
||||||
|
git@git.refining.app: Permission denied (publickey).
|
||||||
|
```
|
||||||
|
**检查步骤**:
|
||||||
|
1. 确认私钥文件存在且权限正确
|
||||||
|
2. 确认 SSH 配置正确
|
||||||
|
3. 确认公钥已添加到 Gitea
|
||||||
|
```bash
|
||||||
|
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"$GITEA_URL/api/v1/user/keys" | jq -r '.[].title'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Git 使用示例
|
||||||
|
|
||||||
|
### 克隆仓库
|
||||||
|
```bash
|
||||||
|
git clone git@git.refining.app:username/repository.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 更新现有仓库的 remote URL
|
||||||
|
```bash
|
||||||
|
git remote set-url origin git@git.refining.app:username/repository.git
|
||||||
|
```
|
||||||
|
|
||||||
|
### 查看当前 remote URL
|
||||||
|
```bash
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 撤销密钥访问
|
||||||
|
|
||||||
|
如果密钥泄露或设备丢失:
|
||||||
|
|
||||||
|
1. **登录 Gitea Web 界面**
|
||||||
|
2. 进入 **Settings → SSH/GPG Keys**
|
||||||
|
3. 找到对应的密钥并删除
|
||||||
|
|
||||||
|
2. **通过 API 删除**
|
||||||
|
```bash
|
||||||
|
# 首先获取密钥 ID
|
||||||
|
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"$GITEA_URL/api/v1/user/keys" | jq '.[] | select(.title=="server-key").id'
|
||||||
|
|
||||||
|
# 删除密钥(将 {id} 替换为实际 ID)
|
||||||
|
curl -s -X DELETE -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"$GITEA_URL/api/v1/user/keys/{id}"
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **在受影响的设备上删除私钥**
|
||||||
|
```bash
|
||||||
|
rm ~/.ssh/gitea_server_key
|
||||||
|
rm ~/.ssh/gitea_server_key.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
## 相关命令
|
||||||
|
|
||||||
|
### 查看 Gitea 上的所有 SSH 密钥
|
||||||
|
```bash
|
||||||
|
curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||||
|
"$GITEA_URL/api/v1/user/keys" | jq -r '.[] | "\(.id): \(.title) (\(.fingerprint))"'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 测试 SSH 连接(详细模式)
|
||||||
|
```bash
|
||||||
|
ssh -T -v git@git.refining.app
|
||||||
|
```
|
||||||
|
|
||||||
|
### 检查 SSH 配置语法
|
||||||
|
```bash
|
||||||
|
ssh -G git.refining.app
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本:1.1*
|
||||||
|
*最后更新:2026-01-28*
|
||||||
|
*集成到 Gitea Skill v1.2*
|
||||||
@@ -16,6 +16,7 @@ Gitea Actions 使用 GitHub Actions 兼容的 workflow 语法,定义在 `.gite
|
|||||||
|
|
||||||
| 类型 | 模板文档 | 适用场景 |
|
| 类型 | 模板文档 | 适用场景 |
|
||||||
|------|---------|---------|
|
|------|---------|---------|
|
||||||
|
| **Rust 后端** | [rust-backend.md](./workflow-templates/rust-backend.md) | **Rust API 服务、微服务、CLI 工具** |
|
||||||
| Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 |
|
| Go 后端 | [go-backend.md](./workflow-templates/go-backend.md) | API 服务、微服务、CLI 工具 |
|
||||||
| Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js |
|
| Node.js 前端 | [nodejs-frontend.md](./workflow-templates/nodejs-frontend.md) | React/Vue/Vite/Next.js |
|
||||||
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
|
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
|
||||||
@@ -28,6 +29,11 @@ Gitea Actions 使用 GitHub Actions 兼容的 workflow 语法,定义在 `.gite
|
|||||||
当用户说"为我的项目生成 workflow"时,AI 会自动检测项目类型:
|
当用户说"为我的项目生成 workflow"时,AI 会自动检测项目类型:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Rust 项目特征
|
||||||
|
if [ -f "Cargo.toml" ] || [ -f "src/main.rs" ]; then
|
||||||
|
project_type="rust"
|
||||||
|
fi
|
||||||
|
|
||||||
# Go 项目特征
|
# Go 项目特征
|
||||||
if [ -f "go.mod" ] || [ -f "main.go" ]; then
|
if [ -f "go.mod" ] || [ -f "main.go" ]; then
|
||||||
project_type="go"
|
project_type="go"
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
VERSION_NAME: ${{ needs.build.outputs.version_name }}
|
VERSION_NAME: ${{ needs.build.outputs.version_name }}
|
||||||
APK_NAME: ${{ needs.build.outputs.apk_name }}
|
APK_NAME: ${{ needs.build.outputs.apk_name }}
|
||||||
run: |
|
run: |
|
||||||
@@ -221,20 +221,20 @@ jobs:
|
|||||||
|
|
||||||
# 创建 Release
|
# 创建 Release
|
||||||
release_id=$(curl -s -X POST \
|
release_id=$(curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION_NAME})\"}" \
|
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION_NAME})\"}" \
|
||||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||||
|
|
||||||
# 上传 APK
|
# 上传 APK
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${APK_NAME}.apk" \
|
-F "attachment=@dist/${APK_NAME}.apk" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
|
|
||||||
# 上传校验和
|
# 上传校验和
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${APK_NAME}.apk.sha256" \
|
-F "attachment=@dist/${APK_NAME}.apk.sha256" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ jobs:
|
|||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.registry }}
|
registry: ${{ env.registry }}
|
||||||
username: ${{ vars.REGISTRY_USERNAME }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
- name: Docker - Setup Buildx
|
- name: Docker - Setup Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -183,9 +183,9 @@ jobs:
|
|||||||
name: ${{ env.SERVICE_PREFIX }}-binary-linux-amd64
|
name: ${{ env.SERVICE_PREFIX }}-binary-linux-amd64
|
||||||
path: dist
|
path: dist
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
BINARY_NAME: ${{ needs.build-and-publish.outputs.binary_name }}
|
BINARY_NAME: ${{ needs.build-and-publish.outputs.binary_name }}
|
||||||
run: |
|
run: |
|
||||||
git_tag=$(git describe --tags --abbrev=0)
|
git_tag=$(git describe --tags --abbrev=0)
|
||||||
@@ -199,20 +199,20 @@ jobs:
|
|||||||
|
|
||||||
# 创建 Release
|
# 创建 Release
|
||||||
release_id=$(curl -s -X POST \
|
release_id=$(curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
||||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||||
|
|
||||||
# 上传二进制文件
|
# 上传二进制文件
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${BINARY_NAME}" \
|
-F "attachment=@dist/${BINARY_NAME}" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
|
|
||||||
# 上传校验和
|
# 上传校验和
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${BINARY_NAME}.sha256" \
|
-F "attachment=@dist/${BINARY_NAME}.sha256" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
```
|
```
|
||||||
@@ -284,7 +284,7 @@ GOARCH=amd64 # 目标架构
|
|||||||
|
|
||||||
| Secret | 用途 |
|
| Secret | 用途 |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| `REGISTRY_PASSWORD` | Docker Registry 密码 |
|
| `REGISTRY_TOKEN` | Docker Registry 密码 |
|
||||||
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ jobs:
|
|||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.registry }}
|
registry: ${{ env.registry }}
|
||||||
username: ${{ vars.REGISTRY_USERNAME }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|
||||||
- name: Docker - Setup Buildx
|
- name: Docker - Setup Buildx
|
||||||
uses: docker/setup-buildx-action@v3
|
uses: docker/setup-buildx-action@v3
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
git_tag=$(git describe --tags --abbrev=0)
|
git_tag=$(git describe --tags --abbrev=0)
|
||||||
api_url="${{ github.server_url }}/api/v1"
|
api_url="${{ github.server_url }}/api/v1"
|
||||||
@@ -197,14 +197,14 @@ jobs:
|
|||||||
|
|
||||||
# 创建 Release
|
# 创建 Release
|
||||||
release_id=$(curl -s -X POST \
|
release_id=$(curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
||||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||||
|
|
||||||
# 上传附件
|
# 上传附件
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-dist.zip" \
|
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-dist.zip" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
```
|
```
|
||||||
@@ -357,7 +357,7 @@ env:
|
|||||||
|
|
||||||
| Secret | 用途 |
|
| Secret | 用途 |
|
||||||
|--------|------|
|
|--------|------|
|
||||||
| `REGISTRY_PASSWORD` | Docker Registry 密码 |
|
| `REGISTRY_TOKEN` | Docker Registry 密码 |
|
||||||
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
1123
skill/gitea/workflow-templates/rust-backend.md
Normal file
1123
skill/gitea/workflow-templates/rust-backend.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
env:
|
env:
|
||||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||||
VERSION: ${{ needs.build-and-upload.outputs.version }}
|
VERSION: ${{ needs.build-and-upload.outputs.version }}
|
||||||
run: |
|
run: |
|
||||||
git_tag=$(git describe --tags --abbrev=0)
|
git_tag=$(git describe --tags --abbrev=0)
|
||||||
@@ -252,14 +252,14 @@ jobs:
|
|||||||
|
|
||||||
# 创建 Release
|
# 创建 Release
|
||||||
release_id=$(curl -s -X POST \
|
release_id=$(curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION})\"}" \
|
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION})\"}" \
|
||||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||||
|
|
||||||
# 上传源码包
|
# 上传源码包
|
||||||
curl -s -X POST \
|
curl -s -X POST \
|
||||||
-H "Authorization: token $GITEA_TOKEN" \
|
-H "Authorization: token $RELEASE_TOKEN" \
|
||||||
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-${VERSION}-source.tar.gz" \
|
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-${VERSION}-source.tar.gz" \
|
||||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user