rspec-tracer 0.6.2 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +333 -260
- data/lib/rspec_tracer/configuration.rb +12 -23
- data/lib/rspec_tracer/html_reporter/Rakefile +18 -0
- data/lib/rspec_tracer/remote_cache/Rakefile +38 -0
- data/lib/rspec_tracer/remote_cache/aws.rb +178 -0
- data/lib/rspec_tracer/remote_cache/cache.rb +36 -144
- data/lib/rspec_tracer/remote_cache/repo.rb +175 -0
- data/lib/rspec_tracer/remote_cache/validator.rb +52 -0
- data/lib/rspec_tracer/reporter.rb +52 -0
- data/lib/rspec_tracer/rspec_runner.rb +14 -4
- data/lib/rspec_tracer/runner.rb +6 -0
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +16 -13
- metadata +28 -21
- data/lib/rspec_tracer/remote_cache/git.rb +0 -113
@@ -14,21 +14,18 @@ module RSpecTracer
|
|
14
14
|
return @root if defined?(@root) && root.nil?
|
15
15
|
|
16
16
|
@cache_path = nil
|
17
|
+
@report_path = nil
|
18
|
+
@coverage_path = nil
|
19
|
+
|
17
20
|
@root = File.expand_path(root || Dir.getwd)
|
18
21
|
end
|
19
22
|
|
20
|
-
def project_name
|
21
|
-
return @project_name if defined?(@project_name) && proj_name.nil?
|
22
|
-
|
23
|
-
@project_name = proj_name if proj_name.is_a?(String)
|
23
|
+
def project_name
|
24
24
|
@project_name ||= File.basename(root).capitalize
|
25
25
|
end
|
26
26
|
|
27
|
-
def cache_dir
|
28
|
-
|
29
|
-
|
30
|
-
@cache_path = nil
|
31
|
-
@cache_dir = dir || DEFAULT_CACHE_DIR
|
27
|
+
def cache_dir
|
28
|
+
@cache_dir ||= (ENV['RSPEC_TRACER_CACHE_DIR'] || DEFAULT_CACHE_DIR)
|
32
29
|
end
|
33
30
|
|
34
31
|
def cache_path
|
@@ -42,15 +39,12 @@ module RSpecTracer
|
|
42
39
|
end
|
43
40
|
end
|
44
41
|
|
45
|
-
def report_dir
|
46
|
-
|
47
|
-
|
48
|
-
@report_path = nil
|
49
|
-
@report_dir = dir || DEFAULT_REPORT_DIR
|
42
|
+
def report_dir
|
43
|
+
@report_dir ||= (ENV['RSPEC_TRACER_REPORT_DIR'] || DEFAULT_REPORT_DIR)
|
50
44
|
end
|
51
45
|
|
52
46
|
def report_path
|
53
|
-
@report_path
|
47
|
+
@report_path ||= begin
|
54
48
|
report_path = File.expand_path(report_dir, root)
|
55
49
|
report_path = File.join(report_path, ENV['TEST_SUITE_ID'].to_s)
|
56
50
|
|
@@ -60,11 +54,8 @@ module RSpecTracer
|
|
60
54
|
end
|
61
55
|
end
|
62
56
|
|
63
|
-
def coverage_dir
|
64
|
-
|
65
|
-
|
66
|
-
@coverage_path = nil
|
67
|
-
@coverage_dir = dir || DEFAULT_COVERAGE_DIR
|
57
|
+
def coverage_dir
|
58
|
+
@coverage_dir ||= (ENV['RSPEC_TRACER_COVERAGE_DIR'] || DEFAULT_COVERAGE_DIR)
|
68
59
|
end
|
69
60
|
|
70
61
|
def coverage_path
|
@@ -103,9 +94,7 @@ module RSpecTracer
|
|
103
94
|
end
|
104
95
|
|
105
96
|
def verbose?
|
106
|
-
|
107
|
-
|
108
|
-
@verbose = ENV.fetch('RSPEC_TRACER_VERBOSE', 'false') == 'true'
|
97
|
+
@verbose ||= (ENV.fetch('RSPEC_TRACER_VERBOSE', 'false') == 'true')
|
109
98
|
end
|
110
99
|
|
111
100
|
def configure(&block)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :assets do
|
4
|
+
desc 'Compiles all assets'
|
5
|
+
task :precompile do
|
6
|
+
require 'sprockets'
|
7
|
+
|
8
|
+
assets = Sprockets::Environment.new do |env|
|
9
|
+
env.append_path 'assets/javascripts'
|
10
|
+
env.append_path 'assets/stylesheets'
|
11
|
+
env.js_compressor = :uglifier
|
12
|
+
env.css_compressor = :yui
|
13
|
+
end
|
14
|
+
|
15
|
+
assets['application.js'].write_to('public/application.js')
|
16
|
+
assets['application.css'].write_to('public/application.css')
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :rspec_tracer do
|
4
|
+
namespace :remote_cache do
|
5
|
+
desc 'Download cache'
|
6
|
+
task :download do
|
7
|
+
unless system('git', 'rev-parse', 'HEAD', out: File::NULL, err: File::NULL)
|
8
|
+
puts 'Not a git repository'
|
9
|
+
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rspec_tracer'
|
14
|
+
|
15
|
+
RSpecTracer::RemoteCache::Cache.new.download
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Upload cache'
|
19
|
+
task :upload do
|
20
|
+
unless system('git', 'rev-parse', 'HEAD', out: File::NULL, err: File::NULL)
|
21
|
+
puts 'Not a git repository'
|
22
|
+
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
unless ENV.fetch('CI', 'false') == 'true' || ENV.fetch('RSPEC_TRACER_UPLOAD_LOCAL_CACHE', 'false') == 'true'
|
27
|
+
puts 'Skipping upload from local development environment'
|
28
|
+
puts 'Use RSPEC_TRACER_UPLOAD_LOCAL_CACHE=true to upload local cache'
|
29
|
+
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rspec_tracer'
|
34
|
+
|
35
|
+
RSpecTracer::RemoteCache::Cache.new.upload
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Aws
|
6
|
+
class AwsError < StandardError; end
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@s3_bucket, @s3_path = setup_s3
|
10
|
+
@aws_cli = setup_aws_cli
|
11
|
+
end
|
12
|
+
|
13
|
+
def branch_refs?(branch_name)
|
14
|
+
key = "#{@s3_path}/branch-refs/#{branch_name}/branch_refs.json"
|
15
|
+
|
16
|
+
system(
|
17
|
+
@aws_cli,
|
18
|
+
's3api',
|
19
|
+
'head-object',
|
20
|
+
'--bucket',
|
21
|
+
@s3_bucket,
|
22
|
+
'--key',
|
23
|
+
key,
|
24
|
+
out: File::NULL,
|
25
|
+
err: File::NULL
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def download_branch_refs(branch_name, file_name)
|
30
|
+
key = "#{@s3_path}/branch-refs/#{branch_name}/branch_refs.json"
|
31
|
+
|
32
|
+
system(
|
33
|
+
@aws_cli,
|
34
|
+
's3api',
|
35
|
+
'get-object',
|
36
|
+
'--bucket',
|
37
|
+
@s3_bucket,
|
38
|
+
'--key',
|
39
|
+
key,
|
40
|
+
file_name,
|
41
|
+
out: File::NULL,
|
42
|
+
err: File::NULL
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def upload_branch_refs(branch_name, file_name)
|
47
|
+
remote_path = "s3://#{@s3_bucket}/#{@s3_path}/branch-refs/#{branch_name}/branch_refs.json"
|
48
|
+
|
49
|
+
raise AwsError, "Failed to upload branch refs for #{branch_name} branch" unless system(
|
50
|
+
@aws_cli,
|
51
|
+
's3',
|
52
|
+
'cp',
|
53
|
+
file_name,
|
54
|
+
remote_path,
|
55
|
+
out: File::NULL,
|
56
|
+
err: File::NULL
|
57
|
+
)
|
58
|
+
|
59
|
+
puts "Uploaded branch refs for #{branch_name} branch to #{remote_path}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def cache_files_list(ref)
|
63
|
+
prefix = "s3://#{@s3_bucket}/#{@s3_path}/#{ref}/"
|
64
|
+
|
65
|
+
`#{@aws_cli} s3 ls #{prefix} --recursive`.chomp.split("\n")
|
66
|
+
end
|
67
|
+
|
68
|
+
def download_file(ref, file_name)
|
69
|
+
remote_path = File.join(s3_dir(ref), file_name)
|
70
|
+
local_path = File.join(RSpecTracer.cache_path, file_name)
|
71
|
+
|
72
|
+
raise AwsError, "Failed to download file #{remote_path}" unless system(
|
73
|
+
@aws_cli,
|
74
|
+
's3',
|
75
|
+
'cp',
|
76
|
+
remote_path,
|
77
|
+
local_path,
|
78
|
+
out: File::NULL,
|
79
|
+
err: File::NULL
|
80
|
+
)
|
81
|
+
|
82
|
+
puts "Downloaded file #{remote_path} to #{local_path}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def download_dir(ref, run_id)
|
86
|
+
remote_dir = s3_dir(ref, run_id)
|
87
|
+
local_dir = File.join(RSpecTracer.cache_path, run_id)
|
88
|
+
|
89
|
+
raise AwsError, "Failed to download files from #{remote_dir}" unless system(
|
90
|
+
@aws_cli,
|
91
|
+
's3',
|
92
|
+
'cp',
|
93
|
+
remote_dir,
|
94
|
+
local_dir,
|
95
|
+
'--recursive',
|
96
|
+
out: File::NULL,
|
97
|
+
err: File::NULL
|
98
|
+
)
|
99
|
+
|
100
|
+
puts "Downloaded cache files from #{remote_dir} to #{local_dir}"
|
101
|
+
rescue AwsError => e
|
102
|
+
FileUtils.rm_rf(local_dir)
|
103
|
+
|
104
|
+
raise e
|
105
|
+
end
|
106
|
+
|
107
|
+
def upload_file(ref, file_name)
|
108
|
+
remote_path = File.join(s3_dir(ref), file_name)
|
109
|
+
local_path = File.join(RSpecTracer.cache_path, file_name)
|
110
|
+
|
111
|
+
raise AwsError, "Failed to upload file #{local_path}" unless system(
|
112
|
+
@aws_cli,
|
113
|
+
's3',
|
114
|
+
'cp',
|
115
|
+
local_path,
|
116
|
+
remote_path,
|
117
|
+
out: File::NULL,
|
118
|
+
err: File::NULL
|
119
|
+
)
|
120
|
+
|
121
|
+
puts "Uploaded file #{local_path} to #{remote_path}"
|
122
|
+
end
|
123
|
+
|
124
|
+
def upload_dir(ref, run_id)
|
125
|
+
remote_dir = s3_dir(ref, run_id)
|
126
|
+
local_dir = File.join(RSpecTracer.cache_path, run_id)
|
127
|
+
|
128
|
+
raise AwsError, "Failed to download files from #{local_dir}" unless system(
|
129
|
+
@aws_cli,
|
130
|
+
's3',
|
131
|
+
'cp',
|
132
|
+
local_dir,
|
133
|
+
remote_dir,
|
134
|
+
'--recursive',
|
135
|
+
out: File::NULL,
|
136
|
+
err: File::NULL
|
137
|
+
)
|
138
|
+
|
139
|
+
puts "Uploaded files from #{local_dir} to #{remote_dir}"
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
|
144
|
+
def setup_s3
|
145
|
+
s3_uri = ENV['RSPEC_TRACER_S3_URI']
|
146
|
+
|
147
|
+
raise AwsError, 'RSPEC_TRACER_S3_URI environment variable is not set' if s3_uri.nil?
|
148
|
+
|
149
|
+
uri_parts = s3_uri[4..-1].split('/')
|
150
|
+
|
151
|
+
raise AwsError, "Invalid S3 URI #{s3_uri}" unless uri_parts.length >= 3 && uri_parts.first.empty?
|
152
|
+
|
153
|
+
[
|
154
|
+
uri_parts[1],
|
155
|
+
uri_parts[2..-1].join('/')
|
156
|
+
]
|
157
|
+
end
|
158
|
+
|
159
|
+
def setup_aws_cli
|
160
|
+
if ENV.fetch('LOCAL_AWS', 'false') == 'true'
|
161
|
+
'awslocal'
|
162
|
+
else
|
163
|
+
'aws'
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def s3_dir(ref, run_id = nil)
|
168
|
+
test_suite_id = ENV['TEST_SUITE_ID']
|
169
|
+
|
170
|
+
if test_suite_id.nil?
|
171
|
+
"s3://#{@s3_bucket}/#{@s3_path}/#{ref}/#{run_id}/".sub(%r{/+$}, '/')
|
172
|
+
else
|
173
|
+
"s3://#{@s3_bucket}/#{@s3_path}/#{ref}/#{test_suite_id}/#{run_id}/".sub(%r{/+$}, '/')
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -1,186 +1,78 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'aws'
|
4
|
+
require_relative 'repo'
|
5
|
+
require_relative 'validator'
|
4
6
|
|
5
7
|
module RSpecTracer
|
6
8
|
module RemoteCache
|
7
9
|
class Cache
|
8
|
-
class
|
9
|
-
|
10
|
-
class CacheUploadError < StandardError; end
|
11
|
-
|
12
|
-
class LocalCacheNotFoundError < StandardError; end
|
13
|
-
|
14
|
-
CACHE_FILES_PER_TEST_SUITE = 8
|
10
|
+
class CacheError < StandardError; end
|
15
11
|
|
16
12
|
def initialize
|
17
|
-
@
|
18
|
-
@
|
19
|
-
'awslocal'
|
20
|
-
else
|
21
|
-
'aws'
|
22
|
-
end
|
13
|
+
@aws = RSpecTracer::RemoteCache::Aws.new
|
14
|
+
@repo = RSpecTracer::RemoteCache::Repo.new(@aws)
|
23
15
|
end
|
24
16
|
|
25
17
|
def download
|
26
|
-
|
27
|
-
puts 'S3 URI is not configured'
|
28
|
-
|
29
|
-
return
|
30
|
-
end
|
18
|
+
return unless cache_ref?
|
31
19
|
|
32
|
-
|
33
|
-
|
34
|
-
if @cache_sha.nil?
|
35
|
-
puts 'Could not find a suitable cache sha to download'
|
36
|
-
|
37
|
-
return
|
38
|
-
end
|
39
|
-
|
40
|
-
download_files
|
41
|
-
|
42
|
-
puts "Downloaded cache from #{@download_prefix} to #{@download_path}"
|
20
|
+
@aws.download_file(@cache_sha, 'last_run.json')
|
21
|
+
@aws.download_dir(@cache_sha, last_run_id)
|
43
22
|
rescue StandardError => e
|
44
|
-
puts "
|
23
|
+
puts "Error: #{e.message}"
|
24
|
+
puts e.backtrace.first(5).join("\n")
|
45
25
|
end
|
46
26
|
|
47
27
|
def upload
|
48
|
-
|
49
|
-
|
28
|
+
@aws.upload_file(@repo.branch_ref, 'last_run.json')
|
29
|
+
@aws.upload_dir(@repo.branch_ref, last_run_id)
|
50
30
|
|
51
|
-
|
52
|
-
end
|
31
|
+
file_name = File.join(RSpecTracer.cache_path, 'branch_refs.json')
|
53
32
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
puts "
|
58
|
-
|
59
|
-
puts "Errored: #{e.message}"
|
33
|
+
write_branch_refs(file_name)
|
34
|
+
@aws.upload_branch_refs(@repo.branch_name, file_name)
|
35
|
+
rescue StandardError => e
|
36
|
+
puts "Error: #{e.message}"
|
37
|
+
puts e.backtrace.first(5).join("\n")
|
60
38
|
end
|
61
39
|
|
62
40
|
private
|
63
41
|
|
64
|
-
def
|
65
|
-
|
66
|
-
@test_suites = ENV['TEST_SUITES']
|
42
|
+
def cache_ref?
|
43
|
+
cache_validator = RSpecTracer::RemoteCache::Validator.new
|
67
44
|
|
68
|
-
|
69
|
-
|
70
|
-
CacheDownloadError,
|
71
|
-
'Both the enviornment variables TEST_SUITE_ID and TEST_SUITES are not set'
|
72
|
-
)
|
73
|
-
end
|
74
|
-
|
75
|
-
@git = RSpecTracer::RemoteCache::Git.new
|
76
|
-
@git.prepare_for_download
|
45
|
+
@cache_sha = @repo.cache_refs.each_key.detect do |ref|
|
46
|
+
puts "Validating ref #{ref}"
|
77
47
|
|
78
|
-
|
79
|
-
|
80
|
-
@cache_sha = nearest_cache_sha
|
81
|
-
end
|
82
|
-
|
83
|
-
def generate_cached_files_count_and_regex
|
84
|
-
if @test_suites.nil?
|
85
|
-
@last_run_files_count = 1
|
86
|
-
@last_run_files_regex = '/%<ref>s/last_run.json$'
|
87
|
-
@cached_files_count = CACHE_FILES_PER_TEST_SUITE
|
88
|
-
@cached_files_regex = '/%<ref>s/[0-9a-f]{32}/.+.json'
|
89
|
-
else
|
90
|
-
@test_suites = @test_suites.to_i
|
91
|
-
@test_suites_regex = (1..@test_suites).to_a.join('|')
|
92
|
-
|
93
|
-
@last_run_files_count = @test_suites
|
94
|
-
@last_run_files_regex = "/%<ref>s/(#{@test_suites_regex})/last_run.json$"
|
95
|
-
@cached_files_count = CACHE_FILES_PER_TEST_SUITE * @test_suites.to_i
|
96
|
-
@cached_files_regex = "/%<ref>s/(#{@test_suites_regex})/[0-9a-f]{32}/.+.json$"
|
48
|
+
cache_validator.valid?(ref, @aws.cache_files_list(ref))
|
97
49
|
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def nearest_cache_sha
|
101
|
-
@git.ref_list.detect do |ref|
|
102
|
-
prefix = "#{@s3_uri}/#{ref}/"
|
103
|
-
|
104
|
-
puts "Testing prefix #{prefix}"
|
105
|
-
|
106
|
-
objects = `#{@aws_s3} s3 ls #{prefix} --recursive`.chomp.split("\n")
|
107
50
|
|
108
|
-
|
109
|
-
|
110
|
-
next if objects.count { |object| object.match?(last_run_regex) } != @last_run_files_count
|
111
|
-
|
112
|
-
cache_regex = Regexp.new(format(@cached_files_regex, ref: ref))
|
51
|
+
if @cache_sha.nil?
|
52
|
+
puts 'Could not find a suitable cache sha to download'
|
113
53
|
|
114
|
-
|
54
|
+
return false
|
115
55
|
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def download_files
|
119
|
-
@download_prefix = "#{@s3_uri}/#{@cache_sha}/#{@test_suite_id}/".sub(%r{/+$}, '/')
|
120
|
-
@download_path = RSpecTracer.cache_path
|
121
|
-
|
122
|
-
raise CacheDownloadError, 'Failed to download cache files' unless system(
|
123
|
-
@aws_s3, 's3', 'cp',
|
124
|
-
File.join(@download_prefix, 'last_run.json'),
|
125
|
-
@download_path,
|
126
|
-
out: File::NULL, err: File::NULL
|
127
|
-
)
|
128
56
|
|
129
|
-
|
57
|
+
true
|
58
|
+
end
|
130
59
|
|
131
|
-
|
132
|
-
|
133
|
-
File.join(@download_prefix, @run_id),
|
134
|
-
File.join(@download_path, @run_id),
|
135
|
-
'--recursive',
|
136
|
-
out: File::NULL, err: File::NULL
|
137
|
-
)
|
60
|
+
def write_branch_refs(file_name)
|
61
|
+
branch_ref_time = `git show --no-patch --format="%ct" #{@repo.branch_ref}`.chomp
|
138
62
|
|
139
|
-
|
63
|
+
puts "Failed to find object #{@repo.branch_ref} commit timestamp" unless $CHILD_STATUS.success?
|
140
64
|
|
141
|
-
|
142
|
-
end
|
65
|
+
ref_list = @repo.branch_refs.merge(@repo.branch_ref => branch_ref_time.to_i)
|
143
66
|
|
144
|
-
|
145
|
-
@git = RSpecTracer::RemoteCache::Git.new
|
146
|
-
@test_suite_id = ENV['TEST_SUITE_ID']
|
147
|
-
@upload_prefix = if @test_suite_id.nil?
|
148
|
-
"#{@s3_uri}/#{@git.branch_ref}/"
|
149
|
-
else
|
150
|
-
"#{@s3_uri}/#{@git.branch_ref}/#{@test_suite_id}/"
|
151
|
-
end
|
152
|
-
|
153
|
-
@upload_path = RSpecTracer.cache_path
|
154
|
-
@run_id = last_run_id
|
155
|
-
end
|
156
|
-
|
157
|
-
def upload_files
|
158
|
-
return if system(
|
159
|
-
@aws_s3, 's3', 'cp',
|
160
|
-
File.join(@upload_path, 'last_run.json'),
|
161
|
-
@upload_prefix,
|
162
|
-
out: File::NULL, err: File::NULL
|
163
|
-
) && system(
|
164
|
-
@aws_s3, 's3', 'cp',
|
165
|
-
File.join(@upload_path, @run_id),
|
166
|
-
File.join(@upload_prefix, @run_id),
|
167
|
-
'--recursive',
|
168
|
-
out: File::NULL, err: File::NULL
|
169
|
-
)
|
170
|
-
|
171
|
-
raise CacheUploadError, 'Failed to upload cache files'
|
67
|
+
File.write(file_name, JSON.pretty_generate(ref_list))
|
172
68
|
end
|
173
69
|
|
174
70
|
def last_run_id
|
175
71
|
file_name = File.join(RSpecTracer.cache_path, 'last_run.json')
|
176
72
|
|
177
|
-
|
178
|
-
|
179
|
-
run_id = JSON.parse(File.read(file_name))['run_id']
|
180
|
-
|
181
|
-
raise LocalCacheNotFoundError, 'Could not find any local cache to upload' if run_id.nil?
|
73
|
+
raise CacheError, 'Could not find any local cache to upload' unless File.file?(file_name)
|
182
74
|
|
183
|
-
run_id
|
75
|
+
JSON.parse(File.read(file_name))['run_id']
|
184
76
|
end
|
185
77
|
end
|
186
78
|
end
|