mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-02 10:31:04 +08:00
Merge pull request #292 from hzb1115/dev
aaafix(workflow): fix github workflow
This commit is contained in:
322
.github/PR_TITLE_GUIDE.md
vendored
Normal file
322
.github/PR_TITLE_GUIDE.md
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
# PR 标题指南
|
||||
|
||||
## 📋 概述
|
||||
|
||||
我们使用 **Conventional Commits** 格式来保持 PR 标题的一致性,但这是**建议性的**,不会阻止你的 PR 被合并。
|
||||
|
||||
## ✅ 推荐格式
|
||||
|
||||
```
|
||||
type(scope): description
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
```
|
||||
feat(trader): add new trading strategy
|
||||
fix(api): resolve authentication issue
|
||||
docs: update README
|
||||
chore(deps): update dependencies
|
||||
ci(workflow): improve GitHub Actions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 详细说明
|
||||
|
||||
### Type(类型)- 必需
|
||||
|
||||
描述这次变更的类型:
|
||||
|
||||
| Type | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `feat` | 新功能 | `feat(trader): add stop-loss feature` |
|
||||
| `fix` | Bug 修复 | `fix(api): handle null response` |
|
||||
| `docs` | 文档变更 | `docs: update installation guide` |
|
||||
| `style` | 代码格式(不影响代码运行) | `style: format code with prettier` |
|
||||
| `refactor` | 重构(既不是新功能也不是修复) | `refactor(exchange): simplify connection logic` |
|
||||
| `perf` | 性能优化 | `perf(ai): optimize prompt processing` |
|
||||
| `test` | 添加或修改测试 | `test(trader): add unit tests` |
|
||||
| `chore` | 构建过程或辅助工具的变动 | `chore: update dependencies` |
|
||||
| `ci` | CI/CD 相关变更 | `ci: add test coverage report` |
|
||||
| `security` | 安全相关修复 | `security: update vulnerable dependencies` |
|
||||
| `build` | 构建系统或外部依赖项变更 | `build: upgrade webpack to v5` |
|
||||
|
||||
### Scope(范围)- 可选
|
||||
|
||||
描述这次变更影响的范围:
|
||||
|
||||
| Scope | 说明 |
|
||||
|-------|------|
|
||||
| `exchange` | 交易所相关 |
|
||||
| `trader` | 交易员/交易策略 |
|
||||
| `ai` | AI 模型相关 |
|
||||
| `api` | API 接口 |
|
||||
| `ui` | 用户界面 |
|
||||
| `frontend` | 前端代码 |
|
||||
| `backend` | 后端代码 |
|
||||
| `security` | 安全相关 |
|
||||
| `deps` | 依赖项 |
|
||||
| `workflow` | GitHub Actions workflows |
|
||||
| `github` | GitHub 配置 |
|
||||
| `actions` | GitHub Actions |
|
||||
| `config` | 配置文件 |
|
||||
| `docker` | Docker 相关 |
|
||||
| `build` | 构建相关 |
|
||||
| `release` | 发布相关 |
|
||||
|
||||
**注意:** 如果变更影响多个范围,可以省略 scope 或选择最主要的。
|
||||
|
||||
### Description(描述)- 必需
|
||||
|
||||
- 使用现在时态("add" 而不是 "added")
|
||||
- 首字母小写
|
||||
- 结尾不加句号
|
||||
- 简洁明了地描述变更内容
|
||||
|
||||
---
|
||||
|
||||
## 🎯 完整示例
|
||||
|
||||
### ✅ 好的 PR 标题
|
||||
|
||||
```
|
||||
feat(trader): add risk management system
|
||||
fix(exchange): resolve connection timeout issue
|
||||
docs: add API documentation for trading endpoints
|
||||
style: apply consistent code formatting
|
||||
refactor(ai): simplify prompt processing logic
|
||||
perf(backend): optimize database queries
|
||||
test(api): add integration tests for auth
|
||||
chore(deps): update TypeScript to 5.0
|
||||
ci(workflow): add automated security scanning
|
||||
security(api): fix SQL injection vulnerability
|
||||
build(docker): optimize Docker image size
|
||||
```
|
||||
|
||||
### ⚠️ 需要改进的标题
|
||||
|
||||
| 不好的标题 | 问题 | 改进后 |
|
||||
|-----------|------|--------|
|
||||
| `update code` | 太模糊 | `refactor(trader): simplify order execution logic` |
|
||||
| `Fixed bug` | 首字母大写,不够具体 | `fix(api): handle edge case in login` |
|
||||
| `Add new feature.` | 有句号,不够具体 | `feat(ui): add dark mode toggle` |
|
||||
| `changes` | 完全不符合格式 | `chore: update dependencies` |
|
||||
| `feat: Added new trading algo` | 时态错误 | `feat(trader): add new trading algorithm` |
|
||||
|
||||
---
|
||||
|
||||
## 🤖 自动检查行为
|
||||
|
||||
### 当 PR 标题不符合格式时
|
||||
|
||||
1. **不会阻止合并** ✅
|
||||
- 检查会标记为"建议"
|
||||
- PR 仍然可以被审查和合并
|
||||
|
||||
2. **会收到友好提示** 💬
|
||||
- 机器人会在 PR 中留言
|
||||
- 提供格式说明和示例
|
||||
- 建议如何改进标题
|
||||
|
||||
3. **可以随时更新** 🔄
|
||||
- 更新 PR 标题后会重新检查
|
||||
- 无需关闭和重新打开 PR
|
||||
|
||||
### 示例评论
|
||||
|
||||
如果你的 PR 标题是 `update workflow`,你会收到这样的评论:
|
||||
|
||||
```markdown
|
||||
## ⚠️ PR Title Format Suggestion
|
||||
|
||||
Your PR title doesn't follow the Conventional Commits format,
|
||||
but this won't block your PR from being merged.
|
||||
|
||||
**Current title:** `update workflow`
|
||||
|
||||
**Recommended format:** `type(scope): description`
|
||||
|
||||
### Valid types:
|
||||
feat, fix, docs, style, refactor, perf, test, chore, ci, security, build
|
||||
|
||||
### Common scopes (optional):
|
||||
exchange, trader, ai, api, ui, frontend, backend, security, deps,
|
||||
workflow, github, actions, config, docker, build, release
|
||||
|
||||
### Examples:
|
||||
- feat(trader): add new trading strategy
|
||||
- fix(api): resolve authentication issue
|
||||
- docs: update README
|
||||
- chore(deps): update dependencies
|
||||
- ci(workflow): improve GitHub Actions
|
||||
|
||||
**Note:** This is a suggestion to improve consistency.
|
||||
Your PR can still be reviewed and merged.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 配置详情
|
||||
|
||||
### 支持的 Types
|
||||
|
||||
在 `.github/workflows/pr-checks.yml` 中配置:
|
||||
|
||||
```yaml
|
||||
types: |
|
||||
feat
|
||||
fix
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
chore
|
||||
ci
|
||||
security
|
||||
build
|
||||
```
|
||||
|
||||
### 支持的 Scopes
|
||||
|
||||
```yaml
|
||||
scopes: |
|
||||
exchange
|
||||
trader
|
||||
ai
|
||||
api
|
||||
ui
|
||||
frontend
|
||||
backend
|
||||
security
|
||||
deps
|
||||
workflow
|
||||
github
|
||||
actions
|
||||
config
|
||||
docker
|
||||
build
|
||||
release
|
||||
```
|
||||
|
||||
### 添加新的 Scope
|
||||
|
||||
如果你需要添加新的 scope,请:
|
||||
|
||||
1. 在 `.github/workflows/pr-checks.yml` 的 `scopes` 部分添加
|
||||
2. 在 `.github/workflows/pr-checks-run.yml` 更新正则表达式(可选)
|
||||
3. 更新本文档
|
||||
|
||||
---
|
||||
|
||||
## 📚 为什么使用 Conventional Commits?
|
||||
|
||||
### 优点
|
||||
|
||||
1. **自动化 Changelog** 📝
|
||||
- 可以自动生成版本更新日志
|
||||
- 清晰地分类各种变更
|
||||
|
||||
2. **语义化版本** 🔢
|
||||
- `feat` → MINOR 版本(1.1.0)
|
||||
- `fix` → PATCH 版本(1.0.1)
|
||||
- `BREAKING CHANGE` → MAJOR 版本(2.0.0)
|
||||
|
||||
3. **更好的可读性** 👀
|
||||
- 一眼看出 PR 的目的
|
||||
- 更容易浏览 Git 历史
|
||||
|
||||
4. **团队协作** 🤝
|
||||
- 统一的提交风格
|
||||
- 降低沟通成本
|
||||
|
||||
### 示例:自动生成的 Changelog
|
||||
|
||||
```markdown
|
||||
## v1.2.0 (2025-11-02)
|
||||
|
||||
### Features
|
||||
- **trader**: add risk management system (#123)
|
||||
- **ui**: add dark mode toggle (#125)
|
||||
|
||||
### Bug Fixes
|
||||
- **api**: resolve authentication issue (#124)
|
||||
- **exchange**: fix connection timeout (#126)
|
||||
|
||||
### Documentation
|
||||
- update API documentation (#127)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习资源
|
||||
|
||||
- **Conventional Commits 官网:** https://www.conventionalcommits.org/
|
||||
- **Angular Commit Guidelines:** https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit
|
||||
- **Semantic Versioning:** https://semver.org/
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQ
|
||||
|
||||
### Q: 我必须遵循这个格式吗?
|
||||
|
||||
**A:** 不必须。这是建议性的,不会阻止你的 PR 被合并。但遵循格式可以提高项目的可维护性。
|
||||
|
||||
### Q: 如果我忘记了怎么办?
|
||||
|
||||
**A:** 机器人会在 PR 中提醒你,你可以随时更新标题。
|
||||
|
||||
### Q: 我可以在一个 PR 中做多种类型的变更吗?
|
||||
|
||||
**A:** 可以,但建议:
|
||||
- 选择最主要的类型
|
||||
- 或者考虑拆分成多个 PR(更易于审查)
|
||||
|
||||
### Q: Scope 可以省略吗?
|
||||
|
||||
**A:** 可以。`requireScope: false` 表示 scope 是可选的。
|
||||
|
||||
示例:`docs: update README` (没有 scope 也可以)
|
||||
|
||||
### Q: 我想添加新的 type 或 scope,怎么做?
|
||||
|
||||
**A:** 提一个 PR 修改 `.github/workflows/pr-checks.yml`,并在本文档中说明新增项的用途。
|
||||
|
||||
### Q: Breaking Changes 怎么表示?
|
||||
|
||||
**A:** 在描述中添加 `BREAKING CHANGE:` 或在 type 后加 `!`:
|
||||
|
||||
```
|
||||
feat!: remove deprecated API
|
||||
feat(api)!: change authentication method
|
||||
|
||||
BREAKING CHANGE: The old /auth endpoint is removed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 统计
|
||||
|
||||
想看项目的 commit 类型分布?运行:
|
||||
|
||||
```bash
|
||||
git log --oneline --no-merges | \
|
||||
grep -oE '^[a-f0-9]+ (feat|fix|docs|style|refactor|perf|test|chore|ci|security|build)' | \
|
||||
cut -d' ' -f2 | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 快速检查清单
|
||||
|
||||
在提交 PR 前,检查你的标题是否:
|
||||
|
||||
- [ ] 包含有效的 type(feat, fix, docs 等)
|
||||
- [ ] 使用小写字母开头
|
||||
- [ ] 使用现在时态("add" 而不是 "added")
|
||||
- [ ] 简洁明了(最好在 50 字符内)
|
||||
- [ ] 准确描述了变更内容
|
||||
|
||||
**记住:** 这些都是建议,不是强制要求!
|
||||
279
.github/PULL_REQUEST_TEMPLATE.md
vendored
279
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,153 +1,288 @@
|
||||
# Pull Request
|
||||
# Pull Request | PR 提交
|
||||
|
||||
## 📝 Description
|
||||
> **💡 提示 Tip:** 推荐 PR 标题格式 Recommended PR title format: `type(scope): description`
|
||||
> 例如 Examples: `feat(trader): add new strategy` | `fix(api): resolve auth issue`
|
||||
> 详情 Details: [PR Title Guide](./PR_TITLE_GUIDE.md)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Description | 描述
|
||||
|
||||
<!-- Provide a brief summary of your changes -->
|
||||
<!-- 简要描述你的变更 -->
|
||||
|
||||
## 🎯 Type of Change
|
||||
**English:**
|
||||
|
||||
**中文:**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Type of Change | 变更类型
|
||||
|
||||
<!-- Mark the relevant option with an "x" -->
|
||||
<!-- 在相关选项上打"x" -->
|
||||
|
||||
- [ ] 🐛 Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] ✨ New feature (non-breaking change which adds functionality)
|
||||
- [ ] 💥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] 📝 Documentation update
|
||||
- [ ] 🎨 Code style update (formatting, renaming)
|
||||
- [ ] ♻️ Refactoring (no functional changes)
|
||||
- [ ] ⚡ Performance improvement
|
||||
- [ ] ✅ Test update
|
||||
- [ ] 🔧 Build/config change
|
||||
- [ ] 🐛 Bug fix | 修复 Bug(不影响现有功能的修复)
|
||||
- [ ] ✨ New feature | 新功能(不影响现有功能的新增)
|
||||
- [ ] 💥 Breaking change | 破坏性变更(会导致现有功能无法正常工作的修复或功能)
|
||||
- [ ] 📝 Documentation update | 文档更新
|
||||
- [ ] 🎨 Code style update | 代码样式更新(格式化、重命名等)
|
||||
- [ ] ♻️ Refactoring | 重构(无功能变更)
|
||||
- [ ] ⚡ Performance improvement | 性能优化
|
||||
- [ ] ✅ Test update | 测试更新
|
||||
- [ ] 🔧 Build/config change | 构建/配置变更
|
||||
- [ ] 🔒 Security fix | 安全修复
|
||||
|
||||
## 🔗 Related Issues
|
||||
---
|
||||
|
||||
## 🔗 Related Issues | 相关 Issue
|
||||
|
||||
<!-- Link related issues below. Use "Closes #123" to auto-close issues when PR is merged -->
|
||||
<!-- 在下方关联相关 issue。使用 "Closes #123" 可以在 PR 合并时自动关闭 issue -->
|
||||
|
||||
- Closes #
|
||||
- Related to #
|
||||
- Closes # | 关闭 #
|
||||
- Related to # | 相关 #
|
||||
|
||||
## 📋 Changes Made
|
||||
---
|
||||
|
||||
## 📋 Changes Made | 具体变更
|
||||
|
||||
<!-- List the specific changes you made -->
|
||||
<!-- 列出你做的具体变更 -->
|
||||
|
||||
**English:**
|
||||
- Change 1
|
||||
- Change 2
|
||||
- Change 3
|
||||
|
||||
## 🧪 Testing
|
||||
**中文:**
|
||||
- 变更 1
|
||||
- 变更 2
|
||||
- 变更 3
|
||||
|
||||
### Manual Testing
|
||||
---
|
||||
|
||||
## 🧪 Testing | 测试
|
||||
|
||||
### Manual Testing | 手动测试
|
||||
|
||||
<!-- Describe how you tested your changes -->
|
||||
<!-- 描述你如何测试你的变更 -->
|
||||
|
||||
- [ ] Tested locally (manual verification)
|
||||
- [ ] Tested on testnet (for exchange integrations)
|
||||
- [ ] Tested with different configurations
|
||||
- [ ] Verified no existing functionality broke
|
||||
- [ ] Tested locally | 本地测试通过
|
||||
- [ ] Tested on testnet | 测试网测试通过(交易所集成相关)
|
||||
- [ ] Tested with different configurations | 测试了不同配置
|
||||
- [ ] Verified no existing functionality broke | 确认没有破坏现有功能
|
||||
|
||||
### Test Environment
|
||||
### Test Environment | 测试环境
|
||||
|
||||
- **OS:** [e.g. macOS, Ubuntu]
|
||||
- **Go Version:** [e.g. 1.21.5]
|
||||
- **Exchange:** [if applicable]
|
||||
- **OS | 操作系统:** [e.g. macOS, Ubuntu, Windows]
|
||||
- **Go Version | Go 版本:** [e.g. 1.21.5]
|
||||
- **Node Version | Node 版本:** [e.g. 18.x] (if applicable | 如适用)
|
||||
- **Exchange | 交易所:** [if applicable | 如适用]
|
||||
|
||||
### Test Results
|
||||
### Test Results | 测试结果
|
||||
|
||||
<!-- Paste relevant test output or describe results -->
|
||||
<!-- 粘贴相关测试输出或描述结果 -->
|
||||
|
||||
```
|
||||
Test output here
|
||||
Test output here | 测试输出
|
||||
```
|
||||
|
||||
## 📸 Screenshots / Demo
|
||||
---
|
||||
|
||||
## 📸 Screenshots / Demo | 截图/演示
|
||||
|
||||
<!-- If applicable, add screenshots or video demo -->
|
||||
<!-- 如适用,添加截图或视频演示 -->
|
||||
|
||||
<!-- For UI changes, include before/after screenshots -->
|
||||
<!-- 对于 UI 变更,包含变更前后的截图 -->
|
||||
|
||||
**Before:**
|
||||
**Before | 变更前:**
|
||||
|
||||
|
||||
**After:**
|
||||
**After | 变更后:**
|
||||
|
||||
|
||||
## ✅ Checklist
|
||||
---
|
||||
|
||||
## ✅ Checklist | 检查清单
|
||||
|
||||
<!-- Mark completed items with an "x" -->
|
||||
<!-- 在已完成的项目上打"x" -->
|
||||
|
||||
### Code Quality
|
||||
### Code Quality | 代码质量
|
||||
|
||||
- [ ] My code follows the project's code style ([Contributing Guide](../CONTRIBUTING.md))
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] My changes generate no new warnings or errors
|
||||
- [ ] Code compiles successfully (`go build` / `npm run build`)
|
||||
- [ ] I have run `go fmt` (for Go code)
|
||||
- [ ] My code follows the project's code style | 我的代码遵循项目代码风格 ([Contributing Guide](../CONTRIBUTING.md))
|
||||
- [ ] I have performed a self-review of my code | 我已进行代码自查
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas | 我已添加代码注释,特别是难以理解的部分
|
||||
- [ ] My changes generate no new warnings or errors | 我的变更没有产生新的警告或错误
|
||||
- [ ] Code compiles successfully | 代码编译成功 (`go build` / `npm run build`)
|
||||
- [ ] I have run `go fmt` (for Go code) | 我已运行 `go fmt`(Go 代码)
|
||||
- [ ] I have run `npm run lint` (for frontend code) | 我已运行 `npm run lint`(前端代码)
|
||||
|
||||
### Testing
|
||||
### Testing | 测试
|
||||
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally
|
||||
- [ ] I have tested on testnet (for trading/exchange changes)
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works | 我已添加证明修复有效或功能正常的测试
|
||||
- [ ] New and existing unit tests pass locally | 新旧单元测试在本地通过
|
||||
- [ ] I have tested on testnet (for trading/exchange changes) | 我已在测试网测试(交易/交易所变更)
|
||||
- [ ] Integration tests pass | 集成测试通过
|
||||
|
||||
### Documentation
|
||||
### Documentation | 文档
|
||||
|
||||
- [ ] I have updated the documentation accordingly
|
||||
- [ ] I have updated the README if needed
|
||||
- [ ] I have added inline code comments where necessary
|
||||
- [ ] I have updated type definitions (for TypeScript changes)
|
||||
- [ ] I have updated the documentation accordingly | 我已相应更新文档
|
||||
- [ ] I have updated the README if needed | 我已更新 README(如需要)
|
||||
- [ ] I have added inline code comments where necessary | 我已在必要处添加代码注释
|
||||
- [ ] I have updated type definitions (for TypeScript changes) | 我已更新类型定义(TypeScript 变更)
|
||||
- [ ] I have updated API documentation (if applicable) | 我已更新 API 文档(如适用)
|
||||
|
||||
### Git
|
||||
|
||||
- [ ] My commits follow the conventional commits format (`feat:`, `fix:`, etc.)
|
||||
- [ ] I have rebased my branch on the latest `dev` branch
|
||||
- [ ] There are no merge conflicts
|
||||
- [ ] My commits follow the conventional commits format | 我的提交遵循 Conventional Commits 格式 (`feat:`, `fix:`, etc.)
|
||||
- [ ] I have rebased my branch on the latest `dev` branch | 我已将分支 rebase 到最新的 `dev` 分支
|
||||
- [ ] There are no merge conflicts | 没有合并冲突
|
||||
- [ ] Commit messages are clear and descriptive | 提交信息清晰明确
|
||||
|
||||
## 🔒 Security Considerations
|
||||
---
|
||||
|
||||
## 🔒 Security Considerations | 安全考虑
|
||||
|
||||
<!-- Answer these questions for security-sensitive changes -->
|
||||
<!-- 对于安全敏感的变更,请回答以下问题 -->
|
||||
|
||||
- [ ] No API keys or secrets are hardcoded
|
||||
- [ ] User inputs are properly validated
|
||||
- [ ] No SQL injection vulnerabilities introduced
|
||||
- [ ] Authentication/authorization properly handled
|
||||
- [ ] N/A (not security-related)
|
||||
- [ ] No API keys or secrets are hardcoded | 没有硬编码 API 密钥或密钥
|
||||
- [ ] User inputs are properly validated | 用户输入已正确验证
|
||||
- [ ] No SQL injection vulnerabilities introduced | 未引入 SQL 注入漏洞
|
||||
- [ ] No XSS vulnerabilities introduced | 未引入 XSS 漏洞
|
||||
- [ ] Authentication/authorization properly handled | 认证/授权已正确处理
|
||||
- [ ] Sensitive data is encrypted | 敏感数据已加密
|
||||
- [ ] N/A (not security-related) | 不适用(非安全相关)
|
||||
|
||||
## ⚡ Performance Impact
|
||||
---
|
||||
|
||||
## ⚡ Performance Impact | 性能影响
|
||||
|
||||
<!-- Describe any performance implications -->
|
||||
<!-- 描述任何性能影响 -->
|
||||
|
||||
- [ ] No significant performance impact
|
||||
- [ ] Performance improved
|
||||
- [ ] Performance may be impacted (explain below)
|
||||
- [ ] No significant performance impact | 无显著性能影响
|
||||
- [ ] Performance improved | 性能提升
|
||||
- [ ] Performance may be impacted (explain below) | 性能可能受影响(请在下方说明)
|
||||
|
||||
<!-- If performance impacted, explain: -->
|
||||
<!-- 如果性能受影响,请说明: -->
|
||||
|
||||
## 📚 Additional Notes
|
||||
**English:**
|
||||
|
||||
**中文:**
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Internationalization | 国际化
|
||||
|
||||
<!-- For UI/documentation changes -->
|
||||
<!-- 对于 UI/文档变更 -->
|
||||
|
||||
- [ ] All user-facing text supports i18n | 所有面向用户的文本支持国际化
|
||||
- [ ] Both English and Chinese versions provided | 提供了中英文版本
|
||||
- [ ] N/A | 不适用
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Notes | 补充说明
|
||||
|
||||
<!-- Any additional information for reviewers -->
|
||||
<!-- 给审查者的任何补充信息 -->
|
||||
|
||||
**English:**
|
||||
|
||||
**中文:**
|
||||
|
||||
---
|
||||
|
||||
## For Bounty Claims
|
||||
## 💰 For Bounty Claims | 赏金申请
|
||||
|
||||
<!-- Fill this section only if claiming a bounty -->
|
||||
<!-- 仅在申请赏金时填写此部分 -->
|
||||
|
||||
- [ ] This PR is for bounty issue #
|
||||
- [ ] All acceptance criteria from the bounty issue are met
|
||||
- [ ] I have included a demo video/screenshots
|
||||
- [ ] I am ready for payment upon merge
|
||||
- [ ] This PR is for bounty issue # | 此 PR 用于赏金 issue #
|
||||
- [ ] All acceptance criteria from the bounty issue are met | 满足赏金 issue 的所有验收标准
|
||||
- [ ] I have included a demo video/screenshots | 我已包含演示视频/截图
|
||||
- [ ] I am ready for payment upon merge | 我准备好在合并后接收付款
|
||||
|
||||
**Payment Details:** <!-- Discuss privately with maintainers -->
|
||||
**Payment Details | 付款详情:** <!-- Discuss privately with maintainers | 与维护者私下讨论 -->
|
||||
|
||||
---
|
||||
|
||||
## 🙏 Reviewer Notes
|
||||
## 🙏 Reviewer Notes | 审查者注意事项
|
||||
|
||||
<!-- Optional: anything specific you want reviewers to focus on? -->
|
||||
<!-- 可选:你希望审查者关注的特定内容? -->
|
||||
|
||||
**English:**
|
||||
|
||||
**中文:**
|
||||
|
||||
---
|
||||
|
||||
## 📋 PR Size Estimate | PR 大小估计
|
||||
|
||||
<!-- This helps reviewers plan their time -->
|
||||
<!-- 这有助于审查者安排时间 -->
|
||||
|
||||
- [ ] 🟢 Small (< 100 lines) | 小(< 100 行)
|
||||
- [ ] 🟡 Medium (100-500 lines) | 中(100-500 行)
|
||||
- [ ] 🔴 Large (> 500 lines) | 大(> 500 行)
|
||||
|
||||
<!-- For large PRs, consider: -->
|
||||
<!-- 对于大型 PR,考虑: -->
|
||||
<!-- - Breaking into smaller, focused PRs | 拆分为更小、更专注的 PR -->
|
||||
<!-- - Providing a detailed explanation | 提供详细说明 -->
|
||||
<!-- - Highlighting the most important changes | 突出最重要的变更 -->
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Review Focus Areas | 审查重点
|
||||
|
||||
<!-- Help reviewers know where to focus their attention -->
|
||||
<!-- 帮助审查者了解重点关注的地方 -->
|
||||
|
||||
Please pay special attention to:
|
||||
请特别注意:
|
||||
|
||||
- [ ] Logic changes | 逻辑变更
|
||||
- [ ] Security implications | 安全影响
|
||||
- [ ] Performance optimization | 性能优化
|
||||
- [ ] API changes | API 变更
|
||||
- [ ] Database schema changes | 数据库架构变更
|
||||
- [ ] UI/UX changes | UI/UX 变更
|
||||
|
||||
---
|
||||
|
||||
**By submitting this PR, I confirm that:**
|
||||
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md)
|
||||
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md)
|
||||
- [ ] My contribution is licensed under the MIT License
|
||||
**提交此 PR,我确认:**
|
||||
|
||||
- [ ] I have read the [Contributing Guidelines](../CONTRIBUTING.md) | 我已阅读[贡献指南](../CONTRIBUTING.md)
|
||||
- [ ] I agree to the [Code of Conduct](../CODE_OF_CONDUCT.md) | 我同意[行为准则](../CODE_OF_CONDUCT.md)
|
||||
- [ ] My contribution is licensed under the MIT License | 我的贡献遵循 MIT 许可证
|
||||
- [ ] I understand this is a voluntary contribution | 我理解这是自愿贡献
|
||||
- [ ] I have the right to submit this code | 我有权提交此代码
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
🌟 感谢你的贡献!Thank you for your contribution!
|
||||
|
||||
贡献者来自世界各地,我们重视每一份贡献。
|
||||
Contributors come from all around the world, and we value every contribution.
|
||||
|
||||
如果你是首次贡献,欢迎加入我们的社区!
|
||||
If this is your first contribution, welcome to our community!
|
||||
|
||||
💬 需要帮助?Feel free to ask questions in:
|
||||
- GitHub Discussions
|
||||
- Discord: [链接 Link]
|
||||
- Telegram: [链接 Link]
|
||||
-->
|
||||
|
||||
176
.github/workflows/README.md
vendored
Normal file
176
.github/workflows/README.md
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
# GitHub Actions Workflows
|
||||
|
||||
This directory contains the GitHub Actions workflows for the NOFX project.
|
||||
|
||||
## 📚 Documentation Index
|
||||
|
||||
- **[README.md](./README.md)** - This file, overview of all workflows
|
||||
- **[PERMISSIONS.md](./PERMISSIONS.md)** - Detailed permission analysis and security model
|
||||
- **[TRIGGERS.md](./TRIGGERS.md)** - Comparison of event triggers (pull_request vs pull_request_target vs workflow_run)
|
||||
- **[FORK_PR_FLOW.md](./FORK_PR_FLOW.md)** - Complete analysis of what happens when a fork PR is submitted
|
||||
- **[FLOW_DIAGRAM.md](./FLOW_DIAGRAM.md)** - Visual flow diagrams and quick reference
|
||||
- **[SECRETS_SCANNING.md](./SECRETS_SCANNING.md)** - Secrets scanning solutions and TruffleHog setup
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
**Want to understand how fork PRs work?** → Read [FLOW_DIAGRAM.md](./FLOW_DIAGRAM.md)
|
||||
|
||||
**Need security details?** → Read [PERMISSIONS.md](./PERMISSIONS.md)
|
||||
|
||||
**Confused about triggers?** → Read [TRIGGERS.md](./TRIGGERS.md)
|
||||
|
||||
## PR Check Workflows
|
||||
|
||||
We use a **two-workflow pattern** to safely handle PR checks from both internal and fork PRs:
|
||||
|
||||
### 1. `pr-checks-run.yml` - Execute Checks
|
||||
|
||||
**Trigger:** On pull request (opened, synchronize, reopened)
|
||||
|
||||
**Permissions:** Read-only
|
||||
|
||||
**Purpose:** Executes all PR checks with read-only permissions, making it safe for fork PRs.
|
||||
|
||||
**What it does:**
|
||||
- ✅ Checks PR title format (Conventional Commits)
|
||||
- ✅ Calculates PR size
|
||||
- ✅ Runs backend checks (Go formatting, vet, tests)
|
||||
- ✅ Runs frontend checks (linting, type checking, build)
|
||||
- ✅ Saves all results as artifacts
|
||||
|
||||
**Security:** Safe for fork PRs because it only has read permissions and cannot access secrets or modify the repository.
|
||||
|
||||
### 2. `pr-checks-comment.yml` - Post Results
|
||||
|
||||
**Trigger:** When `pr-checks-run.yml` completes (workflow_run)
|
||||
|
||||
**Permissions:** Write (pull-requests, issues)
|
||||
|
||||
**Purpose:** Posts check results as PR comments, running in the main repository context.
|
||||
|
||||
**What it does:**
|
||||
- ✅ Downloads artifacts from `pr-checks-run.yml`
|
||||
- ✅ Reads check results
|
||||
- ✅ Posts a comprehensive comment to the PR
|
||||
|
||||
**Security:** Safe because:
|
||||
- Runs in the main repository context (not fork context)
|
||||
- Has write permissions but doesn't execute untrusted code
|
||||
- Only reads pre-generated results from artifacts
|
||||
|
||||
### 3. `pr-checks.yml` - Strict Checks
|
||||
|
||||
**Trigger:** On pull request
|
||||
|
||||
**Permissions:** Read + conditional write
|
||||
|
||||
**Purpose:** Runs mandatory checks that must pass before PR can be merged.
|
||||
|
||||
**What it does:**
|
||||
- ✅ Validates PR title (blocks merge if invalid)
|
||||
- ✅ Auto-labels PR based on size and files changed (non-fork only)
|
||||
- ✅ Runs backend tests (Go)
|
||||
- ✅ Runs frontend tests (React/TypeScript)
|
||||
- ✅ Security scanning (Trivy, Gitleaks)
|
||||
|
||||
**Security:**
|
||||
- Fork PRs: Only runs read-only operations (tests, security scans)
|
||||
- Non-fork PRs: Can add labels and comments
|
||||
- Uses `continue-on-error` for operations that may fail on forks
|
||||
|
||||
## Why Two Workflows for PR Checks?
|
||||
|
||||
### The Problem
|
||||
|
||||
When a PR comes from a forked repository:
|
||||
- GitHub restricts `GITHUB_TOKEN` permissions for security
|
||||
- Fork PRs cannot write comments, add labels, or access secrets
|
||||
- This prevents malicious contributors from:
|
||||
- Stealing repository secrets
|
||||
- Modifying workflow files to execute malicious code
|
||||
- Spamming issues/PRs with automated comments
|
||||
|
||||
### The Solution
|
||||
|
||||
**Two-Workflow Pattern:**
|
||||
|
||||
```
|
||||
Fork PR Submitted
|
||||
↓
|
||||
[pr-checks-run.yml]
|
||||
- Runs with read-only permissions
|
||||
- Executes all checks safely
|
||||
- Saves results to artifacts
|
||||
↓
|
||||
[pr-checks-comment.yml]
|
||||
- Triggered by workflow_run
|
||||
- Runs in main repo context (has write permissions)
|
||||
- Downloads artifacts
|
||||
- Posts comment with results
|
||||
```
|
||||
|
||||
This approach:
|
||||
- ✅ Allows fork PRs to run checks
|
||||
- ✅ Safely posts results as comments
|
||||
- ✅ Prevents security vulnerabilities
|
||||
- ✅ Follows GitHub's best practices
|
||||
|
||||
### Can workflow_run Comment on Fork PRs?
|
||||
|
||||
**Yes! ✅ The permissions are sufficient.**
|
||||
|
||||
**Key Understanding:**
|
||||
- `workflow_run` executes in the **base repository** context
|
||||
- Fork PRs exist in the **base repository** (not in the fork)
|
||||
- The base repository's `GITHUB_TOKEN` has write permissions
|
||||
- Therefore, `workflow_run` can comment on fork PRs
|
||||
|
||||
**Security:**
|
||||
- Fork PR code runs in isolated environment (read-only)
|
||||
- Comment workflow doesn't execute fork code
|
||||
- Only reads pre-generated artifact data
|
||||
|
||||
**For detailed permission analysis, see:** [PERMISSIONS.md](./PERMISSIONS.md)
|
||||
|
||||
## Workflow Comparison
|
||||
|
||||
| Workflow | Fork PRs | Write Access | Blocks Merge | Purpose |
|
||||
|----------|----------|--------------|--------------|---------|
|
||||
| `pr-checks-run.yml` | ✅ Yes | ❌ No | ❌ No | Advisory checks |
|
||||
| `pr-checks-comment.yml` | ✅ Yes | ✅ Yes* | ❌ No | Post results |
|
||||
| `pr-checks.yml` | ✅ Yes | ⚠️ Partial | ✅ Yes | Mandatory checks |
|
||||
|
||||
\* Write access only in main repo context, not available to fork PR code
|
||||
|
||||
## File History
|
||||
|
||||
- `pr-checks-advisory.yml.old` - Old advisory workflow that failed on fork PRs (deprecated)
|
||||
- Now replaced by the two-workflow pattern (`pr-checks-run.yml` + `pr-checks-comment.yml`)
|
||||
|
||||
## Testing the Workflows
|
||||
|
||||
### Test with a Fork PR
|
||||
|
||||
1. Fork the repository
|
||||
2. Make changes in your fork
|
||||
3. Create a PR to the main repository
|
||||
4. Observe:
|
||||
- `pr-checks-run.yml` runs successfully with read-only access
|
||||
- `pr-checks-comment.yml` posts results as a comment
|
||||
- `pr-checks.yml` runs tests but skips labeling
|
||||
|
||||
### Test with a Branch PR
|
||||
|
||||
1. Create a branch in the main repository
|
||||
2. Make changes
|
||||
3. Create a PR
|
||||
4. Observe:
|
||||
- All workflows run with full permissions
|
||||
- Labels are added automatically
|
||||
- Comments are posted
|
||||
|
||||
## References
|
||||
|
||||
- [GitHub Actions: Keeping your GitHub Actions and workflows secure Part 1](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/)
|
||||
- [Safely posting comments from untrusted workflows](https://securitylab.github.com/research/github-actions-building-blocks/)
|
||||
- [GitHub Actions: workflow_run trigger](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_run)
|
||||
248
.github/workflows/pr-checks-comment.yml
vendored
Normal file
248
.github/workflows/pr-checks-comment.yml
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
name: PR Checks - Comment
|
||||
|
||||
# This workflow posts ADVISORY check results as comments
|
||||
# Runs in the main repo context with write permissions (SAFE)
|
||||
# Triggered after pr-checks-run.yml completes
|
||||
#
|
||||
# NOTE: PR title and size checks are handled by pr-checks.yml (no duplication)
|
||||
# This workflow only posts backend/frontend advisory check results
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["PR Checks - Run"]
|
||||
types: [completed]
|
||||
|
||||
# Write permissions - SAFE because runs in main repo context
|
||||
# This token has write access to the base repository
|
||||
# Fork PRs exist in the base repo, so we can comment on them
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
actions: read # Needed to download artifacts
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
name: Post Advisory Check Results
|
||||
runs-on: ubuntu-latest
|
||||
# Only run if the workflow was triggered by a pull_request event
|
||||
if: github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
id: download-artifacts
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
path: artifacts
|
||||
|
||||
- name: Debug workflow run info
|
||||
run: |
|
||||
echo "=== Workflow Run Debug Info ==="
|
||||
echo "Workflow Run ID: ${{ github.event.workflow_run.id }}"
|
||||
echo "Workflow Run Event: ${{ github.event.workflow_run.event }}"
|
||||
echo "Workflow Run Conclusion: ${{ github.event.workflow_run.conclusion }}"
|
||||
echo "Workflow Run Head SHA: ${{ github.event.workflow_run.head_sha }}"
|
||||
|
||||
- name: List downloaded artifacts
|
||||
run: |
|
||||
echo "=== Checking downloaded artifacts ==="
|
||||
ls -la artifacts/ || echo "⚠️ No artifacts directory found"
|
||||
find artifacts/ -type f || echo "⚠️ No files found in artifacts"
|
||||
echo ""
|
||||
echo "Artifact download result: ${{ steps.download-artifacts.outcome }}"
|
||||
|
||||
- name: Read backend results
|
||||
id: backend
|
||||
continue-on-error: true
|
||||
run: |
|
||||
if [ -f artifacts/backend-results/backend-results.json ]; then
|
||||
echo "=== Backend Results JSON ==="
|
||||
cat artifacts/backend-results/backend-results.json
|
||||
echo "pr_number=$(jq -r '.pr_number' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
|
||||
echo "fmt_status=$(jq -r '.fmt_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
|
||||
echo "vet_status=$(jq -r '.vet_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
|
||||
echo "test_status=$(jq -r '.test_status' artifacts/backend-results/backend-results.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
# Read output files
|
||||
if [ -f artifacts/backend-results/fmt-files.txt ]; then
|
||||
echo "fmt_files<<EOF" >> $GITHUB_OUTPUT
|
||||
cat artifacts/backend-results/fmt-files.txt >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
if [ -f artifacts/backend-results/vet-output-short.txt ]; then
|
||||
echo "vet_output<<EOF" >> $GITHUB_OUTPUT
|
||||
cat artifacts/backend-results/vet-output-short.txt >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
if [ -f artifacts/backend-results/test-output-short.txt ]; then
|
||||
echo "test_output<<EOF" >> $GITHUB_OUTPUT
|
||||
cat artifacts/backend-results/test-output-short.txt >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "pr_number=0" >> $GITHUB_OUTPUT
|
||||
echo "⚠️ Backend results artifact not found"
|
||||
fi
|
||||
|
||||
- name: Read frontend results
|
||||
id: frontend
|
||||
continue-on-error: true
|
||||
run: |
|
||||
if [ -f artifacts/frontend-results/frontend-results.json ]; then
|
||||
echo "=== Frontend Results JSON ==="
|
||||
cat artifacts/frontend-results/frontend-results.json
|
||||
echo "build_status=$(jq -r '.build_status' artifacts/frontend-results/frontend-results.json)" >> $GITHUB_OUTPUT
|
||||
|
||||
# Read output files
|
||||
if [ -f artifacts/frontend-results/build-output-short.txt ]; then
|
||||
echo "build_output<<EOF" >> $GITHUB_OUTPUT
|
||||
cat artifacts/frontend-results/build-output-short.txt >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "⚠️ Frontend results artifact not found"
|
||||
fi
|
||||
|
||||
- name: Post advisory results comment
|
||||
if: steps.backend.outputs.pr_number != '0'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const prNumber = ${{ steps.backend.outputs.pr_number }};
|
||||
|
||||
let comment = '## 🤖 Advisory Check Results\n\n';
|
||||
comment += 'These are **advisory** checks to help improve code quality. They won\'t block your PR from being merged.\n\n';
|
||||
comment += '> **Note:** PR title and size checks are handled by the main workflow and may appear in a separate comment.\n\n';
|
||||
|
||||
// Backend checks
|
||||
const fmtStatus = '${{ steps.backend.outputs.fmt_status }}';
|
||||
const vetStatus = '${{ steps.backend.outputs.vet_status }}';
|
||||
const testStatus = '${{ steps.backend.outputs.test_status }}';
|
||||
|
||||
if (fmtStatus || vetStatus || testStatus) {
|
||||
comment += '\n### 🔧 Backend Checks\n\n';
|
||||
|
||||
if (fmtStatus) {
|
||||
comment += '**Go Formatting:** ' + fmtStatus + '\n';
|
||||
const fmtFiles = `${{ steps.backend.outputs.fmt_files }}`;
|
||||
if (fmtFiles && fmtFiles.trim()) {
|
||||
comment += '<details><summary>Files needing formatting</summary>\n\n```\n' + fmtFiles + '\n```\n</details>\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (vetStatus) {
|
||||
comment += '**Go Vet:** ' + vetStatus + '\n';
|
||||
const vetOutput = `${{ steps.backend.outputs.vet_output }}`;
|
||||
if (vetOutput && vetOutput.trim()) {
|
||||
comment += '<details><summary>Issues found</summary>\n\n```\n' + vetOutput.substring(0, 1000) + '\n```\n</details>\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (testStatus) {
|
||||
comment += '**Tests:** ' + testStatus + '\n';
|
||||
const testOutput = `${{ steps.backend.outputs.test_output }}`;
|
||||
if (testOutput && testOutput.trim()) {
|
||||
comment += '<details><summary>Test output</summary>\n\n```\n' + testOutput.substring(0, 1000) + '\n```\n</details>\n\n';
|
||||
}
|
||||
}
|
||||
|
||||
comment += '\n**Fix locally:**\n';
|
||||
comment += '```bash\n';
|
||||
comment += 'go fmt ./... # Format code\n';
|
||||
comment += 'go vet ./... # Check for issues\n';
|
||||
comment += 'go test ./... # Run tests\n';
|
||||
comment += '```\n';
|
||||
}
|
||||
|
||||
// Frontend checks
|
||||
const buildStatus = '${{ steps.frontend.outputs.build_status }}';
|
||||
|
||||
if (buildStatus) {
|
||||
comment += '\n### ⚛️ Frontend Checks\n\n';
|
||||
|
||||
comment += '**Build & Type Check:** ' + buildStatus + '\n';
|
||||
const buildOutput = `${{ steps.frontend.outputs.build_output }}`;
|
||||
if (buildOutput && buildOutput.trim()) {
|
||||
comment += '<details><summary>Build output</summary>\n\n```\n' + buildOutput.substring(0, 1000) + '\n```\n</details>\n\n';
|
||||
}
|
||||
|
||||
comment += '\n**Fix locally:**\n';
|
||||
comment += '```bash\n';
|
||||
comment += 'cd web\n';
|
||||
comment += 'npm run build # Test build (includes type checking)\n';
|
||||
comment += '```\n';
|
||||
}
|
||||
|
||||
comment += '\n---\n\n';
|
||||
comment += '### 📖 Resources\n\n';
|
||||
comment += '- [Contributing Guidelines](https://github.com/tinkle-community/nofx/blob/dev/CONTRIBUTING.md)\n';
|
||||
comment += '- [Migration Guide](https://github.com/tinkle-community/nofx/blob/dev/docs/community/MIGRATION_ANNOUNCEMENT.md)\n\n';
|
||||
comment += '**Questions?** Feel free to ask in the comments! 🙏\n\n';
|
||||
comment += '---\n\n';
|
||||
comment += '*These checks are advisory and won\'t block your PR from being merged. This comment is automatically generated from [pr-checks-run.yml](https://github.com/tinkle-community/nofx/blob/dev/.github/workflows/pr-checks-run.yml).*';
|
||||
|
||||
// Post comment
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment
|
||||
});
|
||||
|
||||
- name: Post fallback comment if no results
|
||||
if: steps.backend.outputs.pr_number == '0'
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// Try to get PR number from the workflow_run event
|
||||
const pulls = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
head: `${context.repo.owner}:${{ github.event.workflow_run.head_branch }}`
|
||||
});
|
||||
|
||||
if (pulls.data.length === 0) {
|
||||
console.log('⚠️ Could not find PR for this workflow run');
|
||||
return;
|
||||
}
|
||||
|
||||
const prNumber = pulls.data[0].number;
|
||||
|
||||
const comment = [
|
||||
'## ⚠️ Advisory Checks - Results Unavailable',
|
||||
'',
|
||||
'The advisory checks workflow completed, but results could not be retrieved.',
|
||||
'',
|
||||
'### Possible reasons:',
|
||||
'- Artifacts were not uploaded successfully',
|
||||
'- Artifacts expired (retention: 1 day)',
|
||||
'- Permission issues',
|
||||
'',
|
||||
'### What to do:',
|
||||
'1. Check the [PR Checks - Run workflow](${{ github.event.workflow_run.html_url }}) logs',
|
||||
'2. Ensure your code passes local checks:',
|
||||
'```bash',
|
||||
'# Backend',
|
||||
'go fmt ./...',
|
||||
'go vet ./...',
|
||||
'go build',
|
||||
'go test ./...',
|
||||
'',
|
||||
'# Frontend (if applicable)',
|
||||
'cd web',
|
||||
'npm run build',
|
||||
'```',
|
||||
'',
|
||||
'---',
|
||||
'',
|
||||
'*This is an automated fallback message. The advisory checks ran but results are not available.*'
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
issue_number: prNumber,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment
|
||||
});
|
||||
160
.github/workflows/pr-checks-run.yml
vendored
Normal file
160
.github/workflows/pr-checks-run.yml
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
name: PR Checks - Run
|
||||
|
||||
# This workflow runs advisory PR checks with read-only permissions
|
||||
# Safe for fork PRs - results are saved as artifacts
|
||||
# Companion workflow (pr-checks-comment.yml) will post comments
|
||||
#
|
||||
# NOTE: This workflow provides ADVISORY checks (non-blocking)
|
||||
# Main blocking checks are in pr-checks.yml
|
||||
# PR title and size checks are handled by pr-checks.yml (no duplication)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches: [main, dev]
|
||||
|
||||
# Read-only permissions - safe for fork PRs
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Backend advisory checks
|
||||
# Different from pr-checks.yml: these use continue-on-error and generate reports
|
||||
backend-checks:
|
||||
name: Backend Checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libta-lib-dev || true
|
||||
go mod download || true
|
||||
|
||||
- name: Check Go formatting
|
||||
id: go-fmt
|
||||
continue-on-error: true
|
||||
run: |
|
||||
UNFORMATTED=$(gofmt -l . 2>/dev/null || echo "")
|
||||
if [ -n "$UNFORMATTED" ]; then
|
||||
echo "status=⚠️ Needs formatting" >> $GITHUB_OUTPUT
|
||||
echo "$UNFORMATTED" | head -10 > fmt-files.txt
|
||||
else
|
||||
echo "status=✅ Good" >> $GITHUB_OUTPUT
|
||||
echo "" > fmt-files.txt
|
||||
fi
|
||||
|
||||
- name: Run go vet
|
||||
id: go-vet
|
||||
continue-on-error: true
|
||||
run: |
|
||||
if go vet ./... 2>&1 | tee vet-output.txt; then
|
||||
echo "status=✅ Good" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "status=⚠️ Issues found" >> $GITHUB_OUTPUT
|
||||
cat vet-output.txt | head -20 > vet-output-short.txt
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
id: go-test
|
||||
continue-on-error: true
|
||||
run: |
|
||||
if go test ./... -v 2>&1 | tee test-output.txt; then
|
||||
echo "status=✅ Passed" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
|
||||
cat test-output.txt | tail -30 > test-output-short.txt
|
||||
fi
|
||||
|
||||
- name: Save backend results
|
||||
if: always()
|
||||
run: |
|
||||
cat > backend-results.json <<EOF
|
||||
{
|
||||
"pr_number": ${{ github.event.pull_request.number }},
|
||||
"fmt_status": "${{ steps.go-fmt.outputs.status }}",
|
||||
"vet_status": "${{ steps.go-vet.outputs.status }}",
|
||||
"test_status": "${{ steps.go-test.outputs.status }}"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Upload backend results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: backend-results
|
||||
path: |
|
||||
backend-results.json
|
||||
fmt-files.txt
|
||||
vet-output-short.txt
|
||||
test-output-short.txt
|
||||
retention-days: 1
|
||||
|
||||
# Frontend advisory checks
|
||||
# Different from pr-checks.yml: these use continue-on-error and generate reports
|
||||
frontend-checks:
|
||||
name: Frontend Checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '18'
|
||||
|
||||
- name: Check if web directory exists
|
||||
id: check-web
|
||||
run: |
|
||||
if [ -d "web" ]; then
|
||||
echo "exists=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "exists=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.check-web.outputs.exists == 'true'
|
||||
working-directory: ./web
|
||||
continue-on-error: true
|
||||
run: npm ci
|
||||
|
||||
- name: Build and Type Check
|
||||
if: steps.check-web.outputs.exists == 'true'
|
||||
id: build
|
||||
working-directory: ./web
|
||||
continue-on-error: true
|
||||
run: |
|
||||
# build script includes: tsc && vite build
|
||||
if npm run build 2>&1 | tee build-output.txt; then
|
||||
echo "status=✅ Success" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
|
||||
cat build-output.txt | tail -30 > build-output-short.txt
|
||||
fi
|
||||
|
||||
- name: Save frontend results
|
||||
if: always() && steps.check-web.outputs.exists == 'true'
|
||||
working-directory: ./web
|
||||
run: |
|
||||
cat > frontend-results.json <<EOF
|
||||
{
|
||||
"pr_number": ${{ github.event.pull_request.number }},
|
||||
"build_status": "${{ steps.build.outputs.status }}"
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Upload frontend results
|
||||
if: always() && steps.check-web.outputs.exists == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: frontend-results
|
||||
path: |
|
||||
web/frontend-results.json
|
||||
web/build-output-short.txt
|
||||
retention-days: 1
|
||||
189
.github/workflows/pr-checks.yml
vendored
189
.github/workflows/pr-checks.yml
vendored
@@ -7,11 +7,13 @@ on:
|
||||
- dev
|
||||
- main
|
||||
|
||||
# Default permissions for all jobs (can be overridden per job)
|
||||
# Default permissions for all jobs
|
||||
# Note: Fork PRs won't have write access for security
|
||||
# Advisory checks use separate workflow (pr-checks-run.yml + pr-checks-comment.yml)
|
||||
permissions:
|
||||
contents: read # Read repository contents
|
||||
pull-requests: write # Manage PRs (labels, comments)
|
||||
issues: write # Manage issues (PRs are issues)
|
||||
pull-requests: write # Manage PRs (labels, comments) - only works for non-fork PRs
|
||||
issues: write # Manage issues (PRs are issues) - only works for non-fork PRs
|
||||
|
||||
jobs:
|
||||
# Validate PR title and description
|
||||
@@ -21,6 +23,8 @@ jobs:
|
||||
# Inherits workflow-level permissions (contents: read, pull-requests: write, issues: write)
|
||||
steps:
|
||||
- name: Check PR title format
|
||||
id: semantic-pr
|
||||
continue-on-error: true # Don't block PR if title format is invalid
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -36,6 +40,7 @@ jobs:
|
||||
chore
|
||||
ci
|
||||
security
|
||||
build
|
||||
scopes: |
|
||||
exchange
|
||||
trader
|
||||
@@ -46,10 +51,70 @@ jobs:
|
||||
backend
|
||||
security
|
||||
deps
|
||||
workflow
|
||||
github
|
||||
actions
|
||||
config
|
||||
docker
|
||||
build
|
||||
release
|
||||
requireScope: false
|
||||
|
||||
- name: Comment on invalid PR title
|
||||
if: steps.semantic-pr.outcome == 'failure'
|
||||
uses: actions/github-script@v7
|
||||
continue-on-error: true # Don't fail for fork PRs
|
||||
with:
|
||||
script: |
|
||||
const prTitle = context.payload.pull_request.title;
|
||||
const isFork = context.payload.pull_request.head.repo.full_name !== context.payload.pull_request.base.repo.full_name;
|
||||
|
||||
const comment = [
|
||||
'## ⚠️ PR Title Format Suggestion',
|
||||
'',
|
||||
"Your PR title doesn't follow the Conventional Commits format, but **this won't block your PR from being merged**.",
|
||||
'',
|
||||
`**Current title:** \`${prTitle}\``,
|
||||
'',
|
||||
'**Recommended format:** `type(scope): description`',
|
||||
'',
|
||||
'### Valid types:',
|
||||
'`feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `security`, `build`',
|
||||
'',
|
||||
'### Common scopes (optional):',
|
||||
'`exchange`, `trader`, `ai`, `api`, `ui`, `frontend`, `backend`, `security`, `deps`, `workflow`, `github`, `actions`, `config`, `docker`, `build`, `release`',
|
||||
'',
|
||||
'### Examples:',
|
||||
'- `feat(trader): add new trading strategy`',
|
||||
'- `fix(api): resolve authentication issue`',
|
||||
'- `docs: update README`',
|
||||
'- `chore(deps): update dependencies`',
|
||||
'- `ci(workflow): improve GitHub Actions`',
|
||||
'',
|
||||
'**Note:** This is a suggestion to improve consistency. Your PR can still be reviewed and merged.',
|
||||
'',
|
||||
'---',
|
||||
'*This is an automated comment. You can update the PR title anytime.*'
|
||||
].join('\n');
|
||||
|
||||
if (!isFork) {
|
||||
try {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: comment
|
||||
});
|
||||
} catch (error) {
|
||||
console.log('Could not post comment (expected for fork PRs):', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('Fork PR - comment will be posted by pr-checks-comment.yml');
|
||||
}
|
||||
|
||||
- name: Check PR size
|
||||
uses: actions/github-script@v7
|
||||
continue-on-error: true # Don't fail for fork PRs
|
||||
with:
|
||||
script: |
|
||||
const pr = context.payload.pull_request;
|
||||
@@ -57,6 +122,9 @@ jobs:
|
||||
const deletions = pr.deletions;
|
||||
const total = additions + deletions;
|
||||
|
||||
// Check if this is a fork PR
|
||||
const isFork = pr.head.repo.full_name !== pr.base.repo.full_name;
|
||||
|
||||
let label = '';
|
||||
let comment = '';
|
||||
|
||||
@@ -71,27 +139,36 @@ jobs:
|
||||
comment = '🚨 This PR is **large** (>' + total + ' lines changed). Please consider breaking it into smaller, focused PRs for easier review.';
|
||||
}
|
||||
|
||||
// Add size label
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels: [label]
|
||||
});
|
||||
// Only add labels/comments for non-fork PRs (fork PRs don't have write permission)
|
||||
if (!isFork) {
|
||||
try {
|
||||
// Add size label
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
labels: [label]
|
||||
});
|
||||
|
||||
// Add comment for large PRs
|
||||
if (total >= 1000) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: comment
|
||||
});
|
||||
// Add comment for large PRs
|
||||
if (total >= 1000) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body: comment
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('Failed to add label/comment (expected for fork PRs):', error.message);
|
||||
}
|
||||
} else {
|
||||
console.log('Fork PR detected - skipping label/comment (will be handled by pr-checks-comment.yml)');
|
||||
}
|
||||
|
||||
# Backend tests
|
||||
backend-tests:
|
||||
name: Backend Tests (Go)
|
||||
# Backend checks (simplified - no TA-Lib required)
|
||||
backend-checks:
|
||||
name: Backend Code Quality (Go)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read # Only need read access for testing
|
||||
@@ -104,11 +181,6 @@ jobs:
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Install TA-Lib
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libta-lib-dev
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -121,28 +193,26 @@ jobs:
|
||||
run: go mod download
|
||||
|
||||
- name: Run go fmt
|
||||
continue-on-error: true # Don't block PR if formatting issues found
|
||||
run: |
|
||||
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
|
||||
echo "Please run 'go fmt' on your code"
|
||||
echo "⚠️ Code formatting issues found. Please run 'go fmt ./...' locally."
|
||||
echo ""
|
||||
echo "Files needing formatting:"
|
||||
gofmt -s -l .
|
||||
echo ""
|
||||
echo "This is a warning and won't block your PR from being merged."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ All Go files are properly formatted"
|
||||
fi
|
||||
|
||||
- name: Run go vet
|
||||
run: go vet ./...
|
||||
|
||||
- name: Run tests
|
||||
run: go test -v -race -coverprofile=coverage.out ./...
|
||||
|
||||
- name: Build
|
||||
run: go build -v -o nofx
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./coverage.out
|
||||
flags: backend
|
||||
|
||||
# Frontend tests
|
||||
frontend-tests:
|
||||
name: Frontend Tests (React/TypeScript)
|
||||
@@ -170,22 +240,17 @@ jobs:
|
||||
working-directory: ./web
|
||||
run: npm ci
|
||||
|
||||
- name: Run linter
|
||||
working-directory: ./web
|
||||
run: npm run lint
|
||||
|
||||
- name: Run type check
|
||||
working-directory: ./web
|
||||
run: npm run type-check || true # Don't fail on type errors for now
|
||||
|
||||
- name: Build
|
||||
- name: Build and Type Check
|
||||
working-directory: ./web
|
||||
run: npm run build
|
||||
# Note: build script runs "tsc && vite build" which includes type checking
|
||||
|
||||
# Auto-label based on files changed
|
||||
auto-label:
|
||||
name: Auto Label PR
|
||||
runs-on: ubuntu-latest
|
||||
# Only run for non-fork PRs (fork PRs don't have write permission)
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
@@ -233,25 +298,43 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run Gitleaks
|
||||
uses: gitleaks/gitleaks-action@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Run TruffleHog OSS
|
||||
uses: trufflesecurity/trufflehog@main
|
||||
with:
|
||||
path: ./
|
||||
base: ${{ github.event.pull_request.base.sha }}
|
||||
head: ${{ github.event.pull_request.head.sha }}
|
||||
extra_args: --debug --only-verified
|
||||
|
||||
# All checks passed
|
||||
all-checks:
|
||||
name: All Checks Passed
|
||||
runs-on: ubuntu-latest
|
||||
needs: [validate-pr, backend-tests, frontend-tests, security-check, secrets-check]
|
||||
needs: [validate-pr, backend-checks, frontend-tests, security-check, secrets-check]
|
||||
if: always()
|
||||
permissions:
|
||||
contents: read # Only need read access for status checking
|
||||
steps:
|
||||
- name: Check all jobs
|
||||
run: |
|
||||
if [ "${{ contains(needs.*.result, 'failure') }}" == "true" ]; then
|
||||
echo "Some checks failed"
|
||||
# Note: validate-pr uses continue-on-error, so it won't block even if title format is invalid
|
||||
# We only care about actual test failures
|
||||
echo "validate-pr: ${{ needs.validate-pr.result }}"
|
||||
echo "backend-checks: ${{ needs.backend-checks.result }}"
|
||||
echo "frontend-tests: ${{ needs.frontend-tests.result }}"
|
||||
echo "security-check: ${{ needs.security-check.result }}"
|
||||
echo "secrets-check: ${{ needs.secrets-check.result }}"
|
||||
|
||||
# Check if any critical checks failed (excluding validate-pr which is advisory)
|
||||
if [[ "${{ needs.backend-checks.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.frontend-tests.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.security-check.result }}" == "failure" ]] || \
|
||||
[[ "${{ needs.secrets-check.result }}" == "failure" ]]; then
|
||||
echo "❌ Critical checks failed"
|
||||
exit 1
|
||||
else
|
||||
echo "All checks passed!"
|
||||
echo "✅ All critical checks passed!"
|
||||
if [[ "${{ needs.validate-pr.result }}" != "success" ]]; then
|
||||
echo "ℹ️ Note: PR title format check is advisory only and doesn't block merging"
|
||||
fi
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user