gush 2.0.2 → 2.1.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/.github/workflows/ruby.yml +71 -0
- data/CHANGELOG.md +12 -5
- data/Gemfile +4 -0
- data/README.md +37 -6
- data/bin/gush +2 -2
- data/gush.gemspec +6 -7
- data/lib/gush/cli/overview.rb +13 -13
- data/lib/gush/cli.rb +51 -20
- data/lib/gush/client.rb +1 -1
- data/lib/gush/configuration.rb +14 -10
- data/lib/gush/graph.rb +43 -32
- data/lib/gush/worker.rb +10 -2
- data/spec/features/integration_spec.rb +5 -7
- data/spec/gush/configuration_spec.rb +6 -0
- data/spec/gush/graph_spec.rb +37 -20
- data/spec/gush/worker_spec.rb +8 -0
- data/spec/spec_helper.rb +5 -9
- metadata +29 -31
- data/.travis.yml +0 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dc70498c39ad506749bed1f55c139d29691e540b849dd39e2ba132b54b511d66
|
|
4
|
+
data.tar.gz: 700d093574e0ff4772c7d36bfeb7792cda44550dd162a48afe17bec5551250cd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac4c1647f57a87600466445a8d24660e3d95016f17adf9e2fe8f99260ba82a2b3e22a339b9456f653834c89c6e4b9ed6bcb50ebc337b804e07355f9a513ce8ca
|
|
7
|
+
data.tar.gz: 1ae01511acfc2e23bb0a671328a7b3e9b650b8589b9620461c2b9667ff9d80a47f03b8a156f249746b4f69e613863db0833be8d6221acdabd211d7eba2daee15
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
2
|
+
# They are provided by a third-party and are governed by
|
|
3
|
+
# separate terms of service, privacy policy, and support
|
|
4
|
+
# documentation.
|
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
|
7
|
+
|
|
8
|
+
name: Ruby
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
pull_request:
|
|
12
|
+
paths-ignore:
|
|
13
|
+
- 'README.md'
|
|
14
|
+
push:
|
|
15
|
+
paths-ignore:
|
|
16
|
+
- 'README.md'
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
services:
|
|
21
|
+
redis:
|
|
22
|
+
image: redis:alpine
|
|
23
|
+
ports: ["6379:6379"]
|
|
24
|
+
options: --entrypoint redis-server
|
|
25
|
+
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
strategy:
|
|
28
|
+
matrix:
|
|
29
|
+
rails_version: ['4.2.7', '5.1.0', '5.2.0', '6.0.0', '6.1.0', '7.0']
|
|
30
|
+
ruby-version: ['2.6', '2.7', '3.0', '3.1']
|
|
31
|
+
exclude:
|
|
32
|
+
- ruby-version: '3.0'
|
|
33
|
+
rails_version: '4.2.7'
|
|
34
|
+
- ruby-version: '3.1'
|
|
35
|
+
rails_version: '4.2.7'
|
|
36
|
+
- ruby-version: '3.0'
|
|
37
|
+
rails_version: '5.0'
|
|
38
|
+
- ruby-version: '3.1'
|
|
39
|
+
rails_version: '5.0'
|
|
40
|
+
- ruby-version: '3.0'
|
|
41
|
+
rails_version: '5.1'
|
|
42
|
+
- ruby-version: '3.1'
|
|
43
|
+
rails_version: '5.1'
|
|
44
|
+
- ruby-version: '3.0'
|
|
45
|
+
rails_version: '5.2'
|
|
46
|
+
- ruby-version: '3.1'
|
|
47
|
+
rails_version: '5.2'
|
|
48
|
+
- ruby-version: '3.0'
|
|
49
|
+
rails_version: '6.0'
|
|
50
|
+
- ruby-version: '3.1'
|
|
51
|
+
rails_version: '6.0'
|
|
52
|
+
- ruby-version: '3.1'
|
|
53
|
+
rails_version: '6.1'
|
|
54
|
+
- ruby-version: '2.6'
|
|
55
|
+
rails_version: '7.0'
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v2
|
|
58
|
+
- name: Set up Ruby
|
|
59
|
+
uses: ruby/setup-ruby@v1
|
|
60
|
+
with:
|
|
61
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
62
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
|
63
|
+
env:
|
|
64
|
+
RAILS_VERSION: "${{ matrix.rails_version }}"
|
|
65
|
+
- name: Install Graphviz
|
|
66
|
+
run: sudo apt-get install graphviz
|
|
67
|
+
- name: Run tests
|
|
68
|
+
run: bundle exec rspec
|
|
69
|
+
env:
|
|
70
|
+
REDIS_URL: redis://localhost:6379/1
|
|
71
|
+
RAILS_VERSION: "${{ matrix.rails_version }}"
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 2.1.0
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Allow RedisMutex’s locking duration and polling interval to be customizable, thanks to @thukim! [See pull request](https://github.com/chaps-io/gush/pull/74)
|
|
13
|
+
- Support for Rails 7.0 and Ruby 3.0-3.1, thanks to @joshRpowell and @kzkn!
|
|
14
|
+
|
|
8
15
|
## 2.0.1
|
|
9
16
|
|
|
10
17
|
### Fixed
|
|
@@ -13,31 +20,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
|
13
20
|
|
|
14
21
|
## 2.0.0
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
### Changed
|
|
17
24
|
|
|
18
25
|
- *[BREAKING]* Store gush jobs on redis hash instead of plain keys - this improves performance when retrieving keys (Thanks to @Saicheg! [See pull request](https://github.com/chaps-io/gush/pull/56))
|
|
19
26
|
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
### Added
|
|
22
29
|
|
|
23
30
|
- Allow setting queue for each job via `:queue` option in `run` method (Thanks to @devilankur18! [See pull request](https://github.com/chaps-io/gush/pull/58))
|
|
24
31
|
|
|
25
32
|
|
|
26
33
|
## 1.1.1 - 2018-06-09
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
### Changed
|
|
29
36
|
|
|
30
37
|
- Relax dependency on ActiveSupport to work with 4.2 up to 5.X (Thanks to @iacobus! [See pull request](https://github.com/chaps-io/gush/pull/54))
|
|
31
38
|
|
|
32
39
|
|
|
33
40
|
## 1.1.0 - 2018-02-05
|
|
34
41
|
|
|
35
|
-
|
|
42
|
+
### Added
|
|
36
43
|
|
|
37
44
|
- Added ability to specify TTL for Redis keys and manually expire whole workflows (Thanks to @dmitrypol! [See pull request](https://github.com/chaps-io/gush/pull/48))
|
|
38
45
|
- Loosened dependency on redis-rb library to >= 3.2 and < 5.0 (Thanks to @mofumofu3n! [See pull request](https://github.com/chaps-io/gush/pull/52))
|
|
39
46
|
|
|
40
|
-
|
|
47
|
+
### Fixed
|
|
41
48
|
|
|
42
49
|
- Improved performance of (de)serializing workflows by not storing job array inside workflow JSON and other smaller improvements ([See pull request](https://github.com/chaps-io/gush/pull/53))
|
|
43
50
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Gush
|
|
1
|
+
# Gush
|
|
2
2
|
|
|
3
3
|
Gush is a parallel workflow runner using only Redis as storage and [ActiveJob](http://guides.rubyonrails.org/v4.2/active_job_basics.html#introduction) for scheduling and executing jobs.
|
|
4
4
|
|
|
@@ -8,14 +8,14 @@ Gush relies on directed acyclic graphs to store dependencies, see [Parallelizing
|
|
|
8
8
|
|
|
9
9
|
## **WARNING - version notice**
|
|
10
10
|
|
|
11
|
-
This README is about the `
|
|
11
|
+
This README is about the latest `master` code, which might differ from what is released on RubyGems. See tags to browse previous READMEs.
|
|
12
12
|
|
|
13
13
|
## Installation
|
|
14
14
|
|
|
15
15
|
### 1. Add `gush` to Gemfile
|
|
16
16
|
|
|
17
17
|
```ruby
|
|
18
|
-
gem 'gush', '~>
|
|
18
|
+
gem 'gush', '~> 2.0'
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
### 2. Create `Gushfile`
|
|
@@ -274,7 +274,7 @@ class EncodeVideo < Gush::Job
|
|
|
274
274
|
end
|
|
275
275
|
```
|
|
276
276
|
|
|
277
|
-
`payloads` is an array containing outputs from all ancestor jobs. So for our `
|
|
277
|
+
`payloads` is an array containing outputs from all ancestor jobs. So for our `EncodeVideo` job from above, the array will look like:
|
|
278
278
|
|
|
279
279
|
|
|
280
280
|
```ruby
|
|
@@ -321,6 +321,23 @@ it will generate a workflow with 5 `NotificationJob`s and one `AdminNotification
|
|
|
321
321
|
|
|
322
322
|

|
|
323
323
|
|
|
324
|
+
### Dynamic queue for jobs
|
|
325
|
+
|
|
326
|
+
There might be a case you want to configure different jobs in the workflow using different queues. Based on the above the example, we want to config `AdminNotificationJob` to use queue `admin` and `NotificationJob` use queue `user`.
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
|
|
330
|
+
class NotifyWorkflow < Gush::Workflow
|
|
331
|
+
def configure(user_ids)
|
|
332
|
+
notification_jobs = user_ids.map do |user_id|
|
|
333
|
+
run NotificationJob, params: {user_id: user_id}, queue: 'user'
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
run AdminNotificationJob, after: notification_jobs, queue: 'admin'
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
```
|
|
340
|
+
|
|
324
341
|
## Command line interface (CLI)
|
|
325
342
|
|
|
326
343
|
### Checking status
|
|
@@ -346,9 +363,23 @@ This requires that you have imagemagick installed on your computer:
|
|
|
346
363
|
bundle exec gush viz <NameOfTheWorkflow>
|
|
347
364
|
```
|
|
348
365
|
|
|
366
|
+
### Customizing locking options
|
|
367
|
+
|
|
368
|
+
In order to prevent getting the RedisMutex::LockError error when having a large number of jobs, you can customize these 2 fields `locking_duration` and `polling_interval` as below
|
|
369
|
+
|
|
370
|
+
```ruby
|
|
371
|
+
# config/initializers/gush.rb
|
|
372
|
+
Gush.configure do |config|
|
|
373
|
+
config.redis_url = "redis://localhost:6379"
|
|
374
|
+
config.concurrency = 5
|
|
375
|
+
config.locking_duration = 2 # how long you want to wait for the lock to be released, in seconds
|
|
376
|
+
config.polling_interval = 0.3 # how long the polling interval should be, in seconds
|
|
377
|
+
end
|
|
378
|
+
```
|
|
379
|
+
|
|
349
380
|
### Cleaning up afterwards
|
|
350
381
|
|
|
351
|
-
Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is ran. This data might be useful for analysis but at a certain point it can be purged via Redis TTL. By default gush and Redis will keep keys forever. To configure expiration you need to 2 things. Create initializer (specify config.ttl in seconds, be different per environment).
|
|
382
|
+
Running `NotifyWorkflow.create` inserts multiple keys into Redis every time it is ran. This data might be useful for analysis but at a certain point it can be purged via Redis TTL. By default gush and Redis will keep keys forever. To configure expiration you need to 2 things. Create initializer (specify config.ttl in seconds, be different per environment).
|
|
352
383
|
|
|
353
384
|
```ruby
|
|
354
385
|
# config/initializers/gush.rb
|
|
@@ -359,7 +390,7 @@ Gush.configure do |config|
|
|
|
359
390
|
end
|
|
360
391
|
```
|
|
361
392
|
|
|
362
|
-
And you need to call `flow.expire!` (optionally passing custom TTL value overriding `config.ttl`). This gives you control whether to expire data for specific workflow. Best NOT to set TTL to be too short (like minutes) but about a week in length. And you can run `Client.expire_workflow` and `Client.expire_job` passing appropriate IDs and TTL (pass -1 to NOT expire) values.
|
|
393
|
+
And you need to call `flow.expire!` (optionally passing custom TTL value overriding `config.ttl`). This gives you control whether to expire data for specific workflow. Best NOT to set TTL to be too short (like minutes) but about a week in length. And you can run `Client.expire_workflow` and `Client.expire_job` passing appropriate IDs and TTL (pass -1 to NOT expire) values.
|
|
363
394
|
|
|
364
395
|
### Avoid overlapping workflows
|
|
365
396
|
|
data/bin/gush
CHANGED
|
@@ -12,7 +12,7 @@ require 'gush'
|
|
|
12
12
|
begin
|
|
13
13
|
Gush::CLI.start(ARGV)
|
|
14
14
|
rescue Gush::WorkflowNotFound
|
|
15
|
-
puts "Workflow not found"
|
|
15
|
+
puts Paint["Workflow not found", :red]
|
|
16
16
|
rescue Gush::DependencyLevelTooDeep
|
|
17
|
-
puts "Dependency level too deep. Perhaps you have a dependency cycle?"
|
|
17
|
+
puts Paint["Dependency level too deep. Perhaps you have a dependency cycle?", :red]
|
|
18
18
|
end
|
data/gush.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "gush"
|
|
7
|
-
spec.version = "2.0
|
|
7
|
+
spec.version = "2.1.0"
|
|
8
8
|
spec.authors = ["Piotrek Okoński"]
|
|
9
9
|
spec.email = ["piotrek@okonski.org"]
|
|
10
10
|
spec.summary = "Fast and distributed workflow runner based on ActiveJob and Redis"
|
|
@@ -17,20 +17,19 @@ Gem::Specification.new do |spec|
|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
18
|
spec.require_paths = ["lib"]
|
|
19
19
|
|
|
20
|
-
spec.add_dependency "activejob", ">= 4.2.7", "< 7.
|
|
20
|
+
spec.add_dependency "activejob", ">= 4.2.7", "< 7.1"
|
|
21
21
|
spec.add_dependency "concurrent-ruby", "~> 1.0"
|
|
22
22
|
spec.add_dependency "multi_json", "~> 1.11"
|
|
23
23
|
spec.add_dependency "redis", ">= 3.2", "< 5"
|
|
24
24
|
spec.add_dependency "redis-mutex", "~> 4.0.1"
|
|
25
25
|
spec.add_dependency "hiredis", "~> 0.6"
|
|
26
|
-
spec.add_dependency "
|
|
27
|
-
spec.add_dependency "terminal-table", "
|
|
28
|
-
spec.add_dependency "
|
|
29
|
-
spec.add_dependency "thor", "
|
|
26
|
+
spec.add_dependency "graphviz", "~> 1.2"
|
|
27
|
+
spec.add_dependency "terminal-table", ">= 1.4", "< 3.1"
|
|
28
|
+
spec.add_dependency "paint", "~> 2.2"
|
|
29
|
+
spec.add_dependency "thor", ">= 0.19", "< 1.3"
|
|
30
30
|
spec.add_dependency "launchy", "~> 2.4"
|
|
31
31
|
spec.add_development_dependency "bundler"
|
|
32
32
|
spec.add_development_dependency "rake", "~> 10.4"
|
|
33
33
|
spec.add_development_dependency "rspec", '~> 3.0'
|
|
34
34
|
spec.add_development_dependency "pry", '~> 0.10'
|
|
35
|
-
spec.add_development_dependency 'fakeredis', '~> 0.5'
|
|
36
35
|
end
|
data/lib/gush/cli/overview.rb
CHANGED
|
@@ -17,11 +17,11 @@ module Gush
|
|
|
17
17
|
elsif workflow.running?
|
|
18
18
|
running_status
|
|
19
19
|
elsif workflow.finished?
|
|
20
|
-
"done"
|
|
20
|
+
Paint["done", :green]
|
|
21
21
|
elsif workflow.stopped?
|
|
22
|
-
"stopped"
|
|
22
|
+
Paint["stopped", :red]
|
|
23
23
|
else
|
|
24
|
-
"ready to start"
|
|
24
|
+
Paint["ready to start", :blue]
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -48,10 +48,10 @@ module Gush
|
|
|
48
48
|
"ID" => workflow.id,
|
|
49
49
|
"Name" => workflow.class.to_s,
|
|
50
50
|
"Jobs" => workflow.jobs.count,
|
|
51
|
-
"Failed jobs" => failed_jobs_count
|
|
52
|
-
"Succeeded jobs" => succeeded_jobs_count
|
|
53
|
-
"Enqueued jobs" => enqueued_jobs_count
|
|
54
|
-
"Running jobs" => running_jobs_count
|
|
51
|
+
"Failed jobs" => Paint[failed_jobs_count, :red],
|
|
52
|
+
"Succeeded jobs" => Paint[succeeded_jobs_count, :green],
|
|
53
|
+
"Enqueued jobs" => Paint[enqueued_jobs_count, :yellow],
|
|
54
|
+
"Running jobs" => Paint[running_jobs_count, :blue],
|
|
55
55
|
"Remaining jobs" => remaining_jobs_count,
|
|
56
56
|
"Started at" => started_at,
|
|
57
57
|
"Status" => status
|
|
@@ -60,7 +60,7 @@ module Gush
|
|
|
60
60
|
|
|
61
61
|
def running_status
|
|
62
62
|
finished = succeeded_jobs_count.to_i
|
|
63
|
-
status = "running"
|
|
63
|
+
status = Paint["running", :yellow]
|
|
64
64
|
status += "\n#{finished}/#{total_jobs_count} [#{(finished*100)/total_jobs_count}%]"
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -69,7 +69,7 @@ module Gush
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def failed_status
|
|
72
|
-
status = "failed"
|
|
72
|
+
status = Paint["failed", :red]
|
|
73
73
|
status += "\n#{failed_job} failed"
|
|
74
74
|
end
|
|
75
75
|
|
|
@@ -77,13 +77,13 @@ module Gush
|
|
|
77
77
|
name = job.name
|
|
78
78
|
case
|
|
79
79
|
when job.failed?
|
|
80
|
-
"[✗] #{name
|
|
80
|
+
"[✗] #{Paint[name, :red]} \n"
|
|
81
81
|
when job.finished?
|
|
82
|
-
"[✓] #{name
|
|
82
|
+
"[✓] #{Paint[name, :green]} \n"
|
|
83
83
|
when job.enqueued?
|
|
84
|
-
"[•] #{name
|
|
84
|
+
"[•] #{Paint[name, :yellow]} \n"
|
|
85
85
|
when job.running?
|
|
86
|
-
"[•] #{name
|
|
86
|
+
"[•] #{Paint[name, :blue]} \n"
|
|
87
87
|
else
|
|
88
88
|
"[ ] #{name} \n"
|
|
89
89
|
end
|
data/lib/gush/cli.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'terminal-table'
|
|
2
|
-
require '
|
|
4
|
+
require 'paint'
|
|
3
5
|
require 'thor'
|
|
4
6
|
require 'launchy'
|
|
5
7
|
|
|
@@ -12,43 +14,45 @@ module Gush
|
|
|
12
14
|
def initialize(*)
|
|
13
15
|
super
|
|
14
16
|
Gush.configure do |config|
|
|
15
|
-
config.gushfile
|
|
16
|
-
config.concurrency
|
|
17
|
-
config.redis_url
|
|
18
|
-
config.namespace
|
|
19
|
-
config.ttl
|
|
17
|
+
config.gushfile = options.fetch("gushfile", config.gushfile)
|
|
18
|
+
config.concurrency = options.fetch("concurrency", config.concurrency)
|
|
19
|
+
config.redis_url = options.fetch("redis", config.redis_url)
|
|
20
|
+
config.namespace = options.fetch("namespace", config.namespace)
|
|
21
|
+
config.ttl = options.fetch("ttl", config.ttl)
|
|
22
|
+
config.locking_duration = options.fetch("locking_duration", config.locking_duration)
|
|
23
|
+
config.polling_interval = options.fetch("polling_interval", config.polling_interval)
|
|
20
24
|
end
|
|
21
25
|
load_gushfile
|
|
22
26
|
end
|
|
23
27
|
|
|
24
|
-
desc "create
|
|
28
|
+
desc "create WORKFLOW_CLASS", "Registers new workflow"
|
|
25
29
|
def create(name)
|
|
26
30
|
workflow = client.create_workflow(name)
|
|
27
31
|
puts "Workflow created with id: #{workflow.id}"
|
|
28
32
|
puts "Start it with command: gush start #{workflow.id}"
|
|
29
33
|
end
|
|
30
34
|
|
|
31
|
-
desc "start [
|
|
35
|
+
desc "start WORKFLOW_ID [ARG ...]", "Starts Workflow with given ID"
|
|
32
36
|
def start(*args)
|
|
33
37
|
id = args.shift
|
|
34
38
|
workflow = client.find_workflow(id)
|
|
35
39
|
client.start_workflow(workflow, args)
|
|
36
40
|
end
|
|
37
41
|
|
|
38
|
-
desc "create_and_start [
|
|
42
|
+
desc "create_and_start WORKFLOW_CLASS [ARG ...]", "Create and instantly start the new workflow"
|
|
39
43
|
def create_and_start(name, *args)
|
|
40
44
|
workflow = client.create_workflow(name)
|
|
41
45
|
client.start_workflow(workflow.id, args)
|
|
42
46
|
puts "Created and started workflow with id: #{workflow.id}"
|
|
43
47
|
end
|
|
44
48
|
|
|
45
|
-
desc "stop
|
|
49
|
+
desc "stop WORKFLOW_ID", "Stops Workflow with given ID"
|
|
46
50
|
def stop(*args)
|
|
47
51
|
id = args.shift
|
|
48
52
|
client.stop_workflow(id)
|
|
49
53
|
end
|
|
50
54
|
|
|
51
|
-
desc "show
|
|
55
|
+
desc "show WORKFLOW_ID", "Shows details about workflow with given ID"
|
|
52
56
|
option :skip_overview, type: :boolean
|
|
53
57
|
option :skip_jobs, type: :boolean
|
|
54
58
|
option :jobs, default: :all
|
|
@@ -60,7 +64,7 @@ module Gush
|
|
|
60
64
|
display_jobs_list_for(workflow, options[:jobs]) unless options[:skip_jobs]
|
|
61
65
|
end
|
|
62
66
|
|
|
63
|
-
desc "rm
|
|
67
|
+
desc "rm WORKFLOW_ID", "Delete workflow with given ID"
|
|
64
68
|
def rm(workflow_id)
|
|
65
69
|
workflow = client.find_workflow(workflow_id)
|
|
66
70
|
client.destroy_workflow(workflow)
|
|
@@ -81,13 +85,39 @@ module Gush
|
|
|
81
85
|
puts Terminal::Table.new(headings: headers, rows: rows)
|
|
82
86
|
end
|
|
83
87
|
|
|
84
|
-
desc "viz
|
|
85
|
-
|
|
88
|
+
desc "viz {WORKFLOW_CLASS|WORKFLOW_ID}", "Displays graph, visualising job dependencies"
|
|
89
|
+
option :filename, type: :string, default: nil
|
|
90
|
+
option :open, type: :boolean, default: nil
|
|
91
|
+
def viz(class_or_id)
|
|
86
92
|
client
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
|
|
94
|
+
begin
|
|
95
|
+
workflow = client.find_workflow(class_or_id)
|
|
96
|
+
rescue WorkflowNotFound
|
|
97
|
+
workflow = nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
unless workflow
|
|
101
|
+
begin
|
|
102
|
+
workflow = class_or_id.constantize.new
|
|
103
|
+
rescue NameError => e
|
|
104
|
+
STDERR.puts Paint["'#{class_or_id}' is not a valid workflow class or id", :red]
|
|
105
|
+
exit 1
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
opts = {}
|
|
110
|
+
|
|
111
|
+
if options[:filename]
|
|
112
|
+
opts[:filename], opts[:path] = File.split(options[:filename])
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
graph = Graph.new(workflow, **opts)
|
|
89
116
|
graph.viz
|
|
90
|
-
|
|
117
|
+
|
|
118
|
+
if (options[:open].nil? && !options[:filename]) || options[:open]
|
|
119
|
+
Launchy.open Pathname.new(graph.path).realpath.to_s
|
|
120
|
+
end
|
|
91
121
|
end
|
|
92
122
|
|
|
93
123
|
private
|
|
@@ -118,13 +148,14 @@ module Gush
|
|
|
118
148
|
|
|
119
149
|
def load_gushfile
|
|
120
150
|
file = client.configuration.gushfile
|
|
121
|
-
|
|
122
|
-
|
|
151
|
+
|
|
152
|
+
unless gushfile.exist?
|
|
153
|
+
raise Thor::Error, Paint["#{file} not found, please add it to your project", :red]
|
|
123
154
|
end
|
|
124
155
|
|
|
125
156
|
load file.to_s
|
|
126
157
|
rescue LoadError
|
|
127
|
-
raise Thor::Error, "failed to require #{file}"
|
|
158
|
+
raise Thor::Error, Paint["failed to require #{file}", :red]
|
|
128
159
|
end
|
|
129
160
|
end
|
|
130
161
|
end
|
data/lib/gush/client.rb
CHANGED
data/lib/gush/configuration.rb
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
module Gush
|
|
2
2
|
class Configuration
|
|
3
|
-
attr_accessor :concurrency, :namespace, :redis_url, :ttl
|
|
3
|
+
attr_accessor :concurrency, :namespace, :redis_url, :ttl, :locking_duration, :polling_interval
|
|
4
4
|
|
|
5
5
|
def self.from_json(json)
|
|
6
6
|
new(Gush::JSON.decode(json, symbolize_keys: true))
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def initialize(hash = {})
|
|
10
|
-
self.concurrency
|
|
11
|
-
self.namespace
|
|
12
|
-
self.redis_url
|
|
13
|
-
self.gushfile
|
|
14
|
-
self.ttl
|
|
10
|
+
self.concurrency = hash.fetch(:concurrency, 5)
|
|
11
|
+
self.namespace = hash.fetch(:namespace, 'gush')
|
|
12
|
+
self.redis_url = hash.fetch(:redis_url, 'redis://localhost:6379')
|
|
13
|
+
self.gushfile = hash.fetch(:gushfile, 'Gushfile')
|
|
14
|
+
self.ttl = hash.fetch(:ttl, -1)
|
|
15
|
+
self.locking_duration = hash.fetch(:locking_duration, 2) # how long you want to wait for the lock to be released, in seconds
|
|
16
|
+
self.polling_interval = hash.fetch(:polling_internal, 0.3) # how long the polling interval should be, in seconds
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def gushfile=(path)
|
|
@@ -24,10 +26,12 @@ module Gush
|
|
|
24
26
|
|
|
25
27
|
def to_hash
|
|
26
28
|
{
|
|
27
|
-
concurrency:
|
|
28
|
-
namespace:
|
|
29
|
-
redis_url:
|
|
30
|
-
ttl:
|
|
29
|
+
concurrency: concurrency,
|
|
30
|
+
namespace: namespace,
|
|
31
|
+
redis_url: redis_url,
|
|
32
|
+
ttl: ttl,
|
|
33
|
+
locking_duration: locking_duration,
|
|
34
|
+
polling_interval: polling_interval
|
|
31
35
|
}
|
|
32
36
|
end
|
|
33
37
|
|
data/lib/gush/graph.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
|
|
1
5
|
module Gush
|
|
2
6
|
class Graph
|
|
3
|
-
attr_reader :workflow, :filename, :path, :
|
|
7
|
+
attr_reader :workflow, :filename, :path, :start_node, :end_node
|
|
4
8
|
|
|
5
9
|
def initialize(workflow, options = {})
|
|
6
10
|
@workflow = workflow
|
|
@@ -9,19 +13,26 @@ module Gush
|
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
def viz
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
@graph = Graphviz::Graph.new(**graph_options)
|
|
17
|
+
@start_node = add_node('start', shape: 'diamond', fillcolor: '#CFF09E')
|
|
18
|
+
@end_node = add_node('end', shape: 'diamond', fillcolor: '#F56991')
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
end
|
|
20
|
+
# First, create nodes for all jobs
|
|
21
|
+
@job_name_to_node_map = {}
|
|
22
|
+
workflow.jobs.each do |job|
|
|
23
|
+
add_job_node(job)
|
|
24
|
+
end
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
# Next, link up the jobs with edges
|
|
27
|
+
workflow.jobs.each do |job|
|
|
28
|
+
link_job_edges(job)
|
|
24
29
|
end
|
|
30
|
+
|
|
31
|
+
format = 'png'
|
|
32
|
+
file_format = path.split('.')[-1]
|
|
33
|
+
format = file_format if file_format.length == 3
|
|
34
|
+
|
|
35
|
+
Graphviz::output(@graph, path: path, format: format)
|
|
25
36
|
end
|
|
26
37
|
|
|
27
38
|
def path
|
|
@@ -29,43 +40,43 @@ module Gush
|
|
|
29
40
|
end
|
|
30
41
|
|
|
31
42
|
private
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
graph.
|
|
43
|
+
|
|
44
|
+
def add_node(name, **specific_options)
|
|
45
|
+
@graph.add_node(name, **node_options.merge(specific_options))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def add_job_node(job)
|
|
49
|
+
@job_name_to_node_map[job.name] = add_node(job.name, label: node_label_for_job(job))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def link_job_edges(job)
|
|
53
|
+
job_node = @job_name_to_node_map[job.name]
|
|
35
54
|
|
|
36
55
|
if job.incoming.empty?
|
|
37
|
-
|
|
56
|
+
@start_node.connect(job_node, **edge_options)
|
|
38
57
|
end
|
|
39
58
|
|
|
40
59
|
if job.outgoing.empty?
|
|
41
|
-
|
|
60
|
+
job_node.connect(@end_node, **edge_options)
|
|
42
61
|
else
|
|
43
62
|
job.outgoing.each do |id|
|
|
44
63
|
outgoing_job = workflow.find_job(id)
|
|
45
|
-
|
|
64
|
+
job_node.connect(@job_name_to_node_map[outgoing_job.name], **edge_options)
|
|
46
65
|
end
|
|
47
66
|
end
|
|
48
67
|
end
|
|
49
68
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
graph.node[key] = value
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def set_edge_options!(graph)
|
|
57
|
-
edge_options.each do |key, value|
|
|
58
|
-
graph.edge[key] = value
|
|
59
|
-
end
|
|
69
|
+
def node_label_for_job(job)
|
|
70
|
+
job.class.to_s
|
|
60
71
|
end
|
|
61
72
|
|
|
62
73
|
def graph_options
|
|
63
74
|
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
dpi: 200,
|
|
76
|
+
compound: true,
|
|
77
|
+
rankdir: "LR",
|
|
78
|
+
center: true,
|
|
79
|
+
format: 'png'
|
|
69
80
|
}
|
|
70
81
|
end
|
|
71
82
|
|
data/lib/gush/worker.rb
CHANGED
|
@@ -30,12 +30,16 @@ module Gush
|
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|
|
33
|
-
attr_reader :client, :workflow_id, :job
|
|
33
|
+
attr_reader :client, :workflow_id, :job, :configuration
|
|
34
34
|
|
|
35
35
|
def client
|
|
36
36
|
@client ||= Gush::Client.new(Gush.configuration)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def configuration
|
|
40
|
+
@configuration ||= client.configuration
|
|
41
|
+
end
|
|
42
|
+
|
|
39
43
|
def setup_job(workflow_id, job_id)
|
|
40
44
|
@workflow_id = workflow_id
|
|
41
45
|
@job ||= client.find_job(workflow_id, job_id)
|
|
@@ -73,7 +77,11 @@ module Gush
|
|
|
73
77
|
|
|
74
78
|
def enqueue_outgoing_jobs
|
|
75
79
|
job.outgoing.each do |job_name|
|
|
76
|
-
RedisMutex.with_lock(
|
|
80
|
+
RedisMutex.with_lock(
|
|
81
|
+
"gush_enqueue_outgoing_jobs_#{workflow_id}-#{job_name}",
|
|
82
|
+
sleep: configuration.polling_interval,
|
|
83
|
+
block: configuration.locking_duration
|
|
84
|
+
) do
|
|
77
85
|
out = client.find_job(workflow_id, job_name)
|
|
78
86
|
|
|
79
87
|
if out.ready_to_start?
|
|
@@ -152,17 +152,15 @@ describe "Workflows" do
|
|
|
152
152
|
flow = PayloadWorkflow.create
|
|
153
153
|
flow.start!
|
|
154
154
|
|
|
155
|
-
perform_one
|
|
156
|
-
expect(flow.reload.find_job(flow.jobs[0].name).output_payload).to eq('first')
|
|
155
|
+
3.times { perform_one }
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
expect(
|
|
157
|
+
outputs = flow.reload.jobs.select { |j| j.klass == 'RepetitiveJob' }.map { |j| j.output_payload }
|
|
158
|
+
expect(outputs).to match_array(['first', 'second', 'third'])
|
|
160
159
|
|
|
161
160
|
perform_one
|
|
162
|
-
expect(flow.reload.find_job(flow.jobs[2].name).output_payload).to eq('third')
|
|
163
161
|
|
|
164
|
-
|
|
165
|
-
expect(
|
|
162
|
+
summary_job = flow.reload.jobs.find { |j| j.klass == 'SummaryJob' }
|
|
163
|
+
expect(summary_job.output_payload).to eq(%w(first second third))
|
|
166
164
|
end
|
|
167
165
|
|
|
168
166
|
it "does not execute `configure` on each job for huge workflows" do
|
|
@@ -8,6 +8,8 @@ describe Gush::Configuration do
|
|
|
8
8
|
expect(subject.concurrency).to eq(5)
|
|
9
9
|
expect(subject.namespace).to eq('gush')
|
|
10
10
|
expect(subject.gushfile).to eq(GUSHFILE.realpath)
|
|
11
|
+
expect(subject.locking_duration).to eq(2)
|
|
12
|
+
expect(subject.polling_interval).to eq(0.3)
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
describe "#configure" do
|
|
@@ -15,10 +17,14 @@ describe Gush::Configuration do
|
|
|
15
17
|
Gush.configure do |config|
|
|
16
18
|
config.redis_url = "redis://localhost"
|
|
17
19
|
config.concurrency = 25
|
|
20
|
+
config.locking_duration = 5
|
|
21
|
+
config.polling_interval = 0.5
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
expect(Gush.configuration.redis_url).to eq("redis://localhost")
|
|
21
25
|
expect(Gush.configuration.concurrency).to eq(25)
|
|
26
|
+
expect(Gush.configuration.locking_duration).to eq(5)
|
|
27
|
+
expect(Gush.configuration.polling_interval).to eq(0.5)
|
|
22
28
|
end
|
|
23
29
|
end
|
|
24
30
|
end
|
data/spec/gush/graph_spec.rb
CHANGED
|
@@ -10,26 +10,43 @@ describe Gush::Graph do
|
|
|
10
10
|
edge = double("edge", :[]= => true)
|
|
11
11
|
graph = double("graph", node: node, edge: edge)
|
|
12
12
|
path = Pathname.new(Dir.tmpdir).join(filename)
|
|
13
|
-
|
|
14
|
-
expect(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
expect(graph).to receive(:
|
|
25
|
-
expect(graph).to receive(:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
expect(graph).to receive(:
|
|
30
|
-
expect(graph).to receive(:
|
|
31
|
-
|
|
32
|
-
expect(
|
|
13
|
+
|
|
14
|
+
expect(Graphviz::Graph).to receive(:new).and_return(graph)
|
|
15
|
+
|
|
16
|
+
node_start = double('start')
|
|
17
|
+
node_end = double('end')
|
|
18
|
+
node_prepare = double('Prepare')
|
|
19
|
+
node_fetch_first_job = double('FetchFirstJob')
|
|
20
|
+
node_fetch_second_job = double('FetchSecondJob')
|
|
21
|
+
node_normalize_job = double('NormalizeJob')
|
|
22
|
+
node_persist_first_job = double('PersistFirstJob')
|
|
23
|
+
|
|
24
|
+
expect(graph).to receive(:add_node).with('start', {shape: 'diamond', fillcolor: '#CFF09E', color: "#555555", style: 'filled'}).and_return(node_start)
|
|
25
|
+
expect(graph).to receive(:add_node).with('end', {shape: 'diamond', fillcolor: '#F56991', color: "#555555", style: 'filled'}).and_return(node_end)
|
|
26
|
+
|
|
27
|
+
standard_options = {:color=>"#555555", :fillcolor=>"white", :label=>"Prepare", :shape=>"ellipse", :style=>"filled"}
|
|
28
|
+
|
|
29
|
+
expect(graph).to receive(:add_node).with(/Prepare/, standard_options.merge(label: "Prepare")).and_return(node_prepare)
|
|
30
|
+
expect(graph).to receive(:add_node).with(/FetchFirstJob/, standard_options.merge(label: "FetchFirstJob")).and_return(node_fetch_first_job)
|
|
31
|
+
expect(graph).to receive(:add_node).with(/FetchSecondJob/, standard_options.merge(label: "FetchSecondJob")).and_return(node_fetch_second_job)
|
|
32
|
+
expect(graph).to receive(:add_node).with(/NormalizeJob/, standard_options.merge(label: "NormalizeJob")).and_return(node_normalize_job)
|
|
33
|
+
expect(graph).to receive(:add_node).with(/PersistFirstJob/, standard_options.merge(label: "PersistFirstJob")).and_return(node_persist_first_job)
|
|
34
|
+
|
|
35
|
+
edge_options = {
|
|
36
|
+
dir: "forward",
|
|
37
|
+
penwidth: 1,
|
|
38
|
+
color: "#555555"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
expect(node_start).to receive(:connect).with(node_prepare, **edge_options)
|
|
42
|
+
expect(node_prepare).to receive(:connect).with(node_fetch_first_job, **edge_options)
|
|
43
|
+
expect(node_prepare).to receive(:connect).with(node_fetch_second_job, **edge_options)
|
|
44
|
+
expect(node_fetch_first_job).to receive(:connect).with(node_persist_first_job, **edge_options)
|
|
45
|
+
expect(node_fetch_second_job).to receive(:connect).with(node_normalize_job, **edge_options)
|
|
46
|
+
expect(node_persist_first_job).to receive(:connect).with(node_normalize_job, **edge_options)
|
|
47
|
+
expect(node_normalize_job).to receive(:connect).with(node_end, **edge_options)
|
|
48
|
+
|
|
49
|
+
expect(graph).to receive(:dump_graph).and_return(nil)
|
|
33
50
|
|
|
34
51
|
subject.viz
|
|
35
52
|
end
|
data/spec/gush/worker_spec.rb
CHANGED
|
@@ -4,6 +4,8 @@ describe Gush::Worker do
|
|
|
4
4
|
subject { described_class.new }
|
|
5
5
|
|
|
6
6
|
let!(:workflow) { TestWorkflow.create }
|
|
7
|
+
let(:locking_duration) { 5 }
|
|
8
|
+
let(:polling_interval) { 0.5 }
|
|
7
9
|
let!(:job) { client.find_job(workflow.id, "Prepare") }
|
|
8
10
|
let(:config) { Gush.configuration.to_json }
|
|
9
11
|
let!(:client) { Gush::Client.new }
|
|
@@ -71,5 +73,11 @@ describe Gush::Worker do
|
|
|
71
73
|
|
|
72
74
|
subject.perform(workflow.id, 'OkayJob')
|
|
73
75
|
end
|
|
76
|
+
|
|
77
|
+
it 'calls RedisMutex.with_lock with customizable locking_duration and polling_interval' do
|
|
78
|
+
expect(RedisMutex).to receive(:with_lock)
|
|
79
|
+
.with(anything, block: 5, sleep: 0.5).twice
|
|
80
|
+
subject.perform(workflow.id, 'Prepare')
|
|
81
|
+
end
|
|
74
82
|
end
|
|
75
83
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'gush'
|
|
2
|
-
require 'fakeredis'
|
|
3
2
|
require 'json'
|
|
4
3
|
require 'pry'
|
|
5
4
|
|
|
@@ -35,12 +34,8 @@ class ParameterTestWorkflow < Gush::Workflow
|
|
|
35
34
|
end
|
|
36
35
|
end
|
|
37
36
|
|
|
38
|
-
class Redis
|
|
39
|
-
def publish(*)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
37
|
|
|
43
|
-
REDIS_URL = "redis://localhost:6379/12"
|
|
38
|
+
REDIS_URL = ENV["REDIS_URL"] || "redis://localhost:6379/12"
|
|
44
39
|
|
|
45
40
|
module GushHelpers
|
|
46
41
|
def redis
|
|
@@ -104,12 +99,13 @@ RSpec.configure do |config|
|
|
|
104
99
|
clear_performed_jobs
|
|
105
100
|
|
|
106
101
|
Gush.configure do |config|
|
|
107
|
-
config.redis_url
|
|
108
|
-
config.gushfile
|
|
102
|
+
config.redis_url = REDIS_URL
|
|
103
|
+
config.gushfile = GUSHFILE
|
|
104
|
+
config.locking_duration = defined?(locking_duration) ? locking_duration : 2
|
|
105
|
+
config.polling_interval = defined?(polling_interval) ? polling_interval : 0.3
|
|
109
106
|
end
|
|
110
107
|
end
|
|
111
108
|
|
|
112
|
-
|
|
113
109
|
config.after(:each) do
|
|
114
110
|
clear_enqueued_jobs
|
|
115
111
|
clear_performed_jobs
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gush
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.0
|
|
4
|
+
version: 2.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Piotrek Okoński
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-09-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activejob
|
|
@@ -19,7 +19,7 @@ dependencies:
|
|
|
19
19
|
version: 4.2.7
|
|
20
20
|
- - "<"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: '7.
|
|
22
|
+
version: '7.1'
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -29,7 +29,7 @@ dependencies:
|
|
|
29
29
|
version: 4.2.7
|
|
30
30
|
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '7.
|
|
32
|
+
version: '7.1'
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
|
34
34
|
name: concurrent-ruby
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -107,7 +107,7 @@ dependencies:
|
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
108
|
version: '0.6'
|
|
109
109
|
- !ruby/object:Gem::Dependency
|
|
110
|
-
name:
|
|
110
|
+
name: graphviz
|
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
|
112
112
|
requirements:
|
|
113
113
|
- - "~>"
|
|
@@ -124,44 +124,56 @@ dependencies:
|
|
|
124
124
|
name: terminal-table
|
|
125
125
|
requirement: !ruby/object:Gem::Requirement
|
|
126
126
|
requirements:
|
|
127
|
-
- - "
|
|
127
|
+
- - ">="
|
|
128
128
|
- !ruby/object:Gem::Version
|
|
129
129
|
version: '1.4'
|
|
130
|
+
- - "<"
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '3.1'
|
|
130
133
|
type: :runtime
|
|
131
134
|
prerelease: false
|
|
132
135
|
version_requirements: !ruby/object:Gem::Requirement
|
|
133
136
|
requirements:
|
|
134
|
-
- - "
|
|
137
|
+
- - ">="
|
|
135
138
|
- !ruby/object:Gem::Version
|
|
136
139
|
version: '1.4'
|
|
140
|
+
- - "<"
|
|
141
|
+
- !ruby/object:Gem::Version
|
|
142
|
+
version: '3.1'
|
|
137
143
|
- !ruby/object:Gem::Dependency
|
|
138
|
-
name:
|
|
144
|
+
name: paint
|
|
139
145
|
requirement: !ruby/object:Gem::Requirement
|
|
140
146
|
requirements:
|
|
141
147
|
- - "~>"
|
|
142
148
|
- !ruby/object:Gem::Version
|
|
143
|
-
version: '
|
|
149
|
+
version: '2.2'
|
|
144
150
|
type: :runtime
|
|
145
151
|
prerelease: false
|
|
146
152
|
version_requirements: !ruby/object:Gem::Requirement
|
|
147
153
|
requirements:
|
|
148
154
|
- - "~>"
|
|
149
155
|
- !ruby/object:Gem::Version
|
|
150
|
-
version: '
|
|
156
|
+
version: '2.2'
|
|
151
157
|
- !ruby/object:Gem::Dependency
|
|
152
158
|
name: thor
|
|
153
159
|
requirement: !ruby/object:Gem::Requirement
|
|
154
160
|
requirements:
|
|
155
|
-
- - "
|
|
161
|
+
- - ">="
|
|
156
162
|
- !ruby/object:Gem::Version
|
|
157
163
|
version: '0.19'
|
|
164
|
+
- - "<"
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '1.3'
|
|
158
167
|
type: :runtime
|
|
159
168
|
prerelease: false
|
|
160
169
|
version_requirements: !ruby/object:Gem::Requirement
|
|
161
170
|
requirements:
|
|
162
|
-
- - "
|
|
171
|
+
- - ">="
|
|
163
172
|
- !ruby/object:Gem::Version
|
|
164
173
|
version: '0.19'
|
|
174
|
+
- - "<"
|
|
175
|
+
- !ruby/object:Gem::Version
|
|
176
|
+
version: '1.3'
|
|
165
177
|
- !ruby/object:Gem::Dependency
|
|
166
178
|
name: launchy
|
|
167
179
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -232,20 +244,6 @@ dependencies:
|
|
|
232
244
|
- - "~>"
|
|
233
245
|
- !ruby/object:Gem::Version
|
|
234
246
|
version: '0.10'
|
|
235
|
-
- !ruby/object:Gem::Dependency
|
|
236
|
-
name: fakeredis
|
|
237
|
-
requirement: !ruby/object:Gem::Requirement
|
|
238
|
-
requirements:
|
|
239
|
-
- - "~>"
|
|
240
|
-
- !ruby/object:Gem::Version
|
|
241
|
-
version: '0.5'
|
|
242
|
-
type: :development
|
|
243
|
-
prerelease: false
|
|
244
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
245
|
-
requirements:
|
|
246
|
-
- - "~>"
|
|
247
|
-
- !ruby/object:Gem::Version
|
|
248
|
-
version: '0.5'
|
|
249
247
|
description: Gush is a parallel workflow runner using Redis as storage and ActiveJob
|
|
250
248
|
for executing jobs.
|
|
251
249
|
email:
|
|
@@ -255,9 +253,9 @@ executables:
|
|
|
255
253
|
extensions: []
|
|
256
254
|
extra_rdoc_files: []
|
|
257
255
|
files:
|
|
256
|
+
- ".github/workflows/ruby.yml"
|
|
258
257
|
- ".gitignore"
|
|
259
258
|
- ".rspec"
|
|
260
|
-
- ".travis.yml"
|
|
261
259
|
- CHANGELOG.md
|
|
262
260
|
- Gemfile
|
|
263
261
|
- LICENSE.txt
|
|
@@ -292,7 +290,7 @@ homepage: https://github.com/chaps-io/gush
|
|
|
292
290
|
licenses:
|
|
293
291
|
- MIT
|
|
294
292
|
metadata: {}
|
|
295
|
-
post_install_message:
|
|
293
|
+
post_install_message:
|
|
296
294
|
rdoc_options: []
|
|
297
295
|
require_paths:
|
|
298
296
|
- lib
|
|
@@ -307,8 +305,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
307
305
|
- !ruby/object:Gem::Version
|
|
308
306
|
version: '0'
|
|
309
307
|
requirements: []
|
|
310
|
-
rubygems_version: 3.
|
|
311
|
-
signing_key:
|
|
308
|
+
rubygems_version: 3.3.3
|
|
309
|
+
signing_key:
|
|
312
310
|
specification_version: 4
|
|
313
311
|
summary: Fast and distributed workflow runner based on ActiveJob and Redis
|
|
314
312
|
test_files:
|
data/.travis.yml
DELETED