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
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]