| # frozen_string_literal: true |
| |
| require 'digest' |
| require 'net/http' |
| |
| def ruby_version |
| File.foreach('rb/lib/selenium/webdriver/version.rb') do |line| |
| return line.split('=').last.strip.tr("'", '') if line.include?('VERSION') |
| end |
| end |
| |
| def setup_gem_credentials |
| gem_dir = File.join(Dir.home, '.gem') |
| credentials = File.join(gem_dir, 'credentials') |
| return if File.exist?(credentials) && File.read(credentials).include?(':rubygems_api_key:') |
| |
| token = ENV.fetch('GEM_HOST_API_KEY', nil) |
| if token.nil? || token.empty? |
| raise 'Missing RubyGems credentials: set GEM_HOST_API_KEY or configure ~/.gem/credentials' |
| end |
| |
| FileUtils.mkdir_p(gem_dir) |
| if File.exist?(credentials) |
| File.open(credentials, 'a') { |f| f.puts(":rubygems_api_key: #{token}") } |
| else |
| File.write(credentials, ":rubygems_api_key: #{token}\n") |
| end |
| File.chmod(0o600, credentials) |
| end |
| |
| desc 'Generate Ruby gems' |
| task :build do |_task, arguments| |
| args = arguments.to_a |
| webdriver = args.delete('webdriver') |
| devtools = args.delete('devtools') |
| |
| Bazel.execute('build', args, '//rb:selenium-webdriver') if webdriver || !devtools |
| Bazel.execute('build', args, '//rb:selenium-devtools') if devtools || !webdriver |
| end |
| |
| desc 'Update generated Ruby files for local development' |
| task :local_dev do |
| puts 'installing ruby, this may take a minute' |
| Bazel.execute('build', [], '@bundle//:bundle') |
| Rake::Task['rb:build'].invoke |
| Rake::Task['grid'].invoke |
| # A command like this is required to move ruby binary into working directory |
| Bazel.execute('build', %w[--test_arg --dry-run], '@bundle//bin:rubocop') |
| end |
| |
| desc 'Validate Ruby release credentials' |
| task :check_credentials do |_task, arguments| |
| nightly = arguments.to_a.include?('nightly') |
| next if nightly |
| |
| credentials = File.join(Dir.home, '.gem', 'credentials') |
| has_file = File.exist?(credentials) && File.read(credentials).include?(':rubygems_api_key:') |
| has_env = ENV.fetch('GEM_HOST_API_KEY', nil) && !ENV['GEM_HOST_API_KEY'].empty? |
| raise 'Missing RubyGems credentials: set GEM_HOST_API_KEY or configure ~/.gem/credentials' unless has_file || has_env |
| end |
| |
| desc 'Push Ruby gems to rubygems' |
| task :release do |_task, arguments| |
| nightly = arguments.to_a.include?('nightly') |
| Rake::Task['rb:check_credentials'].invoke(*arguments.to_a) |
| |
| if nightly |
| if ENV.fetch('GITHUB_TOKEN', '').empty? |
| raise 'Missing GitHub Packages token: set GITHUB_TOKEN for nightly Ruby publish' |
| end |
| |
| ENV['GEM_HOST_API_KEY'] = "Bearer #{ENV.fetch('GITHUB_TOKEN', nil)}" |
| |
| puts 'Bumping Ruby nightly version...' |
| Bazel.execute('run', [], '//rb:selenium-webdriver-bump-nightly-version') |
| |
| puts 'Releasing nightly WebDriver gem...' |
| Bazel.execute('run', ['--config=release'], '//rb:selenium-webdriver-release-nightly') |
| else |
| setup_gem_credentials |
| patch_release = ruby_version.split('.').fetch(2, '0').to_i.positive? |
| |
| puts 'Releasing Ruby gems...' |
| Bazel.execute('run', ['--config=release'], '//rb:selenium-webdriver-release') |
| Bazel.execute('run', ['--config=release'], '//rb:selenium-devtools-release') unless patch_release |
| end |
| end |
| |
| desc 'Verify Ruby packages are published on RubyGems' |
| task :verify do |
| patch_release = ruby_version.split('.').fetch(2, '0').to_i.positive? |
| |
| SeleniumRake.verify_package_published("https://rubygems.org/api/v2/rubygems/selenium-webdriver/versions/#{ruby_version}.json") |
| unless patch_release |
| SeleniumRake.verify_package_published("https://rubygems.org/api/v2/rubygems/selenium-devtools/versions/#{ruby_version}.json") |
| end |
| end |
| |
| desc 'Generate and stage Ruby documentation' |
| task :docs do |_task, arguments| |
| if ruby_version.include?('nightly') && !arguments.to_a.include?('force') |
| abort('Aborting documentation update: nightly versions should not update docs.') |
| end |
| |
| Rake::Task['rb:docs_generate'].invoke |
| |
| FileUtils.mkdir_p('build/docs/api') |
| FileUtils.cp_r('bazel-bin/rb/docs.sh.runfiles/_main/docs/api/rb/.', 'build/docs/api/rb') |
| end |
| |
| desc 'Generate Ruby documentation without staging' |
| task :docs_generate do |
| puts 'Generating Ruby documentation' |
| FileUtils.rm_rf('build/docs/api/rb/') |
| Bazel.execute('run', [], '//rb:docs') |
| end |
| |
| desc 'Install Ruby gem locally' |
| task :install do |
| Bazel.execute('build', [], '//rb:selenium-webdriver') |
| Dir.glob('bazel-bin/rb/selenium-webdriver-*.gem').each do |gem| |
| sh 'gem', 'install', gem |
| end |
| end |
| |
| desc 'Update Ruby changelog' |
| task :changelogs do |
| header = "#{ruby_version} (#{Time.now.strftime('%Y-%m-%d')})\n=========================" |
| SeleniumRake.update_changelog(ruby_version, 'rb', 'rb/lib/', 'rb/CHANGES', header) |
| end |
| |
| desc 'Update Ruby version' |
| task :version, [:version] do |_task, arguments| |
| old_version = ruby_version |
| new_version = SeleniumRake.updated_version(old_version, arguments[:version], '.nightly') |
| puts "Updating Ruby from #{old_version} to #{new_version}" |
| |
| file = 'rb/lib/selenium/webdriver/version.rb' |
| text = File.read(file).gsub(old_version, new_version) |
| File.open(file, 'w') { |f| f.puts text } |
| end |
| |
| desc 'Format Ruby code with rubocop (safe auto-correct only)' |
| task :format do |
| puts ' Running rubocop (safe auto-correct)...' |
| Bazel.execute('run', ['--', '-a', '--fail-level', 'F'], '//rb:rubocop') |
| end |
| |
| desc 'Run Ruby linters (rubocop, steep, docs)' |
| task :lint do |
| puts ' Running rubocop...' |
| Bazel.execute('run', ['--', '-a'], '//rb:rubocop') |
| puts ' Running steep type checker...' |
| Bazel.execute('run', [], '//rb:steep') |
| Rake::Task['rb:docs_generate'].invoke |
| end |
| |
| desc 'Sync gem checksums from Gemfile.lock to MODULE.bazel (use force to re-download all)' |
| task :pin, [:force] do |_task, arguments| |
| gemfile_lock = 'rb/Gemfile.lock' |
| module_bazel = 'MODULE.bazel' |
| force = arguments[:force] == 'force' |
| |
| lock_content = File.read(gemfile_lock) |
| gem_section = lock_content[/GEM\n\s+remote:.*?\n\s+specs:\n(.*?)(?=\n[A-Z]|\Z)/m, 1] |
| gems = gem_section.scan(/^ ([a-zA-Z0-9_-]+) \(([^)]+)\)$/) |
| needed_gems = gems.map { |name, version| "#{name}-#{version}" } |
| |
| # Parse existing checksums from MODULE.bazel |
| module_content = File.read(module_bazel) |
| existing = module_content.scan(/"([^"]+)":\s*"([a-f0-9]{64})"/).to_h |
| |
| # Keep existing checksums for gems still in Gemfile.lock (unless force) |
| checksums = force ? {} : existing.slice(*needed_gems) |
| to_download = needed_gems - checksums.keys |
| |
| puts "Found #{gems.size} gems: #{checksums.size} cached, #{to_download.size} to download..." |
| |
| failed = [] |
| to_download.each do |key| |
| uri = URI("https://rubygems.org/gems/#{key}.gem") |
| response = nil |
| |
| 5.times do |
| response = Net::HTTP.get_response(uri) |
| break unless response.is_a?(Net::HTTPRedirection) |
| |
| uri = URI(response['location']) |
| end |
| |
| unless response.is_a?(Net::HTTPSuccess) |
| puts " #{key}: failed (HTTP #{response.code})" |
| failed << key |
| next |
| end |
| |
| sha = Digest::SHA256.hexdigest(response.body) |
| checksums[key] = sha |
| puts " #{key}: #{sha[0, 16]}..." |
| rescue StandardError => e |
| puts " #{key}: failed (#{e.message})" |
| failed << key |
| end |
| |
| raise "Failed to download checksums for: #{failed.join(', ')}" if failed.any? |
| |
| checksums_lines = checksums.keys.sort.map { |k| " \"#{k}\": \"#{checksums[k]}\"," } |
| formatted = " gem_checksums = {\n#{checksums_lines.join("\n")}\n }," |
| |
| new_content = module_content.sub(/ gem_checksums = \{[^}]+\},/m, formatted) |
| File.write(module_bazel, new_content) |
| end |
| |
| desc 'Update Ruby dependencies and sync checksums to MODULE.bazel' |
| task :update do |
| puts 'updating and pinning gem versions' |
| Bazel.execute('run', [], '//rb:bundle-update') |
| Bazel.execute('run', [], '//rb:rbs-update') |
| Rake::Task['rb:pin'].invoke |
| end |