docs: world-class README audit and rewrite #33
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: Deploy | ||
| on: | ||
| push: | ||
| branches: [main] | ||
| pull_request: | ||
| branches: [main] | ||
| # Cancel in-progress runs for the same branch | ||
| concurrency: | ||
| group: ${{ github.workflow }}-${{ github.ref }} | ||
| cancel-in-progress: true | ||
| jobs: | ||
| # ============================================ | ||
| # TEST JOB | ||
| # ============================================ | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Node.js | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: '20' | ||
| - name: Install dependencies | ||
| run: npm install | ||
| - name: Lint JavaScript | ||
| run: | | ||
| if npm run lint 2>/dev/null; then | ||
| echo "Lint passed" | ||
| else | ||
| echo "No lint script found, skipping" | ||
| fi | ||
| - name: Check HTML validity | ||
| run: | | ||
| echo "Checking HTML files..." | ||
| find . -name "*.html" -type f | head -20 | while read file; do | ||
| if grep -q '<!DOCTYPE html>' "$file"; then | ||
| echo "✓ $file" | ||
| else | ||
| echo "✗ $file missing DOCTYPE" | ||
| fi | ||
| done | ||
| - name: Validate JSON files | ||
| run: | | ||
| echo "Validating JSON files..." | ||
| find . -name "*.json" -type f | while read file; do | ||
| if python3 -m json.tool "$file" > /dev/null 2>&1; then | ||
| echo "✓ $file" | ||
| else | ||
| echo "✗ $file invalid JSON" | ||
| fi | ||
| done | ||
| # ============================================ | ||
| # DEPLOY PREVIEW (Pull Requests) | ||
| # ============================================ | ||
| deploy-preview: | ||
| needs: test | ||
| if: github.event_name == 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Check Cloudflare secrets | ||
| id: check-secrets | ||
| run: | | ||
| if [ -n "${{ secrets.CLOUDFLARE_API_TOKEN }}" ]; then | ||
| echo "has-secrets=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "has-secrets=false" >> $GITHUB_OUTPUT | ||
| echo "⚠️ Cloudflare secrets not configured, skipping deploy" | ||
| fi | ||
| - name: Deploy to Cloudflare Pages (Preview) | ||
| if: steps.check-secrets.outputs.has-secrets == 'true' | ||
| uses: cloudflare/pages-action@v1 | ||
| with: | ||
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| projectName: constraint-theory-web | ||
| directory: . | ||
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Comment PR with preview URL | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const { data: deployments } = await github.rest.repos.listDeployments({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| environment: 'preview', | ||
| per_page: 1 | ||
| }); | ||
| if (deployments.length > 0) { | ||
| const previewUrl = `https://${context.sha.slice(0, 7)}.constraint-theory-web.pages.dev`; | ||
| await github.rest.issues.createComment({ | ||
| owner: context.repo.owner, | ||
| repo: context.repo.repo, | ||
| issue_number: context.issue.number, | ||
| body: `🚀 Preview deployed!\n\n**URL:** ${previewUrl}\n\n_This preview will be updated with new commits._` | ||
| }); | ||
| } | ||
| # ============================================ | ||
| # DEPLOY PRODUCTION (main branch) | ||
| # ============================================ | ||
| deploy-production: | ||
| needs: test | ||
| if: github.ref == 'refs/heads/main' && github.event_name == 'push' | ||
| runs-on: ubuntu-latest | ||
| environment: | ||
| name: production | ||
| url: https://constraint-theory.superinstance.ai | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| - name: Check Cloudflare secrets | ||
| id: check-secrets | ||
| run: | | ||
| if [ -n "${{ secrets.CLOUDFLARE_API_TOKEN }}" ]; then | ||
| echo "has-secrets=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "has-secrets=false" >> $GITHUB_OUTPUT | ||
| echo "⚠️ Cloudflare secrets not configured, skipping deploy" | ||
| fi | ||
| - name: Deploy to Cloudflare Pages (Production) | ||
| if: steps.check-secrets.outputs.has-secrets == 'true' | ||
| uses: cloudflare/pages-action@v1 | ||
| with: | ||
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| projectName: constraint-theory-web | ||
| directory: . | ||
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | ||
| branch: main | ||
| - name: Verify deployment | ||
| run: | | ||
| echo "Waiting for deployment to propagate..." | ||
| sleep 30 | ||
| response=$(curl -s -o /dev/null -w "%{http_code}" https://constraint-theory.superinstance.ai) | ||
| if [ "$response" -eq "200" ]; then | ||
| echo "✅ Deployment verified - site is up" | ||
| else | ||
| echo "⚠️ Site returned status $response" | ||
| fi | ||
| - name: Run health check | ||
| run: | | ||
| health=$(curl -s https://constraint-theory.superinstance.ai/api/health || echo '{"status":"unknown"}') | ||
| echo "Health check response: $health" | ||
| - name: Notify deployment (optional) | ||
| if: ${{ secrets.SLACK_WEBHOOK != '' }} | ||
| uses: slackapi/slack-github-action@v1 | ||
| with: | ||
| payload: | | ||
| { | ||
| "text": "🚀 Deployed constraint-theory-web to production", | ||
| "blocks": [ | ||
| { | ||
| "type": "section", | ||
| "text": { | ||
| "type": "mrkdwn", | ||
| "text": "*Deployment Successful*\n• Repository: ${{ github.repository }}\n• Branch: ${{ github.ref_name }}\n• Commit: ${{ github.sha }}\n• Actor: ${{ github.actor }}" | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| env: | ||
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} | ||
| # ============================================ | ||
| # ROLLBACK (Manual Trigger) | ||
| # ============================================ | ||
| rollback: | ||
| if: github.event_name == 'workflow_dispatch' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| ref: ${{ github.event.inputs.version }} | ||
| - name: Rollback deployment | ||
| uses: cloudflare/pages-action@v1 | ||
| with: | ||
| apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} | ||
| accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} | ||
| projectName: constraint-theory-web | ||
| directory: . | ||
| gitHubToken: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Notify rollback | ||
| run: | | ||
| echo "Rollback to version ${{ github.event.inputs.version }} complete" | ||
| # ============================================ | ||
| # SCHEDULED HEALTH CHECK | ||
| # ============================================ | ||
| health-check: | ||
| if: github.event_name == 'schedule' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Check site health | ||
| run: | | ||
| response=$(curl -s https://constraint-theory.superinstance.ai/api/health) | ||
| status=$(echo "$response" | jq -r '.status') | ||
| if [ "$status" != "ok" ]; then | ||
| echo "::error::Health check failed: $response" | ||
| exit 1 | ||
| fi | ||
| echo "Health check passed: $response" | ||
| - name: Check Core Web Vitals | ||
| run: | | ||
| # Run Lighthouse CI | ||
| npm install -g @lhci/cli | ||
| lhci healthcheck --url https://constraint-theory.superinstance.ai | ||