hastci 0.1.0 → 0.1.2

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: 27d28132add51197aa671ad6dd4144aa7867e6872fd04759b8a58c8374902916
4
- data.tar.gz: 4837752bbdddb42aade93d3ccc557bdb6cf7a199a733be643c0b8c429184eb43
3
+ metadata.gz: b5b2325eb3310d82c948100ad8944303a1cafc0f3824497fa68ff3a2d229184b
4
+ data.tar.gz: 93b8a6e612a613d8491624008fa0e334653aff2b769e437385141c56ad62372b
5
5
  SHA512:
6
- metadata.gz: 0be5ec737d6fe6196af175de67c21edcd39fe30463602ff757b98dadc0141583799ea89459eeec834fffd08e93d319212767067f3f728be446af7c84c8b96cec
7
- data.tar.gz: 2f4118eb2e35de8edff25e53be4d6930d345366bb13f796656609f88ed29cc9063047caf6d46d0361b07172960a3abf08dd29a07d1585a928ede084c2b14da84
6
+ metadata.gz: d8d05f307a3408f5238081361f11c5fafbf5e5f1c1eb822bbeaf8dd42fb172bc4048ca7ae6d3303aa5cf77de543f51cea829313e7efaee814c74df59680debe3
7
+ data.tar.gz: 9b557ef0c58252027907cb79197d00273e100a7735b2072203d68c897c6a966cb0b47d0d985993ebcedf2954ab1238cdab9333902352a28cdb8fd3344d0a5397
data/devenv.lock CHANGED
@@ -3,10 +3,10 @@
3
3
  "devenv": {
4
4
  "locked": {
5
5
  "dir": "src/modules",
6
- "lastModified": 1764877862,
6
+ "lastModified": 1766921574,
7
7
  "owner": "cachix",
8
8
  "repo": "devenv",
9
- "rev": "5b6cb65d7dec827badc6bbceeac934931beb7f4e",
9
+ "rev": "02c9dcf3e050400d8101057f9f00ec458af7c959",
10
10
  "type": "github"
11
11
  },
12
12
  "original": {
@@ -19,10 +19,10 @@
19
19
  "flake-compat": {
20
20
  "flake": false,
21
21
  "locked": {
22
- "lastModified": 1764712249,
22
+ "lastModified": 1766929376,
23
23
  "owner": "edolstra",
24
24
  "repo": "flake-compat",
25
- "rev": "3b279e4317ccfa4865356387935310531357d919",
25
+ "rev": "236f248441a986331cda53b039e7f9fd96e03635",
26
26
  "type": "github"
27
27
  },
28
28
  "original": {
@@ -34,10 +34,10 @@
34
34
  "flake-compat_2": {
35
35
  "flake": false,
36
36
  "locked": {
37
- "lastModified": 1764712249,
37
+ "lastModified": 1766929376,
38
38
  "owner": "edolstra",
39
39
  "repo": "flake-compat",
40
- "rev": "3b279e4317ccfa4865356387935310531357d919",
40
+ "rev": "236f248441a986331cda53b039e7f9fd96e03635",
41
41
  "type": "github"
42
42
  },
43
43
  "original": {
@@ -72,10 +72,10 @@
72
72
  ]
73
73
  },
74
74
  "locked": {
75
- "lastModified": 1763988335,
75
+ "lastModified": 1765911976,
76
76
  "owner": "cachix",
77
77
  "repo": "git-hooks.nix",
78
- "rev": "50b9238891e388c9fdc6a5c49e49c42533a1b5ce",
78
+ "rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
79
79
  "type": "github"
80
80
  },
81
81
  "original": {
@@ -128,10 +128,10 @@
128
128
  ]
129
129
  },
130
130
  "locked": {
131
- "lastModified": 1764740760,
131
+ "lastModified": 1766728475,
132
132
  "owner": "bobvanderlinden",
133
133
  "repo": "nixpkgs-ruby",
134
- "rev": "6747e142e5689bb156d47c9c90fe1596ecd246cb",
134
+ "rev": "f167828eab19c3a7e3faa066a140d92e307f7b16",
135
135
  "type": "github"
136
136
  },
137
137
  "original": {
@@ -18,13 +18,15 @@ module HastCI
18
18
  private_constant :DEFAULT_MAX_RETRIES, :DEFAULT_INITIAL_BACKOFF, :DEFAULT_MAX_BACKOFF,
19
19
  :DEFAULT_KEEP_ALIVE_TIMEOUT, :DEFAULT_OPEN_TIMEOUT, :DEFAULT_READ_TIMEOUT, :DEFAULT_WRITE_TIMEOUT
20
20
 
21
+ API_PATH_PREFIX = "/api/v1"
22
+
21
23
  # Separate connections allow concurrent operations without blocking.
22
24
  # E.g., heartbeat can continue while the main thread waits on a claim.
23
25
  CONNECTION_DEFAULT = :default
24
26
  CONNECTION_HEARTBEAT = :heartbeat
25
27
  CONNECTION_ACK = :ack
26
28
 
27
- private_constant :CONNECTION_DEFAULT, :CONNECTION_HEARTBEAT, :CONNECTION_ACK
29
+ private_constant :API_PATH_PREFIX, :CONNECTION_DEFAULT, :CONNECTION_HEARTBEAT, :CONNECTION_ACK
28
30
 
29
31
  def initialize(config:, sleeper: Kernel.method(:sleep), max_retries: nil, random: Random.new)
30
32
  @config = config
@@ -56,52 +58,54 @@ module HastCI
56
58
  commit_sha: @config.commit_sha
57
59
  )
58
60
  response = ensure_hash_response(
59
- post_json("/runs/init", {
61
+ post_json("#{API_PATH_PREFIX}/runs", {
60
62
  run_key: run_key,
61
63
  worker_id: worker_id,
62
64
  commit_sha: commit_sha
63
65
  }),
64
- context: "/runs/init"
66
+ context: "#{API_PATH_PREFIX}/runs"
65
67
  )
66
68
 
67
69
  {
68
- run_id: fetch_required(response, "run_id", context: "/runs/init"),
69
- status: fetch_required(response, "status", context: "/runs/init").to_sym,
70
- role: fetch_required(response, "role", context: "/runs/init").to_sym
70
+ run_id: fetch_required(response, "run_id", context: "#{API_PATH_PREFIX}/runs"),
71
+ status: fetch_required(response, "status", context: "#{API_PATH_PREFIX}/runs").to_sym,
72
+ role: fetch_required(response, "role", context: "#{API_PATH_PREFIX}/runs").to_sym
71
73
  }
72
74
  end
73
75
 
74
76
  def seed(run_id:, tasks:)
75
- post_json("/runs/#{run_id}/seed", {
77
+ post_json("#{API_PATH_PREFIX}/runs/#{run_id}/seed", {
76
78
  tasks: tasks.map { |name| {name: name} }
77
79
  })
78
80
  end
79
81
 
80
82
  def run_status(run_id:)
81
- response = ensure_hash_response(get_json("/runs/#{run_id}/status"), context: "/runs/status")
83
+ response = ensure_hash_response(
84
+ get_json("#{API_PATH_PREFIX}/runs/#{run_id}"),
85
+ context: "#{API_PATH_PREFIX}/runs/:run_id"
86
+ )
82
87
 
83
88
  {
84
- status: fetch_required(response, "status", context: "/runs/status").to_sym
89
+ status: fetch_required(response, "status", context: "#{API_PATH_PREFIX}/runs/:run_id").to_sym
85
90
  }
86
91
  end
87
92
 
88
93
  def claim(
89
- run_key: @config.run_key,
94
+ run_id:,
90
95
  worker_id: @config.worker_id,
91
96
  batch: @config.claim_batch_size
92
97
  )
93
98
  response = ensure_hash_response(
94
- post_json("/tasks/claim?batch=#{batch}", {
95
- run_key: run_key,
99
+ post_json("#{API_PATH_PREFIX}/runs/#{run_id}/claims?batch=#{batch}", {
96
100
  worker_id: worker_id
97
101
  }),
98
- context: "/tasks/claim"
102
+ context: "#{API_PATH_PREFIX}/runs/:run_id/claims"
99
103
  )
100
104
 
101
105
  tasks = Array(response.fetch("tasks", [])).map do |task_data|
102
106
  Task.new(
103
- id: fetch_required(task_data, "id", context: "/tasks/claim"),
104
- name: fetch_required(task_data, "name", context: "/tasks/claim")
107
+ id: fetch_required(task_data, "id", context: "#{API_PATH_PREFIX}/runs/:run_id/claims"),
108
+ name: fetch_required(task_data, "name", context: "#{API_PATH_PREFIX}/runs/:run_id/claims")
105
109
  )
106
110
  end
107
111
 
@@ -114,7 +118,7 @@ module HastCI
114
118
  end
115
119
 
116
120
  def ack(task_id:, status:, duration_s:, logs:)
117
- post_json("/tasks/#{task_id}/ack", {
121
+ post_json("#{API_PATH_PREFIX}/tasks/#{task_id}/acknowledgment", {
118
122
  status: status.to_s,
119
123
  duration_s: duration_s,
120
124
  logs: logs
@@ -122,11 +126,10 @@ module HastCI
122
126
  end
123
127
 
124
128
  def heartbeat(
125
- run_key: @config.run_key,
129
+ run_id:,
126
130
  worker_id: @config.worker_id
127
131
  )
128
- post_json("/workers/heartbeat", {
129
- run_key: run_key,
132
+ post_json("#{API_PATH_PREFIX}/runs/#{run_id}/heartbeats", {
130
133
  worker_id: worker_id
131
134
  }, pool: CONNECTION_HEARTBEAT)
132
135
 
@@ -176,12 +179,13 @@ module HastCI
176
179
 
177
180
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time if @config.debug
178
181
  log_response(response, request_id, elapsed) if @config.debug
182
+ log_error_body(response, request_id) if @config.debug && response.code.to_i >= 400
179
183
  parse_response(response)
180
184
  end
181
185
  end
182
186
 
183
187
  def log_request(request, request_id)
184
- HastCI.logger.debug("[#{request_id}] #{request.method} #{request.path}")
188
+ HastCI.logger.debug("[#{request_id}] #{request.method} #{@base_url}#{request.path}")
185
189
  return unless request.body
186
190
 
187
191
  body_preview = if request.body.length > 200
@@ -196,6 +200,15 @@ module HastCI
196
200
  HastCI.logger.debug("[#{request_id}] Response: #{response.code} (#{(elapsed_s * 1000).round}ms)")
197
201
  end
198
202
 
203
+ def log_error_body(response, request_id)
204
+ body = response.body.to_s
205
+ return if body.strip.empty?
206
+
207
+ summary = body.match(/No route matches[^<]*/)&.to_s
208
+ body_preview = summary || ((body.length > 200) ? "#{body[0, 200]}... (#{body.length} bytes)" : body)
209
+ HastCI.logger.debug("[#{request_id}] Error body: #{body_preview}")
210
+ end
211
+
199
212
  def log_retry(request_id, attempt)
200
213
  HastCI.logger.debug("[#{request_id}] Retry attempt #{attempt}")
201
214
  end
data/lib/hastci/cli.rb CHANGED
@@ -9,16 +9,22 @@ module HastCI
9
9
  runner_exit_code = ExitCodes::SUCCESS
10
10
  interrupted = false
11
11
 
12
- cleanup_result = HastCI::Session.run(config: config) do |session|
13
- setup_interrupt_handler(session, err) { interrupted = true }
12
+ interrupt_cleanup = nil
14
13
 
15
- session.start!
14
+ begin
15
+ cleanup_result = HastCI::Session.run(config: config) do |session|
16
+ interrupt_cleanup = setup_interrupt_handler(session, err) { interrupted = true }
16
17
 
17
- runner_exit_code = if session.stopping?
18
- ExitCodes::SUCCESS
19
- else
20
- runner_block.call(session, argv, err, out)
18
+ session.start!
19
+
20
+ runner_exit_code = if session.stopping?
21
+ ExitCodes::SUCCESS
22
+ else
23
+ runner_block.call(session, argv, err, out)
24
+ end
21
25
  end
26
+ ensure
27
+ interrupt_cleanup&.call
22
28
  end
23
29
 
24
30
  print_errors(cleanup_result, err: err)
@@ -81,20 +87,37 @@ module HastCI
81
87
 
82
88
  # :nocov:
83
89
  def self.setup_interrupt_handler(session, err, &on_interrupt)
84
- handler = lambda do |signal|
85
- if session.stopping?
86
- err.puts("\nForce quit!")
87
- exit!(1)
88
- else
89
- on_interrupt&.call
90
- session.request_stop!(:user_interrupt)
91
- err.puts("\nReceived #{signal} - shutting down after current task...")
90
+ signals = Queue.new
91
+ signal_count = 0
92
+
93
+ trap("INT") { signals << "SIGINT" }
94
+ trap("TERM") { signals << "SIGTERM" }
95
+
96
+ watcher = Thread.new do
97
+ loop do
98
+ signal = signals.pop
99
+ break if signal == :shutdown
100
+
101
+ already_stopping = session.stopping?
102
+ signal_count += 1
103
+
104
+ if already_stopping || signal_count > 1
105
+ err.puts("\nForce quit!")
106
+ exit!(1)
107
+ else
108
+ on_interrupt&.call
109
+ session.request_stop!(:user_interrupt)
110
+ err.puts("\nReceived #{signal} - shutting down after current task...")
111
+ end
92
112
  end
93
113
  end
94
114
 
95
- trap("INT") { handler.call("SIGINT") }
96
- trap("TERM") { handler.call("SIGTERM") }
115
+ lambda do
116
+ signals << :shutdown
117
+ watcher.join
118
+ end
97
119
  end
120
+
98
121
  private_class_method :setup_interrupt_handler
99
122
  # :nocov:
100
123
  end
data/lib/hastci/config.rb CHANGED
@@ -4,7 +4,7 @@ require "securerandom"
4
4
 
5
5
  module HastCI
6
6
  class Config
7
- DEFAULT_API_BASE_URL = "https://hastci.com/api"
7
+ DEFAULT_API_BASE_URL = "https://hastci.com"
8
8
  DEFAULT_CLAIM_BATCH_SIZE = 10
9
9
  DEFAULT_HEARTBEAT_INTERVAL = 15
10
10
  DEFAULT_SEEDING_TIMEOUT = 300
@@ -7,8 +7,9 @@ module HastCI
7
7
 
8
8
  private_constant :SHUTDOWN_TIMEOUT, :MAX_CONSECUTIVE_FAILURES
9
9
 
10
- def initialize(api_client:, error_collector: nil, interval: 15)
10
+ def initialize(api_client:, run_id:, error_collector: nil, interval: 15)
11
11
  @api_client = api_client
12
+ @run_id = run_id
12
13
  @error_collector = error_collector
13
14
  @interval = interval
14
15
  @running = false
@@ -64,7 +65,7 @@ module HastCI
64
65
  end
65
66
 
66
67
  def send_heartbeat
67
- @api_client.heartbeat
68
+ @api_client.heartbeat(run_id: @run_id)
68
69
  @consecutive_failures = 0
69
70
  rescue RetryExhaustedError, FatalApiError => e
70
71
  @error_collector&.report(e)
@@ -203,6 +203,7 @@ module HastCI
203
203
 
204
204
  @heartbeat = Heartbeat.new(
205
205
  api_client: @api_client,
206
+ run_id: @run_id,
206
207
  interval: @config.heartbeat_interval,
207
208
  error_collector: @error_collector
208
209
  )
@@ -233,7 +234,7 @@ module HastCI
233
234
  max_size: @buffer_max_size,
234
235
  fetcher: lambda { |limit|
235
236
  batch_size = [limit, @config.claim_batch_size].min
236
- @api_client.claim(batch: batch_size)
237
+ @api_client.claim(run_id: @run_id, batch: batch_size)
237
238
  },
238
239
  error_collector: @error_collector,
239
240
  poll_interval: @poll_interval,
@@ -2,6 +2,6 @@
2
2
 
3
3
  # :nocov:
4
4
  module HastCI
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  # :nocov:
@@ -11,7 +11,7 @@
11
11
  "providerState": "a valid API key",
12
12
  "request": {
13
13
  "method": "post",
14
- "path": "/runs/init",
14
+ "path": "/api/v1/runs",
15
15
  "headers": {
16
16
  "Authorization": "Bearer test-api-key",
17
17
  "Content-Type": "application/json"
@@ -39,13 +39,14 @@
39
39
  "Content-Type": "application/json"
40
40
  },
41
41
  "body": {
42
- "run_id": "run-uuid-123",
42
+ "run_id": "abc123def456",
43
43
  "status": "seeding",
44
44
  "role": "seeder"
45
45
  },
46
46
  "matchingRules": {
47
47
  "$.body.run_id": {
48
- "match": "type"
48
+ "match": "regex",
49
+ "regex": "^[0-9A-Za-z_-]{12}$"
49
50
  },
50
51
  "$.body.status": {
51
52
  "match": "regex",
@@ -63,7 +64,7 @@
63
64
  "providerState": "an invalid API key",
64
65
  "request": {
65
66
  "method": "post",
66
- "path": "/runs/init",
67
+ "path": "/api/v1/runs",
67
68
  "headers": {
68
69
  "Authorization": "Bearer invalid-api-key",
69
70
  "Content-Type": "application/json"
@@ -91,7 +92,10 @@
91
92
  "Content-Type": "application/json"
92
93
  },
93
94
  "body": {
94
- "error": "Unauthorized"
95
+ "error": {
96
+ "code": "unauthorized",
97
+ "message": "Unauthorized"
98
+ }
95
99
  },
96
100
  "matchingRules": {
97
101
  "$.body.error": {
@@ -105,9 +109,15 @@
105
109
  "providerState": "a run exists",
106
110
  "request": {
107
111
  "method": "get",
108
- "path": "/runs/run-123/status",
112
+ "path": "/api/v1/runs/abc123def456",
109
113
  "headers": {
110
114
  "Authorization": "Bearer test-api-key"
115
+ },
116
+ "matchingRules": {
117
+ "$.path": {
118
+ "match": "regex",
119
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}$"
120
+ }
111
121
  }
112
122
  },
113
123
  "response": {
@@ -131,7 +141,7 @@
131
141
  "providerState": "a run exists",
132
142
  "request": {
133
143
  "method": "post",
134
- "path": "/runs/run-123/seed",
144
+ "path": "/api/v1/runs/abc123def456/seed",
135
145
  "headers": {
136
146
  "Authorization": "Bearer test-api-key",
137
147
  "Content-Type": "application/json"
@@ -145,6 +155,12 @@
145
155
  "name": "spec/models/post_spec.rb"
146
156
  }
147
157
  ]
158
+ },
159
+ "matchingRules": {
160
+ "$.path": {
161
+ "match": "regex",
162
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/seed$"
163
+ }
148
164
  }
149
165
  },
150
166
  "response": {
@@ -163,15 +179,20 @@
163
179
  "providerState": "tasks are available",
164
180
  "request": {
165
181
  "method": "post",
166
- "path": "/tasks/claim",
182
+ "path": "/api/v1/runs/abc123def456/claims",
167
183
  "query": "batch=10",
168
184
  "headers": {
169
185
  "Authorization": "Bearer test-api-key",
170
186
  "Content-Type": "application/json"
171
187
  },
172
188
  "body": {
173
- "run_key": "test-run-key",
174
189
  "worker_id": "worker-0"
190
+ },
191
+ "matchingRules": {
192
+ "$.path": {
193
+ "match": "regex",
194
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
195
+ }
175
196
  }
176
197
  },
177
198
  "response": {
@@ -182,19 +203,27 @@
182
203
  "body": {
183
204
  "tasks": [
184
205
  {
185
- "id": "task-1",
206
+ "id": 101,
186
207
  "name": "spec/models/user_spec.rb"
187
208
  },
188
209
  {
189
- "id": "task-2",
210
+ "id": 102,
190
211
  "name": "spec/models/post_spec.rb"
212
+ },
213
+ {
214
+ "id": 103,
215
+ "name": "spec/models/comment_spec.rb"
216
+ },
217
+ {
218
+ "id": 104,
219
+ "name": "spec/models/profile_spec.rb"
191
220
  }
192
221
  ],
193
- "queue_state": "ready",
222
+ "queue_state": "draining",
194
223
  "remaining": {
195
- "queued": 10,
196
- "assigned": 2,
197
- "completed": 5
224
+ "queued": 0,
225
+ "assigned": 4,
226
+ "completed": 0
198
227
  }
199
228
  },
200
229
  "matchingRules": {
@@ -204,6 +233,12 @@
204
233
  "$.body.tasks[1].id": {
205
234
  "match": "type"
206
235
  },
236
+ "$.body.tasks[2].id": {
237
+ "match": "type"
238
+ },
239
+ "$.body.tasks[3].id": {
240
+ "match": "type"
241
+ },
207
242
  "$.body.remaining.queued": {
208
243
  "match": "type"
209
244
  },
@@ -221,15 +256,20 @@
221
256
  "providerState": "queue is empty but not drained",
222
257
  "request": {
223
258
  "method": "post",
224
- "path": "/tasks/claim",
259
+ "path": "/api/v1/runs/abc123def456/claims",
225
260
  "query": "batch=10",
226
261
  "headers": {
227
262
  "Authorization": "Bearer test-api-key",
228
263
  "Content-Type": "application/json"
229
264
  },
230
265
  "body": {
231
- "run_key": "test-run-key",
232
266
  "worker_id": "worker-0"
267
+ },
268
+ "matchingRules": {
269
+ "$.path": {
270
+ "match": "regex",
271
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
272
+ }
233
273
  }
234
274
  },
235
275
  "response": {
@@ -244,14 +284,20 @@
244
284
  "providerState": "queue is drained",
245
285
  "request": {
246
286
  "method": "post",
247
- "path": "/tasks/claim",
287
+ "path": "/api/v1/runs/abc123def456/claims",
248
288
  "query": "batch=10",
249
289
  "headers": {
250
- "Authorization": "Bearer test-api-key"
290
+ "Authorization": "Bearer test-api-key",
291
+ "Content-Type": "application/json"
251
292
  },
252
293
  "body": {
253
- "run_key": "test-run-key",
254
294
  "worker_id": "worker-0"
295
+ },
296
+ "matchingRules": {
297
+ "$.path": {
298
+ "match": "regex",
299
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
300
+ }
255
301
  }
256
302
  },
257
303
  "response": {
@@ -264,12 +310,62 @@
264
310
  }
265
311
  }
266
312
  },
313
+ {
314
+ "description": "a request to claim from cancelled run",
315
+ "providerState": "run is cancelled",
316
+ "request": {
317
+ "method": "post",
318
+ "path": "/api/v1/runs/abc123def456/claims",
319
+ "query": "batch=10",
320
+ "headers": {
321
+ "Authorization": "Bearer test-api-key",
322
+ "Content-Type": "application/json"
323
+ },
324
+ "body": {
325
+ "worker_id": "worker-0"
326
+ },
327
+ "matchingRules": {
328
+ "$.path": {
329
+ "match": "regex",
330
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/claims$"
331
+ }
332
+ }
333
+ },
334
+ "response": {
335
+ "status": 200,
336
+ "headers": {
337
+ "Content-Type": "application/json"
338
+ },
339
+ "body": {
340
+ "tasks": [
341
+ ],
342
+ "queue_state": "cancelled",
343
+ "remaining": {
344
+ "queued": 0,
345
+ "assigned": 0,
346
+ "completed": 5
347
+ },
348
+ "should_stop": true
349
+ },
350
+ "matchingRules": {
351
+ "$.body.remaining.queued": {
352
+ "match": "type"
353
+ },
354
+ "$.body.remaining.assigned": {
355
+ "match": "type"
356
+ },
357
+ "$.body.remaining.completed": {
358
+ "match": "type"
359
+ }
360
+ }
361
+ }
362
+ },
267
363
  {
268
364
  "description": "a request to ack a task",
269
365
  "providerState": "a task exists",
270
366
  "request": {
271
367
  "method": "post",
272
- "path": "/tasks/task-123/ack",
368
+ "path": "/api/v1/tasks/101/acknowledgment",
273
369
  "headers": {
274
370
  "Authorization": "Bearer test-api-key",
275
371
  "Content-Type": "application/json"
@@ -282,6 +378,12 @@
282
378
  "failures": [
283
379
  ]
284
380
  }
381
+ },
382
+ "matchingRules": {
383
+ "$.path": {
384
+ "match": "regex",
385
+ "regex": "^\\/api\\/v1\\/tasks\\/\\d+\\/acknowledgment$"
386
+ }
285
387
  }
286
388
  },
287
389
  "response": {
@@ -299,14 +401,19 @@
299
401
  "providerState": "a worker exists",
300
402
  "request": {
301
403
  "method": "post",
302
- "path": "/workers/heartbeat",
404
+ "path": "/api/v1/runs/abc123def456/heartbeats",
303
405
  "headers": {
304
406
  "Authorization": "Bearer test-api-key",
305
407
  "Content-Type": "application/json"
306
408
  },
307
409
  "body": {
308
- "run_key": "test-run-key",
309
410
  "worker_id": "worker-0"
411
+ },
412
+ "matchingRules": {
413
+ "$.path": {
414
+ "match": "regex",
415
+ "regex": "^\\/api\\/v1\\/runs\\/[0-9A-Za-z_-]{12}\\/heartbeats$"
416
+ }
310
417
  }
311
418
  },
312
419
  "response": {
@@ -321,7 +428,7 @@
321
428
  "providerState": "resource not found",
322
429
  "request": {
323
430
  "method": "get",
324
- "path": "/runs/invalid-run/status",
431
+ "path": "/api/v1/runs/invalid-run",
325
432
  "headers": {
326
433
  "Authorization": "Bearer test-api-key"
327
434
  }
@@ -337,7 +444,7 @@
337
444
  "providerState": "server error",
338
445
  "request": {
339
446
  "method": "post",
340
- "path": "/runs/init",
447
+ "path": "/api/v1/runs",
341
448
  "headers": {
342
449
  "Authorization": "Bearer test-api-key",
343
450
  "Content-Type": "application/json"
@@ -359,7 +466,7 @@
359
466
  "providerState": "unexpected redirect",
360
467
  "request": {
361
468
  "method": "post",
362
- "path": "/runs/init",
469
+ "path": "/api/v1/runs",
363
470
  "headers": {
364
471
  "Authorization": "Bearer test-api-key",
365
472
  "Content-Type": "application/json"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hastci
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wojciech Wrona