diff --git a/.github/workflows/PRreview.yml b/.github/workflows/PRreview.yml index 6b5a08072..3667c127c 100644 --- a/.github/workflows/PRreview.yml +++ b/.github/workflows/PRreview.yml @@ -93,7 +93,7 @@ jobs: let text = AI_RESPONSE.replace(/\r\n/g, '\n'); - const MAX_CHARS = 64000; + const MAX_CHARS = 164000; if (!text.trim()) { await github.rest.issues.createComment({ diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index d35618aaa..b6bbbf0b6 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -1,29 +1,85 @@ -name: Test PR Deployment +name: Mega PR Test & Analysis on: issue_comment: types: [created] pull_request: - types: [opened] + types: [opened, synchronize] permissions: contents: write pull-requests: write - pages: write id-token: write + issues: write + checks: write jobs: - test-pr: + mega-test: if: >- - (github.event_name == 'pull_request' && github.event.action == 'opened') || + (github.event_name == 'pull_request') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/test pr')) runs-on: ubuntu-latest - + env: + LHCI_TMP_UPLOAD: ".lighthouseci" steps: - name: Record start time id: start_time run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT + - name: Get PR details + id: pr + uses: actions/github-script@v7 + with: + script: | + const prNumber = context.eventName === 'issue_comment' + ? context.issue.number + : context.payload.number; + + const pr = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: prNumber + }); + return { + ref: pr.data.head.ref, + sha: pr.data.head.sha, + number: pr.data.number + }; + + # ============================================================ + # CRITICAL: Checkout BEFORE paths-filter! + # ============================================================ + + - name: Checkout PR code + uses: actions/checkout@v4 + with: + ref: ${{ fromJSON(steps.pr.outputs.result).sha }} + fetch-depth: 0 + + # ----------------------- + # QUICK: detect changed paths (needs checkout first!) + # ----------------------- + - name: Determine changed paths + id: paths + uses: dorny/paths-filter@v2 + with: + filters: | + docs: + - '**/*.md' + code: + - 'src/**' + - 'public/**' + - 'build/**' + - 'package.json' + - 'package-lock.json' + static: + - 'assets/**' + - 'static/**' + tests: + - 'tests/**' + - 'playwright.config.*' + # outputs: steps.paths.outputs. = 'true' or 'false' + - name: Cancel previous runs uses: styfle/cancel-workflow-action@0.12.0 with: @@ -43,55 +99,47 @@ jobs: content: 'rocket' }); - - name: Post building comment - id: build_comment + - name: Post initial comment + id: mega_comment uses: actions/github-script@v7 with: script: | - // FIXED: Use context.eventName and context.payload - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login : context.payload.pull_request.user.login; - const issueNumber = context.eventName === 'issue_comment' - ? context.issue.number - : context.payload.number; - const comment = await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number: issueNumber, - body: `šŸ”Ø **Building PR for testing...** (requested by @${username}) 🚧\n\n` + - `` + - `šŸ—ļø Please wait while the build completes. This may take a few minutes. ā›ļø` + issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `${checklist_spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `⬜ Performance audit (Lighthouse)\n` + + `⬜ Bundle size analysis\n` + + `⬜ Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` }); return comment.data.id; - - name: Get PR details - id: pr - uses: actions/github-script@v7 - with: - script: | - // FIXED: Use context.eventName and context.payload - const prNumber = context.eventName === 'issue_comment' - ? context.issue.number - : context.payload.number; - - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prNumber - }); - return { - ref: pr.data.head.ref, - sha: pr.data.head.sha, - number: pr.data.number - }; + # ============================================================ + # BUILD PHASE - Only happens ONCE! + # ============================================================ - - name: Checkout PR code - uses: actions/checkout@v4 + - name: Cache node modules + uses: actions/cache@v4 with: - ref: ${{ fromJSON(steps.pr.outputs.result).sha }} - fetch-depth: 0 + path: | + ~/.npm + node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- - name: Setup Node.js uses: actions/setup-node@v4 @@ -99,29 +147,422 @@ jobs: node-version: '20' cache: 'npm' + - name: Update comment - Installing Dependencies + uses: actions/github-script@v7 + with: + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `⬜ Performance audit (Lighthouse)\n` + + `⬜ Bundle size analysis\n` + + `⬜ Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` + }); + - name: Install dependencies run: npm ci + - name: Update comment - Building + uses: actions/github-script@v7 + with: + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `⬜ Performance audit (Lighthouse)\n` + + `⬜ Bundle size analysis\n` + + `⬜ Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` + }); + - name: Build website run: npm run build env: NODE_ENV: production - - name: Add .nojekyll file - run: touch build/.nojekyll + # ============================================================ + # ANALYSIS PHASE - Using the same build! + # ============================================================ + + - name: Update comment - Running Lighthouse + uses: actions/github-script@v7 + with: + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `${checklist_spinner} Performance audit (Lighthouse)\n` + + `⬜ Bundle size analysis\n` + + `⬜ Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` + }); + + - name: Cache LHCI & tools + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-lhci-${{ hashFiles('**/package-lock.json') }} + restore-keys: ${{ runner.os }}-lhci- + + - name: Install Lighthouse CI + run: npm install -g @lhci/cli@0.13.x + + - name: Create Lighthouse config (filesystem only) + run: | + cat > lighthouserc.json << 'EOF' + { + "ci": { + "collect": { + "numberOfRuns": 1, + "url": ["http://localhost:8080"], + "settings": { + "chromeFlags": "--no-sandbox --disable-dev-shm-usage" + } + }, + "upload": { + "target": "filesystem", + "outputDir": ".lighthouseci" + } + } + } + EOF + - name: Start HTTP server for Lighthouse + run: | + npx http-server build -p 8080 & + echo $! > server.pid + for i in {1..30}; do + if curl -s http://localhost:8080 > /dev/null; then + echo "Server is ready!" + break + fi + echo "Waiting for server... ($i/30)" + sleep 1 + done + + - name: Run Lighthouse CI + id: lighthouse + continue-on-error: true + run: | + set +e + lhci autorun + LHCI_EXIT_CODE=$? + set -e + + echo "Lighthouse exit code: $LHCI_EXIT_CODE" + + if [ -d ".lighthouseci" ] && [ "$(ls -A .lighthouseci 2>/dev/null)" ]; then + echo "lighthouse_success=true" >> $GITHUB_OUTPUT + ISSUES_FOUND=$(find .lighthouseci -name "*.json" -type f | wc -l) + echo "issues_count=$ISSUES_FOUND" >> $GITHUB_OUTPUT + + # Extract scores from the report + REPORT_JSON=$(find .lighthouseci -type f -name "*.report.json" | head -n1 || true) + if [ -n "$REPORT_JSON" ] && [ -f "$REPORT_JSON" ]; then + PERF=$(jq -r '.categories.performance.score // "null"' "$REPORT_JSON" 2>/dev/null || echo "null") + ACC=$(jq -r '.categories.accessibility.score // "null"' "$REPORT_JSON" 2>/dev/null || echo "null") + BP=$(jq -r '.categories["best-practices"].score // "null"' "$REPORT_JSON" 2>/dev/null || echo "null") + SEO=$(jq -r '.categories.seo.score // "null"' "$REPORT_JSON" 2>/dev/null || echo "null") + echo "{\"performance\":$PERF,\"accessibility\":$ACC,\"best_practices\":$BP,\"seo\":$SEO}" > lh-summary.json + else + echo "{}" > lh-summary.json + fi + else + echo "lighthouse_success=false" >> $GITHUB_OUTPUT + echo "issues_count=0" >> $GITHUB_OUTPUT + echo "{}" > lh-summary.json + fi + + exit 0 + + - name: Stop HTTP server + if: always() + run: | + if [ -f server.pid ]; then + kill $(cat server.pid) || true + rm server.pid + fi + + - name: Upload Lighthouse results + if: always() + uses: actions/upload-artifact@v4 + with: + name: lighthouse-results + path: .lighthouseci/ + if-no-files-found: warn + + # ============================================================ + # BUNDLE SIZE + # ============================================================ + + - name: Update comment - Bundle Size Analysis + uses: actions/github-script@v7 + with: + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `āœ… Performance audit (Lighthouse)\n` + + `${checklist_spinner} Bundle size analysis\n` + + `⬜ Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` + }); + + - name: Analyze bundle size + id: bundle_analysis + continue-on-error: true + run: | + TOTAL_SIZE=$(du -sh build | cut -f1) + echo "total_size=$TOTAL_SIZE" >> $GITHUB_OUTPUT + echo "### šŸ“¦ Top 5 Largest JavaScript Files:" > bundle-report.md + JS_COUNT=$(find build -type f -name "*.js" | wc -l) + if [ "$JS_COUNT" -gt 0 ]; then + find build -type f -name "*.js" -exec du -h {} \; | sort -rh | head -5 | while read line; do + size=$(echo "$line" | awk '{print $1}') + file=$(echo "$line" | awk '{print $2}' | sed 's|build/||') + echo "- **$size** - \`$file\`" >> bundle-report.md + done + else + echo "- No JavaScript files found" >> bundle-report.md + fi + echo "" >> bundle-report.md + echo "### šŸŽØ Top 5 Largest CSS Files:" >> bundle-report.md + CSS_COUNT=$(find build -type f -name "*.css" | wc -l) + if [ "$CSS_COUNT" -gt 0 ]; then + find build -type f -name "*.css" -exec du -h {} \; | sort -rh | head -5 | while read line; do + size=$(echo "$line" | awk '{print $1}') + file=$(echo "$line" | awk '{print $2}' | sed 's|build/||') + echo "- **$size** - \`$file\`" >> bundle-report.md + done + else + echo "- No CSS files found (might be inlined in JS)" >> bundle-report.md + fi - - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 + - name: Update comment - Accessibility Testing + uses: actions/github-script@v7 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./build - publish_branch: gh-pages - force_orphan: true - commit_message: '[PR Test] Deploy PR #${{ fromJSON(steps.pr.outputs.result).number }} (${{ fromJSON(steps.pr.outputs.result).sha }})' + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `āœ… Performance audit (Lighthouse)\n` + + `āœ… Bundle size analysis\n` + + `${checklist_spinner} Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `⬜ Deploying to test environment` + }); + + - name: Setup Chrome and ChromeDriver + run: | + npx browser-driver-manager install chrome + + - name: Run accessibility tests + id: a11y_test + continue-on-error: true + run: | + npx http-server build -p 9090 & + SERVER_PID=$! + echo "Waiting for server to start..." + for i in {1..30}; do + if curl -s http://localhost:9090 > /dev/null; then + echo "Server is ready!" + break + fi + echo "Attempt $i/30..." + sleep 2 + done + npm install -g @axe-core/cli@4.10.2 + # Separate stderr from JSON output + axe http://localhost:9090 --stdout 2>axe-errors.log > axe-results.json || true + kill $SERVER_PID || true + + # Debug: show file size and first few lines + echo "axe-results.json size: $(wc -c < axe-results.json 2>/dev/null || echo 0) bytes" + + if [ -f "axe-results.json" ] && [ -s "axe-results.json" ]; then + # Validate JSON before parsing - axe returns an array + if jq empty axe-results.json 2>/dev/null; then + # Parse violations and passes from the first result in the array + VIOLATIONS=$(jq '.[0].violations | length' axe-results.json 2>/dev/null || echo "0") + PASSES=$(jq '.[0].passes | length' axe-results.json 2>/dev/null || echo "0") + echo "Successfully parsed: $VIOLATIONS violations, $PASSES passes" + echo "a11y_success=true" >> $GITHUB_OUTPUT + echo "violations=$VIOLATIONS" >> $GITHUB_OUTPUT + echo "passes=$PASSES" >> $GITHUB_OUTPUT + else + echo "JSON parsing failed" + echo "a11y_success=false" >> $GITHUB_OUTPUT + echo "violations=error" >> $GITHUB_OUTPUT + echo "passes=error" >> $GITHUB_OUTPUT + fi + else + echo "File doesn't exist or is empty" + echo "a11y_success=false" >> $GITHUB_OUTPUT + echo "violations=error" >> $GITHUB_OUTPUT + echo "passes=error" >> $GITHUB_OUTPUT + fi + + - name: Upload accessibility results + if: always() + uses: actions/upload-artifact@v4 + with: + name: accessibility-results + path: axe-results.json + if-no-files-found: warn + + # ============================================================ + # VISUAL REGRESSION (Playwright) - only if code or tests changed + # ============================================================ + - name: Visual regression - Install Playwright (only on code/test changes) + if: steps.paths.outputs.code == 'true' || steps.paths.outputs.tests == 'true' + run: | + npm ci + npx playwright install --with-deps + + # ============================================================ + # DEPLOYMENT PHASE + # ============================================================ + + - name: Update comment - Deploying + uses: actions/github-script@v7 + with: + script: | + const initial_spinner = ''; + const checklist_spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `${initial_spinner} **MEGA PR Test & Analysis, requested by @${username}...**\n\n` + + `šŸ—ļø **Build Phase:**\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building project\n\n` + + `šŸ” **Analysis Phase:**\n` + + `āœ… Performance audit (Lighthouse)\n` + + `āœ… Bundle size analysis\n` + + `āœ… Accessibility testing\n\n` + + `šŸš€ **Deployment Phase:**\n` + + `${checklist_spinner} Deploying to test environment` + }); + + - name: Prepare per-PR directory + run: | + mkdir -p ./public/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }} + cp -r ./build/* ./public/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/ + touch ./public/.nojekyll + + - name: Checkout gh-pages branch + id: checkout_gh_pages + uses: actions/checkout@v4 + with: + ref: gh-pages + path: gh-pages + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + continue-on-error: true + + - name: Create gh-pages branch if not exists + if: steps.checkout_gh_pages.outcome == 'failure' + run: | + mkdir -p gh-pages + cd gh-pages + git init -b gh-pages + git remote add origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git + + - name: Sync files to gh-pages + run: | + mkdir -p gh-pages/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }} + cp -r public/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/* gh-pages/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/ + cp public/.nojekyll gh-pages/ || true + + - name: Commit and push to gh-pages + run: | + cd gh-pages + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + git add . + if [ -n "$(git status --porcelain)" ]; then + git commit -m "Deploy PR ${{ fromJSON(steps.pr.outputs.result).number }} by ${{ github.actor }}" + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git + git push origin gh-pages + else + echo "No changes to commit." + fi + + # ============================================================ + # FINAL RESULTS - The Grand Finale! + # ============================================================ - name: Calculate duration id: duration - if: always() run: | END_TIME=$(date +%s) START_TIME=${{ steps.start_time.outputs.timestamp }} @@ -130,33 +571,137 @@ jobs: SECONDS=$((DURATION % 60)) echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT - - name: Update comment with success + - name: Update comment with full results if: success() uses: actions/github-script@v7 with: script: | - // FIXED: Use context.eventName and context.payload + const fs = require('fs'); const prNumber = ${{ fromJSON(steps.pr.outputs.result).number }}; const sha = '${{ fromJSON(steps.pr.outputs.result).sha }}'.substring(0, 7); - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login : context.payload.pull_request.user.login; const duration = '${{ steps.duration.outputs.duration }}'; + const baseUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}`; + const pageUrl = `${baseUrl}/${context.actor}/${prNumber}/`; + + // Bundle analysis + const bundleReport = fs.readFileSync('bundle-report.md', 'utf8'); + const totalSize = '${{ steps.bundle_analysis.outputs.total_size }}'; + + // Lighthouse results - READ THE SCORES! + const lighthouseSuccess = '${{ steps.lighthouse.outputs.lighthouse_success }}' === 'true'; + let lighthouseSection = ''; + if (lighthouseSuccess) { + try { + // Read the summary file we created + const lhSummary = JSON.parse(fs.readFileSync('lh-summary.json', 'utf8')); + + // Find the full report for detailed metrics + const { execSync } = require('child_process'); + let detailedMetrics = ''; + try { + const reportFile = execSync('find .lighthouseci -type f -name "*.report.json" | head -n1', {encoding: 'utf8'}).trim(); + if (reportFile) { + const fullReport = JSON.parse(fs.readFileSync(reportFile, 'utf8')); + const audits = fullReport.audits; + + // Extract detailed performance metrics + detailedMetrics = `\n\n**šŸ“Š Detailed Performance Metrics:**\n` + + `- **First Contentful Paint (FCP):** ${audits['first-contentful-paint']?.displayValue || 'N/A'}\n` + + `- **Largest Contentful Paint (LCP):** ${audits['largest-contentful-paint']?.displayValue || 'N/A'}\n` + + `- **Total Blocking Time (TBT):** ${audits['total-blocking-time']?.displayValue || 'N/A'}\n` + + `- **Cumulative Layout Shift (CLS):** ${audits['cumulative-layout-shift']?.displayValue || 'N/A'}\n` + + `- **Speed Index:** ${audits['speed-index']?.displayValue || 'N/A'}\n` + + `- **Time to Interactive (TTI):** ${audits['interactive']?.displayValue || 'N/A'}\n` + + `- **Max Potential FID:** ${audits['max-potential-fid']?.displayValue || 'N/A'}\n` + + `- **Server Response Time:** ${audits['server-response-time']?.displayValue || 'N/A'}\n` + + `- **DOM Size:** ${audits['dom-size']?.displayValue || 'N/A'}\n` + + `- **Main Thread Work:** ${audits['mainthread-work-breakdown']?.displayValue || 'N/A'}`; + } + } catch (e) { + console.log('Could not extract detailed metrics:', e.message); + } + + // Convert scores to percentages + const perfScore = lhSummary.performance !== null && lhSummary.performance !== 'null' ? Math.round(lhSummary.performance * 100) : 'N/A'; + const accScore = lhSummary.accessibility !== null && lhSummary.accessibility !== 'null' ? Math.round(lhSummary.accessibility * 100) : 'N/A'; + const bpScore = lhSummary.best_practices !== null && lhSummary.best_practices !== 'null' ? Math.round(lhSummary.best_practices * 100) : 'N/A'; + const seoScore = lhSummary.seo !== null && lhSummary.seo !== 'null' ? Math.round(lhSummary.seo * 100) : 'N/A'; + + // Determine emoji based on score + const getEmoji = (score) => { + if (score === 'N/A') return 'ā“'; + if (score >= 90) return '🟢'; + if (score >= 50) return '🟠'; + return 'šŸ”“'; + }; + + lighthouseSection = `**Core Scores:**\n` + + `- ${getEmoji(perfScore)} **Performance:** ${perfScore}/100\n` + + `- ${getEmoji(accScore)} **Accessibility:** ${accScore}/100\n` + + `- ${getEmoji(bpScore)} **Best Practices:** ${bpScore}/100\n` + + `- ${getEmoji(seoScore)} **SEO:** ${seoScore}/100\n` + + detailedMetrics + + `\n\nšŸ“„ [Download full Lighthouse report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`; + } catch (error) { + console.log('Error reading Lighthouse scores:', error.message); + lighthouseSection = `āœ… Audit completed successfully!\n` + + `- View detailed results in [workflow artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`; + } + } else { + lighthouseSection = `āš ļø Lighthouse audit encountered issues\n` + + `- Check [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details\n` + + `- This may be due to app complexity or server startup issues`; + } + + // Accessibility results + const a11ySuccess = '${{ steps.a11y_test.outputs.a11y_success }}' === 'true'; + const a11yViolations = '${{ steps.a11y_test.outputs.violations }}'; + const a11yPasses = '${{ steps.a11y_test.outputs.passes }}'; + let a11ySection = ''; + + if (a11ySuccess && a11yViolations !== 'error') { + const status = a11yViolations === '0' ? 'āœ… All Clear!' : `āš ļø ${a11yViolations} issue(s) found`; + a11ySection = `${status}\n` + + `- āœ… Passed checks: ${a11yPasses}\n` + + `- ${a11yViolations !== '0' ? 'āš ļø' : 'āœ…'} Violations: ${a11yViolations}\n` + + `- Full report available in [artifacts](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`; + } else { + a11ySection = `āš ļø Accessibility testing encountered issues\n` + + `- Check [workflow logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details\n` + + `- Manual accessibility review recommended`; + } + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: commentId, - body: `šŸš€ **PR Test Deployment Complete!**\n\n` + - `Hi, @${username}! Your pull request test deployment is complete! āœ…šŸŽ‰\n\n` + - `View your changes at: https://omniblocks.github.io/scratch-gui\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` + - `- Note: This deployment will be replaced when another PR is tested.` + comment_id: ${{ steps.mega_comment.outputs.result }}, + body: `# šŸŽ‰ MEGA PR Test & Analysis Complete!\n\n` + + `Hi @${username}! Your PR has been fully tested and analyzed! šŸš€āœØ\n\n` + + `---\n\n` + + `### šŸŒ Test Deployment\n` + + `**Live Preview:** ${pageUrl}\n\n` + + `---\n\n` + + `### šŸ”¦ Performance Audit (Lighthouse)\n` + + lighthouseSection + `\n\n` + + `---\n\n` + + `### šŸ“¦ Bundle Size Analysis\n` + + `**Total Build Size:** ${totalSize}\n\n` + + bundleReport + `\n\n` + + `---\n\n` + + `### ♿ Accessibility Testing\n` + + a11ySection + `\n\n` + + `---\n\n` + + `### šŸ“Š Build Details\n` + + `- **PR:** #${prNumber}\n` + + `- **Commit:** \`${sha}\`\n` + + `- **Requested by:** @${username}\n` + + `- **Total time:** ${duration}\n\n` + + `---\n\n` + + `šŸ’” **Pro Tip:** Only one build was needed for all these checks - saving CI/CD minutes! šŸŽÆ` }); - name: Update comment with failure @@ -164,19 +709,30 @@ jobs: uses: actions/github-script@v7 with: script: | - // FIXED: Use context.eventName and context.payload - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; + const duration = '${{ steps.duration.outputs.duration }}' || 'N/A'; + const commentId = '${{ steps.mega_comment.outputs.result }}'; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: commentId, - body: `āŒ **PR Test Deployment Failed** @${username}\n\n` + - `Something went wrong during the build or deployment process.\n` + - `Build time: ${duration}\n\n` + - `Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.` - }); + const failureBody = `āŒ **MEGA PR Test Failed** @${username}\n\n` + + `Something went wrong during the build or analysis process.\n` + + `Total time: ${duration}\n\n` + + `Check the logs for details: [View Logs](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`; + + // If comment was created, update it; otherwise create a new one + if (commentId && commentId !== '') { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: commentId, + body: failureBody + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, + body: failureBody + }); + } diff --git a/.nvmrc b/.nvmrc index 6f7f377bf..53d1c14db 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16 +v22 diff --git a/package-lock.json b/package-lock.json index 83d114db8..d16f89536 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18768,8 +18768,8 @@ }, "node_modules/scratch-vm": { "version": "2.1.46", - "resolved": "git+ssh://git@github.com/OmniBlocks/scratch-vm.git#6fc3d31c2d7d934c50f9ef3f1e1d81347b9b5b98", - "integrity": "sha512-6ssCRpQP+hxVNmtdBccenf3CdmFJ046vow7FXPMpx/Uyir/K3TtyAMlvxCM/8iQkZ35KR5rtTi9BzBmEhiEjeg==", + "resolved": "git+ssh://git@github.com/OmniBlocks/scratch-vm.git#e8e4f8b6ddb3fd584c8980eb686a4afc0577f099", + "integrity": "sha512-0d3ovXmTK/oCEcJOaz3voCserNqiSdIJNgDuvF6gPaTW2nxO+8XXMCBqTUfH4het4LNuOLm6TqggreUVpD2GBA==", "license": "MPL-2.0", "dependencies": { "@turbowarp/json": "^0.1.2", diff --git a/static/404.html b/static/404.html index df9b13c33..5714a9821 100644 --- a/static/404.html +++ b/static/404.html @@ -1,11 +1,11 @@ - + - + Page Not Found - OmniBlocks @@ -193,14 +193,14 @@

Looks like you hit a bug!

OmniBlocks is a Multi-Language IDE in development, based on TurboWarp with enhanced features like a music editor. Future plans include text editors for Python, C, and more!

- Home + Home Take Me Home
- - Sad Boxy. :( + + Sad Boxy. :(
@@ -208,11 +208,11 @@

Looks like you hit a bug!

OmniBlocks is open source (GNU GPL v3) | Media licensed CC BY-SA 4.0
-

Well, there is a man here.
+

There is a man here.
He offers you an egg. Take it?
You got the egg.
- It's an easter egg. Inside is a...
- OmniBlock? + It's an easter egg. Inside is an...
+ OmniBlock...?