rspec-tracer 0.7.0 → 0.8.0
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 +6 -0
- data/README.md +198 -236
- data/lib/rspec_tracer/configuration.rb +12 -23
- 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/version.rb +1 -1
- metadata +6 -4
- data/lib/rspec_tracer/remote_cache/git.rb +0 -113
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Repo
|
6
|
+
class RepoError < StandardError; end
|
7
|
+
|
8
|
+
attr_reader :branch_name, :branch_ref, :branch_refs, :ancestry_refs, :cache_refs
|
9
|
+
|
10
|
+
def initialize(aws)
|
11
|
+
@aws = aws
|
12
|
+
@branch_name = ENV['GIT_BRANCH'].chomp
|
13
|
+
|
14
|
+
raise RepoError, 'GIT_BRANCH environment variable is not set' if @branch_name.nil?
|
15
|
+
|
16
|
+
fetch_head_ref
|
17
|
+
fetch_branch_ref
|
18
|
+
fetch_ancestry_refs
|
19
|
+
fetch_branch_refs
|
20
|
+
generate_cache_refs
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def fetch_head_ref
|
26
|
+
@head_ref = `git rev-parse HEAD`.chomp
|
27
|
+
|
28
|
+
raise RepoError, 'Could not find HEAD commit sha' unless $CHILD_STATUS.success?
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_branch_ref
|
32
|
+
@merged_parents = []
|
33
|
+
@ignored_refs = []
|
34
|
+
|
35
|
+
unless merged?
|
36
|
+
@branch_ref = @head_ref
|
37
|
+
|
38
|
+
return
|
39
|
+
end
|
40
|
+
|
41
|
+
@ignored_refs << @head_ref
|
42
|
+
|
43
|
+
fetch_merged_parents
|
44
|
+
fetch_merged_branch_ref
|
45
|
+
end
|
46
|
+
|
47
|
+
def fetch_ancestry_refs
|
48
|
+
ref_list = `git rev-list --max-count=25 #{@branch_ref}`.chomp.split
|
49
|
+
|
50
|
+
raise RepoError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
51
|
+
|
52
|
+
ref_list = ref_list.to_set - @ignored_refs
|
53
|
+
@ancestry_refs = refs_committer_timestamp(ref_list.to_a)
|
54
|
+
|
55
|
+
return if @ancestry_refs.empty?
|
56
|
+
|
57
|
+
print_refs(@ancestry_refs, 'ancestry')
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch_branch_refs
|
61
|
+
unless @aws.branch_refs?(@branch_name)
|
62
|
+
puts "No branch refs for #{@branch_name} branch found in S3"
|
63
|
+
|
64
|
+
@branch_refs = {}
|
65
|
+
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
download_branch_refs
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_cache_refs
|
73
|
+
ref_list = @ancestry_refs.merge(@branch_refs)
|
74
|
+
|
75
|
+
if ref_list.empty?
|
76
|
+
@cache_refs = {}
|
77
|
+
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
@cache_refs = ref_list.sort_by { |_, timestamp| -timestamp }.to_h
|
82
|
+
|
83
|
+
print_refs(@cache_refs, 'cache')
|
84
|
+
end
|
85
|
+
|
86
|
+
def merged?
|
87
|
+
system('git', 'rev-parse', 'HEAD^2', out: File::NULL, err: File::NULL)
|
88
|
+
end
|
89
|
+
|
90
|
+
def fetch_merged_parents
|
91
|
+
first_parent = `git rev-parse HEAD^1`.chomp
|
92
|
+
@merged_parents << first_parent if $CHILD_STATUS.success?
|
93
|
+
|
94
|
+
second_parent = `git rev-parse HEAD^2`.chomp
|
95
|
+
@merged_parents << second_parent if $CHILD_STATUS.success?
|
96
|
+
|
97
|
+
raise RepoError, 'Could not find merged commit parents' if @merged_parents.length != 2
|
98
|
+
end
|
99
|
+
|
100
|
+
def fetch_merged_branch_ref
|
101
|
+
@origin_head_ref = `git rev-parse origin/HEAD`.chomp
|
102
|
+
@branch_ref = nil
|
103
|
+
|
104
|
+
if @merged_parents.first != @origin_head_ref
|
105
|
+
@branch_ref = @head_ref
|
106
|
+
@ignored_refs = []
|
107
|
+
|
108
|
+
return
|
109
|
+
end
|
110
|
+
|
111
|
+
@branch_ref = @merged_parents.last
|
112
|
+
@ignored_refs = @ignored_refs.to_set | `git rev-list #{@branch_ref}..origin/HEAD`.chomp.split
|
113
|
+
|
114
|
+
raise RepoError, 'Could not find ignored refs' unless $CHILD_STATUS.success?
|
115
|
+
end
|
116
|
+
|
117
|
+
def refs_committer_timestamp(ref_list)
|
118
|
+
return {} if ref_list.empty?
|
119
|
+
|
120
|
+
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
121
|
+
git show
|
122
|
+
--no-patch
|
123
|
+
--format="%H %ct"
|
124
|
+
#{ref_list.join(' ')}
|
125
|
+
COMMAND
|
126
|
+
|
127
|
+
ref_list = `#{command}`.chomp
|
128
|
+
|
129
|
+
raise RepoError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
130
|
+
|
131
|
+
ref_list.split("\n").map(&:split).to_h.transform_values(&:to_i)
|
132
|
+
end
|
133
|
+
|
134
|
+
def download_branch_refs
|
135
|
+
file_name = File.join(RSpecTracer.cache_path, 'branch_refs.json')
|
136
|
+
|
137
|
+
if @aws.download_branch_refs(branch_name, file_name)
|
138
|
+
@branch_refs = JSON.parse(File.read(file_name)).transform_values(&:to_i)
|
139
|
+
|
140
|
+
return if @branch_refs.empty?
|
141
|
+
|
142
|
+
filter_branch_refs
|
143
|
+
print_refs(@branch_refs, 'branch')
|
144
|
+
else
|
145
|
+
@branch_refs = {}
|
146
|
+
|
147
|
+
File.rm_f(file_name)
|
148
|
+
|
149
|
+
puts "Failed to fetch branch refs for #{@branch_name} branch"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def filter_branch_refs
|
154
|
+
if @ancestry_refs.empty?
|
155
|
+
@branch_refs = @branch_refs.sort_by { |_, timestamp| -timestamp }.first(25).to_h
|
156
|
+
|
157
|
+
return
|
158
|
+
end
|
159
|
+
|
160
|
+
oldest_ancestry_time = @ancestry_refs.values.min
|
161
|
+
|
162
|
+
@branch_refs = @branch_refs
|
163
|
+
.select { |_, timestamp| timestamp >= oldest_ancestry_time }
|
164
|
+
.sort_by { |_, timestamp| -timestamp }
|
165
|
+
.first(25)
|
166
|
+
.to_h
|
167
|
+
end
|
168
|
+
|
169
|
+
def print_refs(refs, type)
|
170
|
+
puts "Fetched the following #{type} refs for #{@branch_name} branch:"
|
171
|
+
puts refs.map { |ref, timestamp| " * #{ref} (commit timestamp: #{timestamp})" }.join("\n")
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Validator
|
6
|
+
CACHE_FILES_PER_TEST_SUITE = 8
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@test_suite_id = ENV['TEST_SUITE_ID']
|
10
|
+
@test_suites = ENV['TEST_SUITES']
|
11
|
+
|
12
|
+
if @test_suite_id.nil? ^ @test_suites.nil?
|
13
|
+
raise(
|
14
|
+
ValidationError,
|
15
|
+
'Both the enviornment variables TEST_SUITE_ID and TEST_SUITES are not set'
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
setup
|
20
|
+
end
|
21
|
+
|
22
|
+
def valid?(ref, cache_files)
|
23
|
+
last_run_regex = Regexp.new(format(@last_run_files_regex, ref: ref))
|
24
|
+
|
25
|
+
return false if cache_files.count { |file| file.match?(last_run_regex) } != @last_run_files_count
|
26
|
+
|
27
|
+
cache_regex = Regexp.new(format(@cached_files_regex, ref: ref))
|
28
|
+
|
29
|
+
cache_files.count { |file| file.match?(cache_regex) } == @cached_files_count
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def setup
|
35
|
+
if @test_suites.nil?
|
36
|
+
@last_run_files_count = 1
|
37
|
+
@last_run_files_regex = '/%<ref>s/last_run.json$'
|
38
|
+
@cached_files_count = CACHE_FILES_PER_TEST_SUITE
|
39
|
+
@cached_files_regex = '/%<ref>s/[0-9a-f]{32}/.+.json'
|
40
|
+
else
|
41
|
+
@test_suites = @test_suites.to_i
|
42
|
+
@test_suites_regex = (1..@test_suites).to_a.join('|')
|
43
|
+
|
44
|
+
@last_run_files_count = @test_suites
|
45
|
+
@last_run_files_regex = "/%<ref>s/(#{@test_suites_regex})/last_run.json$"
|
46
|
+
@cached_files_count = CACHE_FILES_PER_TEST_SUITE * @test_suites
|
47
|
+
@cached_files_regex = "/%<ref>s/(#{@test_suites_regex})/[0-9a-f]{32}/.+.json$"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/rspec_tracer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Abhimanyu Singh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-09-
|
11
|
+
date: 2021-09-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|
@@ -94,8 +94,10 @@ files:
|
|
94
94
|
- lib/rspec_tracer/html_reporter/views/flaky_examples.erb
|
95
95
|
- lib/rspec_tracer/html_reporter/views/layout.erb
|
96
96
|
- lib/rspec_tracer/remote_cache/Rakefile
|
97
|
+
- lib/rspec_tracer/remote_cache/aws.rb
|
97
98
|
- lib/rspec_tracer/remote_cache/cache.rb
|
98
|
-
- lib/rspec_tracer/remote_cache/
|
99
|
+
- lib/rspec_tracer/remote_cache/repo.rb
|
100
|
+
- lib/rspec_tracer/remote_cache/validator.rb
|
99
101
|
- lib/rspec_tracer/reporter.rb
|
100
102
|
- lib/rspec_tracer/rspec_reporter.rb
|
101
103
|
- lib/rspec_tracer/rspec_runner.rb
|
@@ -109,7 +111,7 @@ licenses:
|
|
109
111
|
- MIT
|
110
112
|
metadata:
|
111
113
|
homepage_uri: https://github.com/avmnu-sng/rspec-tracer
|
112
|
-
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.
|
114
|
+
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.8.0
|
113
115
|
changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
|
114
116
|
bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
|
115
117
|
post_install_message:
|
@@ -1,113 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RSpecTracer
|
4
|
-
module RemoteCache
|
5
|
-
class Git
|
6
|
-
class GitOperationError < StandardError; end
|
7
|
-
|
8
|
-
attr_reader :branch_ref, :ref_list
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
fetch_head_ref
|
12
|
-
fetch_branch_ref
|
13
|
-
end
|
14
|
-
|
15
|
-
def prepare_for_download
|
16
|
-
fetch_unreachable_refs
|
17
|
-
fetch_ancestry_refs
|
18
|
-
fetch_ordered_refs
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def fetch_head_ref
|
24
|
-
@head_ref = `git rev-parse HEAD`.chomp
|
25
|
-
|
26
|
-
raise GitOperationError, 'Could not find HEAD commit sha' unless $CHILD_STATUS.success?
|
27
|
-
end
|
28
|
-
|
29
|
-
def fetch_branch_ref
|
30
|
-
@merged_parents = []
|
31
|
-
@ignored_refs = []
|
32
|
-
|
33
|
-
unless merged?
|
34
|
-
@branch_ref = @head_ref
|
35
|
-
|
36
|
-
return
|
37
|
-
end
|
38
|
-
|
39
|
-
@ignored_refs << @head_ref
|
40
|
-
|
41
|
-
fetch_merged_parents
|
42
|
-
fetch_merged_branch_ref
|
43
|
-
end
|
44
|
-
|
45
|
-
def merged?
|
46
|
-
system('git', 'rev-parse', 'HEAD^2', out: File::NULL, err: File::NULL)
|
47
|
-
end
|
48
|
-
|
49
|
-
def fetch_merged_parents
|
50
|
-
first_parent = `git rev-parse HEAD^1`.chomp
|
51
|
-
@merged_parents << first_parent if $CHILD_STATUS.success?
|
52
|
-
|
53
|
-
second_parent = `git rev-parse HEAD^2`.chomp
|
54
|
-
@merged_parents << second_parent if $CHILD_STATUS.success?
|
55
|
-
|
56
|
-
raise GitOperationError, 'Could not find merged commit parents' if @merged_parents.length != 2
|
57
|
-
end
|
58
|
-
|
59
|
-
def fetch_merged_branch_ref
|
60
|
-
@origin_head_ref = `git rev-parse origin/HEAD`.chomp
|
61
|
-
@branch_ref = nil
|
62
|
-
|
63
|
-
if @merged_parents.first != @origin_head_ref
|
64
|
-
@branch_ref = @head_ref
|
65
|
-
@ignored_refs = []
|
66
|
-
|
67
|
-
return
|
68
|
-
end
|
69
|
-
|
70
|
-
@branch_ref = @merged_parents.last
|
71
|
-
@ignored_refs = @ignored_refs.to_set | `git rev-list #{@branch_ref}..origin/HEAD`.chomp.split
|
72
|
-
|
73
|
-
raise GitOperationError, 'Could not find ignored refs' unless $CHILD_STATUS.success?
|
74
|
-
end
|
75
|
-
|
76
|
-
def fetch_unreachable_refs
|
77
|
-
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
78
|
-
git fsck
|
79
|
-
--no-progress
|
80
|
-
--unreachable
|
81
|
-
--connectivity-only #{@branch_ref}
|
82
|
-
| awk '/commit/ { print $3 }'
|
83
|
-
| head -n 25
|
84
|
-
COMMAND
|
85
|
-
|
86
|
-
@unreachable_refs = `#{command}`.chomp.split
|
87
|
-
|
88
|
-
raise GitOperationError, 'Could not find unreachable refs' unless $CHILD_STATUS.success?
|
89
|
-
end
|
90
|
-
|
91
|
-
def fetch_ancestry_refs
|
92
|
-
@ancestry_refs = `git rev-list --max-count=25 #{@branch_ref}`.chomp.split
|
93
|
-
|
94
|
-
raise GitOperationError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
95
|
-
end
|
96
|
-
|
97
|
-
def fetch_ordered_refs
|
98
|
-
unordered_refs = (@unreachable_refs.to_set | @ancestry_refs) - @ignored_refs
|
99
|
-
|
100
|
-
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
101
|
-
git rev-list
|
102
|
-
--topo-order
|
103
|
-
--no-walk=sorted
|
104
|
-
#{unordered_refs.to_a.join(' ')}
|
105
|
-
COMMAND
|
106
|
-
|
107
|
-
@ref_list = `#{command}`.chomp.split
|
108
|
-
|
109
|
-
raise GitOperationError, 'Could not find refs to download cache' unless $CHILD_STATUS.success?
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|