fix(unsubscribe): replace in-memory rate limiter with Redis-backed generalLimiter #534
Workflow file for this run
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 Code Quality Checks | |
| on: | |
| pull_request: | |
| branches: [main, master] | |
| types: [opened, synchronize, reopened] | |
| permissions: {} | |
| jobs: | |
| # ───────────────────────────────────────── | |
| # 4. Security scan — detect common issues | |
| # ───────────────────────────────────────── | |
| security-scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Get changed files | |
| id: changed-files | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const { data: files } = await github.rest.pulls.listFiles({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number | |
| }); | |
| return files.map(f => f.filename); | |
| - name: Scan for hardcoded secrets patterns | |
| id: secret-scan | |
| run: | | |
| echo "Scanning for potential secrets in changed files..." | |
| ISSUES="" | |
| # Check for common secret patterns | |
| if git diff origin/main...HEAD -- '*.ts' '*.tsx' '*.js' | grep -E "(password|secret|api_key|apikey|token)\s*=\s*['\"][^'\"]{8,}" --include="*.ts" --include="*.tsx" -i; then | |
| ISSUES="$ISSUES\n- Possible hardcoded secret detected" | |
| fi | |
| # Check for NEXT_PUBLIC_ on sensitive vars | |
| if git diff origin/main...HEAD | grep -E "NEXT_PUBLIC_(DATABASE_URL|SECRET|PASSWORD|PRIVATE)" -i; then | |
| ISSUES="$ISSUES\n- Sensitive variable exposed via NEXT_PUBLIC_ prefix" | |
| fi | |
| # Check for console.log with sensitive data patterns | |
| if git diff origin/main...HEAD -- '*.ts' '*.tsx' | grep -E "console\.(log|error)\(.*?(password|token|secret|key)" -i; then | |
| ISSUES="$ISSUES\n- Possible sensitive data in console.log" | |
| fi | |
| if [ -n "$ISSUES" ]; then | |
| echo "security_issues=true" >> $GITHUB_OUTPUT | |
| echo "issues=$ISSUES" >> $GITHUB_OUTPUT | |
| else | |
| echo "security_issues=false" >> $GITHUB_OUTPUT | |
| fi | |
| continue-on-error: true | |
| - name: Save security findings comment | |
| if: steps.secret-scan.outputs.security_issues == 'true' | |
| uses: actions/github-script@v7 | |
| env: | |
| SECURITY_ISSUES: ${{ steps.secret-scan.outputs.issues }} | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const dir = './security-warning'; | |
| if (!fs.existsSync(dir)){ | |
| fs.mkdirSync(dir, { recursive: true }); | |
| } | |
| const body = `## Security Scan Warning\n\nPotential security issues detected in this PR:\n${process.env.SECURITY_ISSUES}\n\nPlease review before merging. @knoxiboy`; | |
| fs.writeFileSync(path.join(dir, 'security-warning.txt'), body); | |
| - name: Upload security findings artifact | |
| if: steps.secret-scan.outputs.security_issues == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-warning | |
| path: security-warning/ | |
| if-no-files-found: ignore | |
| # ───────────────────────────────────────── | |
| # 5. PR quality gate — basic checks | |
| # ───────────────────────────────────────── | |
| pr-quality-gate: | |
| name: PR Quality Gate | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check PR quality | |
| uses: actions/github-script@v7 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const pr = context.payload.pull_request; | |
| const title = pr.title || ''; | |
| const body = pr.body || ''; | |
| const issues = []; | |
| // 1. Title format check | |
| const validPrefixes = ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore', 'perf', 'ci', 'build', 'revert']; | |
| const hasValidPrefix = validPrefixes.some(p => title.toLowerCase().startsWith(p)); | |
| if (!hasValidPrefix) { | |
| issues.push(`**Title format**: Title should start with a conventional commit prefix (e.g. \`feat:\`, \`fix:\`, \`docs:\`). Current: \`${title}\``); | |
| } | |
| // 2. PR description check | |
| if (body.trim().length < 50) { | |
| issues.push('**Description**: PR description is too short. Please describe what changes were made and why.'); | |
| } | |
| // 3. Check for linked issue | |
| const hasLinkedIssue = /closes?\s+#\d+|fixes?\s+#\d+|resolves?\s+#\d+/i.test(body); | |
| if (!hasLinkedIssue) { | |
| issues.push('**Linked Issue**: No linked issue found. Please add `Closes #<issue-number>` to your PR description.'); | |
| } | |
| // 4. Check for test mention | |
| const hasTestMention = /test|spec|jest|vitest|playwright/i.test(body); | |
| if (!hasTestMention) { | |
| issues.push('**Testing**: No mention of tests in the PR description. Please describe how you tested your changes.'); | |
| } | |
| // Post results | |
| if (issues.length > 0) { | |
| const comment = `## PR Quality Check\n\nThe following items need attention:\n\n${issues.map(i => `- ${i}`).join('\n')}\n\n> These are suggestions to improve PR quality. The PR can still be merged after review.`; | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const dir = './quality-gate-warning'; | |
| if (!fs.existsSync(dir)){ | |
| fs.mkdirSync(dir, { recursive: true }); | |
| } | |
| fs.writeFileSync(path.join(dir, 'quality-gate-warning.txt'), comment); | |
| console.log('Saved quality gate feedback to file'); | |
| } else { | |
| console.log('PR passed all quality checks'); | |
| } | |
| - name: Upload quality gate warning artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: quality-gate-warning | |
| path: quality-gate-warning/ | |
| if-no-files-found: ignore | |