mirror of
https://github.com/gitlabhq/gitlabhq.git
synced 2025-07-25 16:00:50 +00:00
192 lines
4.9 KiB
Ruby
Executable File
192 lines
4.9 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
require 'fileutils'
|
|
require 'json'
|
|
|
|
class TestsMetadata < Struct.new( # rubocop:disable Style/StructInheritance -- Otherwise we cannot define a nested constant
|
|
:mode,
|
|
:knapsack_report_path, :flaky_report_path, :fast_quarantine_path,
|
|
:average_knapsack,
|
|
keyword_init: true)
|
|
|
|
FALLBACK_JSON = '{}'
|
|
|
|
def main
|
|
abort("Unknown mode: `#{mode}`. It must be `retrieve` or `update`.") unless
|
|
mode == 'retrieve' || mode == 'update' || mode == 'verify'
|
|
|
|
if mode == 'verify'
|
|
verify
|
|
else
|
|
prepare_directories
|
|
retrieve
|
|
update if mode == 'update'
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def verify
|
|
verify_knapsack_report
|
|
verify_flaky_report
|
|
verify_fast_quarantine
|
|
puts 'OK'
|
|
end
|
|
|
|
def verify_knapsack_report
|
|
report = JSON.parse(File.read(knapsack_report_path))
|
|
|
|
valid = report.is_a?(Hash) &&
|
|
report.all? do |spec, duration|
|
|
spec.is_a?(String) && duration.is_a?(Numeric)
|
|
end
|
|
|
|
valid || abort("#{knapsack_report_path} is not a valid Knapsack report")
|
|
rescue JSON::ParserError
|
|
abort("#{knapsack_report_path} is not valid JSON")
|
|
end
|
|
|
|
def verify_flaky_report
|
|
# This requires activesupport
|
|
require_relative '../../gems/gitlab-rspec_flaky/lib/gitlab/rspec_flaky/report'
|
|
|
|
Gitlab::RspecFlaky::Report.load(flaky_report_path).flaky_examples.to_h
|
|
rescue JSON::ParserError
|
|
abort("#{flaky_report_path} is not valid JSON")
|
|
end
|
|
|
|
def verify_fast_quarantine
|
|
require_relative '../../tooling/lib/tooling/fast_quarantine'
|
|
|
|
fast_quarantine =
|
|
Tooling::FastQuarantine.new(fast_quarantine_path: fast_quarantine_path)
|
|
|
|
fast_quarantine.identifiers
|
|
end
|
|
|
|
def prepare_directories
|
|
FileUtils.mkdir_p([
|
|
File.dirname(knapsack_report_path),
|
|
File.dirname(flaky_report_path),
|
|
File.dirname(fast_quarantine_path)
|
|
])
|
|
end
|
|
|
|
def retrieve
|
|
tasks = []
|
|
|
|
tasks << async_curl_download_json(
|
|
url: "https://gitlab-org.gitlab.io/gitlab/#{knapsack_report_path}",
|
|
path: knapsack_report_path,
|
|
fallback_content: FALLBACK_JSON
|
|
)
|
|
|
|
tasks << async_curl_download_json(
|
|
url: "https://gitlab-org.gitlab.io/gitlab/#{flaky_report_path}",
|
|
path: flaky_report_path,
|
|
fallback_content: FALLBACK_JSON
|
|
)
|
|
|
|
tasks << async_curl_download(
|
|
url: "https://gitlab-org.gitlab.io/quality/engineering-productivity/fast-quarantine/#{fast_quarantine_path}",
|
|
path: fast_quarantine_path,
|
|
fallback_content: ''
|
|
)
|
|
|
|
tasks.compact.each(&:join)
|
|
end
|
|
|
|
def update
|
|
update_knapsack_report
|
|
update_flaky_report
|
|
# Prune flaky tests that weren't flaky in the last 7 days, *after* updating the flaky tests detected
|
|
# in this pipeline, so that first_flaky_at for tests that are still flaky is maintained.
|
|
prune_flaky_report
|
|
end
|
|
|
|
def update_knapsack_report
|
|
new_reports = Dir["#{File.dirname(knapsack_report_path)}/rspec*.json"]
|
|
|
|
if average_knapsack
|
|
system_abort_if_failed(%W[
|
|
scripts/pipeline/average_reports.rb
|
|
-i #{knapsack_report_path}
|
|
-n #{new_reports.join(',')}
|
|
])
|
|
else
|
|
system_abort_if_failed(%W[
|
|
scripts/merge-reports
|
|
#{knapsack_report_path}
|
|
#{new_reports.join(' ')}
|
|
])
|
|
end
|
|
end
|
|
|
|
def update_flaky_report
|
|
new_reports = Dir["#{File.dirname(flaky_report_path)}/all_*.json"]
|
|
|
|
system_abort_if_failed(%W[
|
|
scripts/merge-reports
|
|
#{flaky_report_path}
|
|
#{new_reports.join(' ')}
|
|
])
|
|
end
|
|
|
|
def prune_flaky_report
|
|
system_abort_if_failed(%W[
|
|
scripts/flaky_examples/prune-old-flaky-examples
|
|
#{flaky_report_path}
|
|
])
|
|
end
|
|
|
|
def async_curl_download_json(**args)
|
|
async_curl_download(**args) do |content|
|
|
JSON.parse(content)
|
|
rescue JSON::ParserError
|
|
false
|
|
end
|
|
end
|
|
|
|
def async_curl_download(url:, path:, fallback_content:)
|
|
if force_download? || !File.exist?(path) # rubocop:disable Style/GuardClause -- This is easier to read
|
|
async do
|
|
success = system(*%W[curl --fail --location -o #{path} #{url}])
|
|
|
|
if success
|
|
if block_given? # rubocop:disable Style/IfUnlessModifier -- This is easier to read
|
|
yield(File.read(path)) || File.write(path, fallback_content)
|
|
end
|
|
else
|
|
File.write(path, fallback_content)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def force_download?
|
|
mode == 'retrieve'
|
|
end
|
|
|
|
def system_abort_if_failed(command)
|
|
system(*command) || abort("Command failed for: #{command.join(' ')}")
|
|
end
|
|
|
|
def async(&task)
|
|
Thread.new(&task)
|
|
end
|
|
end
|
|
|
|
if $PROGRAM_NAME == __FILE__
|
|
TestsMetadata.new(
|
|
mode: ARGV.first,
|
|
knapsack_report_path: ENV['KNAPSACK_RSPEC_SUITE_REPORT_PATH'] ||
|
|
'knapsack/report-master.json',
|
|
flaky_report_path: ENV['FLAKY_RSPEC_SUITE_REPORT_PATH'] ||
|
|
'rspec/flaky/report-suite.json',
|
|
fast_quarantine_path: ENV['RSPEC_FAST_QUARANTINE_PATH'] ||
|
|
'rspec/fast_quarantine-gitlab.txt',
|
|
average_knapsack: ENV['AVERAGE_KNAPSACK_REPORT'] == 'true'
|
|
).main
|
|
end
|