specwrk 0.2.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f9b53c4d462665fee20cfd4f6599770bcbe11b226f9617977a43678d3b6f511
4
- data.tar.gz: ecd33a1c676d898160493fe5fc49c21dc4ef20c038aa2d551b01f21fcb18ddce
3
+ metadata.gz: e3916d92756b30810112db210d2a7ab6627ed250dbddfdc64ba8b0f24248d22c
4
+ data.tar.gz: eaa7c44ddacb99dde93337de842a420ebe8b12157b9dd90b5a158d75be8bebbb
5
5
  SHA512:
6
- metadata.gz: d8819747f5affeb51ea228a030856ea3659b50347ba30c177921cba4d7b966cf79bc7df69b391d2ad16607a7b4273360f82fff105e8d3f624ad3e45b6a07ff35
7
- data.tar.gz: d225da51ec8eb8d165fb82a07cacfcc706a34cff6c7398a36b5202e8212bfd3e7656394fc56b28ca4e9a8e1ae4418eba4d083609da3c8c251981abebcb0e763f
6
+ metadata.gz: 138e0a9358bcdd82472f3ab03c607ca647393c6d79c818d2c470fbd13670e045853e1355d3ca53634e0e8447058d5cc50676ea8692fb3286afa015346765970a
7
+ data.tar.gz: 2480031a9d3a080c3ddda3f4a6b5e736c3331356415da510c77ec8525114c65469c34fd0530a60fed49ccb15cef2ca03b438672920118872cc45fa8393edd8a5
data/lib/specwrk/cli.rb CHANGED
@@ -16,7 +16,7 @@ module Specwrk
16
16
  extend Hookable
17
17
 
18
18
  on_included do |base|
19
- base.unique_option :uri, type: :string, default: ENV.fetch("SPECWRK_SRV_URI", "https://localhost:#{ENV.fetch("SPECWRK_SRV_PORT", "5138")}"), desc: "HTTP URI of the server to pull jobs from. Overrides SPECWRK_SRV_PORT. Default 5138."
19
+ base.unique_option :uri, type: :string, default: ENV.fetch("SPECWRK_SRV_URI", "http://localhost:#{ENV.fetch("SPECWRK_SRV_PORT", "5138")}"), desc: "HTTP URI of the server to pull jobs from. Overrides SPECWRK_SRV_PORT. Default 5138."
20
20
  base.unique_option :key, type: :string, default: ENV.fetch("SPECWRK_SRV_KEY", ""), aliases: ["-k"], desc: "Authentication key clients must use for access. Overrides SPECWRK_SRV_KEY. Default ''."
21
21
  base.unique_option :run, type: :string, default: ENV.fetch("SPECWRK_RUN", "main"), aliases: ["-r"], desc: "The run identifier for this job execution. Overrides SPECWRK_RUN. Default main."
22
22
  base.unique_option :timeout, type: :integer, default: ENV.fetch("SPECWRK_TIMEOUT", "5"), aliases: ["-t"], desc: "The amount of time to wait for the server to respond. Overrides SPECWRK_TIMEOUT. Default 5."
@@ -54,7 +54,8 @@ module Specwrk
54
54
 
55
55
  require "specwrk/worker"
56
56
 
57
- Specwrk::Worker.run!
57
+ status = Specwrk::Worker.run!
58
+ exit(status)
58
59
  end
59
60
  end
60
61
  end
@@ -133,13 +134,21 @@ module Specwrk
133
134
  self.class.setup(**args)
134
135
 
135
136
  start_workers
136
- Process.waitall
137
+ wait_for_workers_exit
137
138
 
138
139
  require "specwrk/cli_reporter"
139
- status = Specwrk::CLIReporter.new.report
140
+ Specwrk::CLIReporter.new.report
140
141
 
141
142
  exit(status)
142
143
  end
144
+
145
+ def wait_for_workers_exit
146
+ @exited_pids = Specwrk.wait_for_pids_exit(@worker_pids)
147
+ end
148
+
149
+ def status
150
+ @exited_pids.value?(1) ? 1 : 0
151
+ end
143
152
  end
144
153
 
145
154
  class Serve < Dry::CLI::Command
@@ -194,44 +203,24 @@ module Specwrk
194
203
  status "Samples seeded ✓"
195
204
  end
196
205
 
197
- wait_for_pids_exit([seed_pid])
206
+ Specwrk.wait_for_pids_exit([seed_pid])
198
207
 
199
208
  return if Specwrk.force_quit
200
209
  status "Starting #{worker_count} workers..."
201
210
  start_workers
202
211
 
203
212
  status "#{worker_count} workers started ✓\n"
204
- wait_for_pids_exit(@worker_pids)
213
+ Specwrk.wait_for_pids_exit(@worker_pids)
205
214
 
206
215
  return if Specwrk.force_quit
207
216
 
208
217
  require "specwrk/cli_reporter"
209
218
  status = Specwrk::CLIReporter.new.report
210
219
 
211
- wait_for_pids_exit([web_pid, seed_pid] + @worker_pids)
220
+ Specwrk.wait_for_pids_exit([web_pid, seed_pid] + @worker_pids)
212
221
  exit(status)
213
222
  end
214
223
 
215
- def wait_for_pids_exit(pids)
216
- exited_pids = {}
217
-
218
- loop do
219
- pids.each do |pid|
220
- next if exited_pids.key? pid
221
-
222
- _, status = Process.waitpid2(pid, Process::WNOHANG)
223
- exited_pids[pid] = status.exitstatus if status&.exitstatus
224
- rescue Errno::ECHILD
225
- exited_pids[pid] = 0
226
- end
227
-
228
- break if exited_pids.keys.length == pids.length
229
- sleep 0.1
230
- end
231
-
232
- exited_pids
233
- end
234
-
235
224
  def status(msg)
236
225
  print "\e[2K\r#{msg}"
237
226
  $stdout.flush
@@ -4,6 +4,12 @@ require "uri"
4
4
  require "net/http"
5
5
  require "json"
6
6
 
7
+ require "specwrk"
8
+
9
+ # Some rspec setups might use webmock, which intercepts specwrk server calls
10
+ # Let's capture the OG HTTP before that happens
11
+ Specwrk.net_http = Net::HTTP
12
+
7
13
  module Specwrk
8
14
  class Client
9
15
  def self.connect?
@@ -18,7 +24,8 @@ module Specwrk
18
24
 
19
25
  def self.build_http
20
26
  uri = URI(ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138"))
21
- Net::HTTP.new(uri.host, uri.port).tap do |http|
27
+ Specwrk.net_http.new(uri.host, uri.port).tap do |http|
28
+ http.use_ssl = uri.scheme == "https"
22
29
  http.open_timeout = ENV.fetch("SPECWRK_TIMEOUT", "5").to_i
23
30
  http.read_timeout = ENV.fetch("SPECWRK_TIMEOUT", "5").to_i
24
31
  http.keep_alive_timeout = 300
@@ -81,6 +88,8 @@ module Specwrk
81
88
  case response.code
82
89
  when "200"
83
90
  JSON.parse(response.body, symbolize_names: true)
91
+ when "204"
92
+ raise WaitingForSeedError
84
93
  when "404"
85
94
  raise NoMoreExamplesError
86
95
  when "410"
@@ -105,28 +114,28 @@ module Specwrk
105
114
  private
106
115
 
107
116
  def get(path, headers: default_headers, body: nil)
108
- request = Net::HTTP::Get.new(path, headers)
117
+ request = Specwrk.net_http::Get.new(path, headers)
109
118
  request.body = body if body
110
119
 
111
120
  make_request(request)
112
121
  end
113
122
 
114
123
  def post(path, headers: default_headers, body: nil)
115
- request = Net::HTTP::Post.new(path, headers)
124
+ request = Specwrk.net_http::Post.new(path, headers)
116
125
  request.body = body if body
117
126
 
118
127
  make_request(request)
119
128
  end
120
129
 
121
130
  def put(path, headers: default_headers, body: nil)
122
- request = Net::HTTP::Put.new(path, headers)
131
+ request = Specwrk.net_http::Put.new(path, headers)
123
132
  request.body = body if body
124
133
 
125
134
  make_request(request)
126
135
  end
127
136
 
128
137
  def delete(path, headers: default_headers, body: nil)
129
- request = Net::HTTP::Delete.new(path, headers)
138
+ request = Specwrk.net_http::Delete.new(path, headers)
130
139
  request.body = body if body
131
140
 
132
141
  make_request(request)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Specwrk
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -94,7 +94,9 @@ module Specwrk
94
94
 
95
95
  if @examples.length.positive?
96
96
  [200, {"Content-Type" => "application/json"}, [JSON.generate(@examples)]]
97
- elsif processing_queue.length.zero?
97
+ elsif pending_queue.length.zero? && processing_queue.length.zero? && completed_queue.length.zero?
98
+ [204, {"Content-Type" => "text/plain"}, ["Waiting for sample to be seeded."]]
99
+ elsif completed_queue.length.positive? && processing_queue.length.zero?
98
100
  [410, {"Content-Type" => "text/plain"}, ["That's a good lad. Run along now and go home."]]
99
101
  else
100
102
  not_found
@@ -5,23 +5,28 @@ module Specwrk
5
5
  class CompletionFormatter
6
6
  RSpec::Core::Formatters.register self, :stop
7
7
 
8
- attr_reader :examples
8
+ attr_reader :examples, :failure
9
9
 
10
10
  def initialize
11
11
  @examples = []
12
+ @failure = false
12
13
  end
13
14
 
14
15
  def stop(group_notification)
15
16
  group_notification.notifications.map do |notification|
17
+ unless failure
18
+ @failure = notification.example.execution_result.status == :failed
19
+ end
20
+
16
21
  examples << {
17
22
  id: notification.example.id,
18
23
  full_description: notification.example.full_description,
19
- status: notification.example.metadata[:execution_result].status,
24
+ status: notification.example.execution_result.status,
20
25
  file_path: notification.example.metadata[:file_path],
21
26
  line_number: notification.example.metadata[:line_number],
22
- started_at: notification.example.metadata[:execution_result].started_at.iso8601(6),
23
- finished_at: notification.example.metadata[:execution_result].finished_at.iso8601(6),
24
- run_time: notification.example.metadata[:execution_result].run_time
27
+ started_at: notification.example.execution_result.started_at.iso8601(6),
28
+ finished_at: notification.example.execution_result.finished_at.iso8601(6),
29
+ run_time: notification.example.execution_result.run_time
25
30
  }
26
31
  end
27
32
  end
@@ -11,6 +11,12 @@ require "specwrk/worker/null_formatter"
11
11
  module Specwrk
12
12
  class Worker
13
13
  class Executor
14
+ attr_reader :example_processed
15
+
16
+ def failure
17
+ completion_formatter.failure
18
+ end
19
+
14
20
  def examples
15
21
  completion_formatter.examples
16
22
  end
@@ -23,6 +29,7 @@ module Specwrk
23
29
  reset!
24
30
 
25
31
  example_ids = examples.map { |example| example[:id] }
32
+ @example_processed ||= true unless example_ids.size.zero? # only ever toggle from nil => true
26
33
 
27
34
  options = RSpec::Core::ConfigurationOptions.new rspec_options + example_ids
28
35
  RSpec::Core::Runner.new(options).run($stderr, $stdout)
@@ -36,16 +36,31 @@ module Specwrk
36
36
  # This will cause workers to 'hang' until all work has been completed
37
37
  # TODO: break here if all the other worker processes on this host are done executing examples
38
38
  sleep 0.5
39
+ rescue WaitingForSeedError
40
+ @seed_wait_count ||= 0
41
+ @seed_wait_count += 1
42
+
43
+ if @seed_wait_count <= 10
44
+ warn "No examples seeded yet, waiting..."
45
+ sleep 1
46
+ else
47
+ warn "No examples seeded, giving up!"
48
+ break
49
+ end
39
50
  end
40
51
 
41
52
  executor.final_output.tap(&:rewind).each_line { |line| $stdout.write line }
42
53
 
43
54
  @heartbeat_thread.kill
44
55
  client.close
56
+
57
+ status
45
58
  rescue Errno::ECONNREFUSED
46
59
  warn "\nServer at #{ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138")} is refusing connections, exiting..."
60
+ 1
47
61
  rescue Errno::ECONNRESET
48
62
  warn "\nServer at #{ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138")} stopped responding to connections, exiting..."
63
+ 1
49
64
  end
50
65
 
51
66
  def execute
@@ -83,6 +98,14 @@ module Specwrk
83
98
 
84
99
  attr_reader :running, :client, :executor
85
100
 
101
+ def status
102
+ return 1 unless executor.example_processed
103
+ return 1 if executor.failure
104
+ return 1 if Specwrk.force_quit
105
+
106
+ 0
107
+ end
108
+
86
109
  def warn(msg)
87
110
  super("#{ENV.fetch("SPECWRK_ID", "specwrk-worker")}: #{msg}")
88
111
  end
data/lib/specwrk.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "specwrk/version"
3
+ require "specwrk/version"
4
4
 
5
5
  module Specwrk
6
6
  Error = Class.new(StandardError)
@@ -8,6 +8,7 @@ module Specwrk
8
8
  # HTTP Client Errors
9
9
  ClientError = Class.new(Error)
10
10
  UnhandledResponseError = Class.new(ClientError)
11
+ WaitingForSeedError = Class.new(ClientError)
11
12
  NoMoreExamplesError = Class.new(ClientError)
12
13
  CompletedAllExamplesError = Class.new(ClientError)
13
14
 
@@ -15,7 +16,27 @@ module Specwrk
15
16
  @starting_pid = Process.pid
16
17
 
17
18
  class << self
18
- attr_accessor :force_quit
19
+ attr_accessor :force_quit, :net_http
19
20
  attr_reader :starting_pid
21
+
22
+ def wait_for_pids_exit(pids)
23
+ exited_pids = {}
24
+
25
+ loop do
26
+ pids.each do |pid|
27
+ next if exited_pids.key? pid
28
+
29
+ _, status = Process.waitpid2(pid, Process::WNOHANG)
30
+ exited_pids[pid] = status.exitstatus if status&.exitstatus
31
+ rescue Errno::ECHILD
32
+ exited_pids[pid] = 1
33
+ end
34
+
35
+ break if exited_pids.keys.length == pids.length
36
+ sleep 0.1
37
+ end
38
+
39
+ exited_pids
40
+ end
20
41
  end
21
42
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: specwrk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Westendorf