chromedriver-binary 0.1.1
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 +7 -0
- data/.idea/.gitignore +8 -0
- data/.idea/inspectionProfiles/profiles_settings.xml +5 -0
- data/.idea/vcs.xml +6 -0
- data/.rspec +3 -0
- data/.rubocop.yml +47 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +97 -0
- data/Rakefile +23 -0
- data/lib/chromedriver/Rakefile +6 -0
- data/lib/chromedriver/binary/chromedriver_downloader.rb +93 -0
- data/lib/chromedriver/binary/download_progress.rb +30 -0
- data/lib/chromedriver/binary/downloader_helper.rb +102 -0
- data/lib/chromedriver/binary/finder.rb +83 -0
- data/lib/chromedriver/binary/platform.rb +57 -0
- data/lib/chromedriver/binary/railtie.rb +16 -0
- data/lib/chromedriver/binary/system_helper.rb +33 -0
- data/lib/chromedriver/binary/tasks/chromedriver.rake +27 -0
- data/lib/chromedriver/binary/version.rb +7 -0
- data/lib/chromedriver/binary/version_resolver.rb +91 -0
- data/lib/chromedriver/binary.rb +49 -0
- data/sig/chromedriver/binary/binary.rbs +40 -0
- data/sig/chromedriver/binary/chromedriver_downloader.rbs +18 -0
- data/sig/chromedriver/binary/download_progress.rbs +14 -0
- data/sig/chromedriver/binary/downloader_helper.rbs +13 -0
- data/sig/chromedriver/binary/platform.rbs +9 -0
- data/sig/chromedriver/binary/system_helper.rbs +7 -0
- data/sig/chromedriver/binary/version_resolver.rbs +7 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e37c7672fc5a5a74432545fb3b11927e0e1a7157e4f1210a43b7116b6d78a67f
|
4
|
+
data.tar.gz: 3ba62d5d9a4c307623c33c53c04e9cc95e461eee852f59d3a2baf7e8da28d893
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b9c2c0d4fec758a0f12d5e70d052e9c9dc1f20cfd5374f3feb132b884a6f56bf44a5c2db22f90b3536aeb83dfbdbc088bc2eae57994dd5820de149c1ead754f
|
7
|
+
data.tar.gz: 041ecdf8e7a3b39dbc870630241968f87173cd348003c2e4089fcb44dd908d4b72aade8fb964d260b44f6b5fffa27f53e9b0bd562b7602ad9ab443e3860aa4b5
|
data/.idea/.gitignore
ADDED
data/.idea/vcs.xml
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
TargetRubyVersion: 3.0
|
4
|
+
|
5
|
+
Style/StringLiterals:
|
6
|
+
EnforcedStyle: double_quotes
|
7
|
+
|
8
|
+
Style/StringLiteralsInInterpolation:
|
9
|
+
EnforcedStyle: double_quotes
|
10
|
+
|
11
|
+
Style/Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
RSpec/SubjectStub:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
RSpec/ExampleLength:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Layout/MultilineMethodCallIndentation:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
Layout/FirstHashElementIndentation:
|
24
|
+
EnforcedStyle: consistent
|
25
|
+
|
26
|
+
Layout/FirstArrayElementIndentation:
|
27
|
+
EnforcedStyle: consistent
|
28
|
+
|
29
|
+
RSpec/MultipleMemoizedHelpers:
|
30
|
+
Max: 10
|
31
|
+
|
32
|
+
Metrics/MethodLength:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Metrics/ClassLength:
|
36
|
+
Enabled: false
|
37
|
+
|
38
|
+
Metrics/ParameterLists:
|
39
|
+
Max: 10
|
40
|
+
|
41
|
+
|
42
|
+
Gemspec/DevelopmentDependencies:
|
43
|
+
EnforcedStyle: gemspec
|
44
|
+
|
45
|
+
plugins:
|
46
|
+
- rubocop-rake
|
47
|
+
- rubocop-rspec
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
chromedriver-binary
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-3.3.4
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Dieter S.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# chromedriver-binary
|
2
|
+
|
3
|
+
A Ruby gem that automatically downloads and installs ChromeDriver binaries that match your installed Chrome browser
|
4
|
+
version.
|
5
|
+
|
6
|
+
> Inspired by [webdrivers](https://github.com/titusfortner/webdrivers/tree/main)
|
7
|
+
|
8
|
+
## Features
|
9
|
+
|
10
|
+
- Automatically detects Chrome browser version
|
11
|
+
- Downloads the matching ChromeDriver version
|
12
|
+
- Works across Windows, macOS, and Linux platforms
|
13
|
+
- Supports environment variable configuration
|
14
|
+
- Minimal dependencies
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'chromedriver-binary'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
```shell
|
27
|
+
$ bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
Or install it yourself:
|
31
|
+
|
32
|
+
```shell
|
33
|
+
$ gem install chromedriver-binary
|
34
|
+
```
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
Simply require the gem in your code:
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
require 'chromedriver/binary'
|
42
|
+
|
43
|
+
Chromedriver::Binary.configure do |config|
|
44
|
+
# default STDOUT logger
|
45
|
+
config.logger = logger
|
46
|
+
|
47
|
+
# Proxy default nil
|
48
|
+
config.proxy_addr = 'myproxy_address.com'
|
49
|
+
config.proxy_port = '8080'
|
50
|
+
config.proxy_user = 'username'
|
51
|
+
config.proxy_pass = 'password'
|
52
|
+
|
53
|
+
# install dir
|
54
|
+
config.install_dir = "/tmp" # default "~/.webdrivers"
|
55
|
+
end
|
56
|
+
|
57
|
+
Chromedriver::Binary::ChromedriverDownloader.update force: true
|
58
|
+
|
59
|
+
Chromedriver::Binary::ChromedriverDownloader.driver_path
|
60
|
+
|
61
|
+
```
|
62
|
+
|
63
|
+
### Rake Tasks
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
|
67
|
+
require "chromedriver/binary"
|
68
|
+
|
69
|
+
load 'chromedriver/Rakefile'
|
70
|
+
```
|
71
|
+
|
72
|
+
The gem will:
|
73
|
+
|
74
|
+
1. Detect your installed Chrome version
|
75
|
+
2. Download the matching ChromeDriver binary if needed
|
76
|
+
3. Make it available in your PATH
|
77
|
+
|
78
|
+
### Configuration
|
79
|
+
|
80
|
+
Use environment variables to customize behavior:
|
81
|
+
|
82
|
+
- `CHROMEDRIVER_CHROME_PATH`: Specify the Chrome binary location
|
83
|
+
- `CHROMEDRIVER_BINARY_PATH`: Override where ChromeDriver is installed
|
84
|
+
|
85
|
+
## Development
|
86
|
+
|
87
|
+
After checking out the repo:
|
88
|
+
|
89
|
+
```shell
|
90
|
+
$ bin/setup # Install dependencies
|
91
|
+
$ rake spec # Run the tests
|
92
|
+
$ bundle exec rake install # Install the gem locally
|
93
|
+
```
|
94
|
+
|
95
|
+
## License
|
96
|
+
|
97
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
|
+
|
8
|
+
require "rubocop/rake_task"
|
9
|
+
|
10
|
+
RuboCop::RakeTask.new
|
11
|
+
|
12
|
+
task default: %i[spec rubocop]
|
13
|
+
|
14
|
+
require "chromedriver/binary"
|
15
|
+
|
16
|
+
load "chromedriver/Rakefile"
|
17
|
+
|
18
|
+
desc "Run tests with coverage"
|
19
|
+
task :coverage do
|
20
|
+
ENV["COVERAGE"] = "true"
|
21
|
+
Rake::Task["spec"].execute
|
22
|
+
puts "Coverage report generated in coverage/ directory"
|
23
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
require_relative "version_resolver"
|
5
|
+
require_relative "platform"
|
6
|
+
require_relative "downloader_helper"
|
7
|
+
require_relative "system_helper"
|
8
|
+
|
9
|
+
module Chromedriver
|
10
|
+
module Binary
|
11
|
+
class ChromedriverDownloader
|
12
|
+
class << self
|
13
|
+
include VersionResolver
|
14
|
+
include Platform
|
15
|
+
include DownloaderHelper
|
16
|
+
include SystemHelper
|
17
|
+
|
18
|
+
# Define where to install ChromeDriver.
|
19
|
+
def install_dir
|
20
|
+
Chromedriver::Binary.install_dir
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the absolute path to the driver_path ChromeDriver binary.
|
24
|
+
def driver_path
|
25
|
+
File.absolute_path File.join(install_dir, file_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Downloads and extracts the latest ChromeDriver.
|
29
|
+
#
|
30
|
+
# @return [String] the full path to the downloaded executable.
|
31
|
+
# rubocop:disable Metrics/AbcSize
|
32
|
+
def update(force: false)
|
33
|
+
return driver_path if up_to_date_binary?(force)
|
34
|
+
|
35
|
+
version = latest_patch_version_for_build
|
36
|
+
zip_filename = "chromedriver_#{platform_id}.zip"
|
37
|
+
zip_path = File.join(install_dir, zip_filename)
|
38
|
+
download_url = direct_url_from_api(version, driver_filename)
|
39
|
+
|
40
|
+
prepare_install_dir
|
41
|
+
log_download_start(version, download_url)
|
42
|
+
|
43
|
+
download_file(download_url, zip_path)
|
44
|
+
extract_zip(zip_path, install_dir)
|
45
|
+
make_executable(driver_path)
|
46
|
+
cleanup_zip(zip_path)
|
47
|
+
|
48
|
+
Chromedriver::Binary.logger.debug "ChromeDriver downloaded and extracted to #{install_dir}"
|
49
|
+
|
50
|
+
driver_path
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubocop:enable Metrics/AbcSize
|
54
|
+
|
55
|
+
def correct_binary?
|
56
|
+
current_installed_version == browser_version || current_installed_version == latest_patch_version_for_build
|
57
|
+
rescue ConnectionError, VersionError
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def up_to_date_binary?(force)
|
64
|
+
if correct_binary? && !force
|
65
|
+
Chromedriver::Binary.logger.debug "A working webdriver version is already on the system"
|
66
|
+
true
|
67
|
+
else
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def prepare_install_dir
|
73
|
+
FileUtils.mkdir_p(install_dir)
|
74
|
+
end
|
75
|
+
|
76
|
+
def log_download_start(version, url)
|
77
|
+
Chromedriver::Binary.logger.debug(
|
78
|
+
"Downloading ChromeDriver version #{version} for #{platform_id} from #{url}..."
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def make_executable(path)
|
83
|
+
FileUtils.chmod "ugo+rx", path
|
84
|
+
Chromedriver::Binary.logger.debug "Completed download and processing of #{path}"
|
85
|
+
end
|
86
|
+
|
87
|
+
def cleanup_zip(zip_path)
|
88
|
+
File.delete(zip_path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Chromedriver
|
4
|
+
module Binary
|
5
|
+
class DownloadProgress
|
6
|
+
def initialize(logger:, step_bytes: 1 * 1024 * 1024)
|
7
|
+
# Default: 1 MB
|
8
|
+
@logger = logger
|
9
|
+
@step_bytes = step_bytes
|
10
|
+
@total_bytes = 0
|
11
|
+
@next_log_at = step_bytes
|
12
|
+
end
|
13
|
+
|
14
|
+
def track(chunk_size)
|
15
|
+
@total_bytes += chunk_size
|
16
|
+
|
17
|
+
return unless @total_bytes >= @next_log_at
|
18
|
+
|
19
|
+
mb = (@total_bytes.to_f / (1024 * 1024)).round(2)
|
20
|
+
@logger.debug("Downloaded ~#{mb} MB 📦")
|
21
|
+
@next_log_at += @step_bytes
|
22
|
+
end
|
23
|
+
|
24
|
+
def finish
|
25
|
+
mb = (@total_bytes.to_f / (1024 * 1024)).round(2)
|
26
|
+
@logger.info("Download complete! ✅ Total: #{mb} MB")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "uri"
|
5
|
+
require "zip"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
require_relative "download_progress"
|
9
|
+
|
10
|
+
module Chromedriver
|
11
|
+
module Binary
|
12
|
+
module DownloaderHelper
|
13
|
+
def download_file(url, destination)
|
14
|
+
uri = URI.parse(url)
|
15
|
+
logger = Chromedriver::Binary.logger
|
16
|
+
http = create_http(uri)
|
17
|
+
|
18
|
+
logger.debug("Saving to: #{destination}")
|
19
|
+
|
20
|
+
exec_get_request(http, uri) do |response|
|
21
|
+
handle_unsuccessful_response(response)
|
22
|
+
write_response_to_file(response, destination, logger)
|
23
|
+
end
|
24
|
+
rescue StandardError => e
|
25
|
+
logger.debug("Error: #{e.message}")
|
26
|
+
raise "Error downloading file: #{e.message}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def exec_get_request(http, uri, &block)
|
30
|
+
request = Net::HTTP::Get.new(uri)
|
31
|
+
Chromedriver::Binary.logger.debug("Sending HTTP GET request...")
|
32
|
+
|
33
|
+
http.request(request, &block)
|
34
|
+
end
|
35
|
+
|
36
|
+
def extract_zip(zip_file, destination)
|
37
|
+
Zip::File.open(zip_file) do |zip|
|
38
|
+
zip.each do |entry|
|
39
|
+
# Extract all files as top-level (ignoring any folder structure)
|
40
|
+
destination_file = File.join(destination, File.basename(entry.name))
|
41
|
+
entry.extract(destination_file) { true }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
rescue StandardError => e
|
45
|
+
raise "Error extracting ZIP file: #{e.message}"
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def handle_unsuccessful_response(response)
|
51
|
+
return if response.is_a?(Net::HTTPSuccess)
|
52
|
+
|
53
|
+
raise "Download failed: #{response.code} #{response.message}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def write_response_to_file(response, destination, logger)
|
57
|
+
progress = DownloadProgress.new(logger: logger)
|
58
|
+
|
59
|
+
File.open(destination, "wb") do |file|
|
60
|
+
response.read_body do |chunk|
|
61
|
+
file.write(chunk)
|
62
|
+
progress.track(chunk.bytesize)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
progress.finish
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_http(uri)
|
70
|
+
logger = Chromedriver::Binary.logger
|
71
|
+
|
72
|
+
logger.debug("Starting download from: #{uri}")
|
73
|
+
|
74
|
+
Net::HTTP.new(
|
75
|
+
uri.host,
|
76
|
+
uri.port,
|
77
|
+
Chromedriver::Binary.proxy_addr,
|
78
|
+
Chromedriver::Binary.proxy_port,
|
79
|
+
Chromedriver::Binary.proxy_user,
|
80
|
+
Chromedriver::Binary.proxy_pass
|
81
|
+
).tap do |http|
|
82
|
+
http.use_ssl = uri.scheme == "https"
|
83
|
+
|
84
|
+
log_proxy_info(http, logger)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def log_proxy_info(http, logger)
|
89
|
+
if http.proxy?
|
90
|
+
source = http.proxy_from_env? ? "environment" : "Chromedriver::Binary"
|
91
|
+
logger.debug("Using proxy from #{source}:")
|
92
|
+
logger.debug("Proxy address: #{http.proxy_address}")
|
93
|
+
logger.debug("Proxy port: #{http.proxy_port}")
|
94
|
+
|
95
|
+
return
|
96
|
+
end
|
97
|
+
|
98
|
+
logger.debug("No proxy is being used.")
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "platform"
|
4
|
+
require_relative "system_helper"
|
5
|
+
|
6
|
+
module Chromedriver
|
7
|
+
module Binary
|
8
|
+
class Finder
|
9
|
+
include Platform
|
10
|
+
include SystemHelper
|
11
|
+
|
12
|
+
def version
|
13
|
+
version = send("#{platform}_version", location)
|
14
|
+
raise VersionError, "Failed to determine Chrome version." if version.nil? || version.empty?
|
15
|
+
|
16
|
+
Chromedriver::Binary.logger.debug "Browser version: #{version}"
|
17
|
+
version[/\d+\.\d+\.\d+\.\d+/]
|
18
|
+
end
|
19
|
+
|
20
|
+
def location
|
21
|
+
if chrome_bin_from_env
|
22
|
+
Chromedriver::Binary.logger.debug "CHROMEDRIVER_CHROME_PATH: #{chrome_bin_from_env}"
|
23
|
+
|
24
|
+
return chrome_bin_from_env
|
25
|
+
end
|
26
|
+
|
27
|
+
send("#{platform}_location").tap do |chrome_bin|
|
28
|
+
raise Chromedriver::Binary::BrowserNotFound, "Failed to determine Chrome binary location." unless chrome_bin
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def chrome_bin_from_env
|
35
|
+
ENV.fetch("CHROMEDRIVER_CHROME_PATH", nil)
|
36
|
+
end
|
37
|
+
|
38
|
+
def find_in_paths(directories, files)
|
39
|
+
directories.each do |dir|
|
40
|
+
files.each do |file|
|
41
|
+
path = File.join(dir, file)
|
42
|
+
return path if file_exists?(path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def file_exists?(path)
|
49
|
+
File.exist?(path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def win_location
|
53
|
+
find_in_paths(%w[LOCALAPPDATA PROGRAMFILES PROGRAMFILES(X86)],
|
54
|
+
['\\Google\\Chrome\\Application\\chrome.exe', '\\Chromium\\Application\\chrome.exe'])
|
55
|
+
end
|
56
|
+
|
57
|
+
def mac_location
|
58
|
+
find_in_paths(["", File.expand_path("~")],
|
59
|
+
["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
60
|
+
"/Applications/Chromium.app/Contents/MacOS/Chromium"])
|
61
|
+
end
|
62
|
+
|
63
|
+
def linux_location
|
64
|
+
find_in_paths(
|
65
|
+
%w[/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin /snap/bin /opt/google/chrome
|
66
|
+
/opt/chromium.org/chromium], %w[google-chrome chrome chromium chromium-browser]
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def win_version(location)
|
71
|
+
call("powershell.exe \"(Get-ItemProperty '#{location}').VersionInfo.ProductVersion\"")&.strip
|
72
|
+
end
|
73
|
+
|
74
|
+
def linux_version(location)
|
75
|
+
call(location, "--product-version")&.strip
|
76
|
+
end
|
77
|
+
|
78
|
+
def mac_version(location)
|
79
|
+
call(location, "--version")&.strip
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rbconfig"
|
4
|
+
|
5
|
+
module Chromedriver
|
6
|
+
module Binary
|
7
|
+
module Platform
|
8
|
+
def platform
|
9
|
+
case RbConfig::CONFIG["host_os"].downcase
|
10
|
+
when /linux/ then "linux"
|
11
|
+
when /darwin/ then "mac"
|
12
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/ then "win"
|
13
|
+
else
|
14
|
+
raise NotImplementedError, "Your OS is not supported by this gem."
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def platform_id
|
19
|
+
case platform
|
20
|
+
when /mac/
|
21
|
+
"mac64"
|
22
|
+
when /linux/
|
23
|
+
if File.exist?("/proc/version") && File.read("/proc/version").include?("Microsoft")
|
24
|
+
"win32"
|
25
|
+
else
|
26
|
+
"linux64"
|
27
|
+
end
|
28
|
+
when /win/
|
29
|
+
"win32"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def file_name
|
34
|
+
platform_id == "win32" ? "chromedriver.exe" : "chromedriver"
|
35
|
+
end
|
36
|
+
|
37
|
+
def driver_filename
|
38
|
+
platform = platform_id
|
39
|
+
case platform
|
40
|
+
when "win32"
|
41
|
+
"win32"
|
42
|
+
when "linux64"
|
43
|
+
"linux64"
|
44
|
+
when "mac64"
|
45
|
+
# If you're on an Apple Silicon Mac, check Ruby platform for arm details.
|
46
|
+
if RUBY_PLATFORM.include?("arm64-darwin")
|
47
|
+
"mac-arm64"
|
48
|
+
else
|
49
|
+
"mac-x64"
|
50
|
+
end
|
51
|
+
else
|
52
|
+
raise "Failed to determine driver filename to download for your OS."
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
module Chromedriver
|
6
|
+
module Binary
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
railtie_name :chromedriver_binary
|
9
|
+
|
10
|
+
rake_tasks do
|
11
|
+
path = File.expand_path(__dir__)
|
12
|
+
Dir.glob("#{path}/tasks/*.rake").each { |f| load f }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Chromedriver
|
6
|
+
module Binary
|
7
|
+
module SystemHelper
|
8
|
+
def call(process, arg = nil)
|
9
|
+
cmd = arg ? [process, arg] : process
|
10
|
+
Chromedriver::Binary.logger.debug "making System call: #{cmd}"
|
11
|
+
|
12
|
+
output, status = capture(*cmd)
|
13
|
+
|
14
|
+
raise "Failed to make system call: #{cmd}" unless status.success?
|
15
|
+
|
16
|
+
Chromedriver::Binary.logger.debug "System call returned: #{output}"
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
def exists?(file)
|
21
|
+
File.exist?(file).tap do |result|
|
22
|
+
Chromedriver::Binary.logger.debug "#{file} is#{" not" unless result} already downloaded"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def capture(*cmd)
|
29
|
+
Open3.capture2(*cmd)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :chromedriver do
|
4
|
+
namespace :binary do
|
5
|
+
require "chromedriver/binary"
|
6
|
+
|
7
|
+
Chromedriver::Binary.logger.level = :info
|
8
|
+
|
9
|
+
desc "Print current chromedriver version"
|
10
|
+
task :version do
|
11
|
+
gem_ver = Chromedriver::Binary::ChromedriverDownloader.current_installed_version
|
12
|
+
if gem_ver
|
13
|
+
Chromedriver::Binary.logger.info "chromedriver #{gem_ver.version}"
|
14
|
+
else
|
15
|
+
Chromedriver::Binary.logger.warn "No existing chromedriver found."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Remove and download updated chromedriver if necessary"
|
20
|
+
task :update do
|
21
|
+
Chromedriver::Binary::ChromedriverDownloader.update force: true
|
22
|
+
# rubocop:disable Layout/LineLength
|
23
|
+
Chromedriver::Binary.logger.info "Updated to chromedriver #{Chromedriver::Binary::ChromedriverDownloader.current_installed_version}"
|
24
|
+
# rubocop:enable Layout/LineLength
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open-uri"
|
4
|
+
require "json"
|
5
|
+
require_relative "finder"
|
6
|
+
require_relative "system_helper"
|
7
|
+
require_relative "downloader_helper"
|
8
|
+
|
9
|
+
module Chromedriver
|
10
|
+
module Binary
|
11
|
+
module VersionResolver
|
12
|
+
include SystemHelper
|
13
|
+
include DownloaderHelper
|
14
|
+
|
15
|
+
BASE_URL = "https://chromedriver.storage.googleapis.com"
|
16
|
+
CHROME_FOR_TESTING_BASE_URL = "https://googlechromelabs.github.io/chrome-for-testing/"
|
17
|
+
|
18
|
+
def normalize_version(version)
|
19
|
+
Gem::Version.new(version.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def browser_build_version
|
23
|
+
normalize_version(browser_version.segments[0..2].join("."))
|
24
|
+
end
|
25
|
+
|
26
|
+
def latest_patch_version_for_build
|
27
|
+
data = fetch_data("latest-patch-versions-per-build.json")
|
28
|
+
patch_version = data.dig(:builds, browser_build_version.to_s.to_sym, :version)
|
29
|
+
raise "No patch version found for build #{browser_build_version}" unless patch_version
|
30
|
+
|
31
|
+
patch_version
|
32
|
+
rescue StandardError => e
|
33
|
+
raise "Error fetching latest patch version: #{e.message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [_ToS] driver_version
|
37
|
+
def direct_url_from_api(driver_version, driver_filename)
|
38
|
+
data = fetch_data("known-good-versions-with-downloads.json")
|
39
|
+
version_data = data[:versions].find { |e| e[:version] == driver_version.to_s }
|
40
|
+
platform = version_data.dig(:downloads, :chromedriver).find { |e| e[:platform] == driver_filename }
|
41
|
+
platform[:url]
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Returns current chromedriver version.
|
46
|
+
#
|
47
|
+
# @return [Gem::Version]
|
48
|
+
def current_installed_version
|
49
|
+
Chromedriver::Binary.logger.debug "Checking current version"
|
50
|
+
return nil unless exists?(driver_path)
|
51
|
+
|
52
|
+
version = query_binary_version
|
53
|
+
return nil if version.nil?
|
54
|
+
|
55
|
+
# Matches 2.46, 2.46.628411 and 73.0.3683.75
|
56
|
+
normalize_version version[/\d+\.\d+(\.\d+)?(\.\d+)?/]
|
57
|
+
end
|
58
|
+
|
59
|
+
def driver_path
|
60
|
+
raise "driver_path not defined"
|
61
|
+
end
|
62
|
+
|
63
|
+
def query_binary_version
|
64
|
+
version = call(driver_path, "--version")
|
65
|
+
Chromedriver::Binary.logger.debug "Current version of #{driver_path} is #{version}"
|
66
|
+
version
|
67
|
+
rescue Errno::ENOENT
|
68
|
+
Chromedriver::Binary.logger.debug "No Such File or Directory: #{driver_path}"
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def fetch_data(json_file)
|
75
|
+
uri = URI.join(CHROME_FOR_TESTING_BASE_URL, json_file)
|
76
|
+
http = create_http(uri)
|
77
|
+
response = nil
|
78
|
+
|
79
|
+
exec_get_request(http, uri) do |resp|
|
80
|
+
response = resp.body
|
81
|
+
end
|
82
|
+
|
83
|
+
JSON.parse(response, symbolize_names: true)
|
84
|
+
end
|
85
|
+
|
86
|
+
def browser_version
|
87
|
+
normalize_version Chromedriver::Binary::Finder.new.version
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "binary/version"
|
4
|
+
require_relative "binary/chromedriver_downloader"
|
5
|
+
require_relative "binary/railtie" if defined?(Rails)
|
6
|
+
require "logger"
|
7
|
+
|
8
|
+
module Chromedriver
|
9
|
+
module Binary
|
10
|
+
class Error < StandardError; end
|
11
|
+
|
12
|
+
class VersionError < Error; end
|
13
|
+
|
14
|
+
class BrowserNotFound < Error; end
|
15
|
+
|
16
|
+
DEFAULT_INSTALL_DIR = File.expand_path("~/.webdrivers")
|
17
|
+
|
18
|
+
@logger = Logger.new($stdout)
|
19
|
+
@logger.level = Logger::DEBUG
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :logger, :proxy_addr, :proxy_port, :proxy_user, :proxy_pass
|
23
|
+
attr_writer :install_dir
|
24
|
+
|
25
|
+
#
|
26
|
+
# Returns the install (download) directory path for the drivers.
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
def install_dir
|
30
|
+
@install_dir ||= ENV["CHROMEDRIVER_INSTALL_DIR"] || DEFAULT_INSTALL_DIR
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Provides a convenient way to configure the gem.
|
35
|
+
#
|
36
|
+
# @example Configure proxy and cache_time
|
37
|
+
# Chromedriver::Binary.configure do |config|
|
38
|
+
# config.proxy_addr = 'myproxy_address.com'
|
39
|
+
# config.proxy_port = '8080'
|
40
|
+
# config.proxy_user = 'username'
|
41
|
+
# config.proxy_pass = 'password'
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
def configure
|
45
|
+
yield self if block_given?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Chromedriver
|
2
|
+
module Binary
|
3
|
+
@logger: Logger
|
4
|
+
|
5
|
+
@install_dir: String
|
6
|
+
|
7
|
+
DEFAULT_INSTALL_DIR: String
|
8
|
+
VERSION: String
|
9
|
+
|
10
|
+
attr_writer self.install_dir: String
|
11
|
+
attr_accessor self.logger: Logger
|
12
|
+
|
13
|
+
attr_accessor self.proxy_addr: String
|
14
|
+
|
15
|
+
attr_accessor self.proxy_port: Integer
|
16
|
+
|
17
|
+
attr_accessor self.proxy_user: String
|
18
|
+
|
19
|
+
attr_accessor self.proxy_pass: String
|
20
|
+
|
21
|
+
#
|
22
|
+
# Returns the install (download) directory path for the drivers.
|
23
|
+
#
|
24
|
+
# @return [String]
|
25
|
+
def self.install_dir: () -> String
|
26
|
+
|
27
|
+
#
|
28
|
+
# Provides a convenient way to configure the gem.
|
29
|
+
#
|
30
|
+
# @example Configure proxy and cache_time
|
31
|
+
# Chromedriver::Binary.configure do |config|
|
32
|
+
# config.proxy_addr = 'myproxy_address.com'
|
33
|
+
# config.proxy_port = '8080'
|
34
|
+
# config.proxy_user = 'username'
|
35
|
+
# config.proxy_pass = 'password'
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
def self.configure: () { (untyped) -> untyped } -> untyped
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Chromedriver
|
2
|
+
module Binary
|
3
|
+
class ChromedriverDownloader
|
4
|
+
extend VersionResolver
|
5
|
+
extend Platform
|
6
|
+
extend DownloaderHelper
|
7
|
+
extend SystemHelper
|
8
|
+
|
9
|
+
def self.install_dir: () -> String
|
10
|
+
|
11
|
+
def self.driver_path: () -> String
|
12
|
+
|
13
|
+
def self.update: (?force: bool) -> String
|
14
|
+
|
15
|
+
def self.correct_binary?: () -> bool
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Chromedriver
|
2
|
+
module Binary
|
3
|
+
class DownloadProgress
|
4
|
+
def initialize: (
|
5
|
+
logger: untyped, # Typically Logger, but untyped for flexibility
|
6
|
+
step_bytes: Integer
|
7
|
+
) -> void
|
8
|
+
|
9
|
+
def track: (Integer) -> void
|
10
|
+
|
11
|
+
def finish: () -> void
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: chromedriver-binary
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dieter S.
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-04-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubyzip
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.21'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.21'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.5'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.5'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.22'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.22'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: webrick
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.9'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.9'
|
125
|
+
description: |
|
126
|
+
A Ruby gem that automatically downloads and installs ChromeDriver binaries that match your installed
|
127
|
+
Chrome browser version.
|
128
|
+
email:
|
129
|
+
- 101627195+dieter-medium@users.noreply.github.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- ".idea/.gitignore"
|
135
|
+
- ".idea/inspectionProfiles/profiles_settings.xml"
|
136
|
+
- ".idea/vcs.xml"
|
137
|
+
- ".rspec"
|
138
|
+
- ".rubocop.yml"
|
139
|
+
- ".ruby-gemset"
|
140
|
+
- ".ruby-version"
|
141
|
+
- CHANGELOG.md
|
142
|
+
- LICENSE.txt
|
143
|
+
- README.md
|
144
|
+
- Rakefile
|
145
|
+
- lib/chromedriver/Rakefile
|
146
|
+
- lib/chromedriver/binary.rb
|
147
|
+
- lib/chromedriver/binary/chromedriver_downloader.rb
|
148
|
+
- lib/chromedriver/binary/download_progress.rb
|
149
|
+
- lib/chromedriver/binary/downloader_helper.rb
|
150
|
+
- lib/chromedriver/binary/finder.rb
|
151
|
+
- lib/chromedriver/binary/platform.rb
|
152
|
+
- lib/chromedriver/binary/railtie.rb
|
153
|
+
- lib/chromedriver/binary/system_helper.rb
|
154
|
+
- lib/chromedriver/binary/tasks/chromedriver.rake
|
155
|
+
- lib/chromedriver/binary/version.rb
|
156
|
+
- lib/chromedriver/binary/version_resolver.rb
|
157
|
+
- sig/chromedriver/binary/binary.rbs
|
158
|
+
- sig/chromedriver/binary/chromedriver_downloader.rbs
|
159
|
+
- sig/chromedriver/binary/download_progress.rbs
|
160
|
+
- sig/chromedriver/binary/downloader_helper.rbs
|
161
|
+
- sig/chromedriver/binary/platform.rbs
|
162
|
+
- sig/chromedriver/binary/system_helper.rbs
|
163
|
+
- sig/chromedriver/binary/version_resolver.rbs
|
164
|
+
homepage: https://github.com/dieter-medium/chromedriver-binary
|
165
|
+
licenses:
|
166
|
+
- MIT
|
167
|
+
metadata:
|
168
|
+
allowed_push_host: https://rubygems.org
|
169
|
+
homepage_uri: https://github.com/dieter-medium/chromedriver-binary
|
170
|
+
source_code_uri: https://github.com/dieter-medium/chromedriver-binary
|
171
|
+
changelog_uri: https://github.com/dieter-medium/chromedriver-binary/blob/master/CHANGELOG.md
|
172
|
+
rubygems_mfa_required: 'true'
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 3.0.0
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubygems_version: 3.5.11
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: Automatically downloads and installs ChromeDriver binaries.
|
192
|
+
test_files: []
|