specwrk 0.5.0 → 0.6.1
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/docker/Dockerfile.server +7 -11
- data/docker/entrypoint.server.sh +2 -2
- data/docker/pitchfork.conf +5 -0
- data/lib/specwrk/cli_reporter.rb +16 -10
- data/lib/specwrk/client.rb +2 -2
- data/lib/specwrk/queue.rb +27 -11
- data/lib/specwrk/version.rb +1 -1
- data/lib/specwrk/web/app.rb +2 -4
- data/lib/specwrk/web/auth.rb +1 -1
- data/lib/specwrk/web/endpoints.rb +39 -19
- data/lib/specwrk/web.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dac5590f1e1c0603205328a69256a082c3ba1ff7ccf3ecd3937103d006706cb8
|
4
|
+
data.tar.gz: d53d623560f4c52a56e57d4d166597e40079bea76ea231d087930db588b7c401
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ecd6b6c0c37e6d17603f63b83e9f4cbfab076db34e67fc3fa433ef06ca66899b273d1858a7667b5946cc4b97ec58ac3e3c7eeea316687cde6fa3a824632e295
|
7
|
+
data.tar.gz: b963354e642948b1939009b8a0c54b126fc5d819b3cd07b14eb8822b0282317ce82dd432c7dcd5e28e5ed212aaf8e18a1a9bf048bb30ed9bd187a60da0d9d04d
|
data/docker/Dockerfile.server
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
FROM ruby:3.4-alpine
|
2
2
|
|
3
|
-
RUN apk add --no-cache
|
4
|
-
build-base \
|
5
|
-
ruby-dev \
|
6
|
-
linux-headers \
|
7
|
-
zlib-dev \
|
8
|
-
libffi-dev
|
3
|
+
RUN apk add --no-cache build-base
|
9
4
|
|
10
5
|
WORKDIR /app
|
11
6
|
|
@@ -13,14 +8,15 @@ RUN mkdir .specwrk/
|
|
13
8
|
|
14
9
|
ARG SPECWRK_SRV_PORT=5138
|
15
10
|
ARG SPECWRK_VERSION=latest
|
16
|
-
ARG
|
11
|
+
ARG GEM_FILE=specwrk-$SPECWRK_VERSION.gem
|
17
12
|
|
18
|
-
COPY $
|
19
|
-
RUN gem install ./$
|
20
|
-
RUN rm ./$
|
13
|
+
COPY $GEM_FILE ./
|
14
|
+
RUN gem install ./$GEM_FILE --no-document
|
15
|
+
RUN rm ./$GEM_FILE
|
21
16
|
|
22
|
-
RUN gem install
|
17
|
+
RUN gem install pitchfork thruster
|
23
18
|
COPY config.ru ./
|
19
|
+
COPY docker/pitchfork.conf ./
|
24
20
|
|
25
21
|
COPY docker/entrypoint.server.sh /usr/local/bin/entrypoint
|
26
22
|
RUN chmod +x /usr/local/bin/entrypoint
|
data/docker/entrypoint.server.sh
CHANGED
@@ -2,6 +2,6 @@
|
|
2
2
|
|
3
3
|
export THRUSTER_HTTP_PORT=${PORT:-5138}
|
4
4
|
export THRUSTER_TARGET_PORT=3000
|
5
|
-
export THRUSTER_HTTP_IDLE_TIMEOUT=${IDLE_TIMEOUT:-
|
5
|
+
export THRUSTER_HTTP_IDLE_TIMEOUT=${IDLE_TIMEOUT:-300}
|
6
6
|
|
7
|
-
exec thrust
|
7
|
+
exec thrust pitchfork -c pitchfork.conf
|
data/lib/specwrk/cli_reporter.rb
CHANGED
@@ -11,7 +11,10 @@ require "rspec/core/formatters/console_codes"
|
|
11
11
|
module Specwrk
|
12
12
|
class CLIReporter
|
13
13
|
def report
|
14
|
-
|
14
|
+
unless Client.connect?
|
15
|
+
puts colorizer.wrap("\nCannot connect to server to generate report. Assuming failure.", :red)
|
16
|
+
return 1
|
17
|
+
end
|
15
18
|
|
16
19
|
puts "\nFinished in #{total_duration} " \
|
17
20
|
"(total execution time of #{total_run_time})\n"
|
@@ -28,8 +31,11 @@ module Specwrk
|
|
28
31
|
puts colorizer.wrap(totals_line, :green)
|
29
32
|
0
|
30
33
|
end
|
31
|
-
rescue Specwrk::UnhandledResponseError
|
32
|
-
puts colorizer.wrap("
|
34
|
+
rescue Specwrk::UnhandledResponseError => e
|
35
|
+
puts colorizer.wrap("\nCannot report, #{e.message}.", :red)
|
36
|
+
|
37
|
+
client.shutdown
|
38
|
+
|
33
39
|
1
|
34
40
|
end
|
35
41
|
|
@@ -43,28 +49,28 @@ module Specwrk
|
|
43
49
|
summary
|
44
50
|
end
|
45
51
|
|
46
|
-
def
|
47
|
-
@
|
52
|
+
def report_data
|
53
|
+
@report_data ||= client.report
|
48
54
|
end
|
49
55
|
|
50
56
|
def total_duration
|
51
|
-
Time.parse(
|
57
|
+
Time.parse(report_data.dig(:meta, :last_finished_at)) - Time.parse(report_data.dig(:meta, :first_started_at))
|
52
58
|
end
|
53
59
|
|
54
60
|
def total_run_time
|
55
|
-
|
61
|
+
report_data.dig(:meta, :total_run_time)
|
56
62
|
end
|
57
63
|
|
58
64
|
def failure_count
|
59
|
-
|
65
|
+
report_data.dig(:meta, :failures)
|
60
66
|
end
|
61
67
|
|
62
68
|
def pending_count
|
63
|
-
|
69
|
+
report_data.dig(:meta, :pending)
|
64
70
|
end
|
65
71
|
|
66
72
|
def example_count
|
67
|
-
|
73
|
+
report_data.dig(:examples).length
|
68
74
|
end
|
69
75
|
|
70
76
|
def client
|
data/lib/specwrk/client.rb
CHANGED
data/lib/specwrk/queue.rb
CHANGED
@@ -6,7 +6,11 @@ require "json"
|
|
6
6
|
module Specwrk
|
7
7
|
# Thread-safe Hash access
|
8
8
|
class Queue
|
9
|
+
attr_reader :created_at
|
10
|
+
|
9
11
|
def initialize(hash = {})
|
12
|
+
@created_at = Time.now
|
13
|
+
|
10
14
|
if block_given?
|
11
15
|
@mutex = Monitor.new # Reentrant locking is required here
|
12
16
|
# It's possible to enter the proc from two threads, so we need to ||= in case
|
@@ -40,8 +44,6 @@ module Specwrk
|
|
40
44
|
end
|
41
45
|
|
42
46
|
class PendingQueue < Queue
|
43
|
-
attr_reader :previous_run_times
|
44
|
-
|
45
47
|
def shift_bucket
|
46
48
|
return bucket_by_file unless previous_run_times
|
47
49
|
|
@@ -59,17 +61,22 @@ module Specwrk
|
|
59
61
|
previous_run_times.dig(:meta, :average_run_time)
|
60
62
|
end
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
-
return unless path
|
65
|
-
return unless File.exist? path
|
64
|
+
def previous_run_times
|
65
|
+
return unless ENV["SPECWRK_OUT"]
|
66
66
|
|
67
|
-
|
68
|
-
|
67
|
+
@previous_run_times ||= begin
|
68
|
+
return unless previous_run_times_file_path
|
69
|
+
return unless File.exist? previous_run_times_file_path
|
69
70
|
|
70
|
-
|
71
|
+
raw_data = File.open(previous_run_times_file_path, "r") do |file|
|
72
|
+
file.flock(File::LOCK_SH)
|
73
|
+
file.read
|
74
|
+
end
|
71
75
|
|
72
|
-
|
76
|
+
@previous_run_times = JSON.parse(raw_data, symbolize_names: true)
|
77
|
+
rescue JSON::ParserError => e
|
78
|
+
warn "#{e.inspect} in file #{previous_run_times_file_path}"
|
79
|
+
nil
|
73
80
|
end
|
74
81
|
end
|
75
82
|
|
@@ -84,6 +91,15 @@ module Specwrk
|
|
84
91
|
|
85
92
|
private
|
86
93
|
|
94
|
+
# We want the most recently modified run time file
|
95
|
+
# report files are prefixed with a timestamp, and Dir.glob should order
|
96
|
+
# alphanumericly
|
97
|
+
def previous_run_times_file_path
|
98
|
+
return unless ENV["SPECWRK_OUT"]
|
99
|
+
|
100
|
+
@previous_run_times_file_path ||= Dir.glob(File.join(ENV["SPECWRK_OUT"], "*-report-*.json")).last
|
101
|
+
end
|
102
|
+
|
87
103
|
# Take elements from the hash where the file_path is the same
|
88
104
|
def bucket_by_file
|
89
105
|
bucket = []
|
@@ -163,7 +179,7 @@ module Specwrk
|
|
163
179
|
@hash.values.each { |example| calculate(example) }
|
164
180
|
|
165
181
|
@output[:meta][:total_run_time] = @run_times.sum
|
166
|
-
@output[:meta][:average_run_time] = @output[:meta][:total_run_time] / @run_times.length.to_f
|
182
|
+
@output[:meta][:average_run_time] = @output[:meta][:total_run_time] / [@run_times.length, 1].max.to_f
|
167
183
|
@output[:meta][:first_started_at] = @first_started_at.iso8601(6)
|
168
184
|
@output[:meta][:last_finished_at] = @last_finished_at.iso8601(6)
|
169
185
|
|
data/lib/specwrk/version.rb
CHANGED
data/lib/specwrk/web/app.rb
CHANGED
@@ -52,10 +52,8 @@ module Specwrk
|
|
52
52
|
|
53
53
|
def setup!
|
54
54
|
if ENV["SPECWRK_OUT"]
|
55
|
-
|
56
55
|
FileUtils.mkdir_p(ENV["SPECWRK_OUT"])
|
57
56
|
ENV["SPECWRK_SRV_LOG"] ||= Pathname.new(File.join(ENV["SPECWRK_OUT"], "server.log")).to_s unless ENV["SPECWRK_SRV_VERBOSE"]
|
58
|
-
ENV["SPECWRK_SRV_OUTPUT"] ||= Pathname.new(File.join(ENV["SPECWRK_OUT"], "report.json")).expand_path(Dir.pwd).to_s
|
59
57
|
end
|
60
58
|
|
61
59
|
if ENV["SPECWRK_SRV_LOG"]
|
@@ -100,8 +98,8 @@ module Specwrk
|
|
100
98
|
Endpoints::Complete
|
101
99
|
when ["POST", "/seed"]
|
102
100
|
Endpoints::Seed
|
103
|
-
when ["GET", "/
|
104
|
-
Endpoints::
|
101
|
+
when ["GET", "/report"]
|
102
|
+
Endpoints::Report
|
105
103
|
when ["DELETE", "/shutdown"]
|
106
104
|
Endpoints::Shutdown
|
107
105
|
else
|
data/lib/specwrk/web/auth.rb
CHANGED
@@ -22,11 +22,11 @@ module Specwrk
|
|
22
22
|
attr_reader :request
|
23
23
|
|
24
24
|
def not_found
|
25
|
-
[404, {"
|
25
|
+
[404, {"content-type" => "text/plain"}, ["This is not the path you're looking for, 'ol chap..."]]
|
26
26
|
end
|
27
27
|
|
28
28
|
def ok
|
29
|
-
[200, {"
|
29
|
+
[200, {"content-type" => "text/plain"}, ["OK, 'ol chap"]]
|
30
30
|
end
|
31
31
|
|
32
32
|
def payload
|
@@ -38,7 +38,7 @@ module Specwrk
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def pending_queue
|
41
|
-
Web::PENDING_QUEUES[
|
41
|
+
Web::PENDING_QUEUES[run_id]
|
42
42
|
end
|
43
43
|
|
44
44
|
def processing_queue
|
@@ -56,6 +56,14 @@ module Specwrk
|
|
56
56
|
def worker
|
57
57
|
workers[request.get_header("HTTP_X_SPECWRK_ID")]
|
58
58
|
end
|
59
|
+
|
60
|
+
def run_id
|
61
|
+
request.get_header("HTTP_X_SPECWRK_RUN")
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_report_file_path
|
65
|
+
@run_report_file_path ||= File.join(ENV["SPECWRK_OUT"], "#{completed_queue.created_at.strftime("%Y%m%dT%H%M%S")}-report-#{run_id}.json").to_s
|
66
|
+
end
|
59
67
|
end
|
60
68
|
|
61
69
|
# Base default response is 404
|
@@ -98,8 +106,8 @@ module Specwrk
|
|
98
106
|
end
|
99
107
|
end
|
100
108
|
|
101
|
-
if pending_queue.length.zero? && processing_queue.length.zero? && completed_queue.length.positive? && ENV["
|
102
|
-
completed_queue.dump_and_write(
|
109
|
+
if pending_queue.length.zero? && processing_queue.length.zero? && completed_queue.length.positive? && ENV["SPECWRK_OUT"]
|
110
|
+
completed_queue.dump_and_write(run_report_file_path)
|
103
111
|
end
|
104
112
|
|
105
113
|
ok
|
@@ -117,38 +125,50 @@ module Specwrk
|
|
117
125
|
end
|
118
126
|
|
119
127
|
if @examples.length.positive?
|
120
|
-
[200, {"
|
128
|
+
[200, {"content-type" => "application/json"}, [JSON.generate(@examples)]]
|
121
129
|
elsif pending_queue.length.zero? && processing_queue.length.zero? && completed_queue.length.zero?
|
122
|
-
[204, {"
|
130
|
+
[204, {"content-type" => "text/plain"}, ["Waiting for sample to be seeded."]]
|
123
131
|
elsif completed_queue.length.positive? && processing_queue.length.zero?
|
124
|
-
[410, {"
|
132
|
+
[410, {"content-type" => "text/plain"}, ["That's a good lad. Run along now and go home."]]
|
125
133
|
else
|
126
134
|
not_found
|
127
135
|
end
|
128
136
|
end
|
129
137
|
end
|
130
138
|
|
131
|
-
class
|
139
|
+
class Report < Base
|
132
140
|
def response
|
133
|
-
data
|
134
|
-
|
135
|
-
processing: {count: processing_queue.length},
|
136
|
-
completed: completed_queue.dump
|
137
|
-
}
|
138
|
-
|
139
|
-
if data.dig(:completed, :examples).length.positive?
|
140
|
-
[200, {"Content-Type" => "application/json"}, [JSON.generate(data)]]
|
141
|
+
if data
|
142
|
+
[200, {"content-type" => "application/json"}, [data]]
|
141
143
|
else
|
142
|
-
|
144
|
+
[404, {"content-type" => "text/plain"}, ["Unable to report on run #{run_id}; no file matching #{"*-report-#{run_id}.json"}"]]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def data
|
151
|
+
return @data if defined? @data
|
152
|
+
|
153
|
+
return unless most_recent_run_report_file
|
154
|
+
return unless File.exist?(most_recent_run_report_file)
|
155
|
+
|
156
|
+
@data = File.open(most_recent_run_report_file, "r") do |file|
|
157
|
+
file.flock(File::LOCK_SH)
|
158
|
+
file.read
|
143
159
|
end
|
144
160
|
end
|
161
|
+
|
162
|
+
def most_recent_run_report_file
|
163
|
+
@most_recent_run_report_file ||= Dir.glob(File.join(ENV["SPECWRK_OUT"], "*-report-#{run_id}.json")).last
|
164
|
+
end
|
145
165
|
end
|
146
166
|
|
147
167
|
class Shutdown < Base
|
148
168
|
def response
|
149
169
|
interupt! if ENV["SPECWRK_SRV_SINGLE_RUN"]
|
150
170
|
|
151
|
-
[200, {"
|
171
|
+
[200, {"content-type" => "text/plain"}, ["✌️"]]
|
152
172
|
end
|
153
173
|
|
154
174
|
def interupt!
|
data/lib/specwrk/web.rb
CHANGED
@@ -4,7 +4,7 @@ require "specwrk/queue"
|
|
4
4
|
|
5
5
|
module Specwrk
|
6
6
|
class Web
|
7
|
-
PENDING_QUEUES = Queue.new { |h, key| h[key] = PendingQueue.new
|
7
|
+
PENDING_QUEUES = Queue.new { |h, key| h[key] = PendingQueue.new }
|
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
10
|
WORKERS = Hash.new { |h, key| h[key] = Hash.new { |h, key| h[key] = {} } }
|
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.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Westendorf
|
@@ -152,6 +152,7 @@ files:
|
|
152
152
|
- config.ru
|
153
153
|
- docker/Dockerfile.server
|
154
154
|
- docker/entrypoint.server.sh
|
155
|
+
- docker/pitchfork.conf
|
155
156
|
- exe/specwrk
|
156
157
|
- lib/specwrk.rb
|
157
158
|
- lib/specwrk/cli.rb
|