specwrk 0.4.5 → 0.4.6

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: b752ad94605b60fec10d72935d86e9d37cc0b3cf7ae107163921259f1895affa
4
- data.tar.gz: 131680d8a177b839c46c11d910841e704d3eff134c0578356f639a22df325d69
3
+ metadata.gz: 29b6d72df4f3e3a4ff055b9d9203e6af31173fbab4fe43235495bcadcbf7c37e
4
+ data.tar.gz: 3b9c0af70e17efd7f5a42529550b49c2ce6b58e62f7b0f8c2fa4d66d14f09822
5
5
  SHA512:
6
- metadata.gz: 5b4c33485613191a342c8b614663a954b181a551ef53efa23fe35ae2c964bcbe843a56493d6afcaae33d88aee9b6272d8edfe7e2323b46fc7d144101c1b8628f
7
- data.tar.gz: ed8d728db0fc51274083efc82ddeb4db2766113893f3bf646feda0061a7ce601d0ebd82109545d8434c989437ec7fb2885607c1b077166066647d9c9c1a383f3
6
+ metadata.gz: b6088d190bcbbd0b274f76d992626c851637c27d132fb5642298c15cfa49f891265836f800bf0dbca77eee3dc680cb606ddb6262cd18bc09776ed7623e81d494
7
+ data.tar.gz: ffcda0f107b0b82d40d3bf2472a1b0447afed956bdb969abc1765e8c158b03a2ffa32c8d7ed2d130b46d5d42294e97d6f32be05ff3c299a61f75b154d4be5465
@@ -0,0 +1,155 @@
1
+ version: 2.1
2
+
3
+ workflows:
4
+ test_workflow:
5
+ jobs:
6
+ - test
7
+ - specwrk-single-node
8
+ - specwrk-multi-node-prepare
9
+ - specwrk-multi-node:
10
+ requires:
11
+ - specwrk-multi-node-prepare
12
+
13
+ jobs:
14
+ test:
15
+ docker:
16
+ - image: cimg/ruby:3.4.4
17
+ working_directory: ~/project
18
+ steps:
19
+ - checkout
20
+
21
+ - restore_cache:
22
+ keys:
23
+ - v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
24
+ - v1-deps-{{ checksum "Gemfile" }}
25
+ - v1-deps-
26
+
27
+ - run:
28
+ name: Install Gems
29
+ command: |
30
+ bundle config path vendor/bundle
31
+ bundle install --jobs 4 --retry 2
32
+
33
+ - save_cache:
34
+ paths:
35
+ - vendor/bundle
36
+ key: v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
37
+
38
+ - run:
39
+ name: Run RSpec
40
+ command: bundle exec rspec
41
+
42
+ specwrk-single-node:
43
+ docker:
44
+ - image: cimg/ruby:3.4.4
45
+ working_directory: ~/project
46
+ steps:
47
+ - checkout
48
+
49
+ - restore_cache:
50
+ keys:
51
+ - v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
52
+ - v1-deps-{{ checksum "Gemfile" }}
53
+ - v1-deps-
54
+
55
+ ## SPECWRK STEP ##
56
+ - restore_cache:
57
+ keys:
58
+ - specwrk-{{ .Branch }}
59
+ - specwrk-
60
+ ## /SPECWRK STEP ##
61
+
62
+ - run:
63
+ name: Install Gems
64
+ command: |
65
+ bundle config path vendor/bundle
66
+ bundle install --jobs 4 --retry 2
67
+
68
+ - save_cache:
69
+ paths:
70
+ - vendor/bundle
71
+ key: v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
72
+
73
+ ## SPECWRK STEP ##
74
+ - run:
75
+ name: Run tests via specwrk start
76
+ command: bundle exec specwrk start --count 2 spec/
77
+ ## /SPECWRK STEP ##
78
+
79
+ ## SPECWRK STEP ##
80
+ - save_cache:
81
+ paths:
82
+ - .specwrk/report.json
83
+ key: specwrk-{{ .Branch }}
84
+ ## /SPECWRK STEP ##
85
+
86
+ specwrk-multi-node-prepare:
87
+ docker:
88
+ - image: cimg/ruby:3.4.4
89
+ steps:
90
+ - checkout
91
+
92
+ - restore_cache:
93
+ keys:
94
+ - gem-cache-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
95
+ - gem-cache-{{ checksum "Gemfile" }}
96
+ - gem-cache-
97
+
98
+ - run:
99
+ name: bundle install
100
+ command: |
101
+ bundle config path vendor/bundle
102
+ bundle install --jobs 4 --retry 2
103
+
104
+ - save_cache:
105
+ paths:
106
+ - vendor/bundle
107
+ key: gem-cache-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
108
+
109
+ ## SPECWRK STEP ##
110
+ - run:
111
+ name: Seed examples to specwrk server
112
+ command: |
113
+ bundle exec specwrk seed \
114
+ --uri "$SPECWRK_URI" \
115
+ --key "$SPECWRK_KEY" \
116
+ --run "$CIRCLE_WORKFLOW_ID" \
117
+ spec/
118
+
119
+ ## /SPECWRK STEP ##
120
+
121
+ specwrk-multi-node:
122
+ parallelism: 2
123
+ docker:
124
+ - image: cimg/ruby:3.4.4
125
+ working_directory: ~/project
126
+ steps:
127
+ - checkout
128
+
129
+ - restore_cache:
130
+ keys:
131
+ - v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
132
+ - v1-deps-{{ checksum "Gemfile" }}
133
+ - v1-deps-
134
+
135
+ - run:
136
+ name: Install Gems
137
+ command: |
138
+ bundle config path vendor/bundle
139
+ bundle install --jobs 4 --retry 2
140
+
141
+ - save_cache:
142
+ paths:
143
+ - vendor/bundle
144
+ key: v1-deps-{{ checksum "Gemfile" }}-{{ checksum "specwrk.gemspec" }}
145
+
146
+ ## SPECWRK STEP ##
147
+ - run:
148
+ name: Run tests via specwrk work
149
+ command: |
150
+ bundle exec specwrk work \
151
+ --uri "$SPECWRK_URI" \
152
+ --key "$SPECWRK_KEY" \
153
+ --run "$CIRCLE_WORKFLOW_ID" \
154
+ --count 2
155
+ ## /SPECWRK STEP ##
data/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2025-05-12
3
+ ## v0.4.5
4
4
 
5
- - Initial release
5
+ - Set ENV var when generating seed examples [#47](https://github.com/danielwestendorf/specwrk/issues/47). by @danielwestendorf
6
+ ## v0.4.4
7
+
8
+ - Don’t dump the completed queue if it’s empty [#45](https://github.com/danielwestendorf/specwrk/issues/45). by @danielwestendorf
9
+ - Move single-run CLI option to only be an option for the `serve` command [#41](https://github.com/danielwestendorf/specwrk/issues/41). by @danielwestendorf
10
+ - Don’t complete examples which are not in the processing queue [#44](https://github.com/danielwestendorf/specwrk/issues/44). by @danielwestendorf
11
+
12
+ ## v0.4.3
13
+
14
+ - Fix CLI’s `--uri` option description. by @danielwestendorf
15
+ - Add an unauthenticated `/health` endpoint to the server [#40](https://github.com/danielwestendorf/specwrk/issues/40). by @danielwestendorf
16
+ - Add support excluding server paths from authentication [#39](https://github.com/danielwestendorf/specwrk/issues/39). by @danielwestendorf
17
+
18
+ ## v0.4.2
19
+
20
+ - Add specwrk variations to CI [#38](https://github.com/danielwestendorf/specwrk/issues/38). by @danielwestendorf
21
+ - Support specifying if subsequent seeds should be ignored for a run [#37](https://github.com/danielwestendorf/specwrk/issues/37). by @danielwestendorf
22
+
23
+ ## v0.4.1
24
+
25
+ - Make the number of seed waits configurable [#35](https://github.com/danielwestendorf/specwrk/issues/35). by @danielwestendorf
26
+
27
+ ## v0.4.0
28
+
29
+ - Worker wait for seeding [#32](https://github.com/danielwestendorf/specwrk/issues/32). by @danielwestendorf
30
+ - Track if the worker has processed *any* examples [#31](https://github.com/danielwestendorf/specwrk/issues/31). by @danielwestendorf
31
+
32
+ ## v0.3.0
33
+
34
+ - Assign `Specwrk.net_http` to `Net::HTTP` before WebMock can mock it [#22](https://github.com/danielwestendorf/specwrk/issues/22). by @danielwestendorf
35
+ - Worker PIDs representative exit status [#20](https://github.com/danielwestendorf/specwrk/issues/20). by @danielwestendorf
36
+ - Track an individual worker’s failures [#19](https://github.com/danielwestendorf/specwrk/issues/19). by @danielwestendorf
37
+ - Default localhost server URI is not SSL. by @danielwestendorf
38
+ - Handle SSL-protected srv endpoints [#18](https://github.com/danielwestendorf/specwrk/issues/18). by @danielwestendorf
39
+
40
+ ## v0.2.0
41
+
42
+ - Tag v0.2.0 by @danielwestendorf
data/README.md CHANGED
@@ -8,7 +8,7 @@ One CLI command to:
8
8
  3. Execute
9
9
 
10
10
  ## Install
11
- Start by adding specwrk to your project or installing it.
11
+ Start by adding `specwrk` to your project or installing it.
12
12
  ```sh
13
13
  $ bundle add specwrk -g development,test
14
14
  ```
@@ -30,7 +30,7 @@ Commands:
30
30
  ```
31
31
 
32
32
  ### `specwrk start -c 8 spec/`
33
- Indended for quick-adhoc runs in development. This command starts a queue server, seeds it with examples from the `spec/` directory, and starts `8` worker processes. It will report the ultimate success or failure.
33
+ Intended for quick ad-hoc local host development or single-node CI runs. This command starts a queue server, seeds it with examples from the `spec/` directory, and starts `8` worker processes. It will report the ultimate success or failure.
34
34
 
35
35
  ```sh
36
36
  $ start --help
@@ -158,14 +158,78 @@ Rails has had easy multi-process test setup for a while now by creating unique t
158
158
  -- Capybara.server_port = 5550
159
159
  ++ Capybara.server_port = 5550 + ENV.fetch("TEST_ENV_NUMBER", "1").to_i
160
160
  ++ Capybara.always_include_port = true
161
+ -- ActiveRecord::Migration.maintain_test_schema!
162
+ ++ ActiveRecord::Migration.maintain_test_schema! unless ENV["SPECWRK_SEED"]
161
163
  ```
162
164
 
165
+ YMMV, but please submit an issue if your setup required more configuration.
166
+
163
167
  ## CI
164
168
  Run `specwrk` in CI in either a single-node or multi-node configuration.
165
169
 
166
170
  ### Single-node, multi-process
167
- Single-node + multi-process is the easiest way to get started. Add it to your existing configuration
171
+ Single-node, multi-process works best when you only have a single node running tests, but that node has many unused CPUs. This is similar to running `specwrk` locally with `bundle exec specwrk start spec/` which spins up a local server, seeds the server with examples that need to be run, and then spawns child worker processes which execute those examples in parallel.
172
+
173
+ Make sure to persist `$SPECWRK_OUT/report.json` between runs so that subsequent run queues can be optimized.
174
+
175
+ [GitHub Actions Example](https://github.com/danielwestendorf/specwrk/blob/main/.github/workflows/specwrk-single-node.yml)
176
+
177
+ [CircleCI Example](https://github.com/danielwestendorf/specwrk/blob/main/.circleci/config.yml) (specwrk-single-node job)
178
+
179
+ ### Multi-node, multi-process
180
+ Multi-node, multi-process works best when have many nodes running tests. This distributes the test execution across the nodes until the queue is for the run is empty, optimizing for slowest specs first. This distributes test execution across all nodes evenly(-ish).
181
+
182
+ To accomplish this, a central queue server is required, examples must be explicitly seeded, and workers explicitly started.
183
+
184
+ 1. Start a centralized queue server (see [Running a persistent Queue Server](#running-a-persistent-queue-server))
185
+ 2. Seed the server with the specs for the current `SPECWWRK_RUN` pointed at your central server
186
+ 3. Execute `specwrk work` for the given process count, for the current `SPECWRK_RUN`, pointed at your central server
187
+
188
+ [GitHub Actions Example](https://github.com/danielwestendorf/specwrk/blob/main/.github/workflows/specwrk-multi-node.yml)
189
+
190
+ [CircleCI Example](https://github.com/danielwestendorf/specwrk/blob/main/.circleci/config.yml) (see specwrk-multi-node-prepare, specwrk-multi-node jobs)
191
+
192
+
193
+ ## Running a persistent Queue Server
194
+ Start a persistent Queue Server given one of the following methods
195
+ - The explicit ruby command `bundle exec specwrk serve --port $PORT`
196
+ - Via [docker image](https://hub.docker.com/repository/docker/danielwestendorf/specwrk-server/general): `docker run -e PORT=5139 -p 5139:5139 docker.io/danielwestendorf/specwrk-server:latest`
197
+ - By mounting the app as an Rack app
198
+ ```ruby
199
+ require 'rack'
200
+ require 'specwrk/web'
201
+
202
+ app = Specwrk::Web.rackup # this is a Rack::Builder instance
203
+
204
+ Rack::Server.start(
205
+ app: app,
206
+ server: 'webrick',
207
+ Host: '0.0.0.0',
208
+ Port: 9292
209
+ )
210
+
211
+ # OR maybe
212
+ # Rack::Handler::WEBrick.run(app, Host: '0.0.0.0', Port: 9292)
213
+ # Rack::Handler::Puma.run(app, Host: '0.0.0.0', Port: 9292)
214
+
215
+ # OR maybe
216
+ # config.ru
217
+ require_relative 'lib/specwrk/web'
218
+
219
+ run Specwrk::Web.rackup
220
+
221
+ # OR maybe
222
+ # config/routes.rb
223
+ Rails.application.routes.draw do
224
+ # everything under /specwrk will be handled by your Rack::Builder
225
+ mount Specwrk::Web.rackup, at: '/specwrk'
226
+ end
227
+ ```
228
+ ### Configuring your Queue Server
229
+ - Secure your server with a key either with the `SPECWRK_SRV_KEY` environment variable or `--key` CLI option
230
+ - Configure the server output to be a persisted volume so your timings survive between restarts with the `SPECWRK_OUT` environment variable or `--out` CLI option
168
231
 
232
+ See [specwrk serve --help](#specwrk-serve) for all possible configuration options.
169
233
 
170
234
  ## Contributing
171
235
 
data/lib/specwrk/cli.rb CHANGED
@@ -122,8 +122,10 @@ module Specwrk
122
122
  Client.new.seed(examples)
123
123
  rescue Errno::ECONNREFUSED
124
124
  puts "Server at #{ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138")} is refusing connections, exiting...#{ENV["SPECWRK_FLUSH_DELIMINATOR"]}"
125
+ exit 1
125
126
  rescue Errno::ECONNRESET
126
127
  puts "Server at #{ENV.fetch("SPECWRK_SRV_URI", "http://localhost:5138")} stopped responding to connections, exiting...#{ENV["SPECWRK_FLUSH_DELIMINATOR"]}"
128
+ exit 1
127
129
  end
128
130
  end
129
131
 
@@ -184,6 +186,9 @@ module Specwrk
184
186
  self.class.setup(**args)
185
187
  $stdout.sync = true
186
188
 
189
+ # nil this env var if it exists to prevent never-ending workers
190
+ ENV["SPECWRK_SRV_URI"] = nil
191
+
187
192
  web_pid = Process.fork do
188
193
  require "specwrk/web"
189
194
  require "specwrk/web/app"
@@ -210,7 +215,10 @@ module Specwrk
210
215
  status "Samples seeded ✓"
211
216
  end
212
217
 
213
- Specwrk.wait_for_pids_exit([seed_pid])
218
+ if Specwrk.wait_for_pids_exit([seed_pid]).value?(1)
219
+ Process.kill("INT", web_pid)
220
+ exit(1)
221
+ end
214
222
 
215
223
  return if Specwrk.force_quit
216
224
  status "Starting #{worker_count} workers..."
@@ -102,13 +102,13 @@ module Specwrk
102
102
  def complete_examples(examples)
103
103
  response = post "/complete", body: examples.to_json
104
104
 
105
- (response.code == "200") ? true : UnhandledResponseError.new("#{response.code}: #{response.body}")
105
+ (response.code == "200") ? true : raise(UnhandledResponseError.new("#{response.code}: #{response.body}"))
106
106
  end
107
107
 
108
108
  def seed(examples)
109
109
  response = post "/seed", body: examples.to_json
110
110
 
111
- (response.code == "200") ? true : UnhandledResponseError.new("#{response.code}: #{response.body}")
111
+ (response.code == "200") ? true : raise(UnhandledResponseError.new("#{response.code}: #{response.body}"))
112
112
  end
113
113
 
114
114
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Specwrk
4
- VERSION = "0.4.5"
4
+ VERSION = "0.4.6"
5
5
  end
@@ -49,7 +49,7 @@ module Specwrk
49
49
  def rackup
50
50
  Rack::Builder.new do
51
51
  use Rack::Runtime
52
- use Specwrk::Web::Logger, $stdout
52
+ use Specwrk::Web::Logger, $stdout, %w[/health]
53
53
  use Specwrk::Web::Auth, %w[/health] # global auth check
54
54
  run Specwrk::Web::App.new # your router
55
55
  end
@@ -82,9 +82,7 @@ module Specwrk
82
82
  def response
83
83
  processing_queue.synchronize do |processing_queue_hash|
84
84
  payload.each do |example|
85
- next unless processing_queue_hash.key?(example[:id])
86
-
87
- processing_queue_hash.delete(example[:id])
85
+ next unless processing_queue_hash.delete(example[:id])
88
86
  completed_queue[example[:id]] = example
89
87
  end
90
88
  end
@@ -3,11 +3,15 @@
3
3
  module Specwrk
4
4
  class Web
5
5
  class Logger
6
- def initialize(app, out = $stdout)
7
- @app, @out = app, out
6
+ def initialize(app, out = $stdout, ignored_paths = [])
7
+ @app = app
8
+ @out = out
9
+ @ignored_paths = ignored_paths
8
10
  end
9
11
 
10
12
  def call(env)
13
+ return @app.call(env) if @ignored_paths.include? env["PATH_INFO"]
14
+
11
15
  start_time = Time.now
12
16
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
13
17
  status, headers, body = @app.call(env)
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.5
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Westendorf
@@ -142,6 +142,7 @@ executables:
142
142
  extensions: []
143
143
  extra_rdoc_files: []
144
144
  files:
145
+ - ".circleci/config.yml"
145
146
  - ".rspec"
146
147
  - ".standard.yml"
147
148
  - CHANGELOG.md