browserstack-local 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/browserstack/fetch_download_source_url.rb +63 -0
- data/lib/browserstack/local.rb +5 -1
- data/lib/browserstack/localbinary.rb +118 -73
- data/lib/browserstack/version.rb +3 -0
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6cc02b00944100bc4c6548ee133ad288a6c96271342c358f1c76398bad4087c
|
|
4
|
+
data.tar.gz: 128f9be2ca356f0efff2cf5fa9e9d92d7a2e68da5430b8329c5251ee5f2b30d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8c014452cb36ae15b6f3e2b1e3e722fa0f2b4d0fdfbaff6a8c22960072765e30942fa32a754654e7132ddc68fb6c8339adc3d6bc4ab5e41574144ef266b6ffac
|
|
7
|
+
data.tar.gz: e6b001e4caeab902ea85c7d0c5cf0ef2f455aee7ce3b36e864b251dc43ec05dee957e44eda2e4f4592d3be464c328bfd60eff7d001c15190e6aab1920ceb2cf6
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'net/https'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'openssl'
|
|
5
|
+
require 'browserstack/localexception'
|
|
6
|
+
|
|
7
|
+
module BrowserStack
|
|
8
|
+
module FetchDownloadSourceUrl
|
|
9
|
+
BS_HOST = 'local.browserstack.com'.freeze
|
|
10
|
+
ENDPOINT_PATH = '/binary/api/v1/endpoint'.freeze
|
|
11
|
+
|
|
12
|
+
def self.call(auth_token:, user_agent:, fallback: false, error_message: nil,
|
|
13
|
+
proxy_host: nil, proxy_port: nil)
|
|
14
|
+
uri = URI::HTTPS.build(host: BS_HOST, path: ENDPOINT_PATH)
|
|
15
|
+
|
|
16
|
+
body = { 'auth_token' => auth_token }
|
|
17
|
+
body['error_message'] = error_message if fallback && error_message
|
|
18
|
+
|
|
19
|
+
http_class = if proxy_host && proxy_port
|
|
20
|
+
Net::HTTP::Proxy(proxy_host, proxy_port.to_i)
|
|
21
|
+
else
|
|
22
|
+
Net::HTTP
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
http = http_class.new(uri.host, uri.port)
|
|
26
|
+
http.use_ssl = true
|
|
27
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
28
|
+
http.open_timeout = 10
|
|
29
|
+
http.read_timeout = 15
|
|
30
|
+
|
|
31
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
32
|
+
req['Content-Type'] = 'application/json'
|
|
33
|
+
req['User-Agent'] = user_agent
|
|
34
|
+
req['X-Local-Fallback-Cloudflare'] = 'true' if fallback
|
|
35
|
+
req.body = JSON.dump(body)
|
|
36
|
+
|
|
37
|
+
res = http.request(req)
|
|
38
|
+
|
|
39
|
+
begin
|
|
40
|
+
parsed = JSON.parse(res.body.to_s)
|
|
41
|
+
rescue JSON::ParserError => e
|
|
42
|
+
raise BrowserStack::LocalException.new(
|
|
43
|
+
"Failed to parse binary endpoint API response (HTTP #{res.code}): #{e.message}"
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if parsed.is_a?(Hash) && parsed['error']
|
|
48
|
+
raise BrowserStack::LocalException.new(
|
|
49
|
+
"Binary endpoint API returned error: #{parsed['error']}"
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
endpoint = parsed.is_a?(Hash) ? parsed.dig('data', 'endpoint') : nil
|
|
54
|
+
if endpoint.nil? || endpoint.to_s.empty?
|
|
55
|
+
raise BrowserStack::LocalException.new(
|
|
56
|
+
"Binary endpoint API returned no endpoint (HTTP #{res.code})"
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
endpoint
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/browserstack/local.rb
CHANGED
|
@@ -64,7 +64,11 @@ class Local
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
@binary_path = if @binary_path.nil?
|
|
67
|
-
BrowserStack::LocalBinary.new
|
|
67
|
+
BrowserStack::LocalBinary.new(
|
|
68
|
+
auth_token: @key,
|
|
69
|
+
proxy_host: @proxy_host,
|
|
70
|
+
proxy_port: @proxy_port
|
|
71
|
+
).binary_path
|
|
68
72
|
else
|
|
69
73
|
@binary_path
|
|
70
74
|
end
|
|
@@ -3,28 +3,27 @@ require 'net/https'
|
|
|
3
3
|
require 'rbconfig'
|
|
4
4
|
require 'openssl'
|
|
5
5
|
require 'tmpdir'
|
|
6
|
+
require 'fileutils'
|
|
6
7
|
require 'browserstack/localexception'
|
|
8
|
+
require 'browserstack/fetch_download_source_url'
|
|
9
|
+
require 'browserstack/version'
|
|
7
10
|
|
|
8
11
|
module BrowserStack
|
|
9
|
-
|
|
12
|
+
|
|
10
13
|
class LocalBinary
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
else
|
|
25
|
-
"https://www.browserstack.com/local-testing/downloads/binaries/BrowserStackLocal-linux-ia32"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
14
|
+
BASE_RETRIES = 9
|
|
15
|
+
FALLBACK_TRIGGER_RETRY = 4
|
|
16
|
+
|
|
17
|
+
def initialize(conf = {})
|
|
18
|
+
@auth_token = conf[:auth_token] || ENV['BROWSERSTACK_ACCESS_KEY']
|
|
19
|
+
@proxy_host = conf[:proxy_host]
|
|
20
|
+
@proxy_port = conf[:proxy_port]
|
|
21
|
+
@user_agent = conf[:user_agent] || "browserstack-local-ruby/#{BrowserStack::VERSION}"
|
|
22
|
+
|
|
23
|
+
@windows = !!(RbConfig::CONFIG['host_os'] =~ /mswin|msys|mingw|cygwin|bccwin|wince|emc/)
|
|
24
|
+
@binary_filename = compute_binary_filename
|
|
25
|
+
@source_url = nil
|
|
26
|
+
@download_error_message = nil
|
|
28
27
|
|
|
29
28
|
@ordered_paths = [
|
|
30
29
|
File.join(File.expand_path('~'), '.browserstack'),
|
|
@@ -33,79 +32,125 @@ class LocalBinary
|
|
|
33
32
|
]
|
|
34
33
|
end
|
|
35
34
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end
|
|
40
|
-
uri = URI.parse(@http_path)
|
|
41
|
-
binary_path = File.join(dest_parent_dir, "BrowserStackLocal#{".exe" if @windows}")
|
|
42
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
43
|
-
http.use_ssl = true
|
|
44
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
45
|
-
|
|
46
|
-
res = http.get(uri.path)
|
|
47
|
-
file = open(binary_path, 'wb')
|
|
48
|
-
file.write(res.body)
|
|
49
|
-
file.close
|
|
50
|
-
FileUtils.chmod 0755, binary_path
|
|
51
|
-
|
|
52
|
-
binary_path
|
|
53
|
-
end
|
|
35
|
+
def binary_path
|
|
36
|
+
dest_parent_dir = get_available_dirs
|
|
37
|
+
bin_path = File.join(dest_parent_dir, dest_binary_name)
|
|
54
38
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
false
|
|
39
|
+
return bin_path if File.exist?(bin_path) && verify_binary(bin_path)
|
|
40
|
+
|
|
41
|
+
File.delete(bin_path) if File.exist?(bin_path)
|
|
42
|
+
download_with_retries(bin_path)
|
|
60
43
|
end
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
dest_parent_dir = get_available_dirs
|
|
64
|
-
binary_path = File.join(dest_parent_dir, "BrowserStackLocal#{".exe" if @windows}")
|
|
45
|
+
private
|
|
65
46
|
|
|
66
|
-
|
|
67
|
-
|
|
47
|
+
def dest_binary_name
|
|
48
|
+
@windows ? 'BrowserStackLocal.exe' : 'BrowserStackLocal'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def compute_binary_filename
|
|
52
|
+
host_os = RbConfig::CONFIG['host_os']
|
|
53
|
+
host_cpu = RbConfig::CONFIG['host_cpu']
|
|
54
|
+
case host_os
|
|
55
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
|
56
|
+
'BrowserStackLocal.exe'
|
|
57
|
+
when /darwin|mac os/
|
|
58
|
+
'BrowserStackLocal-darwin-x64'
|
|
59
|
+
when /linux/
|
|
60
|
+
if host_cpu =~ /arm64|aarch64/
|
|
61
|
+
'BrowserStackLocal-linux-arm64'
|
|
62
|
+
elsif host_os =~ /linux-musl/
|
|
63
|
+
'BrowserStackLocal-alpine'
|
|
64
|
+
elsif 1.size == 8
|
|
65
|
+
'BrowserStackLocal-linux-x64'
|
|
66
|
+
else
|
|
67
|
+
'BrowserStackLocal-linux-ia32'
|
|
68
|
+
end
|
|
68
69
|
else
|
|
69
|
-
|
|
70
|
+
raise BrowserStack::LocalException.new("Unsupported host OS: #{host_os}")
|
|
70
71
|
end
|
|
72
|
+
end
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
raise BrowserStack::LocalException.new('BrowserStack Local binary is corrupt')
|
|
74
|
+
def download_with_retries(bin_path)
|
|
75
|
+
retries = BASE_RETRIES
|
|
76
|
+
while retries > 0
|
|
77
|
+
refresh_source_url(retries) if retries == BASE_RETRIES || retries == FALLBACK_TRIGGER_RETRY
|
|
78
|
+
begin
|
|
79
|
+
download_to(@source_url + '/' + @binary_filename, bin_path)
|
|
80
|
+
return bin_path if verify_binary(bin_path)
|
|
81
|
+
@download_error_message = 'Downloaded binary failed verification'
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
@download_error_message = "Download failed: #{e.message}"
|
|
83
84
|
end
|
|
85
|
+
File.delete(bin_path) if File.exist?(bin_path)
|
|
86
|
+
retries -= 1
|
|
84
87
|
end
|
|
88
|
+
|
|
89
|
+
raise BrowserStack::LocalException.new(
|
|
90
|
+
"Failed to download BrowserStack Local binary after #{BASE_RETRIES} attempts. " \
|
|
91
|
+
"Last error: #{@download_error_message}"
|
|
92
|
+
)
|
|
85
93
|
end
|
|
86
94
|
|
|
87
|
-
|
|
95
|
+
def refresh_source_url(retries)
|
|
96
|
+
is_fallback = (retries == FALLBACK_TRIGGER_RETRY) && !@download_error_message.nil?
|
|
97
|
+
begin
|
|
98
|
+
@source_url = BrowserStack::FetchDownloadSourceUrl.call(
|
|
99
|
+
auth_token: @auth_token,
|
|
100
|
+
user_agent: @user_agent,
|
|
101
|
+
fallback: is_fallback,
|
|
102
|
+
error_message: @download_error_message,
|
|
103
|
+
proxy_host: @proxy_host,
|
|
104
|
+
proxy_port: @proxy_port
|
|
105
|
+
)
|
|
106
|
+
rescue StandardError => e
|
|
107
|
+
raise if @source_url.nil?
|
|
108
|
+
@download_error_message = "Source URL refresh failed: #{e.message}"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def download_to(url, bin_path)
|
|
113
|
+
uri = URI.parse(url)
|
|
114
|
+
http_class = if @proxy_host && @proxy_port
|
|
115
|
+
Net::HTTP::Proxy(@proxy_host, @proxy_port.to_i)
|
|
116
|
+
else
|
|
117
|
+
Net::HTTP
|
|
118
|
+
end
|
|
119
|
+
http = http_class.new(uri.host, uri.port)
|
|
120
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
121
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
122
|
+
http.open_timeout = 10
|
|
123
|
+
http.read_timeout = 30
|
|
124
|
+
|
|
125
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
|
126
|
+
req['User-Agent'] = @user_agent
|
|
127
|
+
|
|
128
|
+
res = http.request(req)
|
|
129
|
+
raise "HTTP #{res.code}" unless res.is_a?(Net::HTTPSuccess)
|
|
130
|
+
|
|
131
|
+
File.open(bin_path, 'wb') { |f| f.write(res.body) }
|
|
132
|
+
FileUtils.chmod 0755, bin_path
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def verify_binary(bin_path)
|
|
136
|
+
binary_response = IO.popen(bin_path + " --version").readline
|
|
137
|
+
!!(binary_response =~ /BrowserStack Local version \d+\.\d+/)
|
|
138
|
+
rescue StandardError
|
|
139
|
+
false
|
|
140
|
+
end
|
|
88
141
|
|
|
89
142
|
def get_available_dirs
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
path = @ordered_paths[i]
|
|
93
|
-
if make_path(path)
|
|
94
|
-
return path
|
|
95
|
-
else
|
|
96
|
-
i += 1
|
|
97
|
-
end
|
|
143
|
+
@ordered_paths.each do |path|
|
|
144
|
+
return path if make_path(path)
|
|
98
145
|
end
|
|
99
146
|
raise BrowserStack::LocalException.new('Error trying to download BrowserStack Local binary')
|
|
100
147
|
end
|
|
101
148
|
|
|
102
149
|
def make_path(path)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return false
|
|
108
|
-
end
|
|
150
|
+
FileUtils.mkdir_p(path) unless File.directory?(path)
|
|
151
|
+
true
|
|
152
|
+
rescue StandardError
|
|
153
|
+
false
|
|
109
154
|
end
|
|
110
155
|
end
|
|
111
156
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: browserstack-local
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- BrowserStack
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-06-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Ruby bindings for BrowserStack Local
|
|
14
14
|
email: support@browserstack.com
|
|
@@ -16,14 +16,16 @@ executables: []
|
|
|
16
16
|
extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
|
18
18
|
files:
|
|
19
|
+
- lib/browserstack/fetch_download_source_url.rb
|
|
19
20
|
- lib/browserstack/local.rb
|
|
20
21
|
- lib/browserstack/localbinary.rb
|
|
21
22
|
- lib/browserstack/localexception.rb
|
|
23
|
+
- lib/browserstack/version.rb
|
|
22
24
|
homepage: http://rubygems.org/gems/browserstack-local
|
|
23
25
|
licenses:
|
|
24
26
|
- MIT
|
|
25
27
|
metadata: {}
|
|
26
|
-
post_install_message:
|
|
28
|
+
post_install_message:
|
|
27
29
|
rdoc_options: []
|
|
28
30
|
require_paths:
|
|
29
31
|
- lib
|
|
@@ -39,7 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
39
41
|
version: '0'
|
|
40
42
|
requirements: []
|
|
41
43
|
rubygems_version: 3.0.3.1
|
|
42
|
-
signing_key:
|
|
44
|
+
signing_key:
|
|
43
45
|
specification_version: 4
|
|
44
46
|
summary: BrowserStack Local
|
|
45
47
|
test_files: []
|