aaafix(workflow): fix github workflow #108
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | name: PR Checks | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened, edited] | |
| branches: | |
| - dev | |
| - main | |
| # 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) - 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 | |
| validate-pr: | |
| name: Validate PR Format | |
| runs-on: ubuntu-latest | |
| # 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 }} | |
| with: | |
| types: | | |
| feat | |
| fix | |
| docs | |
| style | |
| refactor | |
| perf | |
| test | |
| chore | |
| ci | |
| security | |
| build | |
| scopes: | | |
| exchange | |
| trader | |
| ai | |
| api | |
| ui | |
| frontend | |
| 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; | |
| const additions = pr.additions; | |
| 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 = ''; | |
| 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.'; | |
| } | |
| // 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 | |
| }); | |
| } | |
| } 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 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 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: '1.21' | |
| - 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 | |
| continue-on-error: true # Don't block PR if formatting issues found | |
| run: | | |
| if [ "$(gofmt -s -l . | wc -l)" -gt 0 ]; then | |
| 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: Build | |
| run: go build -v -o nofx | |
| # Frontend tests | |
| frontend-tests: | |
| name: Frontend Tests (React/TypeScript) | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read # Only need read access for testing | |
| 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: 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 | |
| issues: write # Required: PRs are issues, labeler needs to modify issue labels | |
| 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 | |
| permissions: | |
| contents: read | |
| security-events: write # Required: Upload SARIF results to GitHub Security | |
| 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 | |
| permissions: | |
| contents: read # Only need read access for scanning | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - 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-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: | | |
| # 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 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 |