rspec-sharder 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7abffffb75542211fc36495edeca3b96f32f59b6975686568b3532d2df6ff9c6
4
- data.tar.gz: 64bccf923c9897fa23205ade6af164ca10eb60dfc3c89b760b9a73be35d382bb
3
+ metadata.gz: c3d09ff7bef003d57985622045c99bb8743834d26f0f2119bc32d9ffdd800822
4
+ data.tar.gz: 9e5f371699047b82862fe5f76288122c10b80b23117dc0ec2d82e6e1f29f312e
5
5
  SHA512:
6
- metadata.gz: 3cbba17bc9116dbe0a8e3f6aef9d7865f57e40c6393d2e3d3243afacfc5dee658d33fe2c729734fb43ad291280a67f124913807f0b5d69eb28f13671d194309f
7
- data.tar.gz: 809210eec8b32c39a87c6f64dbe6e9f699c1f1242b8edb1c0a9ea8120c7945801461744ad17065913f981a81bdfe1b5e1a031a2ce68a746871db995504e39ff1
6
+ metadata.gz: be15f6f4f0b66eef6b50af889e48f3fb3f7cadfd1650314538c3088e4828e19f8f6af77092fa359f081cec206f3957389397e1d52b4e30ff81dd20b2ecb854cd
7
+ data.tar.gz: 4c69946dc056cb146ff452d80b9e31503c12fbaa6cfb14f164268fafd84a7baf13da04b694e77f0b9d9dfea65898f8579f2b7fb81b401bde7ef80b9494f458d8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-sharder (0.0.2)
4
+ rspec-sharder (0.0.3)
5
5
  rspec-core
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -5,22 +5,35 @@ Groups specs into shards, ensuring that each shard has a similar size, and runs
5
5
  the specified shard.
6
6
 
7
7
  Shard size is determined by summing the saved durations for each spec file in
8
- the shard. Durations are saved in .spec_durations. If a spec file is not found
9
- in .spec_durations, the duration is estimated based on the number of examples in
10
- the spec file.
8
+ the shard. Durations are saved in .rspec-sharder-durations. If a spec file is
9
+ not found in .rspec-sharder-durations, the duration is estimated based on the
10
+ number of examples in the spec file.
11
11
 
12
- .spec_durations is generate/updated after a successful run when --persist is
13
- specified, but only for the shard which was actually executed. To generate
14
- durations for all shards simultaneously, run with the default options of 1 total
15
- shards and --persist:
12
+ .rspec-sharder-durations is generated after each successful run, but only for
13
+ the specs which were run as part of the specified shard. Before first use,
14
+ generate .rspec-sharder-durations for all specs by executing with the default
15
+ option of 1 total shards:
16
16
 
17
- bundle exec rspec-sharder --persist -- [<rspec-args...>]
17
+ bundle exec rspec-sharder -- [<rspec-args...>]
18
18
 
19
- Usage: bundle exec rspec-sharder [--total-shards <num> [--shard <num>]] [--persist] -- [<rspec-args...>]
19
+ Commit .rspec-sharder-durations file to your version control.
20
+
21
+ Next, configure 2 or more CI jobs to execute separate shards like:
22
+
23
+ bundle exec rspec-sharder --total-shards 4 --shard 1 -- [<rspec-args...>]
24
+ bundle exec rspec-sharder --total-shards 4 --shard 2 -- [<rspec-args...>]
25
+ bundle exec rspec-sharder --total-shards 4 --shard 3 -- [<rspec-args...>]
26
+ bundle exec rspec-sharder --total-shards 4 --shard 4 -- [<rspec-args...>]
27
+
28
+ Finally, set up some job or process to periodically pull .rspec-sharder-durations
29
+ files from CI, combine them, and commit them to source control. This will ensure
30
+ you pick up updated durations for any new or changed files.
31
+
32
+ Usage: bundle exec rspec-sharder [--total-shards <num> [--shard <num>]] [--no-persist] -- [<rspec-args...>]
20
33
 
21
34
  Options:
22
35
  -h, --help Print this message.
23
36
  -t, --total-shards <num> The total number of shards. Defaults to 1.
24
37
  -s, --shard <num> The shard to run. Defaults to 1.
25
- -p, --persist Save durations to .spec_durations.
38
+ --no-persist Don't save durations to .rspec-sharder-durations.
26
39
  ```
@@ -10,7 +10,7 @@ end
10
10
 
11
11
  @total_shards = 1
12
12
  @shard = 1
13
- @persist = false
13
+ @persist = true
14
14
 
15
15
  @parser = OptionParser.new do |opts|
16
16
  opts.banner = <<~EOF
@@ -18,18 +18,31 @@ end
18
18
  the specified shard.
19
19
 
20
20
  Shard size is determined by summing the saved durations for each spec file in
21
- the shard. Durations are saved in .spec_durations. If a spec file is not found
22
- in .spec_durations, the duration is estimated based on the number of examples in
23
- the spec file.
21
+ the shard. Durations are saved in .rspec-sharder-durations. If a spec file is
22
+ not found in .rspec-sharder-durations, the duration is estimated based on the
23
+ number of examples in the spec file.
24
24
 
25
- .spec_durations is generate/updated after a successful run when --persist is
26
- specified, but only for the shard which was actually executed. To generate
27
- durations for all shards simultaneously, run with the default options of 1 total
28
- shards and --persist:
25
+ .rspec-sharder-durations is generated after each successful run, but only for
26
+ the specs which were run as part of the specified shard. Before first use,
27
+ generate .rspec-sharder-durations for all specs by executing with the default
28
+ option of 1 total shards:
29
29
 
30
- bundle exec rspec-sharder --persist -- [<rspec-args...>]
30
+ bundle exec rspec-sharder -- [<rspec-args...>]
31
31
 
32
- Usage: bundle exec rspec-sharder [--total-shards <num> [--shard <num>]] [--persist] -- [<rspec-args...>]
32
+ Commit .rspec-sharder-durations file to your version control.
33
+
34
+ Next, configure 2 or more CI jobs to execute separate shards like:
35
+
36
+ bundle exec rspec-sharder --total-shards 4 --shard 1 -- [<rspec-args...>]
37
+ bundle exec rspec-sharder --total-shards 4 --shard 2 -- [<rspec-args...>]
38
+ bundle exec rspec-sharder --total-shards 4 --shard 3 -- [<rspec-args...>]
39
+ bundle exec rspec-sharder --total-shards 4 --shard 4 -- [<rspec-args...>]
40
+
41
+ Finally, set up some job or process to periodically pull .rspec-sharder-durations
42
+ files from CI, combine them, and commit them to source control. This will ensure
43
+ you pick up updated durations for any new or changed files.
44
+
45
+ Usage: bundle exec rspec-sharder [--total-shards <num> [--shard <num>]] [--no-persist] -- [<rspec-args...>]
33
46
 
34
47
  Options:
35
48
  EOF
@@ -55,8 +68,8 @@ end
55
68
  end
56
69
  end
57
70
 
58
- opts.on('-p', '--persist', 'Save durations to .spec_durations.') do
59
- @persist = true
71
+ opts.on('--no-persist', "Don't save durations to .rspec-sharder-durations.") do
72
+ @persist = false
60
73
  end
61
74
  end
62
75
 
@@ -70,4 +83,4 @@ fail('fatal: invalid value for --total-shards') unless @total_shards > 0
70
83
  fail('fatal: invalid value for --shard') unless @shard > 0
71
84
  fail('fatal: --shard may not be greater than --total-shards') unless @shard <= @total_shards
72
85
 
73
- RSpec::Sharder.run(total_shards: @total_shards, shard_num: @shard, persist: @persist, rspec_args: ARGV)
86
+ exit RSpec::Sharder.run(total_shards: @total_shards, shard_num: @shard, persist: @persist, rspec_args: ARGV)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module Sharder
5
- VERSION = "0.0.3"
5
+ VERSION = "0.0.4"
6
6
  end
7
7
  end
data/lib/rspec-sharder.rb CHANGED
@@ -27,7 +27,7 @@ module RSpec
27
27
  shards = build_shards(total_shards, shard_num, all_durations)
28
28
  rescue ShardError => e
29
29
  ::RSpec.configuration.error_stream.puts e.message
30
- exit ::RSpec.configuration.failure_exit_cod
30
+ return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code)
31
31
  end
32
32
 
33
33
  print_shards(shards)
@@ -79,7 +79,7 @@ module RSpec
79
79
  if persist
80
80
  ::RSpec.configuration.output_stream.puts <<~EOF
81
81
 
82
- Dry run. Not saving to .spec_durations.
82
+ Dry run. Not saving to .rspec-sharder-durations.
83
83
  EOF
84
84
  end
85
85
  else
@@ -99,27 +99,23 @@ module RSpec
99
99
  EOF
100
100
 
101
101
  if persist
102
- # Write all durations with updates to .spec_durations.
102
+ # Write durations to .rspec-sharder-durations.
103
103
  ::RSpec.configuration.output_stream.puts <<~EOF
104
104
 
105
- Saving to .spec_durations.
105
+ Saving to .rspec-sharder-durations.
106
106
  EOF
107
107
 
108
- new_durations.each do |file_path, duration|
109
- all_durations[file_path] = duration
110
- end
111
-
112
- persist_durations(all_durations)
108
+ persist_durations(new_durations)
113
109
  end
114
110
  elsif persist
115
111
  ::RSpec.configuration.output_stream.puts <<~EOF
116
112
 
117
- RSpec failed. Not saving to .spec_durations.
113
+ RSpec failed. Not saving to .rspec-sharder-durations.
118
114
  EOF
119
115
  end
120
116
  end
121
117
 
122
- exit exit_code
118
+ exit_code
123
119
  end
124
120
 
125
121
  private
@@ -127,36 +123,45 @@ module RSpec
127
123
  def self.load_recorded_durations
128
124
  durations = { }
129
125
 
130
- if File.exist?('.spec_durations')
131
- File.readlines('.spec_durations').each_with_index do |line, index|
126
+ missing_files = 0
127
+
128
+ if File.exist?('.rspec-sharder-durations')
129
+ File.readlines('.rspec-sharder-durations').each_with_index do |line, index|
132
130
  line = line.strip
133
131
 
134
132
  if !line.start_with?('#') && !line.empty?
135
133
  parts = line.split(',')
136
134
 
137
135
  unless parts.length == 2
138
- raise ShardError.new("fatal: invalid .spec_durations at line #{index + 1}")
136
+ raise ShardError.new("fatal: invalid .rspec-sharder-durations at line #{index + 1}")
139
137
  end
140
138
 
141
139
  file_path = parts[0].strip
142
140
 
143
141
  if file_path.empty?
144
- raise ShardError.new("fatal: invalid file path in .spec_durations at line #{index + 1}")
142
+ raise ShardError.new("fatal: invalid file path in .rspec-sharder-durations at line #{index + 1}")
145
143
  end
146
144
 
147
145
  unless File.exist?(file_path)
148
- raise ShardError.new("fatal: file in .spec_durations not found at line #{index + 1}")
146
+ missing_files += 1
149
147
  end
150
148
 
151
149
  begin
152
150
  duration = Integer(parts[1])
153
151
  rescue ArgumentError => e
154
- raise ShardError.new("fatal: invalid .spec_durations at line #{index + 1}")
152
+ raise ShardError.new("fatal: invalid .rspec-sharder-durations at line #{index + 1}")
155
153
  end
156
154
 
157
155
  durations[file_path] = duration
158
156
  end
159
157
  end.compact
158
+
159
+ if missing_files > 0
160
+ ::RSpec.configuration.output_stream.puts <<~EOF
161
+ warning: #{missing_files} file(s) in .rspec-sharder-durations do not exist, consider regenerating
162
+
163
+ EOF
164
+ end
160
165
  end
161
166
 
162
167
  durations
@@ -165,19 +170,26 @@ module RSpec
165
170
  def self.build_shards(total_shards, shard_num, durations)
166
171
  files = { }
167
172
 
173
+ missing_files = 0
168
174
  ::RSpec.world.ordered_example_groups.each do |example_group|
169
175
  file_path = example_group.metadata[:file_path]
170
176
  files[file_path] ||= 0
171
177
  if durations[file_path]
172
178
  files[file_path] = durations[file_path]
173
179
  else
174
- ::RSpec.configuration.output_stream.puts "warning: recorded duration not found for #{file_path}"
175
-
180
+ missing_files += 1
176
181
  # Assume 1000 milliseconds per example.
177
182
  files[file_path] += ::RSpec.world.example_count([example_group]) * 1000
178
183
  end
179
184
  end
180
185
 
186
+ if missing_files > 0
187
+ ::RSpec.configuration.output_stream.puts <<~EOF
188
+ warning: #{missing_files} file(s) in not found in .rspec-sharder-durations, consider regenerating
189
+
190
+ EOF
191
+ end
192
+
181
193
  shards = (1..total_shards).map { { duration: 0, file_paths: [] } }
182
194
 
183
195
  # First sort by duration to ensure large files are distributed evenly.
@@ -222,7 +234,6 @@ module RSpec
222
234
  end
223
235
 
224
236
  def self.print_shards(shards)
225
- ::RSpec.configuration.output_stream.puts
226
237
  shards.each_with_index do |shard, i|
227
238
  ::RSpec.configuration.output_stream.puts(
228
239
  "Shard #{i + 1} (Files: #{shard[:file_paths].size}, Duration: #{pretty_duration(shard[:duration])}):"
@@ -235,9 +246,9 @@ module RSpec
235
246
  end
236
247
 
237
248
  def self.persist_durations(durations)
238
- File.open(".spec_durations", "w+") do |file|
249
+ File.open(".rspec-sharder-durations", "w+") do |file|
239
250
  file.puts <<~EOF
240
- # Generated by rspec-sharder on #{Time.now.to_s}.")
251
+ # Generated by rspec-sharder on #{Time.now.to_s}. See `bundle exec rspec-sharder -h`.
241
252
 
242
253
  EOF
243
254
  durations.sort_by { |file_path, duration| file_path }.each do |file_path, duration|
data/scripts/release.sh CHANGED
@@ -15,6 +15,8 @@ fi
15
15
  NEW_VERSION=$1
16
16
  CURRENT_VERSION=$(grep VERSION lib/rspec-sharder/version.rb | cut -d'"' -f 2)
17
17
 
18
+ bundle exec rspec
19
+
18
20
  echo "Updating from v$CURRENT_VERSION to v$NEW_VERSION. Press enter to continue."
19
21
  read
20
22
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-sharder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-28 00:00:00.000000000 Z
11
+ date: 2021-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-core