Class: Capybara::Lightpanda::Binary

Inherits:
Object
  • Object
show all
Defined in:
lib/capybara/lightpanda/binary.rb

Defined Under Namespace

Classes: Result

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

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.cache_timeObject



48
49
50
# File 'lib/capybara/lightpanda/binary.rb', line 48

def cache_time
  @cache_time ||= Integer(ENV.fetch("LIGHTPANDA_CACHE_TIME", DEFAULT_CACHE_TIME))
end

.install_dirObject



52
53
54
# File 'lib/capybara/lightpanda/binary.rb', line 52

def install_dir
  @install_dir ||= File.dirname(default_binary_path)
end

.loggerObject



56
57
58
59
60
61
# File 'lib/capybara/lightpanda/binary.rb', line 56

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_addrObject

Returns the value of attribute proxy_addr.



46
47
48
# File 'lib/capybara/lightpanda/binary.rb', line 46

def proxy_addr
  @proxy_addr
end

.proxy_passObject

Returns the value of attribute proxy_pass.



46
47
48
# File 'lib/capybara/lightpanda/binary.rb', line 46

def proxy_pass
  @proxy_pass
end

.proxy_portObject

Returns the value of attribute proxy_port.



46
47
48
# File 'lib/capybara/lightpanda/binary.rb', line 46

def proxy_port
  @proxy_port
end

.proxy_userObject

Returns the value of attribute proxy_user.



46
47
48
# File 'lib/capybara/lightpanda/binary.rb', line 46

def proxy_user
  @proxy_user
end

.required_versionObject

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.



41
42
43
# File 'lib/capybara/lightpanda/binary.rb', line 41

def required_version
  @required_version
end

Class Method Details

.configure {|_self| ... } ⇒ Object

Yields:

  • (_self)

Yield Parameters:



63
64
65
# File 'lib/capybara/lightpanda/binary.rb', line 63

def configure
  yield self
end

.current_versionObject

Returns the ‘lightpanda version` output of the cached binary, or nil if the binary isn’t present / not runnable.



119
120
121
122
123
124
125
126
127
# File 'lib/capybara/lightpanda/binary.rb', line 119

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_pathObject



176
177
178
179
180
# File 'lib/capybara/lightpanda/binary.rb', line 176

def default_binary_path
  cache_dir = ENV.fetch("XDG_CACHE_HOME") { File.expand_path("~/.cache") }

  File.join(cache_dir, "lightpanda", "lightpanda")
end

.downloadObject



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/capybara/lightpanda/binary.rb', line 153

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)
  @path = destination

  destination
end

.execObject



137
138
139
# File 'lib/capybara/lightpanda/binary.rb', line 137

def exec(*)
  Kernel.exec(path, *)
end

.fetch(url) ⇒ Object

Raises:



141
142
143
144
145
146
# File 'lib/capybara/lightpanda/binary.rb', line 141

def fetch(url)
  result = run("fetch", "--dump", url)
  raise BinaryError, result.stderr unless result.success?

  result.stdout
end

.install_pathObject

Path the gem writes the downloaded binary to. Honors a user-configured install_dir; otherwise falls back to default_binary_path.



184
185
186
187
188
189
190
# File 'lib/capybara/lightpanda/binary.rb', line 184

def install_path
  if @install_dir
    File.join(@install_dir, "lightpanda")
  else
    default_binary_path
  end
end

.pathObject



67
68
69
# File 'lib/capybara/lightpanda/binary.rb', line 67

def path
  @path ||= update
end

.platform_binaryObject



169
170
171
172
173
174
# File 'lib/capybara/lightpanda/binary.rb', line 169

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

.removeObject

Delete the cached binary. Returns the path that was deleted, or nil if nothing was there.



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/capybara/lightpanda/binary.rb', line 104

def remove
  path = install_path
  unless File.exist?(path)
    log("Nothing to remove at #{path}")
    return nil
  end

  File.delete(path)
  @path = nil
  log("Removed #{path}")
  path
end

.runObject



129
130
131
132
133
134
135
# File 'lib/capybara/lightpanda/binary.rb', line 129

def run(*)
  stdout, stderr, status = Open3.capture3(path, *)

  Result.new(stdout: stdout, stderr: stderr, status: status)
rescue Errno::ENOENT
  raise BinaryNotFoundError, "Lightpanda binary not found"
end

.updateObject

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.



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/capybara/lightpanda/binary.rb', line 78

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

  download
end

.versionObject



148
149
150
151
# File 'lib/capybara/lightpanda/binary.rb', line 148

def version
  result = run("version")
  result.output.strip
end