From a9b64c66052a949159ffa922a7f3a83c093ee631 Mon Sep 17 00:00:00 2001 From: ampelectrecuted <197376797+ampelectrecuted@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:10:01 +0100 Subject: [PATCH 01/43] will this work --- .github/workflows/prtest.yml | 50 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index d35618aaa..d3ad5a1f8 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -7,7 +7,7 @@ on: types: [opened] permissions: - contents: write + contents: read pull-requests: write pages: write id-token: write @@ -18,7 +18,11 @@ jobs: (github.event_name == 'pull_request' && github.event.action == 'opened') || (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/test pr')) runs-on: ubuntu-latest - + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: - name: Record start time id: start_time @@ -48,7 +52,6 @@ jobs: 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 : context.payload.pull_request.user.login; @@ -60,9 +63,7 @@ jobs: 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. ā›ļø` + body: `šŸ”Ø **Building PR for testing...** (requested by @${username}) 🚧` }); return comment.data.id; @@ -71,7 +72,6 @@ jobs: 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; @@ -107,17 +107,20 @@ jobs: env: NODE_ENV: production - - name: Add .nojekyll file - run: touch build/.nojekyll + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public - name: Deploy to GitHub Pages - uses: peaceiris/actions-gh-pages@v3 - 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 }})' + id: deployment + uses: actions/deploy-pages@v4 - name: Calculate duration id: duration @@ -135,7 +138,6 @@ jobs: uses: actions/github-script@v7 with: script: | - // FIXED: Use context.eventName and context.payload 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 }}; @@ -143,20 +145,21 @@ jobs: ? context.payload.comment.user.login : context.payload.pull_request.user.login; const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; 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` + + `Hi, @${username}! Your PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\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.` + `- Build time: ${duration}\n` }); - name: Update comment with failure @@ -164,7 +167,6 @@ 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 @@ -176,7 +178,7 @@ jobs: 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` + + `Something went wrong during build or deployment.\n` + `Build time: ${duration}\n\n` + - `Check the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for details.` + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` }); From 4cb1a103b18162082cc7410abc672ac46ebe5cc7 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:18:14 -0400 Subject: [PATCH 02/43] Update prtest.yml --- .github/workflows/prtest.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index d3ad5a1f8..77c37c0ae 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -63,7 +63,9 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, - body: `šŸ”Ø **Building PR for testing...** (requested by @${username}) 🚧` + body: `šŸ”Ø **Building PR for testing...** (requested by @${username}) 🚧\n\n` + + `` + + `šŸ—ļø Please wait while the build completes. This may take a few minutes. ā›ļø` }); return comment.data.id; From fb792235b993720cb7a3e635854663779efc6b0d Mon Sep 17 00:00:00 2001 From: ampelectrecuted <197376797+ampelectrecuted@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:35:49 +0100 Subject: [PATCH 03/43] new cache epoch --- webpack.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webpack.config.js b/webpack.config.js index 96c30a17c..930bd0c16 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -29,7 +29,7 @@ const htmlWebpackPluginCommon = { }; // When this changes, the path for all JS files will change, bypassing any HTTP caches -const CACHE_EPOCH = 'pentapod'; +const CACHE_EPOCH = 'omnibruh'; const base = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', From f2382300e64b082136c8cff26a75f076ab627e3d Mon Sep 17 00:00:00 2001 From: ampelectrecuted <197376797+ampelectrecuted@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:41:01 +0100 Subject: [PATCH 04/43] speed up with cache --- webpack.config.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 930bd0c16..2e413ea10 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -69,6 +69,9 @@ const base = { 'scratch-render-fonts$': path.resolve(__dirname, 'src/lib/tw-scratch-render-fonts') } }, + cache: { + type: 'filesystem', + }, module: { rules: [{ test: /\.jsx?$/, @@ -87,7 +90,8 @@ const base = { ['react-intl', { messagesDir: './translations/messages/' }]], - presets: ['@babel/preset-env', '@babel/preset-react'] + presets: ['@babel/preset-env', '@babel/preset-react'], + cacheDirectory: true, } }, { @@ -140,7 +144,7 @@ const base = { if (!process.env.CI) { base.plugins.push(new webpack.ProgressPlugin()); - base.plugins.push(new webpack.HotModuleReplacementPlugin()); + base.plugins.push(new webpack.HotModuleReplacementPlugin()); } module.exports = [ From 9b1f7fabb5953a096be6eef9157ab559d65426d8 Mon Sep 17 00:00:00 2001 From: ampelectrecuted <197376797+ampelectrecuted@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:41:16 +0100 Subject: [PATCH 05/43] remove debug --- webpack.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 2e413ea10..23c8f4bfe 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,9 +13,6 @@ const postcssImport = require('postcss-import'); const STATIC_PATH = process.env.STATIC_PATH || '/static'; const {APP_NAME} = require('./src/lib/brand'); -console.log('šŸ” DEBUG: Webpack config loading...'); -console.log('šŸ” DEBUG: APP_VERSION from env:', process.env.APP_VERSION); -console.log('šŸ” DEBUG: Will inject:', JSON.stringify(process.env.APP_VERSION || '')); const root = process.env.ROOT || ''; if (root.length > 0 && !root.endsWith('/')) { From d4fc8b0a157669809b132be8cd95665734dadbf6 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 15:47:21 -0400 Subject: [PATCH 06/43] Update webpack.config.js Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- webpack.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/webpack.config.js b/webpack.config.js index 23c8f4bfe..8855bfa32 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -68,6 +68,9 @@ const base = { }, cache: { type: 'filesystem', + buildDependencies: { + config: [__filename] + } }, module: { rules: [{ From 237aef842e7b96991f2fc31c370efa3df63056cc Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:03:50 -0400 Subject: [PATCH 07/43] Update prtest.yml --- .github/workflows/prtest.yml | 121 +++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 77c37c0ae..e1ea97e81 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -46,28 +46,107 @@ jobs: comment_id: context.payload.comment.id, content: 'rocket' }); +# Replace the existing "Post building comment" section with this: - - name: Post building comment - id: build_comment - uses: actions/github-script@v7 - with: - script: | - 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. ā›ļø` - }); - return comment.data.id; +- name: Post building comment + id: build_comment + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `${spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + return comment.data.id; + +- name: Update comment - Installing Dependencies + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `${spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + +- name: Install dependencies + run: npm ci + +- name: Update comment - Building + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${spinner} Building\n` + + `⬜ Deploying` + }); + +- name: Build website + run: npm run build + env: + NODE_ENV: production + +- name: Update comment - Deploying + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${spinner} Deploying to GitHub Pages` + }); + +- name: Prepare per-PR directory + run: | + # ... (your existing preparation code) + +- name: Update comment - Deployment Complete + if: success() + uses: actions/github-script@v7 + with: + script: | + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + const username = '${{ github.actor }}'; + const prNumber = ${{ fromJSON(steps.pr.outputs.result).number }}; + const duration = ((Date.now() - ${{ steps.start_time.outputs.timestamp }}) / 1000).toFixed(0); + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `āœ… **PR test build deployed!**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `āœ… Deploying to GitHub Pages\n\n` + + `šŸ”— **Preview URL:** ${pageUrl}${username}/${prNumber}/\n` + + `ā±ļø Build time: ${duration}s` + }); - name: Get PR details id: pr From 46358f7590099fc1a898a90ba23ab1a30c4fc8fc Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 16:54:23 -0400 Subject: [PATCH 08/43] Update prtest.yml --- .github/workflows/prtest.yml | 171 ++++++++++++++--------------------- 1 file changed, 68 insertions(+), 103 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index e1ea97e81..b42cef8ff 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -46,107 +46,6 @@ jobs: comment_id: context.payload.comment.id, content: 'rocket' }); -# Replace the existing "Post building comment" section with this: - -- name: Post building comment - id: build_comment - uses: actions/github-script@v7 - with: - script: | - const spinner = ''; - const comment = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, - body: `${spinner} **Building PR for testing...**\n\n` + - `${spinner} Checkout & Setup\n` + - `⬜ Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - return comment.data.id; - -- name: Update comment - Installing Dependencies - uses: actions/github-script@v7 - with: - script: | - const spinner = ''; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + - `āœ… Checkout & Setup\n` + - `${spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - -- name: Install dependencies - run: npm ci - -- name: Update comment - Building - uses: actions/github-script@v7 - with: - script: | - const spinner = ''; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${spinner} Building\n` + - `⬜ Deploying` - }); - -- name: Build website - run: npm run build - env: - NODE_ENV: production - -- name: Update comment - Deploying - uses: actions/github-script@v7 - with: - script: | - const spinner = ''; - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${spinner} Deploying to GitHub Pages` - }); - -- name: Prepare per-PR directory - run: | - # ... (your existing preparation code) - -- name: Update comment - Deployment Complete - if: success() - uses: actions/github-script@v7 - with: - script: | - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - const username = '${{ github.actor }}'; - const prNumber = ${{ fromJSON(steps.pr.outputs.result).number }}; - const duration = ((Date.now() - ${{ steps.start_time.outputs.timestamp }}) / 1000).toFixed(0); - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `āœ… **PR test build deployed!**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `āœ… Deploying to GitHub Pages\n\n` + - `šŸ”— **Preview URL:** ${pageUrl}${username}/${prNumber}/\n` + - `ā±ļø Build time: ${duration}s` - }); - name: Get PR details id: pr @@ -156,7 +55,7 @@ jobs: 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, @@ -168,6 +67,24 @@ jobs: number: pr.data.number }; + - name: Post building comment + id: build_comment + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `${spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + return comment.data.id; + - name: Checkout PR code uses: actions/checkout@v4 with: @@ -180,14 +97,62 @@ jobs: node-version: '20' cache: 'npm' + - name: Update comment - Installing Dependencies + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `${spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + - name: Install dependencies run: npm ci + - name: Update comment - Building + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${spinner} Building\n` + + `⬜ Deploying` + }); + - name: Build website run: npm run build env: NODE_ENV: production + - name: Update comment - Deploying + uses: actions/github-script@v7 + with: + script: | + const spinner = ''; + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${spinner} **Building PR for testing...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${spinner} Deploying to GitHub Pages` + }); + - name: Prepare per-PR directory run: | mkdir -p ./public/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }} @@ -227,7 +192,7 @@ jobs: : context.payload.pull_request.user.login; const duration = '${{ steps.duration.outputs.duration }}'; const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - + await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, From 709dce41f5f30558b90e7d79e2aa63b2ff711de8 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 17:42:35 -0400 Subject: [PATCH 09/43] Update prtest.yml --- .github/workflows/prtest.yml | 66 +++++++++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index b42cef8ff..6fceb659b 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -72,12 +72,15 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + const spinner = ''; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; const comment = await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, - body: `${spinner} **Building PR for testing...**\n\n` + + body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + `${spinner} Checkout & Setup\n` + `⬜ Installing dependencies\n` + `⬜ Building\n` + @@ -101,12 +104,15 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + const 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.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + + body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + `āœ… Checkout & Setup\n` + `${spinner} Installing dependencies\n` + `⬜ Building\n` + @@ -120,12 +126,15 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + const 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.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + + body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + `āœ… Checkout & Setup\n` + `āœ… Installing dependencies\n` + `${spinner} Building\n` + @@ -141,12 +150,15 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + const 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.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing...**\n\n` + + body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + `āœ… Checkout & Setup\n` + `āœ… Installing dependencies\n` + `āœ… Building\n` + @@ -228,3 +240,41 @@ jobs: `Build time: ${duration}\n\n` + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` }); + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + + `- Requested by: @${username}\n` + + `- Build time: ${duration}\n` + }); + + - name: Update comment with failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ steps.build_comment.outputs.result }}; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + + 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 build or deployment.\n` + + `Build time: ${duration}\n\n` + + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + }); From 5c4340714dbdcca7fe7d8e0b0cbb3ccec2620724 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 18:00:48 -0400 Subject: [PATCH 10/43] Update prtest.yml --- .github/workflows/prtest.yml | 140 ++++++++++++++++++++++++++++++++++- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 6fceb659b..29cac3094 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -72,7 +72,8 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + const initial_spinner = ''; + const checklist_spinner = ''; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login : context.payload.pull_request.user.login; @@ -80,8 +81,8 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, - body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `${spinner} Checkout & Setup\n` + + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `${checklist_spinner} Checkout & Setup\n` + `⬜ Installing dependencies\n` + `⬜ Building\n` + `⬜ Deploying` @@ -104,7 +105,56 @@ jobs: uses: actions/github-script@v7 with: script: | - const spinner = ''; + 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building\n` + + `⬜ Deploying` + }); + + - name: Build website + run: npm run build + env: + NODE_ENV: production + + - 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; @@ -112,6 +162,88 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${checklist_spinner} Deploying to GitHub Pages` + }); + + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Calculate duration + id: duration + if: always() + run: | + END_TIME=$(date +%s) + START_TIME=${{ steps.start_time.outputs.timestamp }} + DURATION=$((END_TIME - START_TIME)) + MINUTES=$((DURATION / 60)) + SECONDS=$((DURATION % 60)) + echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + + - name: Update comment with success + if: success() + uses: actions/github-script@v7 + with: + script: | + 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 + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + + `- Requested by: @${username}\n` + + `- Build time: ${duration}\n` + }); + + - name: Update comment with failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ steps.build_comment.outputs.result }}; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + + 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 build or deployment.\n` + + `Build time: ${duration}\n\n` + + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + }); body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + `āœ… Checkout & Setup\n` + `${spinner} Installing dependencies\n` + From 668f2fc2dfcdd3f49104460e96f6b98bf6a46361 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:52:59 -0400 Subject: [PATCH 11/43] Update prtest.yml --- .github/workflows/prtest.yml | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 29cac3094..800d797e1 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -224,6 +224,138 @@ jobs: `- Build time: ${duration}\n` }); + - name: Update comment with failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ steps.build_comment.outputs.result }}; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + + 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 build or deployment.\n` + + `Build time: ${duration}\n\n` + + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + }); + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building\n` + + `⬜ Deploying` + }); + + - name: Build website + run: npm run build + env: + NODE_ENV: production + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${checklist_spinner} Deploying to GitHub Pages` + }); + + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Calculate duration + id: duration + if: always() + run: | + END_TIME=$(date +%s) + START_TIME=${{ steps.start_time.outputs.timestamp }} + DURATION=$((END_TIME - START_TIME)) + MINUTES=$((DURATION / 60)) + SECONDS=$((DURATION % 60)) + echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + + - name: Update comment with success + if: success() + uses: actions/github-script@v7 + with: + script: | + 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 + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + + `- Requested by: @${username}\n` + + `- Build time: ${duration}\n` + }); + - name: Update comment with failure if: failure() uses: actions/github-script@v7 From 9b155576df4674ed0f793e58806f2ec18883e867 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 19:55:51 -0400 Subject: [PATCH 12/43] Update prtest.yml --- .github/workflows/prtest.yml | 132 +++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 800d797e1..ea2728903 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -356,6 +356,138 @@ jobs: `- Build time: ${duration}\n` }); + - name: Update comment with failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ steps.build_comment.outputs.result }}; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + + 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 build or deployment.\n` + + `Build time: ${duration}\n\n` + + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + }); + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building\n` + + `⬜ Deploying` + }); + + - name: Build website + run: npm run build + env: + NODE_ENV: production + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${checklist_spinner} Deploying to GitHub Pages` + }); + + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Calculate duration + id: duration + if: always() + run: | + END_TIME=$(date +%s) + START_TIME=${{ steps.start_time.outputs.timestamp }} + DURATION=$((END_TIME - START_TIME)) + MINUTES=$((DURATION / 60)) + SECONDS=$((DURATION % 60)) + echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + + - name: Update comment with success + if: success() + uses: actions/github-script@v7 + with: + script: | + 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 + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + + `- Requested by: @${username}\n` + + `- Build time: ${duration}\n` + }); + - name: Update comment with failure if: failure() uses: actions/github-script@v7 From 05c676ede15fa7d616d1bd0f7b0b86dff5f2f0d6 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:06:52 -0400 Subject: [PATCH 13/43] Update prtest.yml --- .github/workflows/prtest.yml | 432 +---------------------------------- 1 file changed, 2 insertions(+), 430 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index ea2728903..bae437b28 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -72,7 +72,9 @@ jobs: uses: actions/github-script@v7 with: script: | + // Spinner for the main message (original larger one) const initial_spinner = ''; + // Spinner for checklist items (new smaller one) const checklist_spinner = ''; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login @@ -244,433 +246,3 @@ jobs: `Build time: ${duration}\n\n` + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` }); - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `${checklist_spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${checklist_spinner} Building\n` + - `⬜ Deploying` - }); - - - name: Build website - run: npm run build - env: - NODE_ENV: production - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${checklist_spinner} Deploying to GitHub Pages` - }); - - - 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: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./public - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - - - name: Calculate duration - id: duration - if: always() - run: | - END_TIME=$(date +%s) - START_TIME=${{ steps.start_time.outputs.timestamp }} - DURATION=$((END_TIME - START_TIME)) - MINUTES=$((DURATION / 60)) - SECONDS=$((DURATION % 60)) - echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT - - - name: Update comment with success - if: success() - uses: actions/github-script@v7 - with: - script: | - 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 - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` - }); - - - name: Update comment with failure - if: failure() - uses: actions/github-script@v7 - with: - script: | - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - - 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 build or deployment.\n` + - `Build time: ${duration}\n\n` + - `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` - }); - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `${checklist_spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${checklist_spinner} Building\n` + - `⬜ Deploying` - }); - - - name: Build website - run: npm run build - env: - NODE_ENV: production - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${checklist_spinner} Deploying to GitHub Pages` - }); - - - 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: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./public - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - - - name: Calculate duration - id: duration - if: always() - run: | - END_TIME=$(date +%s) - START_TIME=${{ steps.start_time.outputs.timestamp }} - DURATION=$((END_TIME - START_TIME)) - MINUTES=$((DURATION / 60)) - SECONDS=$((DURATION % 60)) - echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT - - - name: Update comment with success - if: success() - uses: actions/github-script@v7 - with: - script: | - 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 - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` - }); - - - name: Update comment with failure - if: failure() - uses: actions/github-script@v7 - with: - script: | - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - - 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 build or deployment.\n` + - `Build time: ${duration}\n\n` + - `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` - }); - body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `${spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - - - name: Install dependencies - run: npm ci - - - name: Update comment - Building - uses: actions/github-script@v7 - with: - script: | - const 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.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${spinner} Building\n` + - `⬜ Deploying` - }); - - - name: Build website - run: npm run build - env: - NODE_ENV: production - - - name: Update comment - Deploying - uses: actions/github-script@v7 - with: - script: | - const 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.build_comment.outputs.result }}, - body: `${spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${spinner} Deploying to GitHub Pages` - }); - - - 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: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./public - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - - - name: Calculate duration - id: duration - if: always() - run: | - END_TIME=$(date +%s) - START_TIME=${{ steps.start_time.outputs.timestamp }} - DURATION=$((END_TIME - START_TIME)) - MINUTES=$((DURATION / 60)) - SECONDS=$((DURATION % 60)) - echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT - - - name: Update comment with success - if: success() - uses: actions/github-script@v7 - with: - script: | - 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 - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` - }); - - - name: Update comment with failure - if: failure() - uses: actions/github-script@v7 - with: - script: | - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - - 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 build or deployment.\n` + - `Build time: ${duration}\n\n` + - `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` - }); - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` - }); - - - name: Update comment with failure - if: failure() - uses: actions/github-script@v7 - with: - script: | - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - - 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 build or deployment.\n` + - `Build time: ${duration}\n\n` + - `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` - }); From 4305afd41848a071e15449d1f03a60cd43ca74bb Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:45:29 -0400 Subject: [PATCH 14/43] Update prtest.yml --- .github/workflows/prtest.yml | 369 +++++++++++++++++++++++++++++++++-- 1 file changed, 354 insertions(+), 15 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index bae437b28..b662f9240 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -67,29 +67,168 @@ jobs: number: pr.data.number }; - - name: Post building comment + #################################################################### + # Anti-spam confirmation: + # If a prior run for this PR happened within 10 minutes, require a + # reaction on a confirmation comment. This polls for a reaction up to + # 10 minutes (configurable). + #################################################################### + - name: Anti-spam: check recent run and require reaction if within 10m + id: anti_spam + uses: actions/github-script@v7 + with: + script: | + const prNumber = Number(${JSON.stringify("${{ fromJSON(steps.pr.outputs.result).number }}")}); + const owner = context.repo.owner; + const repo = context.repo.repo; + const THREE_LETTERS = 'pr-test-deploy-comment'; // marker + + // Get comments on the PR + const comments = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, + per_page: 100 + }); + + // Find the first bot comment that contains our hidden marker and parse timestamp + let botComment = null; + let lastTimestamp = 0; + for (const c of comments.data) { + if (c.body && c.body.includes('')) { + botComment = c; + const m = /pr-test-deploy-timestamp:\s*(\d+)/.exec(c.body); + if (m) lastTimestamp = Number(m[1]); + break; // we edit the first match (sticky behavior) + } + } + + const now = Math.floor(Date.now() / 1000); + const TEN_MIN = 10 * 60; + + if (lastTimestamp && (now - lastTimestamp) < TEN_MIN) { + // Post a confirmation comment and wait for a reaction on it + const confirm = await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, + body: +`āš ļø **Quick confirmation required** āš ļø + +A PR test was already run less than 10 minutes ago. To prevent accidental spam, please **react to this comment with :rocket:** within 10 minutes to confirm you want to re-run the test. + +(If you prefer, reply with \`/confirm test pr\` as a comment.)` + }); + + const confirmCommentId = confirm.data.id; + + // Poll for reactions up to 10 minutes + const until = Date.now() + TEN_MIN * 1000; + const pollIntervalMs = 10 * 1000; // 10s + let confirmed = false; + + while (Date.now() < until) { + // list reactions for the confirmation comment + const reacts = await github.request('GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions', { + owner, repo, comment_id: confirmCommentId, + mediaType: { previews: ['squirrel-girl'] } + }); + // Accept any reaction from a non-bot user (not the action) + for (const r of reacts.data) { + if (r.user && r.user.type !== 'Bot') { + confirmed = true; + break; + } + } + if (confirmed) break; + // Also check for a textual confirmation comment + const replies = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, + per_page: 100 + }); + for (const rep of replies.data) { + if (rep.body && rep.body.match(/\/confirm\s+test\s+pr/i) && rep.user.type !== 'Bot') { + confirmed = true; + break; + } + } + if (confirmed) break; + // sleep + await new Promise(r => setTimeout(r, pollIntervalMs)); + } + + if (!confirmed) { + // No confirmation — stop the workflow early + core = require('@actions/core'); + core.setFailed("No confirmation reaction received within 10 minutes. Cancelling test deployment to avoid spam."); + return { cancelled: "true" }; + } else { + return { cancelled: "false" }; + } + } else { + return { cancelled: "false" }; + } + + - name: Exit if anti-spam cancelled + if: steps.anti_spam.outputs.cancelled == 'true' + run: | + echo "Cancelled by anti-spam check. Exiting job." + exit 1 + + #################################################################### + # Sticky post / Create or update the single bot comment we use. + # We add a big heading, include a hidden marker and timestamp so + # future runs can find and update the same comment (sticky behavior). + #################################################################### + - name: Post or update sticky building comment id: build_comment uses: actions/github-script@v7 with: script: | - // Spinner for the main message (original larger one) - const initial_spinner = ''; - // Spinner for checklist items (new smaller one) - const checklist_spinner = ''; + const prNumber = Number(${JSON.stringify("${{ fromJSON(steps.pr.outputs.result).number }}")}); + const owner = context.repo.owner; + const repo = context.repo.repo; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login : context.payload.pull_request.user.login; - const comment = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `${checklist_spinner} Checkout & Setup\n` + - `⬜ Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` + + const initial_spinner = ''; + const checklist_spinner = ''; + const marker = ''; + const timestamp = Math.floor(Date.now() / 1000); + + // Find an existing bot comment with our marker (sticky) + const comments = await github.rest.issues.listComments({ + owner, repo, issue_number: prNumber, + per_page: 100 }); - return comment.data.id; + + let existing = null; + for (const c of comments.data) { + if (c.body && c.body.includes(marker)) { + existing = c; + break; // edit the first matching comment + } + } + + const body = `# 🚧 Test PR Deployment\n\n` + + `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `${checklist_spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying\n\n` + + `\n` + + `${marker}`; + + if (existing) { + const updated = await github.rest.issues.updateComment({ + owner, repo, comment_id: existing.id, + body + }); + return updated.data.id; + } else { + const created = await github.rest.issues.createComment({ + owner, repo, issue_number: prNumber, + body + }); + return created.data.id; + } - name: Checkout PR code uses: actions/checkout@v4 @@ -103,6 +242,20 @@ jobs: node-version: '20' cache: 'npm' + #################################################################### + # Cache to speed up Puppeteer/Chromium and npm installs + # - caches npm cache and puppeteer chromium download directory + #################################################################### + - name: Cache npm and Puppeteer + uses: actions/cache@v4 + with: + path: | + ~/.npm + ~/.cache/puppeteer + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ env.GITHUB_SHA || github.run_id }} + restore-keys: | + ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}- + - name: Update comment - Installing Dependencies uses: actions/github-script@v7 with: @@ -116,6 +269,192 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, comment_id: ${{ steps.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying\n\n` + + `\n` + + `` + }); + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building\n` + + `⬜ Deploying\n\n` + + `\n` + + `` + }); + + - name: Build website + run: npm run build + env: + NODE_ENV: production + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${checklist_spinner} Deploying to GitHub Pages\n\n` + + `\n` + + `` + }); + + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Calculate duration + id: duration + if: always() + run: | + END_TIME=$(date +%s) + START_TIME=${{ steps.start_time.outputs.timestamp }} + DURATION=$((END_TIME - START_TIME)) + MINUTES=$((DURATION / 60)) + SECONDS=$((DURATION % 60)) + echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + + #################################################################### + # Screenshot step using Puppeteer: + # - installs puppeteer (cached), waits briefly for site to be ready, + # - captures a full-page screenshot, uploads as artifact, + # - updates the sticky comment with a link to the artifact and screenshot. + #################################################################### + - name: Take screenshot of deployed site (cached Puppeteer) + if: success() + env: + PAGE_URL: ${{ steps.deployment.outputs.page_url }}${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/ + run: | + set -e + # create a tiny Node script on the fly + cat > take_screenshot.js <<'NODE' + const fs = require('fs'); + (async () => { + // lazy install puppeteer if not present (npm ci previously ran and cached chromium) + try { + const puppeteer = require('puppeteer'); + const url = process.env.PAGE_URL; + const out = `screenshot-${process.env.GITHUB_RUN_ID || 'run'}.png`; + const browser = await puppeteer.launch({ + args: ['--no-sandbox','--disable-setuid-sandbox'] + }); + const page = await browser.newPage(); + // Wait a bit for pages with client-side rendering + await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 }).catch(()=>{}); + await page.waitForTimeout(5000); // allow extra rendering time + await page.screenshot({ path: out, fullPage: true }); + await browser.close(); + console.log('SCREENSHOT_PATH=' + out); + fs.writeFileSync('/tmp/screenshot_path', out); + } catch (e) { + console.error('Screenshot failed:', e); + process.exit(0); // don't fail entire workflow if screenshot fails + } + })(); + NODE + + # ensure puppeteer is installed (leverages cached npm/artifacts) + if [ ! -d node_modules/puppeteer ]; then + echo "Installing puppeteer (cached when possible)..." + npm i puppeteer --no-audit --no-fund --silent + fi + + node take_screenshot.js || true + continue-on-error: true + + - name: Upload screenshot artifact (if any) + if: always() + uses: actions/upload-artifact@v4 + with: + name: pr-test-screenshot-${{ github.run_id }} + path: | + screenshot-*.png + /tmp/screenshot_path + + - name: Update comment with success + if: success() + uses: actions/github-script@v7 + with: + script: | + 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 + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + // Try to get screenshot artifact file name from /tmp/screenshot_path if present in runner workspace + const fs = require('fs'); + let screenshotLine = ''; + try { + if (fs.existsSync('/tmp/screenshot_path')) { + const p = fs.readFileSync('/tmp/screenshot_path', 'utf8').trim(); + screenshotLine = `- Screenshot artifact: screenshot attached to workflow run (see "Artifacts" on this run).`; + } else { + // fallback note + screenshotLine = `- Screenshot: (no screenshot available)`; + } + } catch (e) { + screenshotLine = `- Screenshot: (error checking screenshot)`; + } + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: ${{ steps.build_comment.outputs.result }}, body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + `āœ… Checkout & Setup\n` + `${checklist_spinner} Installing dependencies\n` + From 5d661463f71007d13e24ea01161e0894fe9a8afc Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:49:43 -0400 Subject: [PATCH 15/43] Update prtest.yml --- .github/workflows/prtest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index b662f9240..7cf347b67 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -73,7 +73,7 @@ jobs: # reaction on a confirmation comment. This polls for a reaction up to # 10 minutes (configurable). #################################################################### - - name: Anti-spam: check recent run and require reaction if within 10m + - name: Anti-spam - check recent run and require reaction if within 10m id: anti_spam uses: actions/github-script@v7 with: From ace05050cbf8aeb8a22054806958c6f740a6e475 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:00:27 -0400 Subject: [PATCH 16/43] Update prtest.yml --- .github/workflows/prtest.yml | 450 ++++++++--------------------------- 1 file changed, 96 insertions(+), 354 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 7cf347b67..f9b53ad40 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -67,167 +67,64 @@ jobs: number: pr.data.number }; - #################################################################### - # Anti-spam confirmation: - # If a prior run for this PR happened within 10 minutes, require a - # reaction on a confirmation comment. This polls for a reaction up to - # 10 minutes (configurable). - #################################################################### - - name: Anti-spam - check recent run and require reaction if within 10m - id: anti_spam + # ---------- Sticky comment support: find existing comment with hidden marker ---------- + - name: Find existing sticky comment + id: find_comment uses: actions/github-script@v7 with: script: | - const prNumber = Number(${JSON.stringify("${{ fromJSON(steps.pr.outputs.result).number }}")}); - const owner = context.repo.owner; - const repo = context.repo.repo; - const THREE_LETTERS = 'pr-test-deploy-comment'; // marker - - // Get comments on the PR + // We'll look for an existing comment that contains the hidden marker so we can edit it + const issue_number = Number(${ { fromJSON(steps.pr.outputs.result).number }}); + const marker = ''; const comments = await github.rest.issues.listComments({ - owner, repo, issue_number: prNumber, - per_page: 100 + owner: context.repo.owner, + repo: context.repo.repo, + issue_number }); - - // Find the first bot comment that contains our hidden marker and parse timestamp - let botComment = null; - let lastTimestamp = 0; - for (const c of comments.data) { - if (c.body && c.body.includes('')) { - botComment = c; - const m = /pr-test-deploy-timestamp:\s*(\d+)/.exec(c.body); - if (m) lastTimestamp = Number(m[1]); - break; // we edit the first match (sticky behavior) - } + // Prefer bot comment with the marker, otherwise any comment with the marker + const found = comments.data.find(c => (c.user && c.user.type === 'Bot' && c.body && c.body.includes(marker)) || (c.body && c.body.includes(marker))); + if (found) { + return { comment_id: found.id }; } + return { comment_id: null }; - const now = Math.floor(Date.now() / 1000); - const TEN_MIN = 10 * 60; - - if (lastTimestamp && (now - lastTimestamp) < TEN_MIN) { - // Post a confirmation comment and wait for a reaction on it - const confirm = await github.rest.issues.createComment({ - owner, repo, issue_number: prNumber, - body: -`āš ļø **Quick confirmation required** āš ļø - -A PR test was already run less than 10 minutes ago. To prevent accidental spam, please **react to this comment with :rocket:** within 10 minutes to confirm you want to re-run the test. - -(If you prefer, reply with \`/confirm test pr\` as a comment.)` - }); - - const confirmCommentId = confirm.data.id; - - // Poll for reactions up to 10 minutes - const until = Date.now() + TEN_MIN * 1000; - const pollIntervalMs = 10 * 1000; // 10s - let confirmed = false; - - while (Date.now() < until) { - // list reactions for the confirmation comment - const reacts = await github.request('GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions', { - owner, repo, comment_id: confirmCommentId, - mediaType: { previews: ['squirrel-girl'] } - }); - // Accept any reaction from a non-bot user (not the action) - for (const r of reacts.data) { - if (r.user && r.user.type !== 'Bot') { - confirmed = true; - break; - } - } - if (confirmed) break; - // Also check for a textual confirmation comment - const replies = await github.rest.issues.listComments({ - owner, repo, issue_number: prNumber, - per_page: 100 - }); - for (const rep of replies.data) { - if (rep.body && rep.body.match(/\/confirm\s+test\s+pr/i) && rep.user.type !== 'Bot') { - confirmed = true; - break; - } - } - if (confirmed) break; - // sleep - await new Promise(r => setTimeout(r, pollIntervalMs)); - } - - if (!confirmed) { - // No confirmation — stop the workflow early - core = require('@actions/core'); - core.setFailed("No confirmation reaction received within 10 minutes. Cancelling test deployment to avoid spam."); - return { cancelled: "true" }; - } else { - return { cancelled: "false" }; - } - } else { - return { cancelled: "false" }; - } - - - name: Exit if anti-spam cancelled - if: steps.anti_spam.outputs.cancelled == 'true' - run: | - echo "Cancelled by anti-spam check. Exiting job." - exit 1 - - #################################################################### - # Sticky post / Create or update the single bot comment we use. - # We add a big heading, include a hidden marker and timestamp so - # future runs can find and update the same comment (sticky behavior). - #################################################################### - - name: Post or update sticky building comment + # ---------- Post building comment (create or update existing sticky comment) ---------- + - name: Post or update building comment id: build_comment uses: actions/github-script@v7 with: script: | - const prNumber = Number(${JSON.stringify("${{ fromJSON(steps.pr.outputs.result).number }}")}); - const owner = context.repo.owner; - const repo = context.repo.repo; + const initial_spinner = ''; + const checklist_spinner = ''; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login : context.payload.pull_request.user.login; - - const initial_spinner = ''; - const checklist_spinner = ''; - const marker = ''; - const timestamp = Math.floor(Date.now() / 1000); - - // Find an existing bot comment with our marker (sticky) - const comments = await github.rest.issues.listComments({ - owner, repo, issue_number: prNumber, - per_page: 100 - }); - - let existing = null; - for (const c of comments.data) { - if (c.body && c.body.includes(marker)) { - existing = c; - break; // edit the first matching comment - } - } - - const body = `# 🚧 Test PR Deployment\n\n` + - `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `${checklist_spinner} Checkout & Setup\n` + - `⬜ Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying\n\n` + - `\n` + - `${marker}`; - - if (existing) { - const updated = await github.rest.issues.updateComment({ - owner, repo, comment_id: existing.id, + const issue_number = Number(${ { fromJSON(steps.pr.outputs.result).number }}); + const marker = ''; + const body = `${marker}\n${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `${checklist_spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying`; + + // If we found an existing comment, update it; otherwise create a new one + const existingId = ${ { fromJSON(steps.find_comment.outputs.result || '{}').comment_id || 'null' } }; + if (existingId) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingId, body }); - return updated.data.id; + return existingId; } else { - const created = await github.rest.issues.createComment({ - owner, repo, issue_number: prNumber, + const comment = await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number, body }); - return created.data.id; + return comment.data.id; } - name: Checkout PR code @@ -242,20 +139,6 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, node-version: '20' cache: 'npm' - #################################################################### - # Cache to speed up Puppeteer/Chromium and npm installs - # - caches npm cache and puppeteer chromium download directory - #################################################################### - - name: Cache npm and Puppeteer - uses: actions/cache@v4 - with: - path: | - ~/.npm - ~/.cache/puppeteer - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}-${{ env.GITHUB_SHA || github.run_id }} - restore-keys: | - ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}- - - name: Update comment - Installing Dependencies uses: actions/github-script@v7 with: @@ -273,9 +156,7 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, `āœ… Checkout & Setup\n` + `${checklist_spinner} Installing dependencies\n` + `⬜ Building\n` + - `⬜ Deploying\n\n` + - `\n` + - `` + `⬜ Deploying` }); - name: Install dependencies @@ -298,9 +179,7 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, `āœ… Checkout & Setup\n` + `āœ… Installing dependencies\n` + `${checklist_spinner} Building\n` + - `⬜ Deploying\n\n` + - `\n` + - `` + `⬜ Deploying` }); - name: Build website @@ -325,9 +204,7 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, `āœ… Checkout & Setup\n` + `āœ… Installing dependencies\n` + `āœ… Building\n` + - `${checklist_spinner} Deploying to GitHub Pages\n\n` + - `\n` + - `` + `${checklist_spinner} Deploying to GitHub Pages` }); - name: Prepare per-PR directory @@ -345,185 +222,70 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, id: deployment uses: actions/deploy-pages@v4 - - name: Calculate duration - id: duration - if: always() + # ---------- Screenshot step: run AFTER pages deploy (runs only on success) ---------- + - name: Install puppeteer-core (fast, no Chromium download) + if: success() run: | - END_TIME=$(date +%s) - START_TIME=${{ steps.start_time.outputs.timestamp }} - DURATION=$((END_TIME - START_TIME)) - MINUTES=$((DURATION / 60)) - SECONDS=$((DURATION % 60)) - echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + npm install --no-audit --no-fund puppeteer-core@latest + # npm cache is used above via setup-node cache to speed up installs - #################################################################### - # Screenshot step using Puppeteer: - # - installs puppeteer (cached), waits briefly for site to be ready, - # - captures a full-page screenshot, uploads as artifact, - # - updates the sticky comment with a link to the artifact and screenshot. - #################################################################### - - name: Take screenshot of deployed site (cached Puppeteer) + - name: Take screenshot of deployed PR site if: success() - env: - PAGE_URL: ${{ steps.deployment.outputs.page_url }}${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/ + id: take_screenshot run: | - set -e - # create a tiny Node script on the fly - cat > take_screenshot.js <<'NODE' + node <<'NODE_SCRIPT' const fs = require('fs'); + const path = require('path'); + const puppeteer = require('puppeteer-core'); + + // Use runner's chrome executable (no chromium download) + const chromePaths = [ + '/usr/bin/google-chrome-stable', + '/usr/bin/google-chrome', + '/usr/bin/chromium-browser', + '/usr/bin/chromium' + ]; + const execPath = chromePaths.find(p => require('fs').existsSync(p)); + if (!execPath) { + console.error('No bundled chrome found at expected paths. Attempting to launch without executablePath (may download Chromium).'); + } + + // Build target URL (same as in the success comment) + const username = process.env.GITHUB_ACTOR; + const prNumber = ${ { fromJSON(steps.pr.outputs.result).number } }; + const pageRoot = process.env.PAGE_URL || '${{ steps.deployment.outputs.page_url }}'; + const target = `${pageRoot}${username}/${prNumber}/`; + (async () => { - // lazy install puppeteer if not present (npm ci previously ran and cached chromium) + const browser = await puppeteer.launch(execPath ? { executablePath: execPath, args: ['--no-sandbox', '--disable-setuid-sandbox'] } : { args: ['--no-sandbox', '--disable-setuid-sandbox']}); + const page = await browser.newPage(); + // give the site some extra time to settle + const waitMs = process.env.SCREENSHOT_WAIT_MS ? Number(process.env.SCREENSHOT_WAIT_MS) : 8000; try { - const puppeteer = require('puppeteer'); - const url = process.env.PAGE_URL; - const out = `screenshot-${process.env.GITHUB_RUN_ID || 'run'}.png`; - const browser = await puppeteer.launch({ - args: ['--no-sandbox','--disable-setuid-sandbox'] - }); - const page = await browser.newPage(); - // Wait a bit for pages with client-side rendering - await page.goto(url, { waitUntil: 'networkidle2', timeout: 60000 }).catch(()=>{}); - await page.waitForTimeout(5000); // allow extra rendering time - await page.screenshot({ path: out, fullPage: true }); + await page.goto(target, { waitUntil: 'networkidle2', timeout: 60000 }); + await page.waitForTimeout(waitMs); + // optional: set viewport large enough + await page.setViewport({ width: 1280, height: 900 }); + const outPath = path.resolve('screenshot.png'); + await page.screenshot({ path: outPath, fullPage: false }); + console.log('Screenshot saved to', outPath); + await browser.close(); + // print out so subsequent steps can reference it + console.log('::set-output name=path::' + outPath); + } catch (err) { + console.error('Error taking screenshot:', err); await browser.close(); - console.log('SCREENSHOT_PATH=' + out); - fs.writeFileSync('/tmp/screenshot_path', out); - } catch (e) { - console.error('Screenshot failed:', e); - process.exit(0); // don't fail entire workflow if screenshot fails + process.exit(0); // do not fail the job if screenshot fails } })(); - NODE + NODE_SCRIPT - # ensure puppeteer is installed (leverages cached npm/artifacts) - if [ ! -d node_modules/puppeteer ]; then - echo "Installing puppeteer (cached when possible)..." - npm i puppeteer --no-audit --no-fund --silent - fi - - node take_screenshot.js || true - continue-on-error: true - - - name: Upload screenshot artifact (if any) - if: always() - uses: actions/upload-artifact@v4 - with: - name: pr-test-screenshot-${{ github.run_id }} - path: | - screenshot-*.png - /tmp/screenshot_path - - - name: Update comment with success + - name: Upload screenshot artifact if: success() - uses: actions/github-script@v7 - with: - script: | - 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 - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - // Try to get screenshot artifact file name from /tmp/screenshot_path if present in runner workspace - const fs = require('fs'); - let screenshotLine = ''; - try { - if (fs.existsSync('/tmp/screenshot_path')) { - const p = fs.readFileSync('/tmp/screenshot_path', 'utf8').trim(); - screenshotLine = `- Screenshot artifact: screenshot attached to workflow run (see "Artifacts" on this run).`; - } else { - // fallback note - screenshotLine = `- Screenshot: (no screenshot available)`; - } - } catch (e) { - screenshotLine = `- Screenshot: (error checking screenshot)`; - } - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: ${{ steps.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `${checklist_spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${checklist_spinner} Building\n` + - `⬜ Deploying` - }); - - - name: Build website - run: npm run build - env: - NODE_ENV: production - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${checklist_spinner} Deploying to GitHub Pages` - }); - - - 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: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-artifact@v4 with: - path: ./public - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + name: pr-screenshot-${{ github.run_id }} + path: screenshot.png - name: Calculate duration id: duration @@ -549,6 +311,8 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, : context.payload.pull_request.user.login; const duration = '${{ steps.duration.outputs.duration }}'; const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + // Provide a link to artifacts for this run (artifact viewer for the run) + const artifactsLink = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts`; await github.rest.issues.updateComment({ owner: context.repo.owner, @@ -562,26 +326,4 @@ A PR test was already run less than 10 minutes ago. To prevent accidental spam, `- PR: #${prNumber}\n` + `- Commit: \`${sha}\`\n` + `- Requested by: @${username}\n` + - `- Build time: ${duration}\n` - }); - - - name: Update comment with failure - if: failure() - uses: actions/github-script@v7 - with: - script: | - const commentId = ${{ steps.build_comment.outputs.result }}; - const username = context.eventName === 'issue_comment' - ? context.payload.comment.user.login - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - - 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 build or deployment.\n` + - `Build time: ${duration}\n\n` + - `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` - }); + `- Build From a9eead257acce96d9a24d01de499cc863a611cd0 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:04:43 -0400 Subject: [PATCH 17/43] Update prtest.yml --- .github/workflows/prtest.yml | 255 +++-------------------------------- 1 file changed, 18 insertions(+), 237 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index f9b53ad40..af3905999 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -67,263 +67,44 @@ jobs: number: pr.data.number }; - # ---------- Sticky comment support: find existing comment with hidden marker ---------- - - name: Find existing sticky comment - id: find_comment - uses: actions/github-script@v7 - with: - script: | - // We'll look for an existing comment that contains the hidden marker so we can edit it - const issue_number = Number(${ { fromJSON(steps.pr.outputs.result).number }}); - const marker = ''; - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number - }); - // Prefer bot comment with the marker, otherwise any comment with the marker - const found = comments.data.find(c => (c.user && c.user.type === 'Bot' && c.body && c.body.includes(marker)) || (c.body && c.body.includes(marker))); - if (found) { - return { comment_id: found.id }; - } - return { comment_id: null }; - - # ---------- Post building comment (create or update existing sticky comment) ---------- - - name: Post or update building comment + # Create or update a sticky comment (marker-based) with the build spinner + checklist + - name: Post or update building comment (sticky) id: build_comment uses: actions/github-script@v7 with: script: | + const marker = ''; const initial_spinner = ''; const checklist_spinner = ''; + const prNumber = context.eventName === 'issue_comment' ? context.issue.number : context.payload.number; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login : context.payload.pull_request.user.login; - const issue_number = Number(${ { fromJSON(steps.pr.outputs.result).number }}); - const marker = ''; + const issue_number = prNumber; const body = `${marker}\n${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + `${checklist_spinner} Checkout & Setup\n` + `⬜ Installing dependencies\n` + `⬜ Building\n` + `⬜ Deploying`; - // If we found an existing comment, update it; otherwise create a new one - const existingId = ${ { fromJSON(steps.find_comment.outputs.result || '{}').comment_id || 'null' } }; - if (existingId) { + // Look for existing comment with marker (prefer bot-owned) + const comments = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number + }); + + const found = comments.data.find(c => ((c.user && c.user.type === 'Bot') && c.body && c.body.includes(marker)) || (c.body && c.body.includes(marker))); + + if (found) { await github.rest.issues.updateComment({ owner: context.repo.owner, repo: context.repo.repo, - comment_id: existingId, + comment_id: found.id, body }); - return existingId; + return { comment_id: found.id }; } else { const comment = await github.rest.issues.createComment({ owner: context.repo.owner, - repo: context.repo.repo, - issue_number, - body - }); - return comment.data.id; - } - - - name: Checkout PR code - uses: actions/checkout@v4 - with: - ref: ${{ fromJSON(steps.pr.outputs.result).sha }} - fetch-depth: 0 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `${checklist_spinner} Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying` - }); - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `${checklist_spinner} Building\n` + - `⬜ Deploying` - }); - - - name: Build website - run: npm run build - env: - NODE_ENV: production - - - 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.build_comment.outputs.result }}, - body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `āœ… Checkout & Setup\n` + - `āœ… Installing dependencies\n` + - `āœ… Building\n` + - `${checklist_spinner} Deploying to GitHub Pages` - }); - - - 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: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./public - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - - # ---------- Screenshot step: run AFTER pages deploy (runs only on success) ---------- - - name: Install puppeteer-core (fast, no Chromium download) - if: success() - run: | - npm install --no-audit --no-fund puppeteer-core@latest - # npm cache is used above via setup-node cache to speed up installs - - - name: Take screenshot of deployed PR site - if: success() - id: take_screenshot - run: | - node <<'NODE_SCRIPT' - const fs = require('fs'); - const path = require('path'); - const puppeteer = require('puppeteer-core'); - - // Use runner's chrome executable (no chromium download) - const chromePaths = [ - '/usr/bin/google-chrome-stable', - '/usr/bin/google-chrome', - '/usr/bin/chromium-browser', - '/usr/bin/chromium' - ]; - const execPath = chromePaths.find(p => require('fs').existsSync(p)); - if (!execPath) { - console.error('No bundled chrome found at expected paths. Attempting to launch without executablePath (may download Chromium).'); - } - - // Build target URL (same as in the success comment) - const username = process.env.GITHUB_ACTOR; - const prNumber = ${ { fromJSON(steps.pr.outputs.result).number } }; - const pageRoot = process.env.PAGE_URL || '${{ steps.deployment.outputs.page_url }}'; - const target = `${pageRoot}${username}/${prNumber}/`; - - (async () => { - const browser = await puppeteer.launch(execPath ? { executablePath: execPath, args: ['--no-sandbox', '--disable-setuid-sandbox'] } : { args: ['--no-sandbox', '--disable-setuid-sandbox']}); - const page = await browser.newPage(); - // give the site some extra time to settle - const waitMs = process.env.SCREENSHOT_WAIT_MS ? Number(process.env.SCREENSHOT_WAIT_MS) : 8000; - try { - await page.goto(target, { waitUntil: 'networkidle2', timeout: 60000 }); - await page.waitForTimeout(waitMs); - // optional: set viewport large enough - await page.setViewport({ width: 1280, height: 900 }); - const outPath = path.resolve('screenshot.png'); - await page.screenshot({ path: outPath, fullPage: false }); - console.log('Screenshot saved to', outPath); - await browser.close(); - // print out so subsequent steps can reference it - console.log('::set-output name=path::' + outPath); - } catch (err) { - console.error('Error taking screenshot:', err); - await browser.close(); - process.exit(0); // do not fail the job if screenshot fails - } - })(); - NODE_SCRIPT - - - name: Upload screenshot artifact - if: success() - uses: actions/upload-artifact@v4 - with: - name: pr-screenshot-${{ github.run_id }} - path: screenshot.png - - - name: Calculate duration - id: duration - if: always() - run: | - END_TIME=$(date +%s) - START_TIME=${{ steps.start_time.outputs.timestamp }} - DURATION=$((END_TIME - START_TIME)) - MINUTES=$((DURATION / 60)) - SECONDS=$((DURATION % 60)) - echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT - - - name: Update comment with success - if: success() - uses: actions/github-script@v7 - with: - script: | - 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 - : context.payload.pull_request.user.login; - const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; - // Provide a link to artifacts for this run (artifact viewer for the run) - const artifactsLink = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}#artifacts`; - - 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + - `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + - `**Details:**\n` + - `- PR: #${prNumber}\n` + - `- Commit: \`${sha}\`\n` + - `- Requested by: @${username}\n` + - `- Build + repo: context From f7c4255d06d08e7c9c0235345a6a3fd1a4f418db Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Tue, 7 Oct 2025 22:07:53 -0400 Subject: [PATCH 18/43] Update prtest.yml --- .github/workflows/prtest.yml | 194 ++++++++++++++++++++++++++++++----- 1 file changed, 166 insertions(+), 28 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index af3905999..bae437b28 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -67,44 +67,182 @@ jobs: number: pr.data.number }; - # Create or update a sticky comment (marker-based) with the build spinner + checklist - - name: Post or update building comment (sticky) + - name: Post building comment id: build_comment uses: actions/github-script@v7 with: script: | - const marker = ''; + // Spinner for the main message (original larger one) const initial_spinner = ''; + // Spinner for checklist items (new smaller one) const checklist_spinner = ''; - const prNumber = context.eventName === 'issue_comment' ? context.issue.number : context.payload.number; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login : context.payload.pull_request.user.login; - const issue_number = prNumber; - const body = `${marker}\n${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + - `${checklist_spinner} Checkout & Setup\n` + - `⬜ Installing dependencies\n` + - `⬜ Building\n` + - `⬜ Deploying`; - - // Look for existing comment with marker (prefer bot-owned) - const comments = await github.rest.issues.listComments({ + const comment = await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, - issue_number + issue_number: ${{ fromJSON(steps.pr.outputs.result).number }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `${checklist_spinner} Checkout & Setup\n` + + `⬜ Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` }); + return comment.data.id; - const found = comments.data.find(c => ((c.user && c.user.type === 'Bot') && c.body && c.body.includes(marker)) || (c.body && c.body.includes(marker))); - - if (found) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: found.id, - body - }); - return { comment_id: found.id }; - } else { - const comment = await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context + - name: Checkout PR code + uses: actions/checkout@v4 + with: + ref: ${{ fromJSON(steps.pr.outputs.result).sha }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `${checklist_spinner} Installing dependencies\n` + + `⬜ Building\n` + + `⬜ Deploying` + }); + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `${checklist_spinner} Building\n` + + `⬜ Deploying` + }); + + - name: Build website + run: npm run build + env: + NODE_ENV: production + + - 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.build_comment.outputs.result }}, + body: `${initial_spinner} **Building PR for testing, requested by @${username}...**\n\n` + + `āœ… Checkout & Setup\n` + + `āœ… Installing dependencies\n` + + `āœ… Building\n` + + `${checklist_spinner} Deploying to GitHub Pages` + }); + + - 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: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./public + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + + - name: Calculate duration + id: duration + if: always() + run: | + END_TIME=$(date +%s) + START_TIME=${{ steps.start_time.outputs.timestamp }} + DURATION=$((END_TIME - START_TIME)) + MINUTES=$((DURATION / 60)) + SECONDS=$((DURATION % 60)) + echo "duration=${MINUTES}m ${SECONDS}s" >> $GITHUB_OUTPUT + + - name: Update comment with success + if: success() + uses: actions/github-script@v7 + with: + script: | + 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 + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + + 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 PR test deployment is ready! āœ…šŸŽ‰\n\n` + + `šŸŒ View it here:\n` + + `${pageUrl}${username}/${prNumber}/\n\n` + + `**Details:**\n` + + `- PR: #${prNumber}\n` + + `- Commit: \`${sha}\`\n` + + `- Requested by: @${username}\n` + + `- Build time: ${duration}\n` + }); + + - name: Update comment with failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const commentId = ${{ steps.build_comment.outputs.result }}; + const username = context.eventName === 'issue_comment' + ? context.payload.comment.user.login + : context.payload.pull_request.user.login; + const duration = '${{ steps.duration.outputs.duration }}'; + + 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 build or deployment.\n` + + `Build time: ${duration}\n\n` + + `Check logs: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}` + }); From d2c27d1699e0c6475cab4457bf2f9cc9dbff8d3d Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:31:08 -0400 Subject: [PATCH 19/43] Update prtest.yml one last time --- .github/workflows/prtest.yml | 43 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index bae437b28..9d4e71534 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -28,25 +28,7 @@ jobs: id: start_time run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT - - name: Cancel previous runs - uses: styfle/cancel-workflow-action@0.12.0 - with: - workflow_id: 'prtest.yml' - access_token: ${{ github.token }} - all_but_latest: true - - - name: React to comment (if triggered by comment) - if: github.event_name == 'issue_comment' - uses: actions/github-script@v7 - with: - script: | - await github.rest.reactions.createForIssueComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: context.payload.comment.id, - content: 'rocket' - }); - + # Moved this step up to get branch for cancellation - name: Get PR details id: pr uses: actions/github-script@v7 @@ -67,6 +49,29 @@ jobs: number: pr.data.number }; + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.12.0 + with: + workflow_id: 'prtest.yml' + access_token: ${{ github.token }} + all_but_latest: true + # Cancel ONLY runs in the same PR branch + branch: ${{ fromJSON(steps.pr.outputs.result).ref }} + + - name: React to comment (if triggered by comment) + if: github.event_name == 'issue_comment' + uses: actions/github-script@v7 + with: + script: | + await github.rest.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: 'rocket' + }); + + # Removed duplicate "Get PR details" step here (now handled above) + - name: Post building comment id: build_comment uses: actions/github-script@v7 From a9667740a7a53751e6d0b2aede443b75a416b456 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:55:42 -0400 Subject: [PATCH 20/43] Update prtest.yml --- .github/workflows/prtest.yml | 65 ++++++++++++++++++++++++------------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/.github/workflows/prtest.yml b/.github/workflows/prtest.yml index 9d4e71534..fc1467247 100644 --- a/.github/workflows/prtest.yml +++ b/.github/workflows/prtest.yml @@ -7,9 +7,8 @@ on: types: [opened] permissions: - contents: read + contents: write # Added write permission for branch push pull-requests: write - pages: write id-token: write jobs: @@ -19,16 +18,11 @@ jobs: (github.event_name == 'issue_comment' && github.event.issue.pull_request && contains(github.event.comment.body, '/test pr')) runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - name: Record start time id: start_time run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT - # Moved this step up to get branch for cancellation - name: Get PR details id: pr uses: actions/github-script@v7 @@ -55,7 +49,6 @@ jobs: workflow_id: 'prtest.yml' access_token: ${{ github.token }} all_but_latest: true - # Cancel ONLY runs in the same PR branch branch: ${{ fromJSON(steps.pr.outputs.result).ref }} - name: React to comment (if triggered by comment) @@ -70,16 +63,12 @@ jobs: content: 'rocket' }); - # Removed duplicate "Get PR details" step here (now handled above) - - name: Post building comment id: build_comment uses: actions/github-script@v7 with: script: | - // Spinner for the main message (original larger one) const initial_spinner = ''; - // Spinner for checklist items (new smaller one) const checklist_spinner = ''; const username = context.eventName === 'issue_comment' ? context.payload.comment.user.login @@ -182,18 +171,50 @@ jobs: cp -r ./build/* ./public/${{ github.actor }}/${{ fromJSON(steps.pr.outputs.result).number }}/ touch ./public/.nojekyll - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 + # Checkout gh-pages branch (create if missing) + - name: Checkout gh-pages branch + id: checkout_gh_pages + uses: actions/checkout@v4 with: - path: ./public + ref: gh-pages + path: gh-pages + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + continue-on-error: true - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 + # Initialize branch if it doesn't exist + - 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 + + # Sync built files to gh-pages directory + - 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 + + # Commit and push changes + - 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 - name: Calculate duration id: duration - if: always() run: | END_TIME=$(date +%s) START_TIME=${{ steps.start_time.outputs.timestamp }} @@ -214,7 +235,9 @@ jobs: ? context.payload.comment.user.login : context.payload.pull_request.user.login; const duration = '${{ steps.duration.outputs.duration }}'; - const pageUrl = '${{ steps.deployment.outputs.page_url }}'; + // Manually construct deployment URL + const baseUrl = `https://${context.repo.owner}.github.io/${context.repo.repo}`; + const pageUrl = `${baseUrl}/${context.actor}/${prNumber}/`; await github.rest.issues.updateComment({ owner: context.repo.owner, @@ -223,7 +246,7 @@ jobs: body: `šŸš€ **PR Test Deployment Complete!**\n\n` + `Hi, @${username}! Your PR test deployment is ready! āœ…šŸŽ‰\n\n` + `šŸŒ View it here:\n` + - `${pageUrl}${username}/${prNumber}/\n\n` + + `${pageUrl}\n\n` + `**Details:**\n` + `- PR: #${prNumber}\n` + `- Commit: \`${sha}\`\n` + From fa2d557eab404bc3f9ab92a5ff2368fa5ac07a7a Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:57:46 -0400 Subject: [PATCH 21/43] who the heck put v16 bro if you use v16 you better say goodbye to your build it's freaking dying it's v22 --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 6f7f377bf..53d1c14db 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16 +v22 From 907a3b4d7b866d9a2e1ea69cc39726bcc44b91a9 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:57:58 -0400 Subject: [PATCH 22/43] rever --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 53d1c14db..6f7f377bf 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v22 +v16 From f21f3cd6ca1aceda39af46b3361e6f4e334be18c Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 07:58:33 -0400 Subject: [PATCH 23/43] who the heck put node v16 here bruh your builds gonna die if you use it it's not supposed to be that --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 6f7f377bf..53d1c14db 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v16 +v22 From 3362263343280ab2678917e50252f10fd89268a8 Mon Sep 17 00:00:00 2001 From: supervoidcoder <88671013+supervoidcoder@users.noreply.github.com> Date: Wed, 8 Oct 2025 12:05:34 -0400 Subject: [PATCH 24/43] fix images when 404 is not in root --- static/404.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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...?