Nightly Quality Audit #61
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: Nightly Quality Audit | |
| on: | |
| schedule: | |
| - cron: '0 6 * * *' # Daily at 6 AM UTC | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| issues: write | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: false | |
| jobs: | |
| audit: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| - run: npm install | |
| - run: npx playwright install --with-deps | |
| - name: Start server | |
| run: npx http-server . -p 8080 -s & | |
| - name: Wait for server | |
| run: npx wait-on http://localhost:8080 --timeout 15000 | |
| - name: Run all tests | |
| id: tests | |
| run: npx playwright test tests/ --project=chromium --reporter=list,html 2>&1 | tee test-output.txt | |
| continue-on-error: true | |
| - name: Link check | |
| id: links | |
| run: | | |
| npx linkinator http://localhost:8080 \ | |
| --recurse \ | |
| --skip "github.com|googletagmanager" \ | |
| --timeout 10000 \ | |
| --format json > link-report.json || true | |
| # Parse broken links | |
| node -e " | |
| const r = require('./link-report.json'); | |
| const broken = (r.links || []).filter(l => l.state === 'BROKEN'); | |
| const total = (r.links || []).length; | |
| console.log('Total links: ' + total); | |
| console.log('Broken: ' + broken.length); | |
| if (broken.length) { | |
| broken.forEach(l => console.log(' BROKEN: ' + l.url + ' (from ' + l.parent + ')')); | |
| } | |
| // Export for issue creation | |
| const fs = require('fs'); | |
| fs.writeFileSync('broken-links.txt', broken.map(l => l.url + ' (from ' + l.parent + ')').join('\n') || 'None'); | |
| fs.appendFileSync(process.env.GITHUB_ENV, 'BROKEN_COUNT=' + broken.length + '\n'); | |
| " | tee -a $GITHUB_STEP_SUMMARY | |
| - name: Lighthouse audit | |
| run: | | |
| npm install -g @lhci/cli | |
| lhci collect \ | |
| --url=http://localhost:8080/index.html \ | |
| --url=http://localhost:8080/getting-started.html \ | |
| --url=http://localhost:8080/docs/index.html \ | |
| --settings.preset=desktop \ | |
| --numberOfRuns=1 || true | |
| echo "### Lighthouse Audit Complete" >> $GITHUB_STEP_SUMMARY | |
| - name: Create issue for broken links | |
| if: env.BROKEN_COUNT != '0' && env.BROKEN_COUNT != '' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const brokenLinks = fs.readFileSync('broken-links.txt', 'utf8'); | |
| const today = new Date().toISOString().split('T')[0]; | |
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| // Check if an open issue already exists for today | |
| const existing = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'ci-automated,links', | |
| per_page: 5 | |
| }); | |
| const todayIssue = existing.data.find(i => i.title.includes(today)); | |
| if (todayIssue) { | |
| // Update existing issue | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: todayIssue.number, | |
| body: `## Updated Broken Link Report\n\n**Run:** [${context.runId}](${runUrl})\n\n\`\`\`\n${brokenLinks}\n\`\`\`` | |
| }); | |
| } else { | |
| // Create new issue | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `[CI] Broken links detected — ${today}`, | |
| labels: ['bug', 'ci-automated', 'links'], | |
| body: `## Broken Links Detected\n\nThe nightly CI/CD audit found broken links on the website.\n\n**Workflow Run:** [View on GitHub](${runUrl})\n**Date:** ${today}\n\n### Broken Links\n\n\`\`\`\n${brokenLinks}\n\`\`\`\n\n### How to Fix\n\n1. Check if the target page/URL still exists\n2. Update or remove the broken link in the source HTML file\n3. Push the fix — the deploy workflow will re-test automatically` | |
| }); | |
| } | |
| - name: Create issue for test failures | |
| if: steps.tests.outcome == 'failure' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const testOutput = fs.readFileSync('test-output.txt', 'utf8'); | |
| const failedLines = testOutput.split('\n').filter(l => l.includes('✘') || l.includes('FAIL') || l.includes('Error')).slice(0, 30).join('\n'); | |
| const today = new Date().toISOString().split('T')[0]; | |
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| // Check for existing open issue | |
| const existing = await github.rest.issues.listForRepo({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| state: 'open', | |
| labels: 'ci-automated', | |
| per_page: 10 | |
| }); | |
| const todayIssue = existing.data.find(i => i.title.includes('Test failures') && i.title.includes(today)); | |
| if (todayIssue) return; // Already reported today | |
| await github.rest.issues.create({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| title: `[CI] Test failures detected — ${today}`, | |
| labels: ['bug', 'ci-automated'], | |
| body: `## Test Failures Detected\n\nThe nightly CI/CD audit found test failures.\n\n**Workflow Run:** [View on GitHub](${runUrl})\n**Date:** ${today}\n\n### Failed Tests\n\n\`\`\`\n${failedLines || 'See workflow run for details'}\n\`\`\`\n\n### Categories Tested\n\n- **Links** — internal link validation, placeholder detection, button functionality\n- **SEO** — title tags, meta descriptions, canonical links, OG tags, structured data\n- **Performance** — page load time, resource size, FCP, JS errors\n- **Accessibility** — ARIA landmarks, alt text, keyboard navigation, color contrast` | |
| }); | |
| - uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: nightly-reports-${{ github.run_number }} | |
| path: | | |
| playwright-report/ | |
| link-report.json | |
| broken-links.txt | |
| test-output.txt | |
| .lighthouseci/ | |
| retention-days: 30 |