Add snapshot diff viewer to PR preview deploys
Capture screenshots and generate the diff viewer during the preview
workflow, then bake the output into the Docker image so it's served
at /snapshots/diff.html on the Fly preview app.

- Add /snapshots to Roda's static plugin (no-op in prod)
- Add --no-sandbox to Chrome options in CI (needed on Ubuntu)
- Install Ruby and run `rake snapshots:diff` before the Fly deploy

https://claude.ai/code/session_01Bgey3hrVy9av1SHrpdo5bU
change
commit 1d31a02ae67a695188789ff43181582cab2a7291
author Claude <noreply@anthropic.com>
date
parent 3bcadf2f
diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml
index 93981e5..68c3149 100644
--- a/.github/workflows/preview.yml
+++ b/.github/workflows/preview.yml
@@ -19,6 +19,22 @@ jobs:
       url: ${{ steps.deploy.outputs.url }}
     steps:
       - uses: actions/checkout@v6
+
+      - if: github.event.action != 'closed'
+        uses: ruby/setup-ruby@v1
+        with:
+          bundler-cache: true
+
+      - name: Capture snapshot diff
+        if: github.event.action != 'closed'
+        run: |
+          rake snapshots:diff
+          snapshot_dir="${XDG_CACHE_HOME:-$HOME/.cache}/ketchup/snapshots"
+          cp -r "$snapshot_dir" public/snapshots
+          cp public/favicon.svg public/snapshots/
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
       - name: Deploy preview
         id: deploy
         uses: superfly/fly-pr-review-apps@1.5.0
diff --git a/lib/ketchup/snapshots.rb b/lib/ketchup/snapshots.rb
index 6b0aa82..b921cbf 100644
--- a/lib/ketchup/snapshots.rb
+++ b/lib/ketchup/snapshots.rb
@@ -96,10 +96,13 @@ module Ketchup
         FileUtils.rm_rf(@output_dir)
         @output_dir.mkpath
 
+        browser_options = { "force-device-scale-factor" => 2 }
+        browser_options["no-sandbox"] = nil if ENV["CI"]
+
         @browser = Ferrum::Browser.new(
           headless: true,
           window_size: VIEWPORTS.fetch("desktop"),
-          browser_options: { "force-device-scale-factor" => 2 }
+          browser_options: browser_options
         )
 
         @server.call(@browser) do |url|
diff --git a/lib/ketchup/web.rb b/lib/ketchup/web.rb
index 55c635d..9c05e15 100644
--- a/lib/ketchup/web.rb
+++ b/lib/ketchup/web.rb
@@ -11,7 +11,7 @@ require_relative "views/user/show"
 
 class Web < Roda
   plugin :halt
-  plugin :static, %w[ /css /js /favicon.svg ]
+  plugin :static, %w[ /css /js /favicon.svg /snapshots ]
   plugin :all_verbs
   plugin :sessions, secret: CONFIG.session_secret
   plugin :route_csrf, csrf_failure: :empty_403, check_request_methods: %w[POST]