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.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.7.0'
4
+ VERSION = '0.8.0'
5
5
  end
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.7.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-10 00:00:00.000000000 Z
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/git.rb
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.7.0
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