feat: tilejson metadata endpoints and tests (#20) #250
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 Validation | |
| on: | |
| pull_request: | |
| types: [opened, edited, synchronize, reopened] | |
| jobs: | |
| validate-pr: | |
| name: Validate PR Template Compliance | |
| if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: write | |
| issues: read | |
| steps: | |
| - name: Check PR Template Compliance | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const { data: pr } = await github.rest.pulls.get({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| pull_number: context.issue.number | |
| }); | |
| const body = pr.body || ''; | |
| const title = pr.title || ''; | |
| console.log('Validating PR:', title); | |
| console.log('PR body length:', body.length); | |
| const issues = []; | |
| const warnings = []; | |
| // Check 1: Issue link requirement | |
| const hasIssueLink = /(?:Fixes|Closes|Resolves|Related to)\s+#\d+/i.test(body); | |
| if (!hasIssueLink) { | |
| issues.push('❌ **Missing issue link** - Please include "Fixes #123" or similar'); | |
| } | |
| // Check 2: Summary section | |
| const summaryMatch = body.match(/## Summary\s*(?:\s*<!--.*?-->\s*)?\n\s*(.*?)\n/s); | |
| if (!summaryMatch || !summaryMatch[1].trim()) { | |
| issues.push('❌ **Empty summary** - Please describe what this PR does'); | |
| } | |
| // Check 3: Changes Made section | |
| const changesMatch = body.match(/## Changes Made\s*(?:\s*<!--.*?-->\s*)?\n(.*?)(?=\n##|\n---|\n$)/s); | |
| if (!changesMatch || !changesMatch[1].includes('-') || changesMatch[1].trim().length < 10) { | |
| issues.push('❌ **Changes Made incomplete** - Please list key changes with bullet points'); | |
| } | |
| // Check 4: Testing checklist | |
| const testingChecked = body.match(/- \[x\]/i); | |
| if (!testingChecked) { | |
| warnings.push('⚠️ **No testing boxes checked** - Please indicate testing completed'); | |
| } | |
| // Check 5: Pre-PR checklist | |
| const preChecklistComplete = body.match(/## Pre-PR Checklist[\s\S]*?- \[x\].*scripts\/pre-pr-check\.sh/i); | |
| if (!preChecklistComplete) { | |
| issues.push('❌ **Pre-PR validation not confirmed** - Please run `scripts/pre-pr-check.sh` and check the box'); | |
| } | |
| // Check 6: Breaking changes addressed | |
| const hasBreakingSection = body.includes('## Breaking Changes'); | |
| if (!hasBreakingSection) { | |
| warnings.push('⚠️ **Missing Breaking Changes section** - Template may be outdated'); | |
| } | |
| // Check 7: Commit message format (check title) | |
| const titleFormat = /^(feat|fix|docs|style|refactor|test|chore|ci|perf)(\([^)]+\))?: .+( \(#\d+\))?$/i; | |
| if (!titleFormat.test(title)) { | |
| issues.push('❌ **PR title format** - Should be "type: description (#issue)" (e.g., "feat: add login endpoint (#123)")'); | |
| } | |
| // Generate comment | |
| let comment = '## 🤖 PR Template Validation\n\n'; | |
| if (issues.length === 0 && warnings.length === 0) { | |
| comment += '✅ **All checks passed!** Your PR follows the template correctly.\n\n'; | |
| comment += '**Next steps:**\n'; | |
| comment += '- Wait for CI to complete\n'; | |
| comment += '- Address any feedback from LLM architecture review\n'; | |
| comment += '- Request review from team members\n'; | |
| } else { | |
| if (issues.length > 0) { | |
| comment += '### ❌ Issues (must fix)\n\n'; | |
| issues.forEach(issue => comment += `${issue}\n\n`); | |
| } | |
| if (warnings.length > 0) { | |
| comment += '### ⚠️ Warnings (recommended fixes)\n\n'; | |
| warnings.forEach(warning => comment += `${warning}\n\n`); | |
| } | |
| comment += '### 📚 Helpful Links\n'; | |
| comment += '- [PR Template Guide](../blob/trunk/.github/pull_request_template.md)\n'; | |
| comment += '- [Pre-PR Validation](../blob/trunk/scripts/pre-pr-check.sh)\n'; | |
| comment += '- [Commit Message Format](../blob/trunk/CLAUDE.md#commit-guidelines)\n\n'; | |
| comment += '💡 **Tip:** Edit your PR description to address these issues and this check will re-run automatically.\n'; | |
| } | |
| comment += '\n---\n*Automated validation powered by GitHub Actions*'; | |
| // Check if we already have a validation comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number | |
| }); | |
| const existingComment = comments.find(comment => | |
| comment.body.includes('🤖 PR Template Validation') | |
| ); | |
| if (existingComment) { | |
| // Update existing comment | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existingComment.id, | |
| body: comment | |
| }); | |
| } else { | |
| // Create new comment | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: comment | |
| }); | |
| } | |
| // Set status | |
| if (issues.length > 0) { | |
| core.setFailed(`PR template validation failed: ${issues.length} issues found`); | |
| } else { | |
| console.log(`✅ PR template validation passed (${warnings.length} warnings)`); | |
| } |