Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/scripts/sync-docs-screenshots.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const fs = require('fs');
const path = require('path');

const [, , sourceDir, targetDir, toolsDir] = process.argv;
const { PNG } = require(path.join(toolsDir, 'node_modules', 'pngjs'));
const pixelmatch = require(path.join(toolsDir, 'node_modules', 'pixelmatch'));
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const threshold = 0.1;
const maxDiffRatio = 0.001;

const walk = (dir, base = '') => fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
const rel = path.join(base, entry.name);
return entry.isDirectory() ? walk(path.join(dir, entry.name), rel) : [rel];
});

for (const file of walk(sourceDir).filter((f) => f.endsWith('.png'))) {
const sourcePath = path.join(sourceDir, file);
const targetPath = path.join(targetDir, file);

if (fs.existsSync(targetPath)) {
const img1 = PNG.sync.read(fs.readFileSync(sourcePath));
const img2 = PNG.sync.read(fs.readFileSync(targetPath));

if (img1.width === img2.width && img1.height === img2.height) {
const { width, height } = img1;
const numDiffPixels = pixelmatch(img1.data, img2.data, null, width, height, { threshold });

if (numDiffPixels / (width * height) <= maxDiffRatio) {
console.log(`Unchanged, skipping: ${file}`);
continue;
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

console.log(`Updating: ${file}`);
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.copyFileSync(sourcePath, targetPath);
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ jobs:
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: docs-screenshots
path: docs-screenshots/
path: test/output/screenshots/
retention-days: 30
47 changes: 43 additions & 4 deletions .github/workflows/sync-docs-screenshots.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# Pipeline: Playwright calls the screenshot() helper during the CI workflow's
# E2E run, which uploads the results as a "docs-screenshots" artifact. When a PR
# merges, this workflow reuses that artifact (rather than re-running E2E) to
# update combat-command-static via a PR.
#
# Notes on non-obvious choices:
#
# - Triggered on `pull_request: closed` (not `push: main`) so we can look up the
# PR's own CI run by head SHA. A push-triggered run would have a different
# (merge) SHA with no matching artifact.
#
# - pixelmatch/pngquant filter out screenshots whose only changes are rendering
# noise (anti-aliasing, animation timing), so merges don't constantly open
# no-op PRs. pixelmatch is pinned to v5 because v7+ is ESM-only and our
# comparison script uses require().
#
# - The static-site PR uses `--assignee` rather than `--reviewer`: GitHub
# silently drops review requests where the requester is also the PR author,
# which is the case since WORKFLOW_AUTOMATION_TOKEN is ianpaschal's own PAT.
name: Sync Documentation Screenshots

on:
Expand All @@ -14,6 +33,10 @@ jobs:
timeout-minutes: 10

steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false

- name: Find CI run for merged PR
id: find-run
run: |
Expand All @@ -32,7 +55,7 @@ jobs:
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: docs-screenshots
path: docs-screenshots
path: test/output/screenshots
run-id: ${{ steps.find-run.outputs.run_id }}
github-token: ${{ github.token }}

Expand All @@ -43,10 +66,26 @@ jobs:
token: ${{ secrets.WORKFLOW_AUTOMATION_TOKEN }}
path: static-site

- name: Copy screenshots
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '24'

- name: Install image comparison tools
run: |
sudo apt-get update && sudo apt-get install -y pngquant
mkdir -p /tmp/screenshot-tools
npm install --no-save --prefix /tmp/screenshot-tools pixelmatch@5 pngjs

- name: Optimize screenshots
run: |
while IFS= read -r f; do
pngquant --force --ext .png --skip-if-larger "$f" || true
done < <(find test/output/screenshots -name '*.png')

- name: Copy changed screenshots
run: |
mkdir -p static-site/src/assets/docs/guides
cp -r docs-screenshots/. static-site/src/assets/docs/guides/
node .github/scripts/sync-docs-screenshots.cjs test/output/screenshots static-site/src/assets/docs/guides /tmp/screenshot-tools

- name: Open PR with updated screenshots
working-directory: static-site
Expand All @@ -67,7 +106,7 @@ jobs:
--body "Automated screenshot update triggered by ianpaschal/combat-command#${{ github.event.pull_request.number }}." \
--head "$BRANCH" \
--base main \
--reviewer ianpaschal \
--assignee ianpaschal \
|| echo "PR may already exist for this branch"
env:
GH_TOKEN: ${{ secrets.WORKFLOW_AUTOMATION_TOKEN }}
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ lerna-debug.log*
coverage
playwright-report
test-results
docs-screenshots
test/output
test/.auth
node_modules
dist
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default [
},
},
{
files: ['scripts/**/*.{js,cjs,mjs}'],
files: ['scripts/**/*.{js,cjs,mjs}', '.github/**/*.{js,cjs,mjs}'],
languageOptions: {
globals: globals.node,
},
Expand Down
1 change: 1 addition & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export default defineConfig({
...devices['Desktop Firefox'],
launchOptions: { slowMo: process.env.SLOW_MO ? parseInt(process.env.SLOW_MO) : 0 },
viewport: { width: 1280, height: 960 },
deviceScaleFactor: 2,
},
},
],
Expand Down
2 changes: 1 addition & 1 deletion test/helpers/screenshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type Page } from '@playwright/test';
import fs from 'fs';
import path from 'path';

const screenshotsRoot = path.join(process.cwd(), 'docs-screenshots');
const screenshotsRoot = path.join(process.cwd(), 'test/output/screenshots');

export const screenshot = async (page: Page, name: string) => {
await page.waitForFunction(() => !document.getAnimations().some((a) => {
Expand Down
Loading