| # frozen_string_literal: true | |
| require 'English' | |
| $LOAD_PATH.unshift File.expand_path('.') | |
| require 'rake' | |
| require 'net/telnet' | |
| require 'stringio' | |
| require 'fileutils' | |
| require 'open-uri' | |
| require 'git' | |
| require 'find' | |
| Rake.application.instance_variable_set(:@name, 'go') | |
| orig_verbose = verbose | |
| verbose(false) | |
| # The CrazyFun build grammar. There's no magic here, just ruby | |
| require 'rake_tasks/crazy_fun/main' | |
| require 'rake_tasks/selenium_rake/detonating_handler' | |
| require 'rake_tasks/selenium_rake/crazy_fun' | |
| # The CrazyFun builders - Most of these are either partially or fully obsolete | |
| # Note the order here is important - The top 2 are used in inheritance chains | |
| require 'rake_tasks/crazy_fun/mappings/file_copy_hack' | |
| require 'rake_tasks/crazy_fun/mappings/tasks' | |
| require 'rake_tasks/crazy_fun/mappings/rake_mappings' | |
| # Location of all new (non-CrazyFun) methods | |
| require 'rake_tasks/selenium_rake/browsers' | |
| require 'rake_tasks/selenium_rake/checks' | |
| require 'rake_tasks/selenium_rake/cpp_formatter' | |
| require 'rake_tasks/selenium_rake/ie_generator' | |
| require 'rake_tasks/selenium_rake/java_formatter' | |
| require 'rake_tasks/selenium_rake/type_definitions_generator' | |
| # Our modifications to the Rake / Bazel libraries | |
| require 'rake/task' | |
| require 'rake_tasks/rake/task' | |
| require 'rake_tasks/rake/dsl' | |
| require 'rake_tasks/bazel/task' | |
| # These are the final items mixed into the global NS | |
| # These need moving into correct namespaces, and not be globally included | |
| require 'rake_tasks/bazel' | |
| require 'rake_tasks/python' | |
| $DEBUG = orig_verbose != Rake::FileUtilsExt::DEFAULT | |
| $DEBUG = true if ENV['debug'] == 'true' | |
| verbose($DEBUG) | |
| @git = Git.open(__dir__) | |
| def java_version | |
| File.foreach('java/version.bzl') do |line| | |
| return line.split('=').last.strip.tr('"', '') if line.include?('SE_VERSION') | |
| end | |
| end | |
| # The build system used by webdriver is layered on top of rake, and we call it | |
| # "crazy fun" for no readily apparent reason. | |
| # First off, create a new CrazyFun object. | |
| crazy_fun = SeleniumRake::CrazyFun.new | |
| # Secondly, we add the handlers, which are responsible for turning a build | |
| # rule into a (series of) rake tasks. For example if we're looking at a file | |
| # in subdirectory "subdir" contains the line: | |
| # | |
| # java_library(:name => "example", :srcs => ["foo.java"]) | |
| # | |
| # we would generate a rake target of "//subdir:example" which would generate | |
| # a Java JAR at "build/subdir/example.jar". | |
| # | |
| # If crazy fun doesn't know how to handle a particular output type ("java_library" | |
| # in the example above) then it will throw an exception, stopping the build | |
| CrazyFun::Mappings::RakeMappings.new.add_all(crazy_fun) | |
| # Finally, find every file named "build.desc" in the project, and generate | |
| # rake tasks from them. These tasks are normal rake tasks, and can be invoked | |
| # from rake. | |
| # FIXME: the rules for the targets were removed and build files won't load | |
| # crazy_fun.create_tasks(Dir['**/build.desc']) | |
| # If it looks like a bazel target, build it with bazel | |
| rule(%r{//.*}) do |task| | |
| task.out = Bazel.execute('build', %w[], task.name) | |
| end | |
| # Spoof tasks to get CI working with bazel | |
| task '//java/test/org/openqa/selenium/environment/webserver:webserver:uber' => [ | |
| '//java/test/org/openqa/selenium/environment:webserver' | |
| ] | |
| # Java targets required for release. These should all be java_export targets. | |
| # Generated from: bazel query 'kind(maven_publish, set(//java/... //third_party/...))' | sort | |
| JAVA_RELEASE_TARGETS = %w[ | |
| //java/src/org/openqa/selenium/chrome:chrome.publish | |
| //java/src/org/openqa/selenium/chromium:chromium.publish | |
| //java/src/org/openqa/selenium/devtools/v125:v125.publish | |
| //java/src/org/openqa/selenium/devtools/v123:v123.publish | |
| //java/src/org/openqa/selenium/devtools/v124:v124.publish | |
| //java/src/org/openqa/selenium/devtools/v85:v85.publish | |
| //java/src/org/openqa/selenium/edge:edge.publish | |
| //java/src/org/openqa/selenium/firefox:firefox.publish | |
| //java/src/org/openqa/selenium/grid/sessionmap/jdbc:jdbc.publish | |
| //java/src/org/openqa/selenium/grid/sessionmap/redis:redis.publish | |
| //java/src/org/openqa/selenium/grid:bom-dependencies.publish | |
| //java/src/org/openqa/selenium/grid:bom.publish | |
| //java/src/org/openqa/selenium/grid:grid.publish | |
| //java/src/org/openqa/selenium/ie:ie.publish | |
| //java/src/org/openqa/selenium/json:json.publish | |
| //java/src/org/openqa/selenium/manager:manager.publish | |
| //java/src/org/openqa/selenium/os:os.publish | |
| //java/src/org/openqa/selenium/remote/http:http.publish | |
| //java/src/org/openqa/selenium/remote:remote.publish | |
| //java/src/org/openqa/selenium/safari:safari.publish | |
| //java/src/org/openqa/selenium/support:support.publish | |
| //java/src/org/openqa/selenium:client-combined.publish | |
| //java/src/org/openqa/selenium:core.publish | |
| ].freeze | |
| # Notice that because we're using rake, anything you can do in a normal rake | |
| # build can also be done here. For example, here we set the default task | |
| task default: [:grid] | |
| task all: [ | |
| :'selenium-java', | |
| '//java/test/org/openqa/selenium/environment:webserver' | |
| ] | |
| task tests: [ | |
| '//java/test/org/openqa/selenium/htmlunit:htmlunit', | |
| '//java/test/org/openqa/selenium/firefox:test-synthesized', | |
| '//java/test/org/openqa/selenium/ie:ie', | |
| '//java/test/org/openqa/selenium/chrome:chrome', | |
| '//java/test/org/openqa/selenium/edge:edge', | |
| '//java/test/org/openqa/selenium/support:small-tests', | |
| '//java/test/org/openqa/selenium/support:large-tests', | |
| '//java/test/org/openqa/selenium/remote:small-tests', | |
| '//java/test/org/openqa/selenium/remote/server/log:test', | |
| '//java/test/org/openqa/selenium/remote/server:small-tests' | |
| ] | |
| task chrome: ['//java/src/org/openqa/selenium/chrome'] | |
| task grid: [:'selenium-server-standalone'] | |
| task ie: ['//java/src/org/openqa/selenium/ie'] | |
| task firefox: ['//java/src/org/openqa/selenium/firefox'] | |
| task remote: %i[remote_server remote_client] | |
| task remote_client: ['//java/src/org/openqa/selenium/remote'] | |
| task remote_server: ['//java/src/org/openqa/selenium/remote/server'] | |
| task safari: ['//java/src/org/openqa/selenium/safari'] | |
| task selenium: ['//java/src/org/openqa/selenium:core'] | |
| task support: ['//java/src/org/openqa/selenium/support'] | |
| desc 'Build the standalone server' | |
| task 'selenium-server-standalone' => '//java/src/org/openqa/selenium/grid:executable-grid' | |
| task test_javascript: [ | |
| '//javascript/atoms:test-chrome:run', | |
| '//javascript/webdriver:test-chrome:run', | |
| '//javascript/selenium-atoms:test-chrome:run', | |
| '//javascript/selenium-core:test-chrome:run' | |
| ] | |
| task test_chrome: ['//java/test/org/openqa/selenium/chrome:chrome:run'] | |
| task test_edge: ['//java/test/org/openqa/selenium/edge:edge:run'] | |
| task test_chrome_atoms: [ | |
| '//javascript/atoms:test-chrome:run', | |
| '//javascript/chrome-driver:test-chrome:run', | |
| '//javascript/webdriver:test-chrome:run' | |
| ] | |
| task test_htmlunit: [ | |
| '//java/test/org/openqa/selenium/htmlunit:htmlunit:run' | |
| ] | |
| task test_grid: [ | |
| '//java/test/org/openqa/grid/common:common:run', | |
| '//java/test/org/openqa/grid:grid:run', | |
| '//java/test/org/openqa/grid/e2e:e2e:run', | |
| '//java/test/org/openqa/selenium/remote:remote-driver-grid-tests:run' | |
| ] | |
| task test_ie: [ | |
| '//cpp/iedriverserver:win32', | |
| '//cpp/iedriverserver:x64', | |
| '//java/test/org/openqa/selenium/ie:ie:run' | |
| ] | |
| task test_jobbie: [:test_ie] | |
| task test_firefox: ['//java/test/org/openqa/selenium/firefox:marionette:run'] | |
| task test_remote_server: [ | |
| '//java/test/org/openqa/selenium/remote/server:small-tests:run', | |
| '//java/test/org/openqa/selenium/remote/server/log:test:run' | |
| ] | |
| task test_remote: [ | |
| '//java/test/org/openqa/selenium/json:small-tests:run', | |
| '//java/test/org/openqa/selenium/remote:common-tests:run', | |
| '//java/test/org/openqa/selenium/remote:client-tests:run', | |
| '//java/test/org/openqa/selenium/remote:remote-driver-tests:run', | |
| :test_remote_server | |
| ] | |
| task test_safari: ['//java/test/org/openqa/selenium/safari:safari:run'] | |
| task test_support: [ | |
| '//java/test/org/openqa/selenium/support:small-tests:run', | |
| '//java/test/org/openqa/selenium/support:large-tests:run' | |
| ] | |
| task :test_java_webdriver do | |
| if SeleniumRake::Checks.windows? | |
| Rake::Task['test_ie'].invoke | |
| elsif SeleniumRake::Checks.chrome? | |
| Rake::Task['test_chrome'].invoke | |
| elsif SeleniumRake::Checks.edge? | |
| Rake::Task['test_edge'].invoke | |
| else | |
| Rake::Task['test_htmlunit'].invoke | |
| Rake::Task['test_firefox'].invoke | |
| Rake::Task['test_remote_server'].invoke | |
| end | |
| end | |
| task test_java: [ | |
| '//java/test/org/openqa/selenium/atoms:test:run', | |
| :test_java_small_tests, | |
| :test_support, | |
| :test_java_webdriver, | |
| :test_selenium, | |
| 'test_grid' | |
| ] | |
| task test_java_small_tests: [ | |
| '//java/test/org/openqa/selenium:small-tests:run', | |
| '//java/test/org/openqa/selenium/json:small-tests:run', | |
| '//java/test/org/openqa/selenium/support:small-tests:run', | |
| '//java/test/org/openqa/selenium/remote:common-tests:run', | |
| '//java/test/org/openqa/selenium/remote:client-tests:run', | |
| '//java/test/org/openqa/grid/selenium/node:node:run', | |
| '//java/test/org/openqa/grid/selenium/proxy:proxy:run', | |
| '//java/test/org/openqa/selenium/remote/server:small-tests:run', | |
| '//java/test/org/openqa/selenium/remote/server/log:test:run' | |
| ] | |
| task :test do | |
| if SeleniumRake::Checks.python? | |
| Rake::Task['test_py'].invoke | |
| else | |
| Rake::Task['test_javascript'].invoke | |
| Rake::Task['test_java'].invoke | |
| end | |
| end | |
| task test_py: [:py_prep_for_install_release, 'py:marionette_test'] | |
| task build: %i[all firefox remote selenium tests] | |
| desc 'Clean build artifacts.' | |
| task :clean do | |
| rm_rf 'build/' | |
| rm_rf 'java/build/' | |
| rm_rf 'dist/' | |
| end | |
| # Create a new IEGenerator instance | |
| ie_generator = SeleniumRake::IEGenerator.new | |
| # Generate a C++ Header file for mapping between magic numbers and #defines | |
| # in the C++ code. | |
| ie_generator.generate_type_mapping( | |
| name: 'ie_result_type_cpp', | |
| src: 'cpp/iedriver/result_types.txt', | |
| type: 'cpp', | |
| out: 'cpp/iedriver/IEReturnTypes.h' | |
| ) | |
| desc 'Generate Javadocs' | |
| task javadocs: %i[//java/src/org/openqa/selenium/grid:all-javadocs] do | |
| FileUtils.rm_rf('build/docs/api/java') | |
| FileUtils.mkdir_p('build/docs/api/java') | |
| out = 'bazel-bin/java/src/org/openqa/selenium/grid/all-javadocs.jar' | |
| cmd = %(cd build/docs/api/java && jar xf "../../../../#{out}" 2>&1) | |
| cmd = cmd.tr('/', '\\').tr(':', ';') if SeleniumRake::Checks.windows? | |
| raise 'could not unpack javadocs' unless system(cmd) | |
| File.open('build/docs/api/java/stylesheet.css', 'a') do |file| | |
| file.write(<<~STYLE | |
| /* Custom selenium-specific styling */ | |
| .blink { | |
| animation: 2s cubic-bezier(0.5, 0, 0.85, 0.85) infinite blink; | |
| } | |
| @keyframes blink { | |
| 50% { | |
| opacity: 0; | |
| } | |
| } | |
| STYLE | |
| ) | |
| end | |
| end | |
| file 'cpp/iedriver/sizzle.h' => ['//third_party/js/sizzle:sizzle:header'] do | |
| cp 'build/third_party/js/sizzle/sizzle.h', 'cpp/iedriver/sizzle.h' | |
| end | |
| task sizzle_header: ['cpp/iedriver/sizzle.h'] | |
| task ios_driver: [ | |
| '//javascript/atoms/fragments:get_visible_text:ios', | |
| '//javascript/atoms/fragments:click:ios', | |
| '//javascript/atoms/fragments:back:ios', | |
| '//javascript/atoms/fragments:forward:ios', | |
| '//javascript/atoms/fragments:submit:ios', | |
| '//javascript/atoms/fragments:xpath:ios', | |
| '//javascript/atoms/fragments:xpaths:ios', | |
| '//javascript/atoms/fragments:type:ios', | |
| '//javascript/atoms/fragments:get_attribute:ios', | |
| '//javascript/atoms/fragments:clear:ios', | |
| '//javascript/atoms/fragments:is_selected:ios', | |
| '//javascript/atoms/fragments:is_enabled:ios', | |
| '//javascript/atoms/fragments:is_shown:ios', | |
| '//javascript/atoms/fragments:stringify:ios', | |
| '//javascript/atoms/fragments:link_text:ios', | |
| '//javascript/atoms/fragments:link_texts:ios', | |
| '//javascript/atoms/fragments:partial_link_text:ios', | |
| '//javascript/atoms/fragments:partial_link_texts:ios', | |
| '//javascript/atoms/fragments:get_interactable_size:ios', | |
| '//javascript/atoms/fragments:scroll_into_view:ios', | |
| '//javascript/atoms/fragments:get_effective_style:ios', | |
| '//javascript/atoms/fragments:get_element_size:ios', | |
| '//javascript/webdriver/atoms/fragments:get_location_in_view:ios' | |
| ] | |
| # This task does not allow running RBE, to run stamped with RBE use | |
| # ./go java:package['--config=release'] | |
| desc 'Create stamped zipped assets for Java for uploading to GitHub' | |
| task :'java-release-zip' do | |
| Rake::Task['java:package'].invoke('--stamp') | |
| end | |
| task 'release-java': %i[java-release-zip publish-maven] | |
| # TODO: just set the environment variables that maven is asking for | |
| def read_m2_user_pass | |
| user = ENV.fetch('SEL_M2_USER', nil) | |
| pass = ENV.fetch('SEL_M2_PASS', nil) | |
| if user && pass | |
| puts 'Fetching m2 user and pass from environment variables.' | |
| return [user, pass] | |
| end | |
| puts 'Fetching m2 user and pass from /.m2/settings.xml.' | |
| settings = File.read("#{Dir.home}/.m2/settings.xml") | |
| found_section = false | |
| settings.each_line do |line| | |
| if !found_section | |
| found_section = line.include? '<id>sonatype-nexus-staging</id>' | |
| elsif line.include?('<username>') | |
| user = line[%r{<username>(.*?)</username>}, 1] | |
| elsif line.include?('<password>') | |
| pass = line[%r{<password>(.*?)</password>}, 1] | |
| end | |
| break if user && pass | |
| end | |
| [user, pass] | |
| end | |
| desc 'Publish all Java jars to Maven as stable release' | |
| task 'publish-maven': JAVA_RELEASE_TARGETS do | |
| creds = read_m2_user_pass | |
| JAVA_RELEASE_TARGETS.each do |p| | |
| Bazel.execute('run', | |
| ['--stamp', | |
| '--define', | |
| 'maven_repo=https://oss.sonatype.org/service/local/staging/deploy/maven2', | |
| '--define', | |
| "maven_user=#{creds[0]}", | |
| '--define', | |
| "maven_password=#{creds[1]}", | |
| '--define', | |
| 'gpg_sign=true'], | |
| p) | |
| end | |
| end | |
| desc 'Publish all Java jars to Maven as nightly release' | |
| task 'publish-maven-snapshot': JAVA_RELEASE_TARGETS do | |
| creds = read_m2_user_pass | |
| if java_version.end_with?('-SNAPSHOT') | |
| JAVA_RELEASE_TARGETS.each do |p| | |
| Bazel.execute('run', | |
| ['--stamp', | |
| '--define', | |
| 'maven_repo=https://oss.sonatype.org/content/repositories/snapshots', | |
| '--define', | |
| "maven_user=#{creds[0]}", | |
| '--define', | |
| "maven_password=#{creds[1]}", | |
| '--define', | |
| 'gpg_sign=false'], | |
| p) | |
| end | |
| else | |
| puts 'No SNAPSHOT version configured. Targets will not be pushed to the snapshot repo in SonaType.' | |
| end | |
| end | |
| desc 'Install jars to local m2 directory' | |
| task :'maven-install' do | |
| JAVA_RELEASE_TARGETS.each do |p| | |
| Bazel.execute('run', | |
| ['--stamp', | |
| '--define', | |
| "maven_repo=file://#{Dir.home}/.m2/repository", | |
| '--define', | |
| 'gpg_sign=false'], | |
| p) | |
| end | |
| end | |
| desc 'Build the selenium client jars' | |
| task 'selenium-java' => '//java/src/org/openqa/selenium:client-combined' | |
| desc 'Update AUTHORS file' | |
| task :authors do | |
| sh "(git log --use-mailmap --format='%aN <%aE>' ; cat .OLD_AUTHORS) | sort -uf > AUTHORS" | |
| end | |
| namespace :side do | |
| task atoms: [ | |
| '//javascript/atoms/fragments:find-element' | |
| ] do | |
| # TODO: move directly to IDE's directory once the repositories are merged | |
| mkdir_p 'build/javascript/atoms' | |
| atom = 'bazel-bin/javascript/atoms/fragments/find-element.js' | |
| name = File.basename(atom) | |
| puts "Generating #{atom} as #{name}" | |
| File.open(File.join(baseDir, name), 'w') do |f| | |
| f << "// GENERATED CODE - DO NOT EDIT\n" | |
| f << 'module.exports = ' | |
| f << File.read(atom).strip | |
| f << ";\n" | |
| end | |
| end | |
| end | |
| def node_version | |
| File.foreach('javascript/node/selenium-webdriver/package.json') do |line| | |
| return line.split(':').last.strip.tr('",', '') if line.include?('version') | |
| end | |
| end | |
| namespace :node do | |
| atom_list = %w[ | |
| //javascript/atoms/fragments:find-elements | |
| //javascript/atoms/fragments:is-displayed | |
| //javascript/webdriver/atoms:get-attribute | |
| ] | |
| task atoms: atom_list do | |
| base_dir = 'javascript/node/selenium-webdriver/lib/atoms' | |
| mkdir_p base_dir | |
| ['bazel-bin/javascript/atoms/fragments/is-displayed.js', | |
| 'bazel-bin/javascript/webdriver/atoms/get-attribute.js', | |
| 'bazel-bin/javascript/atoms/fragments/find-elements.js'].each do |atom| | |
| name = File.basename(atom) | |
| puts "Generating #{atom} as #{name}" | |
| File.open(File.join(base_dir, name), 'w') do |f| | |
| f << "// GENERATED CODE - DO NOT EDIT\n" | |
| f << 'module.exports = ' | |
| f << File.read(atom).strip | |
| f << ";\n" | |
| end | |
| end | |
| end | |
| desc 'Build Node npm package' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//javascript/node/selenium-webdriver') | |
| end | |
| task :'dry-run' do | |
| Bazel.execute('run', ['--stamp'], | |
| '//javascript/node/selenium-webdriver:selenium-webdriver.publish -- --dry-run=true') | |
| end | |
| desc 'Release Node npm package' | |
| task :release do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| nightly = args.delete('nightly') | |
| Rake::Task['node:version'].invoke('nightly') if nightly | |
| Bazel.execute('run', args, '//javascript/node/selenium-webdriver:selenium-webdriver.publish') | |
| end | |
| desc 'Release Node npm package' | |
| task deploy: :release | |
| desc 'Generate Node documentation' | |
| task :docs, [:skip_update] do |_task, arguments| | |
| FileUtils.rm_rf('build/docs/api/javascript/') | |
| begin | |
| sh 'npm run generate-docs --prefix javascript/node/selenium-webdriver || true', verbose: true | |
| rescue StandardError | |
| puts 'Ensure that npm is installed on your system' | |
| raise | |
| end | |
| unless arguments[:skip_update] | |
| puts 'Updating JavaScript documentation' | |
| puts update_gh_pages ? 'JavaScript Docs updated!' : 'JavaScript Doc update cancelled' | |
| end | |
| end | |
| desc 'Update JavaScript changelog' | |
| task :changelog do | |
| header = "## #{node_version}" | |
| update_changelog(node_version, 'javascript', 'javascript/node/selenium-webdriver/', | |
| 'javascript/node/selenium-webdriver/CHANGES.md', header) | |
| end | |
| desc 'Update Node version' | |
| task :version, [:version] do |_task, arguments| | |
| old_version = node_version | |
| nightly = "-nightly#{Time.now.strftime('%Y%m%d%H%M')}" | |
| new_version = updated_version(old_version, arguments[:version], nightly) | |
| ['javascript/node/selenium-webdriver/package.json', | |
| 'package-lock.json', | |
| 'javascript/node/selenium-webdriver/BUILD.bazel'].each do |file| | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| end | |
| Rake::Task['node:changelog'].invoke unless new_version.include?(nightly) | |
| end | |
| end | |
| def python_version | |
| File.foreach('py/BUILD.bazel') do |line| | |
| return line.split('=').last.strip.tr('"', '') if line.include?('SE_VERSION') | |
| end | |
| end | |
| namespace :py do | |
| desc 'Build Python wheel and sdist with optional arguments' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//py:selenium-wheel') | |
| Bazel.execute('build', args, '//py:selenium-sdist') | |
| end | |
| desc 'Release Python wheel and sdist to pypi' | |
| task :release do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| nightly = args.delete('nightly') | |
| Rake::Task['py:version'].invoke('nightly') if nightly | |
| command = nightly.nil? ? '//py:selenium-release' : '//py:selenium-release-nightly' | |
| Bazel.execute('run', args, command) | |
| end | |
| desc 'generate and copy files required for local development' | |
| task :local_dev do | |
| Bazel.execute('build', [], '//py:selenium') | |
| Rake::Task['grid'].invoke | |
| FileUtils.rm_rf('py/selenium/webdriver/common/devtools/') | |
| FileUtils.cp_r('bazel-bin/py/selenium/webdriver/.', 'py/selenium/webdriver', remove_destination: true) | |
| end | |
| desc 'Update generated Python files for local development' | |
| task :clean do | |
| Bazel.execute('build', [], '//py:selenium') | |
| bazel_bin_path = 'bazel-bin/py/selenium/webdriver' | |
| lib_path = 'py/selenium/webdriver' | |
| dirs = %w[devtools linux mac windows] | |
| dirs.each { |dir| FileUtils.rm_rf("#{lib_path}/common/#{dir}") } | |
| Find.find(bazel_bin_path) do |path| | |
| if File.directory?(path) && dirs.any? { |dir| path.include?("common/#{dir}") } | |
| Find.prune | |
| next | |
| end | |
| next if File.directory?(path) | |
| target_file = File.join(lib_path, path.sub(%r{^#{bazel_bin_path}/}, '')) | |
| if File.exist?(target_file) | |
| puts "Removing target file: #{target_file}" | |
| FileUtils.rm(target_file) | |
| end | |
| end | |
| end | |
| desc 'Generate Python documentation' | |
| task :docs, [:skip_update] do |_task, arguments| | |
| FileUtils.rm_rf('build/docs/api/py/') | |
| FileUtils.rm_rf('build/docs/doctrees/') | |
| begin | |
| sh 'tox -c py/tox.ini -e docs', verbose: true | |
| rescue StandardError | |
| puts 'Ensure that tox is installed on your system' | |
| raise | |
| end | |
| unless arguments[:skip_update] | |
| puts 'Updating Python documentation' | |
| puts update_gh_pages ? 'Python Docs updated!' : 'Python Doc update cancelled' | |
| end | |
| end | |
| desc 'Install Python wheel locally' | |
| task :install do | |
| Bazel.execute('build', [], '//py:selenium-wheel') | |
| begin | |
| sh 'pip install bazel-bin/py/selenium-*.whl' | |
| rescue StandardError | |
| puts 'Ensure that Python and pip are installed on your system' | |
| raise | |
| end | |
| end | |
| desc 'Update Python changelog' | |
| task :changelog do | |
| header = "Selenium #{python_version}" | |
| update_changelog(python_version, 'py', 'py/selenium/webdriver', 'py/CHANGES', header) | |
| end | |
| desc 'Update Python version' | |
| task :version, [:version] do |_task, arguments| | |
| old_version = python_version | |
| nightly = ".dev#{Time.now.strftime('%Y%m%d%H%M')}" | |
| new_version = updated_version(old_version, arguments[:version], nightly) | |
| ['py/setup.py', | |
| 'py/BUILD.bazel', | |
| 'py/selenium/__init__.py', | |
| 'py/selenium/webdriver/__init__.py', | |
| 'py/docs/source/conf.py'].each do |file| | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| end | |
| old_short_version = old_version.split('.')[0..1].join('.') | |
| new_short_version = new_version.split('.')[0..1].join('.') | |
| text = File.read('py/docs/source/conf.py').gsub(old_short_version, new_short_version) | |
| File.open('py/docs/source/conf.py', 'w') { |f| f.puts text } | |
| Rake::Task['py:changelog'].invoke unless new_version.include?(nightly) | |
| end | |
| desc 'Update Python Syntax' | |
| task :lint do | |
| sh 'tox -c py/tox.ini -e linting' | |
| end | |
| namespace :test do | |
| desc 'Python unit tests' | |
| task :unit do | |
| Rake::Task['py:clean'].invoke | |
| Bazel.execute('test', ['--test_size_filters=small'], '//py/...') | |
| end | |
| %i[chrome edge firefox safari].each do |browser| | |
| desc "Python #{browser} tests" | |
| task browser do | |
| Rake::Task['py:clean'].invoke | |
| Bazel.execute('test', [], "//py:common-#{browser}") | |
| Bazel.execute('test', [], "//py:test-#{browser}") | |
| end | |
| end | |
| desc 'Python Remote tests with Firefox' | |
| task :remote do | |
| Rake::Task['py:clean'].invoke | |
| Bazel.execute('test', [], '//py:test-remote') | |
| end | |
| end | |
| namespace :test do | |
| desc 'Python unit tests' | |
| task :unit do | |
| Rake::Task['py:clean'].invoke | |
| Bazel.execute('test', ['--test_size_filters=small'], '//py/...') | |
| end | |
| %i[chrome edge firefox safari].each do |browser| | |
| desc "Python #{browser} tests" | |
| task browser do | |
| Rake::Task['py:clean'].invoke | |
| Bazel.execute('test', %w[--test_output all], "//py:common-#{browser}") | |
| Bazel.execute('test', %w[--test_output all], "//py:test-#{browser}") | |
| end | |
| end | |
| end | |
| end | |
| 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 | |
| namespace :rb do | |
| desc 'Generate Ruby gems' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| 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 | |
| Bazel.execute('build', [], '@bundle//:bundle') | |
| Rake::Task['rb:build'].invoke | |
| Rake::Task['grid'].invoke | |
| end | |
| desc 'Push Ruby gems to rubygems' | |
| task :release do |_task, arguments| | |
| args = arguments.to_a.compact | |
| nightly = args.delete('nightly') | |
| if nightly | |
| Bazel.execute('run', [], '//rb:selenium-webdriver-bump-nightly-version') | |
| Bazel.execute('run', args, '//rb:selenium-webdriver-release-nightly') | |
| else | |
| Bazel.execute('run', args, '//rb:selenium-webdriver-release') | |
| Bazel.execute('run', args, '//rb:selenium-devtools-release') | |
| end | |
| end | |
| desc 'Generate Ruby documentation' | |
| task :docs, [:skip_update] do |_task, arguments| | |
| FileUtils.rm_rf('build/docs/api/rb/') | |
| Bazel.execute('run', [], '//rb:docs') | |
| FileUtils.mkdir_p('build/docs/api') | |
| FileUtils.cp_r('bazel-bin/rb/docs.sh.runfiles/_main/docs/api/rb/.', 'build/docs/api/rb') | |
| unless arguments[:skip_update] | |
| puts 'Updating Ruby documentation' | |
| puts update_gh_pages ? 'Ruby Docs updated!' : 'Ruby Doc update cancelled' | |
| end | |
| end | |
| desc 'Update Ruby changelog' | |
| task :changelog do | |
| header = "#{ruby_version} (#{Time.now.strftime('%Y-%m-%d')})\n=========================" | |
| 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 = updated_version(old_version, arguments[:version], '.nightly') | |
| file = 'rb/lib/selenium/webdriver/version.rb' | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| Rake::Task['rb:changelog'].invoke unless new_version.include?('.nightly') | |
| sh 'cd rb && bundle --version && bundle update' | |
| end | |
| desc 'Update Ruby Syntax' | |
| task :lint do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('run', args, '//rb:lint') | |
| end | |
| end | |
| def dotnet_version | |
| File.foreach('dotnet/selenium-dotnet-version.bzl') do |line| | |
| return line.split('=').last.strip.tr('"', '') if line.include?('SE_VERSION') | |
| end | |
| end | |
| namespace :dotnet do | |
| desc 'Build nupkg files' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//dotnet:all') | |
| end | |
| desc 'Package .NET bindings into zipped assets and stage for release' | |
| task :package do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| Rake::Task['dotnet:build'].invoke(*args) | |
| mkdir_p 'build/dist' | |
| FileUtils.rm_f(Dir.glob('build/dist/*dotnet*')) | |
| FileUtils.copy('bazel-bin/dotnet/release.zip', "build/dist/selenium-dotnet-#{dotnet_version}.zip") | |
| FileUtils.chmod(0o666, "build/dist/selenium-dotnet-#{dotnet_version}.zip") | |
| FileUtils.copy('bazel-bin/dotnet/strongnamed.zip', "build/dist/selenium-dotnet-strongnamed-#{dotnet_version}.zip") | |
| FileUtils.chmod(0o666, "build/dist/selenium-dotnet-strongnamed-#{dotnet_version}.zip") | |
| end | |
| desc 'Upload nupkg files to Nuget' | |
| task :release do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| nightly = args.delete('nightly') | |
| Rake::Task['dotnet:version'].invoke('nightly') if nightly | |
| Rake::Task['dotnet:package'].invoke(*args) | |
| release_version = dotnet_version | |
| api_key = ENV.fetch('NUGET_API_KEY', nil) | |
| push_destination = 'https://api.nuget.org/v3/index.json' | |
| if release_version.include?('-nightly') | |
| # Nightly builds are pushed to GitHub NuGet repository | |
| # This commands will run in GitHub Actions | |
| api_key = ENV.fetch('GITHUB_TOKEN', nil) | |
| github_push_url = 'https://nuget.pkg.github.com/seleniumhq/index.json' | |
| push_destination = 'github' | |
| flags = ['--username', 'seleniumhq', '--password', api_key, '--store-password-in-clear-text', '--name', | |
| push_destination, github_push_url] | |
| sh "dotnet nuget add source #{flags.join(' ')}" | |
| end | |
| ["./bazel-bin/dotnet/src/webdriver/Selenium.WebDriver.#{dotnet_version}.nupkg", | |
| "./bazel-bin/dotnet/src/support/Selenium.Support.#{dotnet_version}.nupkg"].each do |asset| | |
| sh "dotnet nuget push #{asset} --api-key #{api_key} --source #{push_destination}" | |
| end | |
| end | |
| desc 'Generate .NET documentation' | |
| task :docs, [:skip_update] do |_task, arguments| | |
| FileUtils.rm_rf('build/docs/api/dotnet/') | |
| begin | |
| # Pinning to 2.75.3 to avoid breaking changes in newer versions | |
| # See https://github.com/dotnet/docfx/issues/9855 | |
| sh 'dotnet tool uninstall --global docfx || true' | |
| sh 'dotnet tool install --global --version 2.75.3 docfx' | |
| # sh 'dotnet tool update -g docfx' | |
| rescue StandardError | |
| puts 'Please ensure that .NET SDK is installed.' | |
| raise | |
| end | |
| begin | |
| sh 'docfx dotnet/docs/docfx.json' | |
| rescue StandardError | |
| case $CHILD_STATUS.exitstatus | |
| when 127 | |
| raise 'Ensure the dotnet/tools directory is added to your PATH environment variable (e.g., `~/.dotnet/tools`)' | |
| when 255 | |
| puts '.NET documentation build failed, likely because of DevTools namespacing. This is ok; continuing' | |
| else | |
| raise | |
| end | |
| end | |
| unless arguments[:skip_update] | |
| puts 'Updating .NET documentation' | |
| puts update_gh_pages ? '.NET Docs updated!' : '.NET Doc update cancelled' | |
| end | |
| end | |
| desc 'Update .NET changelog' | |
| task :changelog do | |
| header = "v#{dotnet_version}\n======" | |
| update_changelog(dotnet_version, 'dotnet', 'dotnet/src/', 'dotnet/CHANGELOG', header) | |
| end | |
| desc 'Update .NET version' | |
| task :version, [:version] do |_task, arguments| | |
| old_version = dotnet_version | |
| nightly = "-nightly#{Time.now.strftime('%Y%m%d%H%M')}" | |
| new_version = updated_version(old_version, arguments[:version], nightly) | |
| file = 'dotnet/selenium-dotnet-version.bzl' | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| Rake::Task['dotnet:changelog'].invoke unless new_version.include?(nightly) | |
| end | |
| end | |
| namespace :java do | |
| desc 'Build Java Client Jars' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//java/src/org/openqa/selenium:client-combined') | |
| end | |
| desc 'Build Grid Jar' | |
| task :grid do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//java/src/org/openqa/selenium/grid:grid') | |
| end | |
| desc 'Package Java bindings and grid into releasable packages and stage for release' | |
| task :package do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| Bazel.execute('build', args, '//java/src/org/openqa/selenium:client-zip') | |
| Bazel.execute('build', args, '//java/src/org/openqa/selenium/grid:server-zip') | |
| Bazel.execute('build', args, '//java/src/org/openqa/selenium/grid:executable-grid') | |
| mkdir_p 'build/dist' | |
| Dir.glob('build/dist/*{java,server}*').each { |file| FileUtils.rm_f(file) } | |
| FileUtils.copy('bazel-bin/java/src/org/openqa/selenium/grid/server-zip.zip', | |
| "build/dist/selenium-server-#{java_version}.zip") | |
| FileUtils.chmod(0o666, "build/dist/selenium-server-#{java_version}.zip") | |
| FileUtils.copy('bazel-bin/java/src/org/openqa/selenium/client-zip.zip', | |
| "build/dist/selenium-java-#{java_version}.zip") | |
| FileUtils.chmod(0o666, "build/dist/selenium-java-#{java_version}.zip") | |
| FileUtils.copy('bazel-bin/java/src/org/openqa/selenium/grid/selenium', | |
| "build/dist/selenium-server-#{java_version}.jar") | |
| FileUtils.chmod(0o777, "build/dist/selenium-server-#{java_version}.jar") | |
| end | |
| desc 'Deploy all jars to Maven' | |
| task :release do |_task, arguments| | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| nightly = args.delete('nightly') | |
| JAVA_RELEASE_TARGETS.each { |target| Bazel.execute('build', args, target) } | |
| user, password = read_m2_user_pass | |
| repo = nightly ? 'content/repositories/snapshots' : 'service/local/staging/deploy/maven2' | |
| gpg = nightly ? 'false' : 'true' | |
| Rake::Task['java:version'].invoke if nightly | |
| release_args = ['--stamp', | |
| '--define', | |
| "maven_repo=https://oss.sonatype.org/#{repo}", | |
| '--define', | |
| "maven_user=#{user}", | |
| "--define", | |
| "maven_password=#{password}", | |
| '--define', | |
| "gpg_sign=#{gpg}"] | |
| JAVA_RELEASE_TARGETS.each { |target| Bazel.execute('run', release_args, target) } | |
| end | |
| desc 'Install jars to local m2 directory' | |
| task install: :'maven-install' | |
| desc 'Generate Java documentation' | |
| task :docs, [:skip_update] do |_task, arguments| | |
| Rake::Task['javadocs'].invoke | |
| unless arguments[:skip_update] | |
| puts 'Updating Java documentation' | |
| puts update_gh_pages ? 'Java Docs updated!' : 'Java Doc update cancelled' | |
| end | |
| end | |
| desc 'Update Maven dependencies' | |
| task :update do | |
| # Make sure things are in a good state to start with | |
| args = ['--action_env=RULES_JVM_EXTERNAL_REPIN=1'] | |
| Bazel.execute('run', args, '@unpinned_maven//:pin') | |
| file_path = 'MODULE.bazel' | |
| content = File.read(file_path) | |
| # For some reason ./go wrapper is not outputting from Open3, so cannot use Bazel class directly | |
| output = `bazel run @maven//:outdated` | |
| output.scan(/\S+ \[\S+-alpha\]/).each do |match| | |
| puts "WARNING — Cannot automatically update alpha version of: #{match}" | |
| end | |
| versions = output.scan(/(\S+) \[\S+ -> (\S+)\]/).to_h | |
| versions.each do |artifact, version| | |
| if artifact.match?('graphql') | |
| puts 'WARNING — Cannot automatically update graphql' | |
| next | |
| end | |
| replacement = artifact.include?('googlejavaformat') ? "#{artifact}:jar:#{version}" : "#{artifact}:#{version}" | |
| content.gsub!(/#{artifact}:(jar:)?\d+\.\d+[^\\"]+/, replacement) | |
| end | |
| File.write(file_path, content) | |
| args = ['--action_env=RULES_JVM_EXTERNAL_REPIN=1'] | |
| Bazel.execute('run', args, '@unpinned_maven//:pin') | |
| end | |
| desc 'Update Java changelog' | |
| task :changelog do | |
| header = "v#{java_version}\n======" | |
| update_changelog(java_version, 'java', 'java/src/org/', 'java/CHANGELOG', header) | |
| end | |
| desc 'Update Java version' | |
| task :version, [:version] do |_task, arguments| | |
| old_version = java_version | |
| new_version = updated_version(old_version, arguments[:version], '-SNAPSHOT') | |
| file = 'java/version.bzl' | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| Rake::Task['java:changelog'].invoke unless new_version.include?('-SNAPSHOT') | |
| end | |
| end | |
| def rust_version | |
| File.foreach('rust/BUILD.bazel') do |line| | |
| return line.split('=').last.strip.tr('",', '') if line.include?('version =') | |
| end | |
| end | |
| namespace :rust do | |
| desc 'Build Selenium Manager' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Bazel.execute('build', args, '//rust:selenium-manager') | |
| end | |
| desc 'Update the rust lock files' | |
| task :update do | |
| sh 'CARGO_BAZEL_REPIN=true bazel sync --only=crates' | |
| end | |
| desc 'Update Rust changelog' | |
| task :changelog do | |
| header = "#{rust_version}\n======" | |
| version = rust_version.split('.').tap(&:shift).join('.') | |
| update_changelog(version, 'rust', 'rust/src', 'rust/CHANGELOG.md', header) | |
| end | |
| # Rust versioning is currently difficult compared to the others because we are using the 0.4.x pattern | |
| # until Selenium Manager comes out of beta | |
| desc 'Update Rust version' | |
| task :version, [:version] do |_task, arguments| | |
| old_version = rust_version.dup | |
| equivalent_version = if old_version.include?('nightly') | |
| "#{old_version.split(/\.|-/)[0...-1].tap(&:shift).join('.')}.0-nightly" | |
| else | |
| old_version.split('.').tap(&:shift).append('0').join('.') | |
| end | |
| updated = updated_version(equivalent_version, arguments[:version], '-nightly') | |
| new_version = updated.split(/\.|-/).tap { |v| v.delete_at(2) }.unshift('0').join('.').gsub('.nightly', '-nightly') | |
| ['rust/Cargo.toml', 'rust/BUILD.bazel'].each do |file| | |
| text = File.read(file).gsub(old_version, new_version) | |
| File.open(file, 'w') { |f| f.puts text } | |
| end | |
| Rake::Task['rust:changelog'].invoke unless new_version.include?('-nightly') | |
| Rake::Task['rust:update'].invoke | |
| end | |
| # Creating a special task for this because Rust version needs to be managed at a different place than | |
| # everything else; want to use changelog updates later in process | |
| namespace :version do | |
| desc 'Commits updates from Rust version changes' | |
| task :commit do | |
| @git.reset | |
| commit!("update Rust version to #{rust_version}", | |
| ['rust/BUILD.bazel', 'rust/Cargo.Bazel.lock', 'rust/Cargo.lock', 'rust/Cargo.toml']) | |
| commit!('Rust Changelog', ['rust/CHANGELOG.md']) | |
| end | |
| end | |
| end | |
| namespace :all do | |
| desc 'Update all API Documentation' | |
| task :docs do | |
| FileUtils.rm_rf('build/docs/api') | |
| Rake::Task['java:docs'].invoke(true) | |
| Rake::Task['py:docs'].invoke(true) | |
| Rake::Task['rb:docs'].invoke(true) | |
| Rake::Task['dotnet:docs'].invoke(true) | |
| Rake::Task['node:docs'].invoke(true) | |
| puts 'Updating All API Docs' | |
| puts update_gh_pages ? 'AP Docs updated!' : 'API Doc update cancelled' | |
| end | |
| desc 'Build all artifacts for all language bindings' | |
| task :build do |_task, arguments| | |
| args = arguments.to_a.compact | |
| Rake::Task['java:build'].invoke(*args) | |
| Rake::Task['py:build'].invoke(*args) | |
| Rake::Task['rb:build'].invoke(*args) | |
| Rake::Task['dotnet:build'].invoke(*args) | |
| Rake::Task['node:build'].invoke(*args) | |
| end | |
| desc 'Release all artifacts for all language bindings' | |
| task :release do |_task, arguments| | |
| Rake::Task['clean'].invoke | |
| tag = @git.add_tag("selenium-#{java_version}") | |
| @git.push('origin', tag.name) | |
| args = arguments.to_a.compact.empty? ? ['--stamp'] : arguments.to_a.compact | |
| Rake::Task['java:release'].invoke(*args) | |
| Rake::Task['py:release'].invoke(*args) | |
| Rake::Task['rb:release'].invoke(*args) | |
| Rake::Task['dotnet:release'].invoke(*args) | |
| Rake::Task['node:release'].invoke(*args) | |
| Rake::Task['create_release_notes'].invoke | |
| Rake::Task['all:docs'].invoke | |
| Rake::Task['all:version'].invoke('nightly') | |
| puts 'Committing nightly version updates' | |
| commit!('update versions to nightly', ['java/version.bzl', | |
| 'rb/lib/selenium/webdriver/version.rb', | |
| 'rb/Gemfile.lock', | |
| 'py/setup.py', | |
| 'py/BUILD.bazel', | |
| 'py/selenium/__init__.py', | |
| 'py/selenium/webdriver/__init__.py', | |
| 'py/docs/source/conf.py', | |
| 'rust/BUILD.bazel', | |
| 'rust/Cargo.Bazel.lock', | |
| 'rust/Cargo.lock', | |
| 'rust/Cargo.toml']) | |
| print 'Do you want to push the committed changes? (Y/n): ' | |
| response = $stdin.gets.chomp.downcase | |
| @git.push if %w[y yes].include?(response) | |
| end | |
| desc 'Update everything in preparation for a release' | |
| task :prepare, [:channel] do |_task, arguments| | |
| chrome_channel = arguments[:channel] || 'Stable' | |
| args = Array(chrome_channel) ? ['--', "--chrome_channel=#{chrome_channel.capitalize}"] : [] | |
| Bazel.execute('run', args, '//scripts:pinned_browsers') | |
| commit!('Update pinned browser versions', ['common/repositories.bzl']) | |
| Bazel.execute('run', args, '//scripts:update_cdp') | |
| commit!('Update supported versions for Chrome DevTools', | |
| ['common/devtools/', | |
| 'dotnet/src/webdriver/DevTools/', | |
| 'dotnet/src/webdriver/WebDriver.csproj', | |
| 'dotnet/test/common/DevTools/', | |
| 'dotnet/test/common/CustomDriverConfigs/', | |
| 'dotnet/selenium-dotnet-version.bzl', | |
| 'java/src/org/openqa/selenium/devtools/', | |
| 'javascript/node/selenium-webdriver/BUILD.bazel', | |
| 'py/BUILD.bazel', | |
| 'rb/lib/selenium/devtools/', | |
| 'rb/Gemfile.lock', | |
| 'Rakefile']) | |
| Bazel.execute('run', args, '//scripts:selenium_manager') | |
| commit!('Update selenium manager version', ['common/selenium_manager.bzl']) | |
| Rake::Task['java:update'].invoke | |
| commit!('Update Maven Dependencies', ['java/maven_deps.bzl', 'java/maven_install.json']) | |
| Rake::Task['authors'].invoke | |
| commit!('Update authors file', ['AUTHORS']) | |
| # Note that this does not include Rust version changes that are handled in separate rake:version task | |
| Rake::Task['all:version'].invoke | |
| commit!("FIX CHANGELOGS BEFORE MERGING!\n\nUpdate versions and change logs to release Selenium #{java_version}", | |
| ['dotnet/CHANGELOG', | |
| 'dotnet/selenium-dotnet-version.bzl', | |
| 'java/CHANGELOG', | |
| 'java/version.bzl', | |
| 'javascript/node/selenium-webdriver/CHANGES.md', | |
| 'javascript/node/selenium-webdriver/package.json', | |
| 'package-lock.json', | |
| 'py/docs/source/conf.py', | |
| 'py/selenium/__init__.py', | |
| 'py/selenium/webdriver/__init__.py', | |
| 'py/BUILD.bazel', | |
| 'py/CHANGES', | |
| 'py/setup.py', | |
| 'rb/lib/selenium/webdriver/version.rb', | |
| 'rb/CHANGES', | |
| 'rb/Gemfile.lock', | |
| 'rust/CHANGELOG.md']) | |
| end | |
| desc 'Update all versions' | |
| task :version, [:version] do |_task, arguments| | |
| version = arguments[:version] || 'nightly' | |
| Rake::Task['java:version'].invoke(version) | |
| Rake::Task['rb:version'].invoke(version) | |
| Rake::Task['node:version'].invoke(version) | |
| Rake::Task['py:version'].invoke(version) | |
| Rake::Task['dotnet:version'].invoke(version) | |
| end | |
| end | |
| at_exit do | |
| system 'sh', '.git-fixfiles' if File.exist?('.git') && !SeleniumRake::Checks.windows? | |
| end | |
| desc 'Create Release Notes for Minor Release' | |
| task :create_release_notes do | |
| range = "#{previous_tag(java_version)}...HEAD" | |
| format = '* [\\`%h\\`](http://github.com/seleniumhq/selenium/commit/%H) - %s :: %aN' | |
| git_log_command = %(git --no-pager log "#{range}" --pretty=format:"#{format}" --reverse) | |
| git_log_output = `#{git_log_command}` | |
| release_notes = <<~RELEASE_NOTES | |
| ### Changelog | |
| For each component's detailed changelog, please check: | |
| * [Ruby](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES) | |
| * [Python](https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES) | |
| * [JavaScript](https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/node/selenium-webdriver/CHANGES.md) | |
| * [Java](https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG) | |
| * [DotNet](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/CHANGELOG) | |
| * [IEDriverServer](https://github.com/SeleniumHQ/selenium/blob/trunk/cpp/iedriverserver/CHANGELOG) | |
| ### Commits in this release | |
| <details> | |
| <summary>Click to see all the commits included in this release</summary> | |
| #{git_log_output} | |
| </details> | |
| RELEASE_NOTES | |
| FileUtils.mkdir_p('build/dist') | |
| release_notes_file = "build/dist/release_notes_#{java_version}.md" | |
| File.write(release_notes_file, release_notes) | |
| puts "Release notes have been generated at: #{release_notes_file}" | |
| end | |
| def updated_version(current, desired = nil, nightly = nil) | |
| if !desired.nil? && desired != 'nightly' | |
| # If desired is present, return full 3 digit version | |
| desired.split('.').tap { |v| v << 0 while v.size < 3 }.join('.') | |
| elsif current.split(/\.|-/).size > 3 | |
| # if current version is already nightly, just need to bump it; this will be noop for some languages | |
| pattern = /-?\.?(nightly|SNAPSHOT|dev)\d*$/ | |
| current.gsub(pattern, nightly) | |
| elsif current.split(/\.|-/).size == 3 | |
| # if current version is not nightly, need to bump the version and make nightly | |
| "#{current.split(/\.|-/).tap { |i| (i[1] = i[1].to_i + 1) && (i[2] = 0) }.join('.')}#{nightly}" | |
| end | |
| end | |
| # TODO: make this less insane | |
| # rubocop:disable all | |
| def update_gh_pages | |
| origin_reference = @git.current_branch | |
| origin_reference ||= begin | |
| # This allows updating docs from a tagged commit instead of a branch | |
| puts 'commit is not at HEAD, checking for matching tag' | |
| tag = @git.tags.detect { |t| t.sha == @git.revparse('HEAD') } | |
| tag ? tag.name : raise(StandardError, 'Must be on a tagged commit or at the HEAD of a branch to update API Docs') | |
| end | |
| puts 'Checking out gh-pages' | |
| begin | |
| @git.checkout('gh-pages') | |
| rescue Git::FailedError => e | |
| # This happens when the working directory is not clean and things need to be stashed or committed | |
| line = e.message.lines[2].gsub('output: "error: ', '') | |
| puts line.gsub('\t', "\t").split('\n')[0...-2].join("\n") | |
| # TODO: we could offer to automatically fix with a stash, but there may be edge cases | |
| print 'Manually Fix and Retry? (Y/n):' | |
| response = $stdin.gets.chomp.downcase | |
| return false unless %w[y yes].include?(response) | |
| retry | |
| end | |
| puts 'Updating gh-pages branch from upstream repository' | |
| begin | |
| @git.pull | |
| rescue Git::FailedError => e | |
| # This happens when upstream is not already set | |
| line = e.message.lines[2].gsub('output: "error: ', '') | |
| puts line.gsub('\t', "\t").split('\n').delete_if(&:empty?)[-2...-1].join("\n") | |
| print 'Manually Fix and Retry? (Y/n):' | |
| response = $stdin.gets.chomp.downcase | |
| return restore_git(origin_reference) unless %w[y yes].include?(response) | |
| retry | |
| end | |
| %w[java rb py dotnet javascript].each do |language| | |
| next unless Dir.exist?("build/docs/api/#{language}") && !Dir.empty?("build/docs/api/#{language}") | |
| puts "Deleting #{language} directory in docs/api since corresponding directory in build/docs/api is not empty" | |
| FileUtils.rm_rf("docs/api/#{language}") | |
| puts 'Moving documentation files from untracked build directory to tracked docs directory' | |
| FileUtils.mv("build/docs/api/#{language}", "docs/api/#{language}") | |
| end | |
| print 'Do you want to commit the changes? (Y/n): ' | |
| response = $stdin.gets.chomp.downcase | |
| return restore_git(origin_reference) unless %w[y yes].include?(response) | |
| puts 'Committing changes' | |
| commit!('updating all API docs', ['docs/api/']) | |
| puts 'Pushing changes to upstream repository' | |
| @git.push | |
| puts "Checking out originating branch/tag — #{origin_reference}" | |
| @git.checkout(origin_reference) | |
| true | |
| end | |
| # rubocop:disable all | |
| def restore_git(origin_reference) | |
| puts 'Stashing docs changes for gh-pages' | |
| Git::Stash.new(@git, 'docs changes for gh-pages') | |
| puts "Checking out originating branch/tag — #{origin_reference}" | |
| @git.checkout(origin_reference) | |
| false | |
| end | |
| def previous_tag(current_version, language = nil) | |
| version = current_version.split(/\.|-/) | |
| if version.size > 3 | |
| puts 'WARNING - Changelogs not updated when set to prerelease' | |
| elsif version[2].to_i > 1 | |
| # specified as patch release | |
| patch_version = (version[2].to_i - 1).to_s | |
| "selenium-#{[[version[0]], version[1], patch_version].join('.')}-#{language}" | |
| elsif version[2] == '1' | |
| # specified as patch release; special case | |
| "selenium-#{[[version[0]], version[1], '0'].join('.')}" | |
| else | |
| minor_version = (version[1].to_i - 1) | |
| tags = @git.tags.map(&:name) | |
| tag = language ? tags.reverse.find { |tag| tag.match?(/selenium-4\.#{minor_version}.*-#{language}/) } : nil | |
| tag || "selenium-#{[[version[0]], minor_version, '0'].join('.')}" | |
| end | |
| end | |
| def update_changelog(version, language, path, changelog, header) | |
| tag = previous_tag(version, language) | |
| log = `git --no-pager log #{tag}...HEAD --pretty=format:">>> %B" --reverse #{path}` | |
| commits = log.split('>>>').map { |entry| | |
| lines = entry.split("\n") | |
| lines.reject! { |line| line.match?(/^(----|Co-authored|Signed-off)/) || line.empty? } | |
| lines.join("\n") | |
| }.join("\n>>>") | |
| File.open(changelog, 'r+') do |file| | |
| new_content = "#{header}\n#{commits}\n\n#{file.read}" | |
| file.rewind | |
| file.write(new_content) | |
| file.truncate(file.pos) | |
| end | |
| end | |
| def commit!(message, files = [], all: false) | |
| files.each do |file| | |
| puts "adding: #{file}" | |
| @git.add(file) | |
| end | |
| all ? @git.commit_all(message) : @git.commit(message) | |
| rescue Git::FailedError => e | |
| puts e.message | |
| end |