Module: Stagehand::Local::Binary

Defined in:
lib/stagehand/local.rb

Class Method Summary collapse

Class Method Details

.binary_filenameObject



85
86
87
88
89
90
# File 'lib/stagehand/local.rb', line 85

def binary_filename
  platform, arch = platform_tag
  name = "stagehand-server-v3-#{platform}-#{arch}"
  name += ".exe" if platform == "win32"
  name
end

.cache_dirObject



112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/stagehand/local.rb', line 112

def cache_dir
  root =
    if Stagehand::Local.macos?
      File.join(home_dir || Dir.tmpdir, "Library", "Caches")
    elsif Stagehand::Local.windows?
      ENV["LOCALAPPDATA"] || File.join(home_dir || Dir.tmpdir, "AppData", "Local")
    else
      ENV["XDG_CACHE_HOME"] || File.join(home_dir || Dir.tmpdir, ".cache")
    end
  version = Stagehand::VERSION.to_s
  File.join(root, "stagehand", "lib", "ruby_#{version}")
end

.download_binary(tag, dest_path) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/stagehand/local.rb', line 155

def download_binary(tag, dest_path)
  filename = binary_filename
  url = URI("https://github.com/#{STAGEHAND_REPO}/releases/download/#{tag}/#{filename}")
  FileUtils.mkdir_p(File.dirname(dest_path))

  tmp_path = "#{dest_path}.tmp"
  download_with_redirects(url, tmp_path, limit: 3)
  FileUtils.mv(tmp_path, dest_path)
  ensure_executable(dest_path)
rescue StandardError => e
  FileUtils.rm_f(tmp_path)
  hint = manual_download_hint(filename, dest_path)
  raise "Failed to download Stagehand driver binary: #{e.message}. #{hint}"
end

.download_with_redirects(url, dest_path, limit:) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/stagehand/local.rb', line 175

def download_with_redirects(url, dest_path, limit:)
  raise "Too many redirects while downloading Stagehand driver binary." if limit <= 0

  request = Net::HTTP::Get.new(url)
  request["User-Agent"] = DEFAULT_USER_AGENT
  response = http_request(url, request)

  case response
  when Net::HTTPRedirection
    location = response["location"]
    raise "Missing redirect location." if location.nil?

    download_with_redirects(URI(location), dest_path, limit: limit - 1)
  when Net::HTTPSuccess
    File.binwrite(dest_path, response.body)
    nil
  else
    raise "Failed to download binary: #{response.code} #{response.message}"
  end
end

.ensure_executable(path) ⇒ Object



204
205
206
207
208
209
210
211
# File 'lib/stagehand/local.rb', line 204

def ensure_executable(path)
  return path if Stagehand::Local.windows?
  return path unless File.exist?(path)

  mode = File.stat(path).mode
  File.chmod(mode | 0o100, path)
  path
end

.fetch_latest_tagObject



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/stagehand/local.rb', line 138

def fetch_latest_tag
  url = URI("https://api.github.com/repos/#{STAGEHAND_REPO}/releases?per_page=15")
  request = Net::HTTP::Get.new(url)
  request["User-Agent"] = DEFAULT_USER_AGENT
  response = http_request(url, request)
  unless response.is_a?(Net::HTTPSuccess)
    raise "Failed to fetch releases: #{response.code} #{response.message}"
  end

  releases = JSON.parse(response.body.to_s)
  releases.each do |release|
    tag = release["tag_name"]
    return tag if tag.is_a?(String) && tag.start_with?("stagehand-server-v3/")
  end
  raise "Failed to find stagehand-server-v3 release tag"
end

.home_dirObject



125
126
127
128
129
# File 'lib/stagehand/local.rb', line 125

def home_dir
  Dir.home
rescue StandardError
  nil
end

.http_request(url, request) ⇒ Object



196
197
198
199
200
201
202
# File 'lib/stagehand/local.rb', line 196

def http_request(url, request)
  Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == "https") do |http|
    http.open_timeout = 30
    http.read_timeout = DOWNLOAD_TIMEOUT_S
    http.request(request)
  end
end

.manual_download_hint(filename, dest_path) ⇒ Object



170
171
172
173
# File 'lib/stagehand/local.rb', line 170

def manual_download_hint(filename, dest_path)
  "Download #{filename} from https://github.com/#{STAGEHAND_REPO}/releases " \
    "and save it to: #{dest_path}."
end

.platform_tagObject



72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/stagehand/local.rb', line 72

def platform_tag
  platform = if Stagehand::Local.macos?
    "darwin"
  elsif Stagehand::Local.windows?
    "win32"
  else
    "linux"
  end
  cpu = RbConfig::CONFIG["host_cpu"].to_s.downcase
  arch = cpu.include?("arm") || cpu.include?("aarch64") ? "arm64" : "x64"
  [platform, arch]
end

.resolve_binary_pathObject



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/stagehand/local.rb', line 92

def resolve_binary_path
  env = ENV["STAGEHAND_SEA_BINARY"].to_s
  return ensure_executable(env) unless env.empty?

  filename = binary_filename
  cache_path = File.join(cache_dir, filename)
  return ensure_executable(cache_path) if File.file?(cache_path)

  Stagehand::Local.download_mutex.synchronize do
    return ensure_executable(cache_path) if File.file?(cache_path)

    version = ENV["STAGEHAND_SERVER_VERSION"].to_s
    version = "latest" if version.empty?
    tag = resolve_version(version)
    download_binary(tag, cache_path)
  end

  ensure_executable(cache_path)
end

.resolve_version(version) ⇒ Object



131
132
133
134
135
136
# File 'lib/stagehand/local.rb', line 131

def resolve_version(version)
  return fetch_latest_tag if version.empty? || version == "latest"
  return version if version.start_with?("stagehand-server-v3/")

  "stagehand-server-v3/#{version}"
end