feat: pr validation

This commit is contained in:
zbhan
2025-11-01 18:25:44 -04:00
parent 935c3d094e
commit 9a604f9b27
22 changed files with 6607 additions and 0 deletions

127
.github/labeler.yml vendored Normal file
View File

@@ -0,0 +1,127 @@
# Auto-labeler configuration
# Automatically adds labels based on changed files
# Area: Frontend
'area: frontend':
- changed-files:
- any-glob-to-any-file:
- 'web/**/*'
- '*.tsx'
- '*.ts'
- '*.jsx'
- '*.js'
- '*.css'
# Area: Backend
'area: backend':
- changed-files:
- any-glob-to-any-file:
- '**/*.go'
- 'go.mod'
- 'go.sum'
- 'cmd/**/*'
- 'internal/**/*'
- 'pkg/**/*'
# Area: Exchange
'area: exchange':
- changed-files:
- any-glob-to-any-file:
- 'internal/exchange/**/*'
- 'pkg/exchange/**/*'
- '**/binance*.go'
- '**/hyperliquid*.go'
- '**/aster*.go'
- '**/okx*.go'
- '**/bybit*.go'
# Area: AI
'area: ai':
- changed-files:
- any-glob-to-any-file:
- 'internal/ai/**/*'
- 'pkg/ai/**/*'
- '**/deepseek*.go'
- '**/qwen*.go'
- '**/openai*.go'
- '**/claude*.go'
# Area: API
'area: api':
- changed-files:
- any-glob-to-any-file:
- 'internal/api/**/*'
- 'pkg/api/**/*'
- '**/handler*.go'
- '**/router*.go'
# Area: Security
'area: security':
- changed-files:
- any-glob-to-any-file:
- '**/auth*.go'
- '**/jwt*.go'
- '**/encryption*.go'
- '**/crypto*.go'
- 'SECURITY.md'
# Area: Database
'area: database':
- changed-files:
- any-glob-to-any-file:
- 'internal/database/**/*'
- 'internal/db/**/*'
- '**/migration*.go'
- '**/*.sql'
- '**/schema*.go'
# Area: UI/UX
'area: ui/ux':
- changed-files:
- any-glob-to-any-file:
- 'web/src/components/**/*'
- 'web/src/pages/**/*'
- '**/*.css'
- '**/style*.ts'
# Area: Deployment
'area: deployment':
- changed-files:
- any-glob-to-any-file:
- 'Dockerfile'
- 'docker-compose*.yml'
- '.github/workflows/**/*'
- 'start.sh'
- '**/*deploy*.md'
# Type: Documentation
'type: documentation':
- changed-files:
- any-glob-to-any-file:
- 'docs/**/*'
- '*.md'
- 'README*'
- 'CHANGELOG*'
- 'CONTRIBUTING.md'
- 'CODE_OF_CONDUCT.md'
# Type: Test
'type: test':
- changed-files:
- any-glob-to-any-file:
- '**/*_test.go'
- 'test/**/*'
- '**/*.test.ts'
- '**/*.test.tsx'
- '**/*.spec.ts'
# Dependencies
'dependencies':
- changed-files:
- any-glob-to-any-file:
- 'go.mod'
- 'go.sum'
- 'package.json'
- 'package-lock.json'
- 'web/package.json'
- 'web/package-lock.json'

180
.github/labels.yml vendored Normal file
View File

@@ -0,0 +1,180 @@
# GitHub Labels Configuration
# Use https://github.com/crazy-max/ghaction-github-labeler to sync labels
# Priority Labels
- name: "priority: critical"
color: "d73a4a"
description: "Critical priority - requires immediate attention"
- name: "priority: high"
color: "ff6b6b"
description: "High priority - should be addressed soon"
- name: "priority: medium"
color: "fbca04"
description: "Medium priority - normal queue"
- name: "priority: low"
color: "0e8a16"
description: "Low priority - nice to have"
# Type Labels
- name: "type: bug"
color: "d73a4a"
description: "Something isn't working"
- name: "type: feature"
color: "a2eeef"
description: "New feature or request"
- name: "type: enhancement"
color: "84b6eb"
description: "Improvement to existing feature"
- name: "type: documentation"
color: "0075ca"
description: "Documentation improvements"
- name: "type: security"
color: "ee0701"
description: "Security-related changes"
- name: "type: performance"
color: "f9d0c4"
description: "Performance improvements"
- name: "type: refactor"
color: "fbca04"
description: "Code refactoring"
- name: "type: test"
color: "c5def5"
description: "Test-related changes"
# Status Labels
- name: "status: needs review"
color: "fbca04"
description: "PR is ready for review"
- name: "status: needs changes"
color: "d93f0b"
description: "PR needs changes based on review"
- name: "status: on hold"
color: "fef2c0"
description: "PR/issue is on hold"
- name: "status: in progress"
color: "0e8a16"
description: "Currently being worked on"
- name: "status: blocked"
color: "d93f0b"
description: "Blocked by another issue/PR"
# Area Labels (aligned with roadmap)
- name: "area: security"
color: "ee0701"
description: "Security enhancements (Phase 1.1)"
- name: "area: ai"
color: "7057ff"
description: "AI capabilities and models (Phase 1.2)"
- name: "area: exchange"
color: "0075ca"
description: "Exchange integrations (Phase 1.3)"
- name: "area: architecture"
color: "d4c5f9"
description: "Project structure refactoring (Phase 1.4)"
- name: "area: ui/ux"
color: "c2e0c6"
description: "User experience improvements (Phase 1.5)"
- name: "area: frontend"
color: "bfdadc"
description: "Frontend (React/TypeScript)"
- name: "area: backend"
color: "c5def5"
description: "Backend (Go)"
- name: "area: api"
color: "0e8a16"
description: "API endpoints"
- name: "area: database"
color: "f9d0c4"
description: "Database changes"
- name: "area: deployment"
color: "fbca04"
description: "Deployment and CI/CD"
# Special Labels
- name: "good first issue"
color: "7057ff"
description: "Good for newcomers"
- name: "help wanted"
color: "008672"
description: "Extra attention is needed"
- name: "bounty"
color: "1d76db"
description: "Bounty available for this issue"
- name: "bounty: claimed"
color: "5319e7"
description: "Bounty has been claimed"
- name: "bounty: paid"
color: "0e8a16"
description: "Bounty has been paid"
- name: "RFC"
color: "d4c5f9"
description: "Request for Comments - needs discussion"
- name: "breaking change"
color: "d73a4a"
description: "Includes breaking changes"
- name: "duplicate"
color: "cfd3d7"
description: "This issue or pull request already exists"
- name: "invalid"
color: "e4e669"
description: "This doesn't seem right"
- name: "wontfix"
color: "ffffff"
description: "This will not be worked on"
- name: "dependencies"
color: "0366d6"
description: "Dependency updates"
# Roadmap Phases
- name: "roadmap: phase-1"
color: "0e8a16"
description: "Core Infrastructure Enhancement"
- name: "roadmap: phase-2"
color: "fbca04"
description: "Testing & Stability"
- name: "roadmap: phase-3"
color: "0075ca"
description: "Universal Market Expansion"
- name: "roadmap: phase-4"
color: "7057ff"
description: "Advanced AI & Automation"
- name: "roadmap: phase-5"
color: "d73a4a"
description: "Enterprise & Scaling"

331
.github/workflows/pr-checks-advisory.yml vendored Normal file
View File

@@ -0,0 +1,331 @@
name: PR Checks (Advisory)
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [main, dev]
# These checks are advisory only - they won't block PR merging
# Results will be posted as comments to help contributors improve their PRs
jobs:
pr-info:
name: PR Information
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Check PR title format
id: check-title
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
# Check if title follows conventional commits
if echo "$PR_TITLE" | grep -qE "^(feat|fix|docs|style|refactor|perf|test|chore|ci|security)(\(.+\))?: .+"; then
echo "status=✅ Good" >> $GITHUB_OUTPUT
echo "message=PR title follows Conventional Commits format" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Suggestion" >> $GITHUB_OUTPUT
echo "message=Consider using Conventional Commits format: type(scope): description" >> $GITHUB_OUTPUT
fi
- name: Calculate PR size
id: pr-size
run: |
ADDITIONS=${{ github.event.pull_request.additions }}
DELETIONS=${{ github.event.pull_request.deletions }}
TOTAL=$((ADDITIONS + DELETIONS))
if [ $TOTAL -lt 100 ]; then
echo "size=🟢 Small" >> $GITHUB_OUTPUT
echo "label=size: small" >> $GITHUB_OUTPUT
elif [ $TOTAL -lt 500 ]; then
echo "size=🟡 Medium" >> $GITHUB_OUTPUT
echo "label=size: medium" >> $GITHUB_OUTPUT
else
echo "size=🔴 Large" >> $GITHUB_OUTPUT
echo "label=size: large" >> $GITHUB_OUTPUT
echo "suggestion=Consider breaking this into smaller PRs for easier review" >> $GITHUB_OUTPUT
fi
echo "lines=$TOTAL" >> $GITHUB_OUTPUT
- name: Post advisory comment
uses: actions/github-script@v7
with:
script: |
const titleStatus = '${{ steps.check-title.outputs.status }}';
const titleMessage = '${{ steps.check-title.outputs.message }}';
const prSize = '${{ steps.pr-size.outputs.size }}';
const prLines = '${{ steps.pr-size.outputs.lines }}';
const sizeSuggestion = '${{ steps.pr-size.outputs.suggestion }}' || '';
let comment = '## 🤖 PR Advisory Feedback\n\n';
comment += 'Thank you for your contribution! Here\'s some automated feedback to help improve your PR:\n\n';
comment += '### PR Title\n';
comment += titleStatus + ' ' + titleMessage + '\n\n';
comment += '### PR Size\n';
comment += prSize + ' (' + prLines + ' lines changed)\n';
if (sizeSuggestion) {
comment += '\n💡 **Suggestion:** ' + sizeSuggestion + '\n';
}
comment += '\n---\n\n';
comment += '### 📖 New PR Management System\n\n';
comment += 'We\'re introducing a new PR management system! These checks are **advisory only** and won\'t block your PR.\n\n';
comment += '**Want to check your PR against new standards?**\n';
comment += '```bash\n';
comment += '# Run the PR health check tool\n';
comment += './scripts/pr-check.sh\n';
comment += '```\n\n';
comment += 'This tool will:\n';
comment += '- 🔍 Analyze your PR (doesn\'t modify anything)\n';
comment += '- ✅ Show what\'s already good\n';
comment += '- ⚠️ Point out issues\n';
comment += '- 💡 Give specific suggestions on how to fix\n\n';
comment += '**Learn more:**\n';
comment += '- [Migration Guide](https://github.com/tinkle-community/nofx/blob/dev/docs/community/MIGRATION_ANNOUNCEMENT.md)\n';
comment += '- [Contributing Guidelines](https://github.com/tinkle-community/nofx/blob/dev/CONTRIBUTING.md)\n\n';
comment += '**Questions?** Just ask in the comments! We\'re here to help. 🙏\n\n';
comment += '---\n\n';
comment += '*This is an automated message. It won\'t affect your PR being merged.*';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
backend-checks:
name: Backend Checks (Advisory)
runs-on: ubuntu-latest
permissions:
pull-requests: write
continue-on-error: true
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 "files<<EOF" >> $GITHUB_OUTPUT
echo "$UNFORMATTED" | head -10 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
else
echo "status=✅ Good" >> $GITHUB_OUTPUT
echo "files=" >> $GITHUB_OUTPUT
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
echo "output=" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Issues found" >> $GITHUB_OUTPUT
echo "output<<EOF" >> $GITHUB_OUTPUT
cat vet-output.txt | head -20 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
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
echo "output=" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
echo "output<<EOF" >> $GITHUB_OUTPUT
cat test-output.txt | tail -30 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Post backend feedback
if: always()
uses: actions/github-script@v7
with:
script: |
const fmtStatus = '${{ steps.go-fmt.outputs.status }}' || '⚠️ Skipped';
const vetStatus = '${{ steps.go-vet.outputs.status }}' || '⚠️ Skipped';
const testStatus = '${{ steps.go-test.outputs.status }}' || '⚠️ Skipped';
const fmtFiles = `${{ steps.go-fmt.outputs.files }}`;
const vetOutput = `${{ steps.go-vet.outputs.output }}`;
const testOutput = `${{ steps.go-test.outputs.output }}`;
let comment = '## 🔧 Backend Checks (Advisory)\n\n';
comment += '### Go Formatting\n';
comment += fmtStatus + '\n';
if (fmtFiles) {
comment += '\nFiles needing formatting:\n```\n' + fmtFiles + '\n```\n';
}
comment += '\n### Go Vet\n';
comment += vetStatus + '\n';
if (vetOutput) {
comment += '\n```\n' + vetOutput.substring(0, 500) + '\n```\n';
}
comment += '\n### Tests\n';
comment += testStatus + '\n';
if (testOutput) {
comment += '\n```\n' + testOutput.substring(0, 1000) + '\n```\n';
}
comment += '\n---\n\n';
comment += '💡 **To fix locally:**\n';
comment += '```bash\n';
comment += '# Format code\n';
comment += 'go fmt ./...\n\n';
comment += '# Check for issues\n';
comment += 'go vet ./...\n\n';
comment += '# Run tests\n';
comment += 'go test ./...\n';
comment += '```\n\n';
comment += '*These checks are advisory and won\'t block merging. Need help? Just ask!*';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
frontend-checks:
name: Frontend Checks (Advisory)
runs-on: ubuntu-latest
permissions:
pull-requests: write
continue-on-error: true
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: Run linter
if: steps.check-web.outputs.exists == 'true'
id: lint
working-directory: ./web
continue-on-error: true
run: |
if npm run lint 2>&1 | tee lint-output.txt; then
echo "status=✅ Good" >> $GITHUB_OUTPUT
echo "output=" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Issues found" >> $GITHUB_OUTPUT
echo "output<<EOF" >> $GITHUB_OUTPUT
cat lint-output.txt | head -20 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Type check
if: steps.check-web.outputs.exists == 'true'
id: typecheck
working-directory: ./web
continue-on-error: true
run: |
if npm run type-check 2>&1 | tee typecheck-output.txt; then
echo "status=✅ Good" >> $GITHUB_OUTPUT
echo "output=" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Issues found" >> $GITHUB_OUTPUT
echo "output<<EOF" >> $GITHUB_OUTPUT
cat typecheck-output.txt | head -20 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Build
if: steps.check-web.outputs.exists == 'true'
id: build
working-directory: ./web
continue-on-error: true
run: |
if npm run build 2>&1 | tee build-output.txt; then
echo "status=✅ Success" >> $GITHUB_OUTPUT
echo "output=" >> $GITHUB_OUTPUT
else
echo "status=⚠️ Failed" >> $GITHUB_OUTPUT
echo "output<<EOF" >> $GITHUB_OUTPUT
cat build-output.txt | tail -20 >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
fi
- name: Post frontend feedback
if: always() && steps.check-web.outputs.exists == 'true'
uses: actions/github-script@v7
with:
script: |
const lintStatus = '${{ steps.lint.outputs.status }}' || '⚠️ Skipped';
const typecheckStatus = '${{ steps.typecheck.outputs.status }}' || '⚠️ Skipped';
const buildStatus = '${{ steps.build.outputs.status }}' || '⚠️ Skipped';
const lintOutput = `${{ steps.lint.outputs.output }}`;
const typecheckOutput = `${{ steps.typecheck.outputs.output }}`;
const buildOutput = `${{ steps.build.outputs.output }}`;
let comment = '## ⚛️ Frontend Checks (Advisory)\n\n';
comment += '### Linting\n';
comment += lintStatus + '\n';
if (lintOutput) {
comment += '\n```\n' + lintOutput.substring(0, 500) + '\n```\n';
}
comment += '\n### Type Checking\n';
comment += typecheckStatus + '\n';
if (typecheckOutput) {
comment += '\n```\n' + typecheckOutput.substring(0, 500) + '\n```\n';
}
comment += '\n### Build\n';
comment += buildStatus + '\n';
if (buildOutput) {
comment += '\n```\n' + buildOutput.substring(0, 500) + '\n```\n';
}
comment += '\n---\n\n';
comment += '💡 **To fix locally:**\n';
comment += '```bash\n';
comment += 'cd web\n\n';
comment += '# Fix linting issues\n';
comment += 'npm run lint -- --fix\n\n';
comment += '# Check types\n';
comment += 'npm run type-check\n\n';
comment += '# Test build\n';
comment += 'npm run build\n';
comment += '```\n\n';
comment += '*These checks are advisory and won\'t block merging. Need help? Just ask!*';
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});

237
.github/workflows/pr-checks.yml vendored Normal file
View File

@@ -0,0 +1,237 @@
name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened, edited]
branches:
- dev
- main
jobs:
# Validate PR title and description
validate-pr:
name: Validate PR Format
runs-on: ubuntu-latest
steps:
- name: Check PR title format
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
style
refactor
perf
test
chore
ci
security
scopes: |
exchange
trader
ai
api
ui
frontend
backend
security
deps
requireScope: false
- name: Check PR size
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const additions = pr.additions;
const deletions = pr.deletions;
const total = additions + deletions;
let label = '';
let comment = '';
if (total < 300) {
label = 'size: small';
comment = '✅ This PR is **small** and easy to review!';
} else if (total < 1000) {
label = 'size: medium';
comment = '⚠️ This PR is **medium** sized. Consider breaking it into smaller PRs if possible.';
} else {
label = 'size: large';
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]
});
// 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
});
}
# Backend tests
backend-tests:
name: Backend Tests (Go)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
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:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Download dependencies
run: go mod download
- name: Run go fmt
run: |
if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then
echo "Please run 'go fmt' on your code"
gofmt -s -l .
exit 1
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)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Cache Node modules
uses: actions/cache@v4
with:
path: web/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('web/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
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
working-directory: ./web
run: npm run build
# Auto-label based on files changed
auto-label:
name: Auto Label PR
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/labeler@v5
with:
configuration-path: .github/labeler.yml
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Check for security issues
security-check:
name: Security Scan
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: 'trivy-results.sarif'
# Check for secrets in code
secrets-check:
name: Check for Secrets
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Run Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# All checks passed
all-checks:
name: All Checks Passed
runs-on: ubuntu-latest
needs: [validate-pr, backend-tests, frontend-tests, security-check, secrets-check]
if: always()
steps:
- name: Check all jobs
run: |
if [ "${{ contains(needs.*.result, 'failure') }}" == "true" ]; then
echo "Some checks failed"
exit 1
else
echo "All checks passed!"
fi