Module: PlaywrightRuntime

Defined in:
app/services/playwright_runtime.rb

Overview

Resolves how to obtain a Playwright browser for the current environment, and
yields a connected browser to callers via PlaywrightRuntime.with_browser.

Two modes, selected by the PLAYWRIGHT_SERVER_URL env var:

  1. REMOTE (production / staging) — PLAYWRIGHT_SERVER_URL is set, e.g.
    ws://heatwave-playwright:3000/ws. The Kamal app image ships NO Node /
    Playwright / browsers (see Dockerfile + .dockerignore); browsers run in a
    separate playwright Kamal accessory (mcr.microsoft.com/playwright) and the
    app connects over the kamal network via connect_to_browser_server. See the
    playwright accessory in config/deploy.yml / config/deploy.staging.yml.

  2. LOCAL (development) — PLAYWRIGHT_SERVER_URL is unset; launch a browser with
    the local driver resolved by PlaywrightRuntime.cli_path. Lookup order:
    a. node_modules/.bin/playwright (where yarn install provides it)
    b. ~/.playwright-driver/ (standalone driver, bundles its own Node)
    c. npx playwright (fallback)

The standalone driver is installed with:
wget https://playwright.azureedge.net/builds/driver/playwright--linux.zip
unzip -d ~/.playwright-driver/

VERSION LOCKSTEP: in remote mode the accessory image tag MUST equal the gem's
Playwright::COMPATIBLE_PLAYWRIGHT_VERSION (the wire protocol is version-matched).
Bumping playwright-ruby-client means bumping the accessory image tag in the same PR.

Constant Summary collapse

STANDALONE_DRIVER_DIR =

Standalone driver dir (local mode).

File.join(Dir.home, '.playwright-driver').freeze

Class Method Summary collapse

Class Method Details

.cli_pathString

Resolves the local Playwright CLI executable for Playwright.create (local mode).

Returns:

  • (String)

    the CLI command (node_modules binary, standalone driver, or npx playwright)

Raises:

  • (RuntimeError)

    when no local driver is found and PLAYWRIGHT_SERVER_URL is unset



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'app/services/playwright_runtime.rb', line 79

def cli_path
  local = Rails.root.join('node_modules/.bin/playwright')
  return local.to_s if File.exist?(local)

  standalone_node = File.join(STANDALONE_DRIVER_DIR, 'node')
  standalone_cli  = File.join(STANDALONE_DRIVER_DIR, 'package', 'cli.js')
  return "#{standalone_node} #{standalone_cli}" if File.exist?(standalone_node) && File.exist?(standalone_cli)

  npx = find_npx
  return "#{npx} playwright" if npx

  raise "Playwright runtime not found. Set PLAYWRIGHT_SERVER_URL to use the remote " \
        "Playwright accessory, install the standalone driver in #{STANDALONE_DRIVER_DIR}, " \
        "or ensure node_modules/.bin/playwright exists (yarn install)."
end

.find_npxString?

Locates the npx executable, if any, for the local-driver fallback.

Returns:

  • (String, nil)

    absolute path to npx, or nil when not on PATH



98
99
100
101
# File 'app/services/playwright_runtime.rb', line 98

def find_npx
  path = `which npx 2>/dev/null`.strip
  path.presence
end

.remote?Boolean

True when a remote Playwright server is configured (production / staging).

Returns:

  • (Boolean)


44
45
46
# File 'app/services/playwright_runtime.rb', line 44

def remote?
  server_url.present?
end

.server_urlString?

ws:// endpoint of the remote Playwright server accessory, or nil in local mode.

Returns:

  • (String, nil)


38
39
40
# File 'app/services/playwright_runtime.rb', line 38

def server_url
  ENV['PLAYWRIGHT_SERVER_URL'].presence
end

.with_browser(launch_options: {}, browser_type: 'chromium') {|browser| ... } ⇒ Object

Yields a connected Playwright::Browser, picking remote vs local per server_url,
and cleans it up afterwards. The block's return value is passed through.

launch_options apply ONLY to a local launch — a remote browser is pre-launched
by playwright run-server on the accessory, so the client cannot set its CLI args
(e.g. --no-sandbox / --disable-dev-shm-usage). The accessory image handles the
sandbox; size /dev/shm via the accessory's options: { shm-size } instead.

Parameters:

  • launch_options (Hash) (defaults to: {})

    passed to chromium.launch in local mode only

  • browser_type (String) (defaults to: 'chromium')

    'chromium' | 'firefox' | 'webkit'

Yield Parameters:

  • browser (Playwright::Browser)

Returns:

  • (Object)

    the block's return value, passed through



60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'app/services/playwright_runtime.rb', line 60

def with_browser(launch_options: {}, browser_type: 'chromium', &)
  if (ws = server_url)
    Playwright.connect_to_browser_server(ws, browser_type: browser_type, &)
  else
    Playwright.create(playwright_cli_executable_path: cli_path) do |playwright|
      browser = playwright.public_send(browser_type).launch(**launch_options)
      begin
        yield(browser)
      ensure
        browser&.close
      end
    end
  end
end