Class: Capybara::Lightpanda::Binary
- Inherits:
-
Object
- Object
- Capybara::Lightpanda::Binary
- Defined in:
- lib/capybara/lightpanda/binary.rb
Constant Summary collapse
- GITHUB_RELEASE_URL =
"https://github.com/lightpanda-io/browser/releases/download"- PLATFORMS =
{ %w[x86_64 linux] => "lightpanda-x86_64-linux", %w[aarch64 darwin] => "lightpanda-aarch64-macos", %w[arm64 darwin] => "lightpanda-aarch64-macos", }.freeze
- DEFAULT_CACHE_TIME =
86_400- PROVISION_HINT =
One-liner that re-provisions the binary from a process with no HTTP-stubbing loaded (VCR/WebMock guard the test process itself). Referenced from the stale-fallback warning and BETA_TESTING.md.
"bundle exec ruby -r capybara-lightpanda " \ "-e 'Capybara::Lightpanda::Binary.remove; puts Capybara::Lightpanda::Binary.update'"
Class Attribute Summary collapse
- .cache_time ⇒ Object
- .install_dir ⇒ Object
- .logger ⇒ Object
-
.proxy_addr ⇒ Object
Returns the value of attribute proxy_addr.
-
.proxy_pass ⇒ Object
Returns the value of attribute proxy_pass.
-
.proxy_port ⇒ Object
Returns the value of attribute proxy_port.
-
.proxy_user ⇒ Object
Returns the value of attribute proxy_user.
-
.required_version ⇒ Object
Set a specific release tag (e.g. “0.3.0”) to pin downloads to that release.
Class Method Summary collapse
- .configure {|_self| ... } ⇒ Object
-
.current_version ⇒ Object
Returns the ‘lightpanda version` output of the cached binary, or nil if the binary isn’t present / not runnable.
- .default_binary_path ⇒ Object
- .download ⇒ Object
-
.install_path ⇒ Object
Path the gem writes the downloaded binary to.
- .platform_binary ⇒ Object
-
.remove ⇒ Object
Delete the cached binary.
-
.update ⇒ Object
Canonical entrypoint: ensure the binary at install_path is current, download if needed, return its path.
-
.update_hint(binary_path) ⇒ Object
Build a path-appropriate “how to update” command for Process’s too-old-binary error.
Class Attribute Details
.cache_time ⇒ Object
41 42 43 |
# File 'lib/capybara/lightpanda/binary.rb', line 41 def cache_time @cache_time ||= Integer(ENV.fetch("LIGHTPANDA_CACHE_TIME", DEFAULT_CACHE_TIME)) end |
.install_dir ⇒ Object
45 46 47 |
# File 'lib/capybara/lightpanda/binary.rb', line 45 def install_dir @install_dir ||= File.dirname(default_binary_path) end |
.logger ⇒ Object
49 50 51 52 53 54 |
# File 'lib/capybara/lightpanda/binary.rb', line 49 def logger return @logger if defined?(@logger) && @logger return nil unless ENV["LIGHTPANDA_DEBUG"] @logger = Capybara::Lightpanda::Logger.new($stderr.tap { |s| s.sync = true }) end |
.proxy_addr ⇒ Object
Returns the value of attribute proxy_addr.
39 40 41 |
# File 'lib/capybara/lightpanda/binary.rb', line 39 def proxy_addr @proxy_addr end |
.proxy_pass ⇒ Object
Returns the value of attribute proxy_pass.
39 40 41 |
# File 'lib/capybara/lightpanda/binary.rb', line 39 def proxy_pass @proxy_pass end |
.proxy_port ⇒ Object
Returns the value of attribute proxy_port.
39 40 41 |
# File 'lib/capybara/lightpanda/binary.rb', line 39 def proxy_port @proxy_port end |
.proxy_user ⇒ Object
Returns the value of attribute proxy_user.
39 40 41 |
# File 'lib/capybara/lightpanda/binary.rb', line 39 def proxy_user @proxy_user end |
.required_version ⇒ Object
Set a specific release tag (e.g. “0.3.0”) to pin downloads to that release. When nil, the rolling “nightly” tag is used. The pin only affects download URL construction — the gem’s MINIMUM_NIGHTLY_BUILD floor is still enforced at process start.
34 35 36 |
# File 'lib/capybara/lightpanda/binary.rb', line 34 def required_version @required_version end |
Class Method Details
.configure {|_self| ... } ⇒ Object
56 57 58 |
# File 'lib/capybara/lightpanda/binary.rb', line 56 def configure yield self end |
.current_version ⇒ Object
Returns the ‘lightpanda version` output of the cached binary, or nil if the binary isn’t present / not runnable.
136 137 138 139 140 141 142 143 144 |
# File 'lib/capybara/lightpanda/binary.rb', line 136 def current_version path = install_path return nil unless File.executable?(path) stdout, _, status = Open3.capture3(path, "version") status.success? ? stdout.strip : nil rescue Errno::ENOENT nil end |
.default_binary_path ⇒ Object
198 199 200 201 202 |
# File 'lib/capybara/lightpanda/binary.rb', line 198 def default_binary_path cache_dir = ENV.fetch("XDG_CACHE_HOME") { File.("~/.cache") } File.join(cache_dir, "lightpanda", "lightpanda") end |
.download ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/capybara/lightpanda/binary.rb', line 146 def download binary_name = platform_binary tag = required_version || "nightly" url = "#{GITHUB_RELEASE_URL}/#{tag}/#{binary_name}" destination = install_path log("Downloading #{binary_name} (#{tag}) → #{destination}") FileUtils.mkdir_p(File.dirname(destination)) download_file(url, destination) FileUtils.chmod(0o755, destination) destination end |
.install_path ⇒ Object
Path the gem writes the downloaded binary to. Honors a user-configured install_dir; otherwise falls back to default_binary_path.
206 207 208 209 210 211 212 |
# File 'lib/capybara/lightpanda/binary.rb', line 206 def install_path if @install_dir File.join(@install_dir, "lightpanda") else default_binary_path end end |
.platform_binary ⇒ Object
191 192 193 194 195 196 |
# File 'lib/capybara/lightpanda/binary.rb', line 191 def platform_binary arch = normalize_arch(RbConfig::CONFIG["host_cpu"]) os = normalize_os(RbConfig::CONFIG["host_os"]) PLATFORMS[[arch, os]] || raise(UnsupportedPlatformError, "Unsupported platform: #{arch}-#{os}") end |
.remove ⇒ Object
Delete the cached binary. Returns the path that was deleted, or nil if nothing was there.
122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/capybara/lightpanda/binary.rb', line 122 def remove path = install_path unless File.exist?(path) log("Nothing to remove at #{path}") return nil end File.delete(path) log("Removed #{path}") path end |
.update ⇒ Object
Canonical entrypoint: ensure the binary at install_path is current, download if needed, return its path. Pinned (required_version set) never re-downloads when present. Unpinned re-downloads when older than cache_time. When unpinned and the gem cache is empty/stale, an already-installed ‘lightpanda` on PATH (e.g. via Homebrew) wins over re-downloading — keeps test suites running under VCR/WebMock from triggering surprise HTTP to github.com.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/capybara/lightpanda/binary.rb', line 67 def update destination = install_path if required_version if File.executable?(destination) log("Pinned #{required_version} present at #{destination}") return destination end return download end if cached_fresh?(destination) log("Cached binary at #{destination} is fresh (< #{cache_time}s)") return destination end if (system_path = system_binary_path) log("Using lightpanda from PATH at #{system_path}") return system_path end # Stale-or-absent cache, nothing on PATH: refresh from the network. # If that fails (GitHub 5xx, DNS/connect timeouts, SocketError) but a # usable — if stale — binary is already cached, keep using it rather # than hard-failing. A cold cache (nothing on disk) still surfaces the # error. The MINIMUM_NIGHTLY_BUILD floor is enforced downstream in # Process#start, so a sub-floor binary can't slip in. # # Deliberately StandardError, not Exception: WebMock's # NetConnectNotAllowedError descends from Exception so it propagates # through app rescue blocks by design — a test suite that blocks net # connections SHOULD fail loudly here, not silently fall back. CI # pre-provisions the binary outside that guard instead (real-apps.yml). begin download rescue StandardError => e raise unless File.executable?(destination) # Kernel.warn, not log: log() is silent unless LIGHTPANDA_DEBUG or # an explicit logger is set, and this fallback is exactly the # moment the user needs to hear about — a VCR-guarded suite (whose # UnhandledHTTPRequestError is a StandardError, unlike raw # WebMock's Exception) lands here silently, keeps a stale binary, # and later hits a confusing MINIMUM_NIGHTLY_BUILD floor error # with no trace of the blocked download. warn("[capybara-lightpanda] Binary download failed (#{e.class}: #{e.}); " \ "falling back to the cached binary at #{destination}. " \ "If your suite stubs HTTP (VCR/WebMock), pre-provision from an " \ "unstubbed process: #{PROVISION_HINT}") destination end end |
.update_hint(binary_path) ⇒ Object
Build a path-appropriate “how to update” command for Process’s too-old-binary error. Three branches:
-
Symlink into a ‘/Cellar/` directory → installed via Homebrew; suggest `brew update && brew upgrade lightpanda` (brew pins each user’s binary at install time and doesn’t refresh on its own when the tap publishes a newer nightly).
-
Path equals our own cache → suggest the require-the-gem one-liner. NOT the lightpanda:binary:* rake tasks: in a Rails app the gem usually sits in the :test Gemfile group, so the tasks only exist under RAILS_ENV=test (the Railtie can’t help a plain ‘bundle exec rake` in development), and outside Rails they’re never loaded at all. The one-liner requires the gem explicitly, so it works from any environment. The ‘remove` step is required because `update` honors `cache_time` and would otherwise no-op on a too-old-but-not-yet-expired file.
-
Anything else (user-managed install at a custom path) → keep the curl-overwrite suggestion, since we don’t know how the file got there.
180 181 182 183 184 185 186 187 188 189 |
# File 'lib/capybara/lightpanda/binary.rb', line 180 def update_hint(binary_path) if brew_managed?(binary_path) "brew update && brew upgrade lightpanda" elsif binary_path == install_path PROVISION_HINT else "curl -sL #{GITHUB_RELEASE_URL}/nightly/#{platform_binary} " \ "-o #{binary_path} && chmod +x #{binary_path}" end end |