specwrk 0.4.11 → 0.5.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 +28 -0
- data/README.md +1 -1
- data/lib/specwrk/cli.rb +7 -3
- data/lib/specwrk/client.rb +2 -0
- data/lib/specwrk/version.rb +1 -1
- data/lib/specwrk/web/app.rb +27 -0
- data/lib/specwrk/web/endpoints.rb +12 -19
- data/lib/specwrk/web.rb +8 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 331e0982599a516c3c862c4a5c33a825c71256f5f95e6d1656c0ad410541df06
|
4
|
+
data.tar.gz: c7d93bf3add6e6de08cad22f8036ef89b01752b0e6dfe42615035d9e301ecff5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9daa901f8a7b7f453079f766d09e47fb621cf85e1cdb6111303e1f8b17cc3091312b2128ca141328f1c8a28216151b991f431cbb4550445ce137349dcb30f3c5
|
7
|
+
data.tar.gz: f0ae841735759d62e9ad6607065ca7bc8747a9d85e8906dea19019af7c63183df26b82cce51bc2a8c453cf4e1bb7d3d0b0c900c917cf689730cdac6028efaae5
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,33 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## v0.4.11
|
4
|
+
|
5
|
+
- Move logic out of CLI methods ([#64](https://github.com/danielwestendorf/specwrk/issues/64)) by @danielwestendorf
|
6
|
+
- Add thruster in front of puma ([#65](https://github.com/danielwestendorf/specwrk/issues/65)) by @danielwestendorf
|
7
|
+
- Revise heartbeats logic ([#66](https://github.com/danielwestendorf/specwrk/issues/66)) by @danielwestendorf
|
8
|
+
|
9
|
+
## v0.4.9
|
10
|
+
|
11
|
+
- Remove single-run env var (it was not what I wanted) by @danielwestendorf
|
12
|
+
|
13
|
+
## v0.4.8
|
14
|
+
|
15
|
+
- Fix `config.ru` by @danielwestendorf
|
16
|
+
|
17
|
+
## v0.4.7
|
18
|
+
|
19
|
+
- Switch to Puma for the Docker image (#59) by @danielwestendorf
|
20
|
+
|
21
|
+
## v0.4.6
|
22
|
+
|
23
|
+
- Nil env var that will prevent start command from completing ([#56](https://github.com/danielwestendorf/specwrk/issues/56)) by @danielwestendorf
|
24
|
+
- Better handling of seed failing ([#57](https://github.com/danielwestendorf/specwrk/issues/57)) by @danielwestendorf
|
25
|
+
- Silence health logging ([#58](https://github.com/danielwestendorf/specwrk/issues/58)) by @danielwestendorf
|
26
|
+
- Add missing CCI caching of `report.json` ([#55](https://github.com/danielwestendorf/specwrk/issues/55)) by @danielwestendorf
|
27
|
+
- Add CircleCI examples ([#50](https://github.com/danielwestendorf/specwrk/issues/50)) by @danielwestendorf
|
28
|
+
- Better GHA Examples ([#49](https://github.com/danielwestendorf/specwrk/issues/49)) by @danielwestendorf
|
29
|
+
- Skip key lookup and rely on the result of `Hash#delete` instead by @danielwestendorf
|
30
|
+
|
3
31
|
## v0.4.5
|
4
32
|
|
5
33
|
- Set ENV var when generating seed examples [#47](https://github.com/danielwestendorf/specwrk/issues/47). by @danielwestendorf
|
data/README.md
CHANGED
@@ -129,7 +129,7 @@ Description:
|
|
129
129
|
Start one or more worker processes
|
130
130
|
|
131
131
|
Options:
|
132
|
-
--id=VALUE # The identifier for this worker.
|
132
|
+
--id=VALUE # The identifier for this worker. Overrides SPECWRK_ID. If none provided one in the format of specwrk-worker-8_RAND_CHARS-COUNT_INDEX will be used
|
133
133
|
--count=VALUE, -c VALUE # The number of worker processes you want to start, default: 1
|
134
134
|
--output=VALUE, -o VALUE # Directory where worker output is stored. Overrides SPECWRK_OUT, default: ".specwrk/"
|
135
135
|
--seed-waits=VALUE, -w VALUE # Number of times the worker will wait for examples to be seeded to the server. 1sec between attempts. Overrides SPECWRK_SEED_WAITS, default: "10"
|
data/lib/specwrk/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "pathname"
|
4
|
+
require "securerandom"
|
4
5
|
|
5
6
|
require "dry/cli"
|
6
7
|
|
@@ -33,14 +34,15 @@ module Specwrk
|
|
33
34
|
extend Hookable
|
34
35
|
|
35
36
|
on_included do |base|
|
36
|
-
base.unique_option :id, type: :string,
|
37
|
+
base.unique_option :id, type: :string, desc: "The identifier for this worker. Overrides SPECWRK_ID. If none provided one in the format of specwrk-worker-8_RAND_CHARS-COUNT_INDEX will be used"
|
37
38
|
base.unique_option :count, type: :integer, default: 1, aliases: ["-c"], desc: "The number of worker processes you want to start"
|
38
39
|
base.unique_option :output, type: :string, default: ENV.fetch("SPECWRK_OUT", ".specwrk/"), aliases: ["-o"], desc: "Directory where worker output is stored. Overrides SPECWRK_OUT"
|
39
40
|
base.unique_option :seed_waits, type: :integer, default: ENV.fetch("SPECWRK_SEED_WAITS", "10"), aliases: ["-w"], desc: "Number of times the worker will wait for examples to be seeded to the server. 1sec between attempts. Overrides SPECWRK_SEED_WAITS"
|
40
41
|
end
|
41
42
|
|
42
|
-
on_setup do |
|
43
|
-
ENV["SPECWRK_ID"]
|
43
|
+
on_setup do |count:, output:, seed_waits:, id: "specwrk-worker-#{SecureRandom.uuid[0, 8]}", **|
|
44
|
+
ENV["SPECWRK_ID"] ||= id # Unique default. Don't override the ENV value here
|
45
|
+
|
44
46
|
ENV["SPECWRK_COUNT"] = count.to_s
|
45
47
|
ENV["SPECWRK_SEED_WAITS"] = seed_waits.to_s
|
46
48
|
ENV["SPECWRK_OUT"] = Pathname.new(output).expand_path(Dir.pwd).to_s
|
@@ -115,6 +117,8 @@ module Specwrk
|
|
115
117
|
|
116
118
|
Client.wait_for_server!
|
117
119
|
Client.new.seed(examples)
|
120
|
+
file_count = examples.group_by { |e| e[:file_path] }.keys.size
|
121
|
+
puts "🌱 Seeded #{examples.size} examples across #{file_count} files"
|
118
122
|
rescue Errno::ECONNREFUSED
|
119
123
|
puts "Server at #{ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138")} is refusing connections, exiting...#{ENV["SPECWRK_FLUSH_DELIMINATOR"]}"
|
120
124
|
exit 1
|
data/lib/specwrk/client.rb
CHANGED
@@ -150,7 +150,9 @@ module Specwrk
|
|
150
150
|
|
151
151
|
def default_headers
|
152
152
|
@default_headers ||= {}.tap do |h|
|
153
|
+
h["User-Agent"] = "Specwrk/#{VERSION}"
|
153
154
|
h["Authorization"] = "Bearer #{ENV["SPECWRK_SRV_KEY"]}" if ENV["SPECWRK_SRV_KEY"]
|
155
|
+
h["X-Specwrk-Id"] = ENV.fetch("SPECWRK_ID", "specwrk-client")
|
154
156
|
h["X-Specwrk-Run"] = ENV["SPECWRK_RUN"] if ENV["SPECWRK_RUN"]
|
155
157
|
h["Content-Type"] = "application/json"
|
156
158
|
end
|
data/lib/specwrk/version.rb
CHANGED
data/lib/specwrk/web/app.rb
CHANGED
@@ -13,6 +13,7 @@ rescue LoadError
|
|
13
13
|
require "rack/handler/webrick"
|
14
14
|
end
|
15
15
|
|
16
|
+
require "specwrk/web"
|
16
17
|
require "specwrk/web/logger"
|
17
18
|
require "specwrk/web/auth"
|
18
19
|
require "specwrk/web/endpoints"
|
@@ -20,6 +21,8 @@ require "specwrk/web/endpoints"
|
|
20
21
|
module Specwrk
|
21
22
|
class Web
|
22
23
|
class App
|
24
|
+
REAP_INTERVAL = 330 # HTTP connection timeout + some buffer
|
25
|
+
|
23
26
|
class << self
|
24
27
|
def run!
|
25
28
|
Process.setproctitle "specwrk-server"
|
@@ -73,6 +76,10 @@ module Specwrk
|
|
73
76
|
end
|
74
77
|
end
|
75
78
|
|
79
|
+
def initialize
|
80
|
+
@reaper_thread = Thread.new { reaper } unless ENV["SPECWRK_SRV_SINGLE_RUN"]
|
81
|
+
end
|
82
|
+
|
76
83
|
def call(env)
|
77
84
|
env[:request] ||= Rack::Request.new(env)
|
78
85
|
|
@@ -101,6 +108,26 @@ module Specwrk
|
|
101
108
|
Endpoints::NotFound
|
102
109
|
end
|
103
110
|
end
|
111
|
+
|
112
|
+
def reaper
|
113
|
+
until Specwrk.force_quit
|
114
|
+
sleep REAP_INTERVAL
|
115
|
+
|
116
|
+
reap
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def reap
|
121
|
+
Web::WORKERS.each do |run, workers|
|
122
|
+
most_recent_last_seen_at = workers.map { |id, worker| worker[:last_seen_at] }.max
|
123
|
+
next unless most_recent_last_seen_at
|
124
|
+
|
125
|
+
# Don't consider runs which aren't at least REAP_INTERVAL sec stale
|
126
|
+
if most_recent_last_seen_at < Time.now - REAP_INTERVAL
|
127
|
+
Web.clear_run_queues(run)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
104
131
|
end
|
105
132
|
end
|
106
133
|
end
|
@@ -8,6 +8,9 @@ module Specwrk
|
|
8
8
|
class Base
|
9
9
|
def initialize(request)
|
10
10
|
@request = request
|
11
|
+
|
12
|
+
worker[:first_seen_at] ||= Time.now
|
13
|
+
worker[:last_seen_at] = Time.now
|
11
14
|
end
|
12
15
|
|
13
16
|
def response
|
@@ -45,6 +48,14 @@ module Specwrk
|
|
45
48
|
def completed_queue
|
46
49
|
Web::COMPLETED_QUEUES[request.get_header("HTTP_X_SPECWRK_RUN")]
|
47
50
|
end
|
51
|
+
|
52
|
+
def workers
|
53
|
+
Web::WORKERS[request.get_header("HTTP_X_SPECWRK_RUN")]
|
54
|
+
end
|
55
|
+
|
56
|
+
def worker
|
57
|
+
workers[request.get_header("HTTP_X_SPECWRK_ID")]
|
58
|
+
end
|
48
59
|
end
|
49
60
|
|
50
61
|
# Base default response is 404
|
@@ -135,25 +146,7 @@ module Specwrk
|
|
135
146
|
|
136
147
|
class Shutdown < Base
|
137
148
|
def response
|
138
|
-
if ENV["SPECWRK_SRV_SINGLE_RUN"]
|
139
|
-
interupt!
|
140
|
-
elsif processing_queue.length.positive?
|
141
|
-
# Push any processing jobs back into the pending queue
|
142
|
-
processing_queue.synchronize do |processing_queue_hash|
|
143
|
-
pending_queue.synchronize do |pending_queue_hash|
|
144
|
-
processing_queue_hash.each do |id, example|
|
145
|
-
pending_queue_hash[id] = example
|
146
|
-
processing_queue_hash.delete(id)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
elsif processing_queue.length.zero? && pending_queue.length.zero?
|
152
|
-
# All done, we can clear the completed queue
|
153
|
-
completed_queue.clear
|
154
|
-
end
|
155
|
-
|
156
|
-
# TODO: clear any zombie queues
|
149
|
+
interupt! if ENV["SPECWRK_SRV_SINGLE_RUN"]
|
157
150
|
|
158
151
|
[200, {"Content-Type" => "text/plain"}, ["✌️"]]
|
159
152
|
end
|
data/lib/specwrk/web.rb
CHANGED
@@ -7,9 +7,16 @@ module Specwrk
|
|
7
7
|
PENDING_QUEUES = Queue.new { |h, key| h[key] = PendingQueue.new.tap { |q| q.previous_run_times_file = ENV["SPECWRK_SRV_OUTPUT"] } }
|
8
8
|
PROCESSING_QUEUES = Queue.new { |h, key| h[key] = Queue.new }
|
9
9
|
COMPLETED_QUEUES = Queue.new { |h, key| h[key] = CompletedQueue.new }
|
10
|
+
WORKERS = Hash.new { |h, key| h[key] = Hash.new { |h, key| h[key] = {} } }
|
10
11
|
|
11
12
|
def self.clear_queues
|
12
|
-
[PENDING_QUEUES, PROCESSING_QUEUES, COMPLETED_QUEUES].each(&:clear)
|
13
|
+
[PENDING_QUEUES, PROCESSING_QUEUES, COMPLETED_QUEUES, WORKERS].each(&:clear)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.clear_run_queues(run)
|
17
|
+
[PENDING_QUEUES, PROCESSING_QUEUES, COMPLETED_QUEUES, WORKERS].each do |queue|
|
18
|
+
queue.delete(run)
|
19
|
+
end
|
13
20
|
end
|
14
21
|
end
|
15
22
|
end
|