Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95d131eb51 | ||
|
|
8fa9df03a0 | ||
|
|
330aea4d16 | ||
|
|
bb93a6bc6c | ||
|
|
3cb55db864 | ||
|
|
f62146b655 | ||
|
|
425ca5b5fd | ||
|
|
f36b0159bd | ||
|
|
740ff4e1d8 | ||
|
|
135ea99deb | ||
|
|
6bc2506842 | ||
|
|
c0b5f15895 | ||
|
|
32d674a4c0 | ||
|
|
02d870a2d6 | ||
|
|
417f3e6d2b | ||
|
|
ece54efc14 | ||
|
|
d5ad180fae | ||
|
|
fa227d9689 | ||
|
|
6153911e46 | ||
|
|
7084e9be35 | ||
|
|
ef67dc7dd2 | ||
|
|
9a39fa4725 | ||
|
|
491a7897c4 | ||
|
|
87b09e0ffd | ||
|
|
26c333dc84 |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -18,3 +18,9 @@ config.env
|
||||
|
||||
# Gitea 配置(如果不小心复制到这里)
|
||||
gitea-config.env
|
||||
|
||||
# Superpowers 插件和外部依赖
|
||||
superpowers/
|
||||
plugin/
|
||||
.runner
|
||||
package-lock.json
|
||||
|
||||
@@ -127,3 +127,7 @@
|
||||
## 6. skill 和 command
|
||||
|
||||
- 默认在oc 的全局配置目录下创建 skill 和 command
|
||||
|
||||
## 7. 临时文件
|
||||
|
||||
- 临时创建的脚本或文件,使用结束后,主动删除和清理。
|
||||
|
||||
64
README.md
64
README.md
@@ -1,64 +0,0 @@
|
||||
# OpenCode
|
||||
|
||||
## 目录
|
||||
|
||||
```
|
||||
opencode/
|
||||
├── command/ # CLI 命令定义
|
||||
│ ├── git-commit.md # 自动生成提交信息并提交
|
||||
│ ├── git-pull.md # 拉取远程最新变更
|
||||
│ ├── git-push.md # 提交+创建标签+推送(一键完成)
|
||||
│ ├── git-push-tags.md # 推送所有标签到远程
|
||||
│ ├── gitea-config.md # 查看 Gitea 配置和 Runner 状态
|
||||
│ ├── gitea-create-repo.md # 在 Gitea 创建新仓库
|
||||
│ ├── gitea-create-runner.md # 创建并启动 Gitea Actions Runner
|
||||
│ ├── gitea-delete-runner.md # 删除已配置的 Runner
|
||||
│ ├── gitea-list-runners.md # 列出所有已配置的 Runners
|
||||
│ ├── gitea-reset.md # 重置 Gitea 配置
|
||||
│ ├── gitea-switch-org.md # 切换默认组织
|
||||
│ └── review.md # 代码审查命令
|
||||
│
|
||||
├── skill/ # 可复用技能库和指南
|
||||
│ ├── git/ # Git 工作流程和版本管理
|
||||
│ │ ├── SKILL.md # 完整的 Git 工作流程指南
|
||||
│ │ └── quick-reference.md # Git 快速参考
|
||||
│ │
|
||||
│ ├── gitea/ # Gitea 平台集成
|
||||
│ │ ├── SKILL.md # Gitea 完整指南
|
||||
│ │ ├── setup-guide.md # 初始化和配置指南
|
||||
│ │ ├── repository-operations.md # 仓库操作指南
|
||||
│ │ ├── runner-management.md # Runner 管理指南
|
||||
│ │ ├── api-reference.md # Gitea API 参考
|
||||
│ │ ├── troubleshooting.md # 常见问题和解决方案
|
||||
│ │ ├── workflow-generator.md # Workflow 自动生成工具
|
||||
│ │ └── workflow-templates/ # CI/CD Workflow 模板库
|
||||
│ │ ├── android-app.md # Android App 构建 Workflow
|
||||
│ │ ├── go-backend.md # Go 后端服务 Workflow
|
||||
│ │ ├── nodejs-frontend.md # Node.js 前端 Workflow
|
||||
│ │ └── wechat-miniprogram.md # 微信小程序 Workflow
|
||||
│ │
|
||||
│ ├── android-developer/ # Android 开发指南
|
||||
│ │ └── SKILL.md # Android 项目开发规范
|
||||
│ │
|
||||
│ ├── ios-developer/ # iOS 开发指南
|
||||
│ │ └── SKILL.md # iOS 项目开发规范
|
||||
│ │
|
||||
│ ├── go-developer/ # Go 后端开发指南
|
||||
│ │ └── SKILL.md # Go 项目开发规范
|
||||
│ │
|
||||
│ ├── electron-developer/ # Electron 桌面应用指南
|
||||
│ │ └── SKILL.md # Electron 项目开发规范
|
||||
│ │
|
||||
│ └── mqtts-developer/ # MQTT over TLS/SSL 开发指南
|
||||
│ ├── SKILL.md # MQTT 完整指南
|
||||
│ ├── README.md # MQTT 项目说明
|
||||
│ ├── setup-mqtts-acme.md # ACME 证书配置
|
||||
│ ├── mqtts-client-config.md # 客户端配置
|
||||
│ ├── mqtts-quick-reference.md # 快速参考
|
||||
│ └── USAGE_EXAMPLES.md # 使用示例
|
||||
│
|
||||
├── README.md # 项目说明文档(当前文件)
|
||||
├── AGENTS.md # 全局开发规范和指南
|
||||
├── opencode.json # 项目配置文件
|
||||
└── .gitignore # Git 忽略文件配置
|
||||
```
|
||||
@@ -1,11 +1,21 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {},
|
||||
"model": "moonshotai-cn/kimi-k2.5",
|
||||
"permission": "allow",
|
||||
"plugin": ["@mohak34/opencode-notifier@latest"],
|
||||
"plugin": [
|
||||
"@mohak34/opencode-notifier@latest"
|
||||
],
|
||||
"provider": {
|
||||
"opencode": {
|
||||
"models": {
|
||||
"claude-sonnet-4-5": {
|
||||
"options": {
|
||||
"thinking": {
|
||||
"type": "enabled",
|
||||
"budgetTokens": 16000
|
||||
}
|
||||
}
|
||||
},
|
||||
"claude-opus-4-5": {
|
||||
"options": {
|
||||
"thinking": {
|
||||
@@ -15,11 +25,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"zhipuai-coding-plan": {
|
||||
"options": {
|
||||
"apiKey": "0f76aea86295476dbfa98724013b0fe8.o2EaJVqcl4Cf7WLP"
|
||||
}
|
||||
}
|
||||
}
|
||||
"mcp": {}
|
||||
}
|
||||
27
skill/apple/SKILL.md
Normal file
27
skill/apple/SKILL.md
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: apple
|
||||
description: Apple 开发者账号管理、注册最佳实践与风控规避指南
|
||||
---
|
||||
|
||||
# Apple Developer Account Management
|
||||
|
||||
本 Skill 提供了 Apple 开发者账号申请、管理及风控规避的最佳实践指南。
|
||||
|
||||
## 功能文档
|
||||
|
||||
| 文档 | 说明 |
|
||||
|------|------|
|
||||
| [注册最佳实践](./registration-guide.md) | Apple 开发者账号注册流程、风控规避与问题诊断指南 |
|
||||
|
||||
## 核心原则 (Core Principles)
|
||||
|
||||
1. **物理与数据隔离**:注册环境必须保持绝对干净(一机、一号、一网、一卡)。
|
||||
2. **真实身份**:必须使用真实的个人身份信息,严禁使用虚假资料。
|
||||
3. **设备纯净**:使用未被 Apple 标记过的设备进行操作。
|
||||
4. **网络安全**:避免使用公共 WiFi 或 VPN,优先使用移动数据网络。
|
||||
|
||||
## 常见问题
|
||||
|
||||
- **联系支持 (Contact Support)**:通常由风控系统触发,原因是设备关联、网络污染或账号行为异常。
|
||||
- **支付失败**:通常由于信用卡被关联或账单地址不匹配。
|
||||
- **身份验证失败**:证件照片不清晰或与填写的名字不匹配。
|
||||
129
skill/apple/registration-guide.md
Normal file
129
skill/apple/registration-guide.md
Normal file
@@ -0,0 +1,129 @@
|
||||
# Apple 开发者账号注册最佳实践报告 (2025版)
|
||||
|
||||
本指南针对 Apple 开发者账户注册过程中常见的“联系 Apple 支持”风控拦截问题,提供详细的诊断与注册最佳实践。
|
||||
|
||||
## 1. 问题诊断:为什么会提示“联系支持”?
|
||||
|
||||
当 Apple 的风控算法判定注册行为存在“高风险”或“非真人特征”时,会阻断流程。常见触发原因如下:
|
||||
|
||||
* **设备污染(Device Tainting):最常见原因**
|
||||
* 该 iPhone/iPad 曾登录过被封禁的 Apple ID。
|
||||
* 该设备曾频繁切换不同的 Apple ID。
|
||||
* **关键点**:Apple 记录的是设备的 **UDID/序列号**。简单的“退出登录”或“还原设置”通常不足以清洗被列入黑名单的设备指纹。
|
||||
* **网络环境污染**
|
||||
* 使用了公司的公共 Wi-Fi(如果公司内有其他同事的账号被封,该 IP 可能已被标记)。
|
||||
* 使用了 VPN 或代理工具(IP 地址频繁跳变或属于数据中心号段)。
|
||||
* **账号行为异常**
|
||||
* 新注册的 Apple ID **没有任何使用记录**(如下载 App、充值、云同步),直接申请开发者,会被视为“养号/机器注册”。
|
||||
* 填写的身份信息(拼音/英文名)与证件或信用卡持卡人姓名不完全一致。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心原则:彻底的物理与数据隔离
|
||||
|
||||
为了确保注册成功,必须严格执行**“独立环境”**原则。请放弃“仅仅退出旧账号”的想法,必须把注册环境视为一个全新的“无菌室”。
|
||||
|
||||
**黄金法则:**
|
||||
> **1 台干净设备 + 1 个独立网络 + 1 套真实身份 + 1 张独立银行卡**
|
||||
|
||||
---
|
||||
|
||||
## 3. 最佳实践流程指南
|
||||
|
||||
请按照以下步骤重新规划注册流程(建议换人或换全套环境):
|
||||
|
||||
### 第一阶段:环境准备(硬件与网络)
|
||||
|
||||
1. **设备选择(非常重要)**
|
||||
* **最佳**:使用从未申请过开发者账号、未登录过违规账号的 iPhone 或 iPad。
|
||||
* **次选**:如果必须用旧设备,必须进行**DFU模式彻底刷机**(不只是“抹掉所有内容和设置”),但这仍有风险,因为硬件序列号无法改变。
|
||||
2. **网络环境**
|
||||
* **绝对禁止**:使用公司 Wi-Fi、公共场所 Wi-Fi、VPN。
|
||||
* **必须使用**:手机运营商的 **4G/5G 移动数据网络**。这是最干净、最受信任的 IP 来源。
|
||||
3. **手机号码**
|
||||
* 使用该申请人实名认证的手机号。
|
||||
* **严禁**:使用虚拟运营商号码(170/171段)、接码平台号码。
|
||||
4. **邮箱选择**
|
||||
* **个人注册**:
|
||||
* **推荐**:Gmail、iCloud 邮箱、Outlook/Hotmail
|
||||
* **不推荐**:QQ 邮箱(通过率较低,风控系统可能视为高风险)
|
||||
* **关键**:确保邮箱有真实使用记录,非新注册账号
|
||||
* **组织注册(公司账号)**:
|
||||
* **必须**:公司域名邮箱(如 `@company.com`)
|
||||
* **要求**:邮箱域名必须与公司官网域名一致
|
||||
* **验证**:确保邮箱能正常收发邮件(Apple 会发送验证邮件)
|
||||
|
||||
### 第二阶段:账号“养护”(关键缓冲期)
|
||||
|
||||
不要注册完 Apple ID 马上申请开发者,这非常像机器行为。
|
||||
|
||||
1. **注册 Apple ID**:在设备上直接注册,开启双重认证(2FA)。
|
||||
* **邮箱选择**:优先使用 Gmail 或 iCloud 邮箱,避免使用 QQ 邮箱
|
||||
* **身份验证**:填写真实姓名(拼音/英文),与身份证一致
|
||||
2. **模拟真实用户行为(建议 1-3 天)**:
|
||||
* 登录 App Store 下载几个常用 App(微信、抖音等)。
|
||||
* 开启 iCloud 同步(通讯录、照片)。
|
||||
* 设置并使用 Apple Pay(绑定用于支付的信用卡)。
|
||||
* 如果可能,充值少量金额购买一个 App。
|
||||
* **目的**:向 Apple 证明这是一个“活人”在使用的“主力机”。
|
||||
|
||||
### 第三阶段:正式申请
|
||||
|
||||
1. **下载应用**:使用该 Apple ID 登录 App Store 下载 **Apple Developer App**。
|
||||
2. **填写信息**:
|
||||
* **姓名**:必须填写真实姓名(拼音/英文),必须与**身份证**和**信用卡**上的拼音完全一致。不要用英文昵称(如 Peter Zhang)。
|
||||
* **身份证号**:输入无误。
|
||||
3. **人脸识别**:
|
||||
* 确保光线充足,背景简单。
|
||||
* 必须由账号持有人本人进行扫脸。
|
||||
4. **支付环节**:
|
||||
* **信用卡**:必须使用**从未在其他开发者账号使用过**的 Visa/MasterCard 信用卡。
|
||||
* **账单地址**:尽量填写真实账单地址。
|
||||
|
||||
---
|
||||
|
||||
## 4. 邮箱选择详细指南
|
||||
|
||||
### 官方要求
|
||||
- **个人注册**:可以使用任何邮箱,但**公司域名邮箱**为最佳选择
|
||||
- **组织注册**:**必须**使用公司域名邮箱(如 `@company.com`)
|
||||
|
||||
### 邮箱类型推荐(按优先级)
|
||||
1. **公司域名邮箱**(最佳)
|
||||
- 适用于组织注册,必须使用
|
||||
- 邮箱域名必须与公司官网域名一致
|
||||
2. **Gmail**(次佳)
|
||||
- 国际通用,信誉度高
|
||||
- 风控系统认可度高
|
||||
3. **iCloud 邮箱**(Apple 自家服务)
|
||||
- 最受 Apple 信任
|
||||
- 与 Apple 生态系统无缝集成
|
||||
4. **Outlook/Hotmail**(微软系)
|
||||
- 信誉良好,通过率较高
|
||||
5. **其他主流企业邮箱**
|
||||
- 如企业微信邮箱、阿里云邮箱等
|
||||
|
||||
### 不推荐邮箱类型
|
||||
- **QQ 邮箱**:通过率较低,风控系统可能视为高风险
|
||||
- **163/126 邮箱**:有一定风险,不如 Gmail 可靠
|
||||
- **虚拟邮箱/临时邮箱**:绝对禁止使用
|
||||
- **接码平台邮箱**:会被直接拒绝
|
||||
|
||||
### 关键原则
|
||||
- **邮箱历史**:使用有真实使用记录的邮箱,避免新注册账号
|
||||
- **一致性**:邮箱信息与身份信息保持一致
|
||||
- **可访问性**:确保邮箱能正常收发邮件,Apple 会发送验证邮件
|
||||
|
||||
## 5. 针对已触发风控情况的建议
|
||||
|
||||
如果您的注册尝试已经触发了“联系支持”提示:
|
||||
|
||||
1. **放弃当前环境**:不要继续在已报错的设备上重试,也不要用已报错的 Apple ID 重试。
|
||||
2. **执行“新号新机”方案**:
|
||||
* 找一位**还没有注册过**开发者账号的同事。
|
||||
* 使用该同事**自己的 iPhone**(确保该手机没乱登过别人的 ID)。
|
||||
* 关闭 Wi-Fi,使用 **4G/5G**。
|
||||
* **更换邮箱**:使用 Gmail 或公司域名邮箱
|
||||
* 按照上述“最佳实践”流程操作。
|
||||
3. **关于申诉**:
|
||||
* 除非您确信没有任何违规(设备绝对纯净、身份绝对真实),否则申诉解封的成功率极低,建议直接采用新方案。
|
||||
@@ -13,6 +13,7 @@ You are an expert in Git version control and repository management.
|
||||
|------|------|
|
||||
| [Commit Workflow](./commit-workflow.md) | 提交暂存文件,自动生成提交信息并创建版本标签 |
|
||||
| [Push Workflow](./push-workflow.md) | 提交并推送到远程仓库的完整工作流 |
|
||||
| [SSH Keychain](./ssh-keychain.md) | SSH 密钥管理与 macOS Keychain 自动解锁配置 |
|
||||
|
||||
## 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、仓库的创建和管理功能。
|
||||
|
||||
## 最新改进 (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
|
||||
~/.config/gitea/
|
||||
```
|
||||
|
||||
### 仓库操作目录
|
||||
可以在任何项目目录执行,技能会自动:
|
||||
1. 从 `~/.config/gitea/config.env` 读取配置
|
||||
2. 在当前目录创建仓库、初始化 Git 等
|
||||
|
||||
### Windows
|
||||
```powershell
|
||||
# PowerShell
|
||||
@@ -28,7 +56,9 @@ $env:USERPROFILE\.config\gitea\
|
||||
C:\Users\YourUsername\.config\gitea\
|
||||
```
|
||||
|
||||
所有配置文件、Runner 目录、日志文件都存储在此目录下。请确保该目录具有适当的读写权限。
|
||||
**重要区分**:
|
||||
- **配置/Runner 操作**:在 `~/.config/gitea/` 目录执行
|
||||
- **仓库操作**:可在任何项目目录执行(自动加载配置)
|
||||
|
||||
**目录结构(所有平台通用):**
|
||||
```
|
||||
@@ -40,9 +70,9 @@ C:\Users\YourUsername\.config\gitea\
|
||||
```
|
||||
|
||||
**平台兼容性:**
|
||||
- **macOS**: `~/.config/gitea/`(完全支持)
|
||||
- **Linux**: `~/.config/gitea/`(完全支持)
|
||||
- **Windows**: `%USERPROFILE%\.config\gitea\`(Act Runner 支持,但该技能的命令和脚本需要适配)
|
||||
- **macOS**: `~/.config/gitea/`(配置目录),任意目录(仓库操作)
|
||||
- **Linux**: `~/.config/gitea/`(配置目录),任意目录(仓库操作)
|
||||
- **Windows**: `%USERPROFILE%\.config\gitea\`(配置目录),任意目录(仓库操作)
|
||||
|
||||
**Windows 用户注意事项:**
|
||||
- Gitea Act Runner 官方支持 Windows 平台(包括 Host 模式)
|
||||
@@ -57,6 +87,7 @@ C:\Users\YourUsername\.config\gitea\
|
||||
| 功能模块 | 文档 | 说明 |
|
||||
|---------|------|------|
|
||||
| 环境配置 | [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 |
|
||||
| 自动创建脚本 | [create-runner.md](./create-runner.md) | 包含完整的 Runner 创建 Bash 脚本 |
|
||||
| Workflow 生成 | [workflow-generator.md](./workflow-generator.md) | 根据项目类型生成 CI/CD workflow |
|
||||
@@ -72,6 +103,7 @@ C:\Users\YourUsername\.config\gitea\
|
||||
- "生成 workflow"、"CI/CD"
|
||||
- "创建仓库"、"gitea 仓库"
|
||||
- "gitea 配置"、"gitea token"
|
||||
- "ssh key"、"ssh密钥"、"gitea ssh"、"部署密钥"
|
||||
|
||||
## 首次使用
|
||||
|
||||
@@ -89,7 +121,13 @@ C:\Users\YourUsername\.config\gitea\
|
||||
- 可在所有个人仓库的 Actions workflow 中使用
|
||||
- 使用方式:`${{ 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)
|
||||
|
||||
@@ -100,6 +138,7 @@ C:\Users\YourUsername\.config\gitea\
|
||||
| `/gitea-config` | 查看当前 Gitea 配置和 Runner 状态 |
|
||||
| `/gitea-reset` | 重置 Gitea 配置(交互式向导) |
|
||||
| `/gitea-switch-org` | 切换默认组织 |
|
||||
| `/gitea-setup-ssh` | 创建 SSH 密钥并部署到 Gitea 服务器 |
|
||||
| `/gitea-create-runner` | 创建并启动新 Runner(默认 host 模式) |
|
||||
| `/gitea-list-runners` | 列出所有 Runner 及其状态 |
|
||||
| `/gitea-delete-runner` | 删除指定 Runner |
|
||||
@@ -183,12 +222,30 @@ AI: 检测到 Go 项目,服务目录: ./backend
|
||||
[自动生成 .gitea/workflows/backend.yml]
|
||||
```
|
||||
|
||||
### 4. 创建仓库
|
||||
### 4. 创建仓库(智能解析,默认私有)
|
||||
|
||||
**智能解析特性**:
|
||||
- 自动识别 `组织/仓库` 格式,优先使用指定组织
|
||||
- 默认创建私有仓库(除非明确指定公开)
|
||||
- 支持自动初始化 Git 仓库并推送代码
|
||||
|
||||
**示例**:
|
||||
```
|
||||
用户: /gitea-create-repo my-project
|
||||
AI: [使用配置的 Gitea URL 和默认组织创建仓库]
|
||||
✓ 仓库创建成功: ai/my-project
|
||||
用户: /gitea-create-repo shigongcao/shigongcao
|
||||
AI: [智能解析为 shigongcao 组织下的 shigongcao 仓库]
|
||||
✓ 仓库创建成功: 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 工具 |
|
||||
| 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 |
|
||||
| Android 应用 | [android-app.md](./workflow-templates/android-app.md) | Kotlin/Java/Jetpack Compose |
|
||||
| 微信小程序 | [wechat-miniprogram.md](./workflow-templates/wechat-miniprogram.md) | 微信小程序 CI/CD |
|
||||
@@ -349,6 +408,31 @@ AI 会自动:
|
||||
|
||||
详见:[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 调用
|
||||
|
||||
所有与 Gitea 服务器的交互都通过 API 完成,使用配置文件中的:
|
||||
@@ -357,6 +441,38 @@ AI 会自动:
|
||||
|
||||
详见:[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`(仅所有者可读写)
|
||||
@@ -376,10 +492,23 @@ AI 会自动:
|
||||
|
||||
## 版本
|
||||
|
||||
- **Skill Version**: 1.0
|
||||
- **Last Updated**: 2026-01-12
|
||||
- **整合内容**: gitea-runner + gitea-workflow
|
||||
- **新增功能**: 统一配置管理、Runner CRUD、智能 labels 检测
|
||||
- **Skill Version**: 1.4
|
||||
- **Last Updated**: 2026-01-29
|
||||
- **整合内容**: gitea-runner + gitea-workflow + 增强仓库管理 + SSH 密钥管理 + 完整 Dockerfile 模板库
|
||||
- **主要改进**:
|
||||
- 仓库创建智能解析(优先使用指定组织)
|
||||
- **简化验证**:默认假设组织存在,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
|
||||
|
||||
## 相关资源
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -69,10 +69,10 @@ LOCAL_MAP=$(mktemp)
|
||||
FINAL_LIST=$(mktemp)
|
||||
|
||||
# 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" \
|
||||
-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
|
||||
# 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
|
||||
- 设置 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"
|
||||
```
|
||||
|
||||
#### 步骤 2: 解析输入
|
||||
#### 步骤 2: 智能解析输入(简洁高效版)
|
||||
|
||||
```bash
|
||||
input="$1"
|
||||
|
||||
# 解析 owner/repo
|
||||
# 智能解析:优先使用用户指定的组织
|
||||
if [[ "$input" =~ / ]]; then
|
||||
owner=$(echo "$input" | cut -d'/' -f1)
|
||||
repo=$(echo "$input" | cut -d'/' -f2)
|
||||
echo "使用指定组织: $owner"
|
||||
echo "提示:假设组织存在,如不存在会在API创建时提示"
|
||||
else
|
||||
# 使用默认组织或当前用户
|
||||
# 未指定组织,使用默认组织或当前用户
|
||||
if [ -z "$GITEA_DEFAULT_ORG" ]; then
|
||||
# 获取当前用户
|
||||
echo "未指定组织,获取当前用户..."
|
||||
owner=$(curl -s -H "Authorization: token $GITEA_TOKEN" \
|
||||
"${GITEA_URL}/api/v1/user" | jq -r '.login')
|
||||
|
||||
if [ -z "$owner" ] || [ "$owner" = "null" ]; then
|
||||
echo "❌ 无法获取当前用户信息,请使用 owner/repo 格式"
|
||||
echo "❌ 无法获取当前用户信息,请使用 组织/仓库 格式"
|
||||
echo "例如:/gitea-create-repo shigongcao/shigongcao"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -94,9 +126,21 @@ else
|
||||
repo="$input"
|
||||
fi
|
||||
|
||||
# 解析可见性
|
||||
# 解析可见性:默认私有仓库(除非明确指定公开)
|
||||
visibility="${2:-private}"
|
||||
if [[ "$visibility" != "private" && "$visibility" != "public" ]]; then
|
||||
echo "⚠️ 可见性参数无效,使用默认值: private"
|
||||
visibility="private"
|
||||
fi
|
||||
|
||||
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: 验证仓库名
|
||||
@@ -109,12 +153,73 @@ if ! [[ "$repo" =~ ^[a-zA-Z0-9_.-]+$ ]]; then
|
||||
fi
|
||||
```
|
||||
|
||||
#### 步骤 4: 调用 API 创建
|
||||
#### 步骤 4: 调用 API 创建(带详细错误处理)
|
||||
|
||||
```bash
|
||||
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 \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"${repo}\",
|
||||
\"private\": ${private_bool},
|
||||
\"auto_init\": false,
|
||||
\"default_branch\": \"main\",
|
||||
\"description\": \"\"
|
||||
}" \
|
||||
"${GITEA_URL}/api/v1/orgs/${owner}/repos")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
# 处理响应
|
||||
case "$http_code" in
|
||||
201)
|
||||
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" \
|
||||
@@ -124,40 +229,41 @@ response=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
\"auto_init\": false,
|
||||
\"default_branch\": \"main\"
|
||||
}" \
|
||||
"${GITEA_URL}/api/v1/orgs/${owner}/repos")
|
||||
|
||||
http_code=$(echo "$response" | tail -n1)
|
||||
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
|
||||
201)
|
||||
echo "✓ 仓库创建成功"
|
||||
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)
|
||||
echo "❌ 仓库已存在"
|
||||
echo "❌ 仓库已存在: $owner/$repo"
|
||||
echo "请使用不同的仓库名或删除现有仓库"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo "❌ 创建失败 (HTTP $http_code)"
|
||||
echo "$body" | jq -r '.message // empty'
|
||||
error_msg=$(echo "$body" | jq -r '.message // "未知错误"')
|
||||
echo "❌ 创建失败 (HTTP $http_code): $error_msg"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -183,25 +289,78 @@ echo " SSH URL: $ssh_url"
|
||||
echo ""
|
||||
```
|
||||
|
||||
#### 步骤 6: 添加 Git Remote
|
||||
#### 步骤 6: Git 仓库集成(简洁高效版)
|
||||
|
||||
```bash
|
||||
read -p "是否将此仓库添加为 Git remote? [Y/n] " add_remote
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "Git 仓库集成"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
|
||||
if [[ ! "$add_remote" =~ ^[Nn]$ ]]; then
|
||||
# 检查是否是 Git 仓库
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
echo "当前目录不是 Git 仓库"
|
||||
read -p "是否初始化 Git 仓库? [Y/n] " init_git
|
||||
read -p "是否初始化 Git 仓库并添加 remote? [Y/n] " init_git
|
||||
|
||||
if [[ ! "$init_git" =~ ^[Nn]$ ]]; then
|
||||
git init
|
||||
echo "✓ Git 仓库已初始化"
|
||||
else
|
||||
if [[ "$init_git" =~ ^[Nn]$ ]]; then
|
||||
echo "跳过 Git 初始化,仅创建远程仓库。"
|
||||
echo "您可以在需要时手动执行:"
|
||||
echo " git init"
|
||||
echo " git remote add origin \"$clone_url\""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 初始化 Git 仓库
|
||||
echo "正在初始化 Git 仓库..."
|
||||
git init
|
||||
echo "✓ Git 仓库已初始化"
|
||||
|
||||
# 检查 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 已更新为: $clone_url"
|
||||
else
|
||||
echo "保持现有的 origin remote"
|
||||
fi
|
||||
else
|
||||
git remote add origin "$clone_url"
|
||||
echo "✓ origin remote 已添加: $clone_url"
|
||||
fi
|
||||
|
||||
# 可选:添加文件、提交并推送
|
||||
echo ""
|
||||
read -p "是否添加当前文件、提交并推送到远程仓库? [Y/n] " push_code
|
||||
|
||||
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)
|
||||
@@ -211,17 +370,23 @@ if [[ ! "$add_remote" =~ ^[Nn]$ ]]; then
|
||||
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
|
||||
|
||||
# 显示 remote 信息
|
||||
echo ""
|
||||
echo "当前 remote:"
|
||||
git remote -v
|
||||
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
|
||||
- **最后更新**: 2026-01-12
|
||||
- **文档版本**: 1.1
|
||||
- **最后更新**: 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
|
||||
- **Windows**: 使用 **Host Mode**(需在 workflow 中指定 `shell: powershell`)
|
||||
- **Linux**: 两种模式均可,Docker Mode 隔离性更好
|
||||
- **Docker Mode**: 必须使用 `catthehacker/ubuntu:act-*` 镜像(内置 Docker CLI)
|
||||
|
||||
**Windows Host Mode 注意事项**:
|
||||
- Bash 默认不可用,需在 workflow 中指定 shell:
|
||||
@@ -577,9 +578,51 @@ labels:
|
||||
```yaml
|
||||
labels:
|
||||
- "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 匹配
|
||||
|
||||
**方法 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
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
| 任务 | 命令 |
|
||||
@@ -1001,6 +1144,7 @@ tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
||||
| 创建 runner | `/gitea-create-runner`(自动安装、配置、启动)|
|
||||
| 列出 runners | `/gitea-list-runners` |
|
||||
| 删除 runner | `/gitea-delete-runner` |
|
||||
| 恢复 runner | 参考「恢复被删除的 Runner」章节 |
|
||||
| 手动启动 | `nohup act_runner daemon --config <config.yaml> > <log> 2>&1 &` |
|
||||
| 停止 runner | `pkill -f "act_runner daemon --config.*<name>"` |
|
||||
| 查看状态 | `ps aux \| grep act_runner` |
|
||||
@@ -1016,6 +1160,9 @@ tail -f ~/.config/gitea/runners/runner-macbook-pro/runner.log
|
||||
|
||||
## 版本
|
||||
|
||||
- **文档版本**: 1.0
|
||||
- **最后更新**: 2026-01-12
|
||||
- **文档版本**: 1.2
|
||||
- **最后更新**: 2026-01-24
|
||||
- **更新内容**:
|
||||
- Docker Mode 镜像选择说明(必须使用包含 Docker CLI 的镜像)
|
||||
- Runner 恢复流程
|
||||
- **兼容性**: 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 工具 |
|
||||
| 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 |
|
||||
@@ -28,6 +29,11 @@ Gitea Actions 使用 GitHub Actions 兼容的 workflow 语法,定义在 `.gite
|
||||
当用户说"为我的项目生成 workflow"时,AI 会自动检测项目类型:
|
||||
|
||||
```bash
|
||||
# Rust 项目特征
|
||||
if [ -f "Cargo.toml" ] || [ -f "src/main.rs" ]; then
|
||||
project_type="rust"
|
||||
fi
|
||||
|
||||
# Go 项目特征
|
||||
if [ -f "go.mod" ] || [ -f "main.go" ]; then
|
||||
project_type="go"
|
||||
|
||||
@@ -211,7 +211,7 @@ jobs:
|
||||
|
||||
- name: Create Release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
VERSION_NAME: ${{ needs.build.outputs.version_name }}
|
||||
APK_NAME: ${{ needs.build.outputs.apk_name }}
|
||||
run: |
|
||||
@@ -221,20 +221,20 @@ jobs:
|
||||
|
||||
# 创建 Release
|
||||
release_id=$(curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION_NAME})\"}" \
|
||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||
|
||||
# 上传 APK
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${APK_NAME}.apk" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
|
||||
# 上传校验和
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${APK_NAME}.apk.sha256" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
```
|
||||
|
||||
@@ -134,8 +134,8 @@ jobs:
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.registry }}
|
||||
username: ${{ vars.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Docker - Setup Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -185,7 +185,7 @@ jobs:
|
||||
|
||||
- name: Create Release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
BINARY_NAME: ${{ needs.build-and-publish.outputs.binary_name }}
|
||||
run: |
|
||||
git_tag=$(git describe --tags --abbrev=0)
|
||||
@@ -199,20 +199,20 @@ jobs:
|
||||
|
||||
# 创建 Release
|
||||
release_id=$(curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||
|
||||
# 上传二进制文件
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${BINARY_NAME}" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
|
||||
# 上传校验和
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${BINARY_NAME}.sha256" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
```
|
||||
@@ -284,7 +284,7 @@ GOARCH=amd64 # 目标架构
|
||||
|
||||
| Secret | 用途 |
|
||||
|--------|------|
|
||||
| `REGISTRY_PASSWORD` | Docker Registry 密码 |
|
||||
| `REGISTRY_TOKEN` | Docker Registry 密码 |
|
||||
| `RELEASE_TOKEN` | Gitea API 令牌(创建 Release) |
|
||||
|
||||
---
|
||||
|
||||
@@ -132,8 +132,8 @@ jobs:
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.registry }}
|
||||
username: ${{ vars.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Docker - Setup Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
@@ -183,7 +183,7 @@ jobs:
|
||||
|
||||
- name: Create Release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
run: |
|
||||
git_tag=$(git describe --tags --abbrev=0)
|
||||
api_url="${{ github.server_url }}/api/v1"
|
||||
@@ -197,14 +197,14 @@ jobs:
|
||||
|
||||
# 创建 Release
|
||||
release_id=$(curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag}\"}" \
|
||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||
|
||||
# 上传附件
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-dist.zip" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
```
|
||||
@@ -357,7 +357,7 @@ env:
|
||||
|
||||
| Secret | 用途 |
|
||||
|--------|------|
|
||||
| `REGISTRY_PASSWORD` | Docker Registry 密码 |
|
||||
| `REGISTRY_TOKEN` | Docker Registry 密码 |
|
||||
| `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
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
VERSION: ${{ needs.build-and-upload.outputs.version }}
|
||||
run: |
|
||||
git_tag=$(git describe --tags --abbrev=0)
|
||||
@@ -252,14 +252,14 @@ jobs:
|
||||
|
||||
# 创建 Release
|
||||
release_id=$(curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"tag_name\":\"${git_tag}\",\"name\":\"Release ${git_tag} (v${VERSION})\"}" \
|
||||
"${api_url}/repos/${repo}/releases" | jq -r '.id')
|
||||
|
||||
# 上传源码包
|
||||
curl -s -X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H "Authorization: token $RELEASE_TOKEN" \
|
||||
-F "attachment=@dist/${{ env.SERVICE_PREFIX }}-${VERSION}-source.tar.gz" \
|
||||
"${api_url}/repos/${repo}/releases/${release_id}/assets"
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: opencode
|
||||
description: Create and manage OpenCode commands and skills with templates and best practices. Use this skill when users ask to create commands or skills.
|
||||
description: Create and manage OpenCode commands and skills with templates and best practices. Includes skill synchronization to Cursor IDE. Use this skill when users ask to create commands, skills, or sync to Cursor.
|
||||
---
|
||||
|
||||
# OpenCode Command and Skill Management
|
||||
@@ -13,6 +13,8 @@ You are an expert in OpenCode configuration and extension development. This skil
|
||||
- "添加command", "添加命令", "add command"
|
||||
- "添加skill", "添加技能", "add skill"
|
||||
- "opcode command", "opencode skill"
|
||||
- "同步技能", "sync skills", "sync to cursor", "同步到cursor"
|
||||
- "技能同步", "skill sync", "同步opencode技能"
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -34,6 +36,11 @@ For detailed skill creation guidelines, refer to:
|
||||
- `skill/opencode/skill-creation.md` - Complete guide to creating OpenCode skills
|
||||
- Includes: directory structure, naming rules, templates, testing
|
||||
|
||||
### Skill Synchronization (OpenCode → Cursor)
|
||||
For syncing OpenCode skills to Cursor IDE, refer to:
|
||||
- `skill/opencode/sync-to-cursor.md` - Guide to synchronize OpenCode skills to Cursor IDE
|
||||
- Includes: sync strategies, operation steps, error handling, best practices
|
||||
|
||||
## Quick Start
|
||||
|
||||
### To Create a Command
|
||||
|
||||
242
skill/opencode/sync-to-cursor.md
Normal file
242
skill/opencode/sync-to-cursor.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# OpenCode 技能同步到 Cursor IDE 指南
|
||||
|
||||
将 OpenCode 技能同步到 Cursor IDE 的技能目录,实现配置一致和统一管理。
|
||||
|
||||
## 目的
|
||||
|
||||
- 保持 OpenCode 和 Cursor 的技能配置一致
|
||||
- 简化技能维护工作流
|
||||
- 支持一键同步操作
|
||||
- 提供错误处理和回滚机制
|
||||
|
||||
## 目录结构
|
||||
|
||||
### 源目录(OpenCode 技能)
|
||||
```
|
||||
~/.config/opencode/skill/
|
||||
├── git/
|
||||
├── android/
|
||||
├── electron/
|
||||
└── ...其他技能
|
||||
```
|
||||
|
||||
### 目标目录(Cursor 技能)
|
||||
```
|
||||
~/.cursor/skills/
|
||||
├── git/
|
||||
├── android/
|
||||
├── electron/
|
||||
└── ...其他技能
|
||||
```
|
||||
|
||||
**注意**:Cursor 目录中可能包含额外的技能(如 `opencode/`),同步时应保留这些额外技能不被删除。
|
||||
|
||||
## 同步策略
|
||||
|
||||
### 默认同步操作
|
||||
1. **覆盖更新**:用 OpenCode 版本覆盖 Cursor 中的同名技能
|
||||
2. **新增技能**:复制 OpenCode 中有但 Cursor 中没有的技能
|
||||
3. **保留额外**:保留 Cursor 中独有的技能(如 `opencode/`)
|
||||
4. **不删除**:不同步删除操作,仅添加和更新
|
||||
|
||||
### 文件级同步
|
||||
- 递归复制整个技能目录
|
||||
- 保留文件权限和时间戳
|
||||
- 使用 `cp -Rf` 强制覆盖已存在的文件
|
||||
|
||||
## 操作步骤
|
||||
|
||||
### 1. 检查目录结构
|
||||
```bash
|
||||
# 检查源目录
|
||||
ls -la ~/.config/opencode/skill/
|
||||
|
||||
# 检查目标目录
|
||||
ls -la ~/.cursor/skills/ 2>/dev/null || echo "目标目录不存在"
|
||||
```
|
||||
|
||||
### 2. 同步技能
|
||||
```bash
|
||||
# 确保目标目录存在
|
||||
mkdir -p ~/.cursor/skills/
|
||||
|
||||
# 同步所有技能(覆盖更新)
|
||||
cp -Rf ~/.config/opencode/skill/* ~/.cursor/skills/
|
||||
```
|
||||
|
||||
### 3. 验证同步结果
|
||||
```bash
|
||||
# 比较目录结构
|
||||
echo "源目录:"
|
||||
ls ~/.config/opencode/skill/
|
||||
echo "目标目录:"
|
||||
ls ~/.cursor/skills/
|
||||
|
||||
# 检查特定技能文件
|
||||
diff ~/.config/opencode/skill/git/SKILL.md ~/.cursor/skills/git/SKILL.md
|
||||
```
|
||||
|
||||
### 4. 完整同步脚本
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# 配置路径
|
||||
SOURCE_DIR="$HOME/.config/opencode/skill"
|
||||
TARGET_DIR="$HOME/.cursor/skills"
|
||||
|
||||
echo "开始同步 OpenCode 技能到 Cursor..."
|
||||
echo "源目录: $SOURCE_DIR"
|
||||
echo "目标目录: $TARGET_DIR"
|
||||
|
||||
# 检查源目录
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "错误: 源目录不存在 $SOURCE_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 创建目标目录(如不存在)
|
||||
mkdir -p "$TARGET_DIR"
|
||||
|
||||
# 同步技能
|
||||
echo "正在同步技能..."
|
||||
cp -Rf "$SOURCE_DIR"/* "$TARGET_DIR"/
|
||||
|
||||
# 验证结果
|
||||
echo "同步完成。验证结果:"
|
||||
echo "已同步技能:"
|
||||
ls "$SOURCE_DIR"
|
||||
echo ""
|
||||
echo "目标目录内容:"
|
||||
ls "$TARGET_DIR"
|
||||
|
||||
# 检查文件差异
|
||||
echo ""
|
||||
echo "检查关键文件差异:"
|
||||
for skill in $(ls "$SOURCE_DIR"); do
|
||||
if [ -f "$SOURCE_DIR/$skill/SKILL.md" ] && [ -f "$TARGET_DIR/$skill/SKILL.md" ]; then
|
||||
if ! diff -q "$SOURCE_DIR/$skill/SKILL.md" "$TARGET_DIR/$skill/SKILL.md" >/dev/null; then
|
||||
echo " ⚠️ $skill/SKILL.md 存在差异"
|
||||
else
|
||||
echo " ✓ $skill/SKILL.md 一致"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "同步完成!"
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
### 常见问题
|
||||
|
||||
#### 1. 目标目录不存在
|
||||
```bash
|
||||
# 检查并创建目录
|
||||
if [ ! -d ~/.cursor/skills ]; then
|
||||
mkdir -p ~/.cursor/skills
|
||||
echo "已创建目录: ~/.cursor/skills"
|
||||
fi
|
||||
```
|
||||
|
||||
#### 2. 权限不足
|
||||
```bash
|
||||
# 检查权限
|
||||
ls -ld ~/.cursor
|
||||
# 如果需要,调整权限(谨慎操作)
|
||||
# chmod 755 ~/.cursor
|
||||
```
|
||||
|
||||
#### 3. 磁盘空间不足
|
||||
```bash
|
||||
# 检查可用空间
|
||||
df -h ~/.cursor
|
||||
```
|
||||
|
||||
#### 4. 文件冲突
|
||||
```bash
|
||||
# 备份现有文件后再同步
|
||||
BACKUP_DIR=~/.cursor/skills-backup-$(date +%Y%m%d)
|
||||
cp -R ~/.cursor/skills "$BACKUP_DIR"
|
||||
echo "已备份到: $BACKUP_DIR"
|
||||
```
|
||||
|
||||
### 回滚操作
|
||||
```bash
|
||||
# 从备份恢复
|
||||
BACKUP_DIR=~/.cursor/skills-backup-20250123
|
||||
if [ -d "$BACKUP_DIR" ]; then
|
||||
rm -rf ~/.cursor/skills
|
||||
cp -R "$BACKUP_DIR" ~/.cursor/skills
|
||||
echo "已从备份恢复: $BACKUP_DIR"
|
||||
fi
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 定期同步
|
||||
建议在以下时机同步技能:
|
||||
- 添加新技能后
|
||||
- 更新现有技能后
|
||||
- 定期维护时(如每周一次)
|
||||
|
||||
### 版本控制
|
||||
```bash
|
||||
# 将 OpenCode 配置目录加入版本控制
|
||||
cd ~/.config/opencode
|
||||
git status
|
||||
git add skill/
|
||||
git commit -m "feat: 更新技能配置"
|
||||
```
|
||||
|
||||
### 自动化脚本
|
||||
将同步脚本保存为 `~/.config/opencode/command/sync-skills.md` 作为 OpenCode 命令:
|
||||
|
||||
```markdown
|
||||
---
|
||||
description: 同步 OpenCode 技能到 Cursor IDE
|
||||
---
|
||||
#!/bin/bash
|
||||
# 同步脚本内容...
|
||||
```
|
||||
|
||||
## 集成到 OpenCode Workflow
|
||||
|
||||
### 作为技能使用
|
||||
在 OpenCode 技能中引用此文档:
|
||||
```markdown
|
||||
## 相关文档
|
||||
- [技能同步指南](./skill-sync.md) - 同步 OpenCode 技能到 Cursor
|
||||
```
|
||||
|
||||
### 作为命令调用
|
||||
用户可直接请求:"同步技能到 Cursor" 或 "sync skills to cursor"
|
||||
|
||||
### 触发时机
|
||||
- 用户修改技能配置后
|
||||
- 安装新技能后
|
||||
- 系统配置变更时
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **备份重要数据**:同步前建议备份 Cursor 技能目录
|
||||
2. **网络环境**:如果使用网络同步,确保连接稳定
|
||||
3. **兼容性**:确保技能格式与 Cursor 兼容
|
||||
4. **测试验证**:同步后测试关键技能是否正常工作
|
||||
|
||||
## 故障排除
|
||||
|
||||
| 问题 | 可能原因 | 解决方案 |
|
||||
|------|----------|----------|
|
||||
| 同步后技能不生效 | Cursor 缓存 | 重启 Cursor IDE |
|
||||
| 文件权限错误 | 权限不足 | 检查目录权限 |
|
||||
| 目标目录只读 | 系统限制 | 以管理员权限运行 |
|
||||
| 部分技能缺失 | 同步中断 | 重新执行同步 |
|
||||
|
||||
## 更新记录
|
||||
|
||||
- **2026-01-23**:创建初始版本
|
||||
- **功能**:基础同步、错误处理、最佳实践
|
||||
|
||||
---
|
||||
|
||||
*该文档是 OpenCode 技能管理的一部分,用于维护技能配置的一致性。*
|
||||
Reference in New Issue
Block a user