2026-01-23 22:35:39 -06:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
|
|
require 'find'
|
|
|
|
|
|
|
|
|
|
def python_version
|
|
|
|
|
File.foreach('py/BUILD.bazel') do |line|
|
|
|
|
|
return line.split('=').last.strip.tr('"', '') if line.include?('SE_VERSION')
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
def setup_pypirc
|
|
|
|
|
pypirc = File.join(Dir.home, '.pypirc')
|
|
|
|
|
return if File.exist?(pypirc) && File.read(pypirc).match?(/^\[pypi\]/m)
|
|
|
|
|
|
|
|
|
|
token = ENV.fetch('TWINE_PASSWORD', nil)
|
|
|
|
|
raise 'Missing PyPI credentials: set TWINE_PASSWORD or configure ~/.pypirc' if token.nil? || token.empty?
|
|
|
|
|
|
|
|
|
|
pypi_section = <<~PYPIRC
|
|
|
|
|
[pypi]
|
|
|
|
|
username = __token__
|
|
|
|
|
password = #{token}
|
|
|
|
|
PYPIRC
|
|
|
|
|
|
|
|
|
|
if File.exist?(pypirc)
|
|
|
|
|
File.open(pypirc, 'a') { |f| f.puts("\n#{pypi_section}") }
|
|
|
|
|
else
|
|
|
|
|
File.write(pypirc, pypi_section)
|
|
|
|
|
end
|
|
|
|
|
File.chmod(0o600, pypirc)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Build Python wheel and sdist with optional arguments'
|
|
|
|
|
task :build do |_task, arguments|
|
|
|
|
|
args = arguments.to_a
|
|
|
|
|
Bazel.execute('build', args, '//py:selenium-wheel')
|
|
|
|
|
Bazel.execute('build', args, '//py:selenium-sdist')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Validate Python release credentials'
|
|
|
|
|
task :check_credentials do |_task, arguments|
|
|
|
|
|
nightly = arguments.to_a.include?('nightly')
|
|
|
|
|
next if nightly
|
|
|
|
|
|
|
|
|
|
pypirc = File.join(Dir.home, '.pypirc')
|
|
|
|
|
has_pypirc = File.exist?(pypirc) && File.read(pypirc).match?(/^\[pypi\]/m)
|
|
|
|
|
has_env = ENV.fetch('TWINE_PASSWORD', nil) && !ENV['TWINE_PASSWORD'].empty?
|
|
|
|
|
raise 'Missing PyPI credentials: set TWINE_PASSWORD or configure ~/.pypirc' unless has_pypirc || has_env
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Release Python wheel and sdist to pypi'
|
|
|
|
|
task :release do |_task, arguments|
|
|
|
|
|
nightly = arguments.to_a.include?('nightly')
|
|
|
|
|
Rake::Task['py:check_credentials'].invoke(*arguments.to_a)
|
|
|
|
|
setup_pypirc unless nightly
|
|
|
|
|
|
|
|
|
|
if nightly
|
|
|
|
|
puts 'Updating Python version to nightly...'
|
|
|
|
|
Rake::Task['py:version'].invoke('nightly')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
command = nightly ? '//py:selenium-release-nightly' : '//py:selenium-release'
|
|
|
|
|
puts "Running Python release command: #{command}"
|
|
|
|
|
Bazel.execute('run', ['--config=release'], command)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Verify Python package is published on PyPI'
|
|
|
|
|
task :verify do
|
|
|
|
|
SeleniumRake.verify_package_published("https://pypi.org/pypi/selenium/#{python_version}/json")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Copy known generated files for local development (use `./go py:local_dev all` to copy everything)'
|
|
|
|
|
task :local_dev, [:all] do |_task, arguments|
|
|
|
|
|
Bazel.execute('build', [], '//py:selenium')
|
|
|
|
|
|
|
|
|
|
bazel_bin = 'bazel-bin/py/selenium/webdriver'
|
|
|
|
|
lib_path = 'py/selenium/webdriver'
|
|
|
|
|
|
|
|
|
|
copy_all = arguments[:all] == 'all'
|
|
|
|
|
if copy_all
|
|
|
|
|
FileUtils.rm_rf("#{lib_path}/common/devtools")
|
|
|
|
|
FileUtils.cp_r("#{bazel_bin}/.", lib_path, remove_destination: true)
|
|
|
|
|
else
|
|
|
|
|
%w[common/devtools common/linux common/mac common/windows].each do |dir|
|
|
|
|
|
src = "#{bazel_bin}/#{dir}"
|
|
|
|
|
dest = "#{lib_path}/#{dir}"
|
|
|
|
|
next unless Dir.exist?(src)
|
|
|
|
|
|
|
|
|
|
FileUtils.rm_rf(dest)
|
|
|
|
|
FileUtils.cp_r(src, dest)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
%w[getAttribute.js isDisplayed.js findElements.js].each do |atom|
|
2026-03-18 18:25:00 -04:00
|
|
|
dest = "#{lib_path}/remote/#{atom}"
|
|
|
|
|
FileUtils.rm_f(dest)
|
|
|
|
|
FileUtils.cp("#{bazel_bin}/remote/#{atom}", dest)
|
2026-01-23 22:35:39 -06:00
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2026-01-31 14:26:06 -06:00
|
|
|
desc 'Generate and stage Python documentation'
|
2026-01-23 22:35:39 -06:00
|
|
|
task :docs do |_task, arguments|
|
|
|
|
|
if python_version.match?(/^\d+\.\d+\.\d+\.\d+$/) && !arguments.to_a.include?('force')
|
|
|
|
|
abort('Aborting documentation update: nightly versions should not update docs.')
|
|
|
|
|
end
|
2026-01-31 14:26:06 -06:00
|
|
|
|
|
|
|
|
Rake::Task['py:docs_generate'].invoke
|
|
|
|
|
|
|
|
|
|
FileUtils.mkdir_p('build/docs/api')
|
|
|
|
|
FileUtils.cp_r('bazel-bin/py/docs/_build/html/.', 'build/docs/api/py')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Generate Python documentation without staging'
|
|
|
|
|
task :docs_generate do
|
2026-01-23 22:35:39 -06:00
|
|
|
puts 'Generating Python documentation'
|
|
|
|
|
|
|
|
|
|
FileUtils.rm_rf('build/docs/api/py/')
|
|
|
|
|
|
|
|
|
|
# Generate API listing and stub files in source tree
|
|
|
|
|
Bazel.execute('run', [], '//py:generate-api-listing')
|
|
|
|
|
Bazel.execute('run', [], '//py:sphinx-autogen')
|
|
|
|
|
|
|
|
|
|
# Build docs (outputs to bazel-bin)
|
|
|
|
|
Bazel.execute('build', [], '//py:docs')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Install Python wheel locally'
|
|
|
|
|
task :install do
|
|
|
|
|
Bazel.execute('build', [], '//py:selenium-wheel')
|
|
|
|
|
sh 'pip install bazel-bin/py/selenium-*.whl'
|
|
|
|
|
end
|
|
|
|
|
|
2026-01-25 15:23:17 -06:00
|
|
|
desc 'Pin Python dependencies'
|
|
|
|
|
task :pin do
|
|
|
|
|
Bazel.execute('run', [], '//scripts:update_py_deps')
|
|
|
|
|
Bazel.execute('run', [], '//py:requirements.update')
|
|
|
|
|
SeleniumRake.git.add('py/requirements.txt')
|
|
|
|
|
SeleniumRake.git.add('py/requirements_lock.txt')
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
desc 'Update Python dependencies (alias for pin)'
|
|
|
|
|
task update: :pin
|
|
|
|
|
|
2026-01-23 22:35:39 -06:00
|
|
|
desc 'Update Python changelog'
|
|
|
|
|
task :changelogs do
|
|
|
|
|
header = "Selenium #{python_version}"
|
|
|
|
|
SeleniumRake.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 = ".#{Time.now.strftime('%Y%m%d%H%M')}"
|
|
|
|
|
new_version = SeleniumRake.updated_version(old_version, arguments[:version], nightly)
|
|
|
|
|
puts "Updating Python from #{old_version} to #{new_version}"
|
|
|
|
|
|
|
|
|
|
['py/pyproject.toml',
|
|
|
|
|
'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('.')
|
|
|
|
|
|
|
|
|
|
conf = 'py/docs/source/conf.py'
|
|
|
|
|
text = File.read(conf).gsub(old_short_version, new_short_version)
|
|
|
|
|
File.open(conf, 'w') { |f| f.puts text }
|
|
|
|
|
end
|
|
|
|
|
|
2026-01-31 14:26:06 -06:00
|
|
|
desc 'Format Python code with ruff'
|
|
|
|
|
task :format do
|
2026-01-25 13:47:35 -06:00
|
|
|
puts ' Running ruff format...'
|
2026-01-31 14:26:06 -06:00
|
|
|
Bazel.execute('run', [], '//py:ruff-format')
|
2026-01-25 13:47:35 -06:00
|
|
|
end
|
|
|
|
|
|
2026-01-31 14:26:06 -06:00
|
|
|
desc 'Run Python linters (ruff check, mypy, docs)'
|
|
|
|
|
task :lint do
|
2026-01-23 22:35:39 -06:00
|
|
|
puts ' Running ruff check...'
|
2026-02-02 12:46:51 -05:00
|
|
|
Bazel.execute('run', [], '//py:ruff-check')
|
2026-01-25 13:47:35 -06:00
|
|
|
puts ' Running mypy...'
|
|
|
|
|
Bazel.execute('run', [], '//py:mypy')
|
2026-01-31 14:26:06 -06:00
|
|
|
Rake::Task['py:docs_generate'].invoke
|
2026-01-23 22:35:39 -06:00
|
|
|
end
|