Skip to content

Visual Screenshots

Visual Screenshots #83

name: Visual Screenshots
on:
deployment_status:
permissions:
contents: write
pull-requests: write
jobs:
screenshot:
# Only run when Vercel preview deployment succeeds
if: |
github.event.deployment_status.state == 'success' &&
startsWith(github.event.deployment.environment, 'Preview')
runs-on: ubuntu-latest
steps:
- name: Find PR for this deployment
id: pr
uses: actions/github-script@v7
with:
result-encoding: string
script: |
const ref = context.payload.deployment.ref;
const { data: prs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${ref}`,
state: 'open',
});
if (prs.length === 0) {
core.info(`No open PR found for ref: ${ref}`);
return '';
}
core.info(`Found PR #${prs[0].number}`);
return String(prs[0].number);
- name: Skip if not a PR deployment
if: steps.pr.outputs.result == ''
run: |
echo "Not a PR deployment — skipping screenshot capture."
exit 0
- uses: actions/checkout@v4
if: steps.pr.outputs.result != ''
with:
fetch-depth: 0
- name: Setup Node
if: steps.pr.outputs.result != ''
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
if: steps.pr.outputs.result != ''
working-directory: frontend
run: npm ci
- name: Install Playwright Chromium
if: steps.pr.outputs.result != ''
working-directory: frontend
run: npx playwright install chromium --with-deps
- name: Capture screenshots
if: steps.pr.outputs.result != ''
working-directory: frontend
env:
BASE_URL: ${{ github.event.deployment_status.target_url }}
run: npx playwright test tests/visual-screenshots.spec.ts --project=chromium --reporter=list
- name: Push screenshots to screenshots branch
if: steps.pr.outputs.result != ''
env:
PR_NUMBER: ${{ steps.pr.outputs.result }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Ensure orphan screenshots branch exists on remote
if git ls-remote --exit-code origin screenshots; then
git fetch origin screenshots
git checkout screenshots
else
git checkout --orphan screenshots
git rm -rf . --quiet
git commit --allow-empty -m "chore: init screenshots branch"
git push origin screenshots
fi
# Copy screenshots into pr-{number}/ folder
mkdir -p "pr-${PR_NUMBER}"
cp frontend/test-screenshots/*.png "pr-${PR_NUMBER}/"
git add "pr-${PR_NUMBER}/"
git diff --cached --quiet && echo "No screenshot changes" && exit 0
git commit -m "screenshots: PR #${PR_NUMBER} — $(date -u +%Y-%m-%dT%H:%M:%SZ)"
git push origin screenshots
- name: Post or update PR comment
if: steps.pr.outputs.result != ''
uses: actions/github-script@v7
env:
PR_NUMBER: ${{ steps.pr.outputs.result }}
PREVIEW_URL: ${{ github.event.deployment_status.target_url }}
with:
script: |
const pr = parseInt(process.env.PR_NUMBER);
const owner = context.repo.owner;
const repo = context.repo.repo;
const base = `https://raw.githubusercontent.com/${owner}/${repo}/screenshots/pr-${pr}`;
const url = process.env.PREVIEW_URL;
const pages = [
{ key: 'landing', label: 'Landing — Hero' },
{ key: 'showcase', label: 'Screenshot Showcase' },
{ key: 'architecture', label: 'Architecture Section' },
{ key: 'dashboard', label: 'Dashboard' },
];
const rows = pages
.map(p => `| **${p.label}** | ![${p.label}](${base}/${p.key}.png) |`)
.join('\n');
const body = [
'## 📸 Visual Screenshots',
'',
`> Preview: ${url}`,
'',
'| Page | Screenshot |',
'|------|-----------|',
rows,
'',
`*Updated ${new Date().toUTCString()}*`,
].join('\n');
// Replace previous bot comment if one exists
const { data: comments } = await github.rest.issues.listComments({
owner, repo, issue_number: pr,
});
const existing = comments.find(
c => c.user.type === 'Bot' && c.body.includes('📸 Visual Screenshots')
);
if (existing) {
await github.rest.issues.updateComment({
owner, repo, comment_id: existing.id, body,
});
core.info(`Updated comment ${existing.id}`);
} else {
await github.rest.issues.createComment({
owner, repo, issue_number: pr, body,
});
core.info('Created new screenshot comment');
}