From 55a9de2d40f72575f8f9671136e89d5a8af18457 Mon Sep 17 00:00:00 2001 From: Florian Metz Date: Sat, 6 Jun 2026 19:36:54 +0200 Subject: [PATCH] feat: post PR asset previews as DiMerP with some personality - Authenticate via the DiMerP GitHub App token (same pattern as dns-check/assets-updater) so comments and labels come from DiMerP[bot] instead of github-actions[bot] - Skip on forks where the app secrets don't exist - Give the comment a voice: per-PR stable quip, themed deletion/empty states and footer --- .github/scripts/pr-assets.mjs | 29 +++++++++++++++++++++-------- .github/workflows/pr-assets.yml | 11 +++++++++-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/.github/scripts/pr-assets.mjs b/.github/scripts/pr-assets.mjs index 73b3bbdf42de..d51f017a5087 100644 --- a/.github/scripts/pr-assets.mjs +++ b/.github/scripts/pr-assets.mjs @@ -16,6 +16,17 @@ const IMAGE_URL_RE = /https?:\/\/[^\s"'`)\]]+\.(?:png|jpe?g|gif|webp|svg|ico)(?: // GitHub caps comment bodies at 65536 characters. const MAX_COMMENT_LENGTH = 65000 +// DiMerP's opening lines — picked by PR number so the quip stays stable +// across pushes to the same PR. +const QUIPS = [ + 'Fresh pixels, hot off the CDN — I lined them all up for inspection. 👀', + 'I fetched these assets so you don\'t have to. My hobbies include embedding images and silently judging diffs.', + 'Behold, the gallery! Admission is free, but I do accept tips in the form of approvals.', + 'Another day, another diff. Here\'s everything shiny I could dig out of this one.', + 'I rummaged through the diff and found these. Finders keepers? No? Fine, they\'re yours.', + 'Asset inspection complete. No pixels were harmed in the making of this comment. ðŸĪ–', +] + /** * Extract the activity folder key from a changed file path. * Example: websites/Y/YouTube/v2/presence.ts -> websites/Y/YouTube/v2 @@ -85,24 +96,25 @@ export function displayName(folder) { /** * Render the sticky comment body for the collected activities. + * The PR number keeps DiMerP's quip stable across pushes. */ -export function buildCommentBody(activities) { +export function buildCommentBody(activities, pullNumber = 0) { if (activities.length === 0) - return `${COMMENT_MARKER}\nNo activity assets in this PR.` + return `${COMMENT_MARKER}\nNo activity assets in this PR. I looked everywhere. Twice. ðŸĪ–` const sections = activities.map((activity) => { const lines = [`### ${activity.name} (\`${activity.folder}\`) — ${activity.type}`, ''] if (activity.type === 'deletion') { - lines.push('_Activity deleted in this PR._') + lines.push('_This activity is being sent to the shadow realm. Press F to pay respects. ðŸŠĶ_') return lines.join('\n') } if (activity.metadataError) - lines.push('_⚠ïļ `metadata.json` could not be loaded — logo/thumbnail previews unavailable._', '') + lines.push('_⚠ïļ I couldn\'t load `metadata.json` — no logo/thumbnail previews, my crystal ball is in the shop._', '') if (activity.assets.length === 0) { - lines.push('_No asset URLs found in this change._') + lines.push('_No asset URLs found in this change — not a single pixel to show off._') return lines.join('\n') } @@ -112,8 +124,9 @@ export function buildCommentBody(activities) { return lines.join('\n') }) - const header = `${COMMENT_MARKER}\n## 🖞ïļ Activity asset preview\n` - const footer = '\n---\nAuto-generated — updates on each push.' + const quip = QUIPS[pullNumber % QUIPS.length] + const header = `${COMMENT_MARKER}\n## 🖞ïļ Activity asset preview\n\n_${quip}_\n` + const footer = '\n---\nBeep boop — I refresh this comment on every push, so no need to scroll. ðŸĪ–' let omitted = 0 let body = [header, ...sections].join('\n') @@ -178,7 +191,7 @@ async function upsertComment(github, context, core, activities) { return } - const body = buildCommentBody(activities) + const body = buildCommentBody(activities, context.payload.pull_request.number) if (existing) { await github.rest.issues.updateComment({ owner: context.repo.owner, diff --git a/.github/workflows/pr-assets.yml b/.github/workflows/pr-assets.yml index 3abee826dc67..c97c2ce98127 100644 --- a/.github/workflows/pr-assets.yml +++ b/.github/workflows/pr-assets.yml @@ -6,8 +6,6 @@ on: permissions: contents: read - issues: write - pull-requests: write concurrency: group: pr-assets-${{ github.event.pull_request.number }} @@ -15,8 +13,16 @@ concurrency: jobs: pr-assets: + # The DiMerP app secrets only exist upstream — skip on forks. + if: github.repository_owner == 'PreMiD' runs-on: ubuntu-latest steps: + - uses: actions/create-github-app-token@v3 + id: generate-token + with: + app-id: ${{ secrets.APP_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + # SECURITY: no `ref:` on purpose — this checks out the trusted base # branch, never fork code. All PR content is read via the GitHub API. - name: Sparse checkout PR assets script @@ -28,6 +34,7 @@ jobs: - name: Post asset preview comment and sync labels uses: actions/github-script@v9 with: + github-token: ${{ steps.generate-token.outputs.token }} script: | const { pathToFileURL } = require('node:url') const script = pathToFileURL(`${process.env.GITHUB_WORKSPACE}/.github/scripts/pr-assets.mjs`)