Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,6 @@ jobs:
gem-trusted-publishing: ${{ matrix.language == 'ruby' }}
node-version: ${{ matrix.language == 'javascript' && '24.14.1' || '' }}
run: |
if [ "${{ matrix.language == 'java' && (needs.parse-tag.outputs.language == 'all' || needs.parse-tag.outputs.language == 'java') && github.run_attempt > 1 }}" = "true" ]; then
echo "::error::Java release is not yet rerun-safe β€” check/drop the staging repo at https://central.sonatype.com/publishing/deployments and publish manually"
exit 1
fi
if [ "${{ needs.parse-tag.outputs.language == 'all' || needs.parse-tag.outputs.language == matrix.language }}" = "true" ]; then
./go ${{ matrix.language }}:release
else
Expand Down
16 changes: 12 additions & 4 deletions rake_tasks/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,20 @@ def self.aggregate_errors(**steps)
raise failures.join("\n\n")
end

# Takes a url or a prepared request, so callers needing headers can build their own.
def self.get_request(target)
request = target.is_a?(Net::HTTPRequest) ? target : Net::HTTP::Get.new(URI(target))
uri = request.uri
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https',
open_timeout: 10, read_timeout: 60) do |http|
http.request(request)
end
end

def self.verify_package_published(url)
puts "Verifying #{url}..."
uri = URI(url)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.request(Net::HTTP::Get.new(uri))
end
res = get_request(url)
raise "Package not published: #{url}" unless res.is_a?(Net::HTTPSuccess)

puts 'Verified!'
Expand Down
16 changes: 0 additions & 16 deletions rake_tasks/dotnet.rake
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,6 @@ desc 'Build, package, and push nupkg files to NuGet'
task :release do |_task, arguments|
nightly = arguments.to_a.include?('nightly')

unless nightly
already_published = begin
Rake::Task['dotnet:verify'].invoke
true
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
rescue StandardError
false
ensure
Rake::Task['dotnet:verify'].reenable
end

if already_published
puts '.NET packages already published β€” skipping release.'
next
end
end

Rake::Task['dotnet:check_credentials'].invoke(*arguments.to_a)

if nightly
Expand Down
173 changes: 101 additions & 72 deletions rake_tasks/java.rake
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'base64'
require 'json'
require 'net/http'

# use #java_release_targets to access this list
Expand Down Expand Up @@ -72,64 +73,111 @@ def verify_java_release_targets
raise error_message
end

def read_m2_user_pass
settings_path = File.join(Dir.home, '.m2', 'settings.xml')
unless File.exist?(settings_path)
warn "Maven settings file not found at #{settings_path}"
return
end
module Sonatype
module_function

def load_credentials
ENV['MAVEN_USER'] ||= ENV.fetch('SEL_M2_USER', nil)
ENV['MAVEN_PASSWORD'] ||= ENV.fetch('SEL_M2_PASS', nil)
return if ENV['MAVEN_PASSWORD'] && ENV['MAVEN_USER']

puts 'Maven environment variables not set, inspecting ~/.m2/settings.xml.'
settings = File.read(settings_path)
found_section = false
settings.each_line do |line|
if !found_section
found_section = line.include? '<id>central</id>'
elsif line.include?('<username>')
ENV['MAVEN_USER'] = line[%r{<username>(.*?)</username>}, 1]
elsif line.include?('<password>')
ENV['MAVEN_PASSWORD'] = line[%r{<password>(.*?)</password>}, 1]
settings_path = File.join(Dir.home, '.m2', 'settings.xml')
unless File.exist?(settings_path)
warn "Maven settings file not found at #{settings_path}"
return
end
break if ENV['MAVEN_PASSWORD'] && ENV['MAVEN_USER']
end
end

def sonatype_auth_token
read_m2_user_pass unless ENV['MAVEN_PASSWORD'] && ENV['MAVEN_USER']
Base64.strict_encode64("#{ENV.fetch('MAVEN_USER')}:#{ENV.fetch('MAVEN_PASSWORD')}")
end
puts 'Maven environment variables not set, inspecting ~/.m2/settings.xml.'
settings = File.read(settings_path)
found_section = false
settings.each_line do |line|
if !found_section
found_section = line.include? '<id>central</id>'
elsif line.include?('<username>')
ENV['MAVEN_USER'] = line[%r{<username>(.*?)</username>}, 1]
elsif line.include?('<password>')
ENV['MAVEN_PASSWORD'] = line[%r{<password>(.*?)</password>}, 1]
end
break if ENV['MAVEN_PASSWORD'] && ENV['MAVEN_USER']
end
end

def trigger_sonatype_publish(token)
puts 'Triggering Sonatype upload with automatic publishing...'
url = 'https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/' \
'org.seleniumhq?publishing_type=automatic'
uri = URI(url)
def auth_token
load_credentials
Base64.strict_encode64("#{ENV.fetch('MAVEN_USER')}:#{ENV.fetch('MAVEN_PASSWORD')}")
end

req = Net::HTTP::Post.new(uri)
req['Authorization'] = "Basic #{token}"
req['Accept'] = '*/*'
req['Content-Length'] = '0'
def trigger_publish
puts 'Triggering Sonatype upload with automatic publishing...'
url = 'https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/' \
'org.seleniumhq?publishing_type=automatic'
uri = URI(url)

req = Net::HTTP::Post.new(uri)
req['Authorization'] = "Basic #{auth_token}"
req['Accept'] = '*/*'
req['Content-Length'] = '0'

begin
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
open_timeout: 10, read_timeout: 180) do |http|
http.request(req)
end
rescue Net::ReadTimeout, Net::OpenTimeout => e
warn <<~MSG
Request timed out.
The deployment may still have been created on the server.
Check https://central.sonatype.com/publishing/deployments for status.
MSG
raise e
end

begin
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true,
open_timeout: 10, read_timeout: 180) do |http|
http.request(req)
unless res.is_a?(Net::HTTPSuccess)
warn "Failed to trigger upload (HTTP #{res.code}): #{res.body}"
exit(1)
end
rescue Net::ReadTimeout, Net::OpenTimeout => e
warn <<~MSG
Request timed out.
The deployment may still have been created on the server.
Check https://central.sonatype.com/publishing/deployments for status.
MSG
raise e

puts 'Upload triggered β€” Sonatype will automatically validate and publish.'
end

def request_json(request)
request['Accept'] = 'application/json'
res = SeleniumRake.get_request(request)
raise "#{request.uri.path} returned HTTP #{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)

JSON.parse(res.body)
end

unless res.is_a?(Net::HTTPSuccess)
warn "Failed to trigger upload (HTTP #{res.code}): #{res.body}"
exit(1)
# Staging repositories are keyed by (user, IP, user agent) and the search defaults to the caller's
# IP, so ip=any is required to see one opened by a previous attempt on a different runner.
def staging_repositories
req = Net::HTTP::Get.new(URI('https://ossrh-staging-api.central.sonatype.com/manual/search/repositories?ip=any'))
req['Authorization'] = "Basic #{auth_token}"
request_json(req).fetch('repositories')
end

puts 'Upload triggered β€” Sonatype will automatically validate and publish.'
def published?(version)
query = "namespace=org.seleniumhq.selenium&name=selenium-java&version=#{version}"
req = Net::HTTP::Get.new(URI("https://central.sonatype.com/api/v1/publisher/published?#{query}"))
# The Portal documents Bearer, where the staging API shim above takes Basic. Same credential.
req['Authorization'] = "Bearer #{auth_token}"
request_json(req).fetch('published')
end

# A repository still open or closed is a previous attempt that has not landed β€” mid-publish, or
# rejected by the Portal. Both want a look at the deployments page before another deploy piles on.
def already_deployed?(version)
unfinished = %w[open closed]
if staging_repositories.any? { |repo| unfinished.include?(repo['state']) }
raise 'A previous attempt left a staging repository behind; check it at ' \
'https://central.sonatype.com/publishing/deployments before releasing again.'
end

return false unless published?(version)

puts "#{version} is already deployed β€” skipping the deploy."
true
end
end

desc 'Build Java Client Jars'
Expand Down Expand Up @@ -260,11 +308,8 @@ desc 'Validate Java release credentials'
task :check_credentials do |_task, arguments|
nightly = arguments.to_a.include?('nightly')

has_env = (ENV['MAVEN_USER'] || ENV.fetch('SEL_M2_USER',
nil)) && (ENV['MAVEN_PASSWORD'] || ENV.fetch('SEL_M2_PASS', nil))
settings = File.join(Dir.home, '.m2', 'settings.xml')
has_file = File.exist?(settings) && File.read(settings).include?('<id>central</id>')
unless has_env || has_file
Sonatype.load_credentials
unless ENV['MAVEN_USER'] && ENV['MAVEN_PASSWORD']
raise 'Missing Maven credentials: set MAVEN_USER/MAVEN_PASSWORD or configure ~/.m2/settings.xml'
end

Expand All @@ -279,26 +324,8 @@ task :release do |_task, arguments|
args = arguments.to_a
nightly = args.delete('nightly')

unless nightly
already_published = begin
SeleniumRake.verify_package_published(maven_central_pom_url)
true
rescue StandardError
false
end

if already_published
puts 'Java packages already published β€” skipping release.'
next
end
end

Rake::Task['java:check_credentials'].invoke(*(nightly ? ['nightly'] : []))

ENV['MAVEN_USER'] ||= ENV.fetch('SEL_M2_USER', nil)
ENV['MAVEN_PASSWORD'] ||= ENV.fetch('SEL_M2_PASS', nil)
token = sonatype_auth_token

repo_domain = 'central.sonatype.com'
repo = if nightly
"#{repo_domain}/repository/maven-snapshots"
Expand All @@ -317,12 +344,14 @@ task :release do |_task, arguments|
Rake::Task['java:package'].invoke('--config=release')
Rake::Task['java:build'].invoke('--config=release')

puts "Releasing Java artifacts to Maven repository at '#{ENV.fetch('MAVEN_REPO', nil)}'"
next if !nightly && Sonatype.already_deployed?(java_version)

puts "Deploying Java artifacts to '#{ENV.fetch('MAVEN_REPO', nil)}'"
java_release_targets.each { |target| Bazel.execute('run', ['--config=release'], target) }
Comment thread
titusfortner marked this conversation as resolved.

next if nightly

trigger_sonatype_publish(token)
Sonatype.trigger_publish
end

def maven_central_pom_url
Expand Down
Loading