Files
nofx/.github/workflows/pr-checks.yml
tangmengqiu 0168f766de fix(ci): Add comprehensive permissions to pr-checks workflow
Add workflow-level default permissions and explicit per-job permissions
following the principle of least privilege:
Workflow-level (default):
- contents: read - Read repository contents
- pull-requests: write - Manage PR labels and comments
- issues: write - Manage issues (PRs are issues in GitHub API)
Job-level overrides:
- validate-pr: Inherits workflow defaults (needs issue/PR write access)
- backend-tests: Downgrade to read-only (no write operations needed)
- frontend-tests: Downgrade to read-only (no write operations needed)
- auto-label: Add missing issues:write (labeler operates on PR issues)
- security-check: Add security-events:write (upload SARIF results)
- secrets-check: Downgrade to read-only (scanning only)
- all-checks: Downgrade to read-only (status checking only)
This fixes:
1. Potential 403 errors when auto-label tries to add labels to PR issues
2. Missing permission for uploading security scan results
3. Overly permissive access for read-only jobs
Related: #282
Co-Authored-By: tinkle-community <tinklefund@gmail.com>
2025-11-02 18:23:28 -05:00

258 lines
6.9 KiB
YAML

name: PR Checks
on:
pull_request:
types: [opened, synchronize, reopened, edited]
branches:
- dev
- main
# Default permissions for all jobs (can be overridden per job)
permissions:
contents: read # Read repository contents
pull-requests: write # Manage PRs (labels, comments)
issues: write # Manage issues (PRs are issues)
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
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
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: 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
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: 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:
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 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()
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"
exit 1
else
echo "All checks passed!"
fi