whenever 1.1.2 → 1.1.3

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: e6e3f0d94253d0ac6d5a7f856afeb690b25f83a551e74581ebcc3cb0320855bc
4
- data.tar.gz: 8d8ccccb89a2d7e4c5dd8d6744314888c88928a0bf7b6b3ed2bc7e195cae4290
3
+ metadata.gz: 85714fb8b50231a67eca8db2f67211fc11e8bb07bbb76db5333882676f626802
4
+ data.tar.gz: 9523c1ec86b34bd8fcd9244ebbc37206857ef25bdcce6cbb661769e9231d3ed5
5
5
  SHA512:
6
- metadata.gz: f7942baf77e31bb83add51eea178e5ff11d724196a23cc45a47668813acb8ce428f1ab27ca806544ee935e67b8d164a6586e0a6bfda8eb33afb2ef0ea33c88d8
7
- data.tar.gz: 30691f3aa03c58df894d3d516c21fedd9a6a3d23563ebaa9d267977b59a822c37071f4fc3663aae115f008865f426a899ce436badbb6883643aaa24b7e2a146c
6
+ metadata.gz: bd84f4e82fcd6aff274fef804ca4fd2e834d6817778fd09d56287a013039005e1ecaeb12e8fde657502709bcabcd8dc31bdd0d99cd163edefe1d0b8889d2a78d
7
+ data.tar.gz: 59728cc54eae8cd88fe0d911ff6bb2a5571fdc0f257dc93ab8564c3d7795f7583aece8c21eede62ae6b99b5a264c3b4ece7e222dac77f3a2b1401f3f3237605b
@@ -3,6 +3,8 @@ on:
3
3
  pull_request:
4
4
  push:
5
5
  branches: [ main ]
6
+ permissions:
7
+ contents: read
6
8
  jobs:
7
9
  test:
8
10
  runs-on: ${{ matrix.os }}
@@ -23,7 +25,7 @@ jobs:
23
25
  - os: macos-15-intel
24
26
  ruby-version: '2.5'
25
27
  steps:
26
- - uses: actions/checkout@v6
28
+ - uses: actions/checkout@v7
27
29
  name: Set up Ruby ${{ matrix.ruby-version }}
28
30
  - uses: ruby/setup-ruby@v1
29
31
  env:
@@ -74,7 +76,7 @@ jobs:
74
76
  env:
75
77
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/activesupport${{ matrix.rails-version }}.gemfile
76
78
  steps:
77
- - uses: actions/checkout@v6
79
+ - uses: actions/checkout@v7
78
80
  name: Set up Ruby ${{ matrix.ruby-version }}
79
81
  - uses: ruby/setup-ruby@v1
80
82
  with:
data/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  ### unreleased
2
2
 
3
3
 
4
+ ### 1.1.3 / September 15, 2026
5
+
6
+ * Add support for sequential job definitions https://github.com/javan/whenever/pull/854
7
+
8
+ * CI: Bump actions/checkout from 6 to 7 https://github.com/javan/whenever/pull/876
9
+
10
+ * CI: Restrict GitHub Actions workflow permissions to contents: read https://github.com/javan/whenever/pull/875
11
+
12
+
4
13
  ### 1.1.2 / January 18, 2026
5
14
 
6
15
  * Add description as comment in crontab https://github.com/javan/whenever/pull/776
@@ -11,7 +20,7 @@
11
20
 
12
21
  * CI: Use macos-15-intel instead of macos-13 for Ruby 2.4 and 2.5 https://github.com/javan/whenever/pull/871
13
22
 
14
- ### 1.1.1 / Dec 8, 2025
23
+ ### 1.1.1 / December 8, 2025
15
24
 
16
25
  * job_list `.respond_to?` updated to an instance method https://github.com/javan/whenever/pull/830
17
26
 
@@ -23,7 +32,7 @@
23
32
 
24
33
  * Require MFA for gem releases https://github.com/javan/whenever/pull/867
25
34
 
26
- ### 1.1.0 / Nov 21, 2025
35
+ ### 1.1.0 / November 21, 2025
27
36
 
28
37
  * Splat whenever_roles when passing to roles https://github.com/javan/whenever/pull/777
29
38
 
@@ -161,7 +170,7 @@
161
170
  * Process params in job templates. [Austin Ziegler]
162
171
 
163
172
 
164
- ### 0.8.4 / July 22, 2012
173
+ ### 0.8.4 / July 22, 2013
165
174
 
166
175
  * Don't require schedule file when clearing. [Javan Makhmali]
167
176
 
data/README.md CHANGED
@@ -59,6 +59,14 @@ every 3.hours do # 1.minute 1.day 1.week 1.month 1.year is also supported
59
59
  command "/usr/bin/my_great_command"
60
60
  end
61
61
 
62
+ every 3.hours, sequential: true do
63
+ # Jobs in this block that don't set their own sequence will default to run in the same sequence (not in parallel)
64
+ runner "MyModel.some_process", halt_sequence_on_failure: true # If this job fails, subsequent jobs will not run (defaults to false)
65
+ rake "my:rake:task"
66
+ command "/usr/bin/my_great_command", sequence: nil # This job runs in parallel with other jobs that are not part of a sequence
67
+ runner "MyModel.task_to_run_at_four_thirty_in_the_morning", sequence: 'other' # This job runs sequentially with other jobs in the sequence called 'other'
68
+ end
69
+
62
70
  every 1.day, at: '4:30 am' do
63
71
  runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
64
72
  end
data/lib/whenever/job.rb CHANGED
@@ -2,7 +2,7 @@ require 'shellwords'
2
2
 
3
3
  module Whenever
4
4
  class Job
5
- attr_reader :at, :roles, :mailto, :description
5
+ attr_reader :at, :roles, :mailto, :description, :sequence, :halt_sequence_on_failure
6
6
 
7
7
  def initialize(options = {})
8
8
  @options = options
@@ -10,6 +10,8 @@ module Whenever
10
10
  @template = options.delete(:template)
11
11
  @mailto = options.fetch(:mailto, :default_mailto)
12
12
  @job_template = options.delete(:job_template) || ":job"
13
+ @sequence = options.delete(:sequence)
14
+ @halt_sequence_on_failure = options.delete(:halt_sequence_on_failure)
13
15
  @roles = Array(options.delete(:roles))
14
16
  @description = options.delete(:description)
15
17
  @options[:output] = options.has_key?(:output) ? Whenever::Output::Redirection.new(options[:output]).to_s : ''
@@ -48,6 +48,12 @@ module Whenever
48
48
  def every(frequency, options = {})
49
49
  @current_time_scope = frequency
50
50
  @options = options
51
+
52
+ if @options[:sequential]
53
+ @default_sequence_id ||= 0
54
+ @options[:sequence] ||= "default_sequence_#{@default_sequence_id += 1}"
55
+ end
56
+
51
57
  yield
52
58
  end
53
59
 
@@ -138,10 +144,17 @@ module Whenever
138
144
  def cron_jobs_of_time(time, jobs)
139
145
  shortcut_jobs, regular_jobs = [], []
140
146
 
147
+ filtered_jobs = jobs.select do |job|
148
+ roles.empty? || roles.any? { |r| job.has_role?(r) }
149
+ end
150
+
151
+ grouped_jobs = filtered_jobs.group_by(&:sequence)
152
+ grouped_jobs.each do |sequence, jobs|
153
+ grouped_jobs[sequence] = JobSequence.new(jobs) if sequence
154
+ end
155
+ jobs = grouped_jobs.values.flatten
156
+
141
157
  jobs.each do |job|
142
- next unless roles.empty? || roles.any? do |r|
143
- job.has_role?(r)
144
- end
145
158
  Whenever::Output::Cron.output(time, job, :chronic_options => @chronic_options) do |cron|
146
159
  cron << "\n\n"
147
160
  cron = (job.description.strip + "\n").gsub(/^(.*)$/, '# \1') + cron unless job.description.to_s.empty?
@@ -0,0 +1,36 @@
1
+ require 'shellwords'
2
+
3
+ module Whenever
4
+ class JobSequence
5
+ attr_reader :at, :roles, :mailto, :description
6
+
7
+ def initialize(jobs, options = {})
8
+ validate!(jobs)
9
+
10
+ @jobs = jobs
11
+ @options = options
12
+ @at = options.fetch(:at, primary_job.at)
13
+ @mailto = options.fetch(:mailto, primary_job.mailto || :default_mailto)
14
+ @roles = Array(options.delete(:roles), *primary_job.roles)
15
+ @description = options.fetch(:description) { @jobs.map { |job| job.description.to_s.strip }.reject(&:empty?).uniq.join("\n") }
16
+ end
17
+
18
+ def output
19
+ @jobs.map { |job| [job.output, job.halt_sequence_on_failure ? ' && ' : ' ; '] }.flatten[0..-2].join
20
+ end
21
+
22
+ def has_role?(role)
23
+ roles.empty? || roles.include?(role)
24
+ end
25
+
26
+ private
27
+
28
+ def primary_job
29
+ @jobs.first
30
+ end
31
+
32
+ def validate!(jobs)
33
+ raise ArgumentError, "Jobs in a sequence don't support different `at` values" if jobs.map(&:at).uniq.count > 1
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module Whenever
2
- VERSION = '1.1.2'
2
+ VERSION = '1.1.3'
3
3
  end
data/lib/whenever.rb CHANGED
@@ -2,6 +2,7 @@ require "whenever/version"
2
2
  require 'whenever/numeric'
3
3
  require 'whenever/numeric_seconds'
4
4
  require 'whenever/job_list'
5
+ require 'whenever/job_sequence'
5
6
  require 'whenever/job'
6
7
  require 'whenever/command_line'
7
8
  require 'whenever/cron'
@@ -0,0 +1,187 @@
1
+ require 'test_helper'
2
+
3
+ class OutputJobsWithSequenceTest < Whenever::TestCase
4
+ test "defined jobs with a sequence argument specified per-job" do
5
+ output = Whenever.cron \
6
+ <<-file
7
+ every 2.hours do
8
+ command "blahblah", sequence: 'backups'
9
+ command "foofoo", sequence: 'backups'
10
+ command "barbar"
11
+ end
12
+ file
13
+
14
+ output_without_empty_line = lines_without_empty_line(output.lines)
15
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
16
+ assert_equal two_hours + " /bin/bash -l -c 'barbar'", output_without_empty_line.shift
17
+ end
18
+
19
+ test "defined jobs with a sequence argument specified on the group" do
20
+ output = Whenever.cron \
21
+ <<-file
22
+ every 2.hours, sequence: 'backups' do
23
+ command "blahblah"
24
+ command "foofoo"
25
+ end
26
+ file
27
+
28
+ output_without_empty_line = lines_without_empty_line(output.lines)
29
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
30
+ end
31
+
32
+ test "defined jobs with a sequence argument and a job that halts the sequence on failure" do
33
+ output = Whenever.cron \
34
+ <<-file
35
+ every 2.hours, sequence: 'backups' do
36
+ command "blahblah", halt_sequence_on_failure: true
37
+ command "foofoo"
38
+ end
39
+ file
40
+
41
+ output_without_empty_line = lines_without_empty_line(output.lines)
42
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' && /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
43
+ end
44
+
45
+ test "defined jobs with a sequences specified on the group and jobs" do
46
+ output = Whenever.cron \
47
+ <<-file
48
+ every 2.hours, sequence: 'backups' do
49
+ command "blahblah"
50
+ command "barbar", sequence: nil
51
+ command "foofoo"
52
+ command "bazbaz", sequence: 'bees'
53
+ command "buzzbuzz", sequence: 'bees'
54
+ end
55
+ file
56
+
57
+ output_without_empty_line = lines_without_empty_line(output.lines)
58
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
59
+ assert_equal two_hours + " /bin/bash -l -c 'barbar'", output_without_empty_line.shift
60
+ assert_equal two_hours + " /bin/bash -l -c 'bazbaz' ; /bin/bash -l -c 'buzzbuzz'", output_without_empty_line.shift
61
+ end
62
+
63
+ test "defined jobs with a multiple groups with sequences specified on the group and jobs" do
64
+ output = Whenever.cron \
65
+ <<-file
66
+ every 2.hours, sequence: 'backups' do
67
+ command "blahblah"
68
+ command "barbar", sequence: nil
69
+ command "bazbaz", sequence: 'bees'
70
+ end
71
+
72
+ every 2.hours, sequence: 'backups' do
73
+ command "foofoo"
74
+ command "buzzbuzz", sequence: 'bees'
75
+ end
76
+ file
77
+
78
+ output_without_empty_line = lines_without_empty_line(output.lines)
79
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
80
+ assert_equal two_hours + " /bin/bash -l -c 'barbar'", output_without_empty_line.shift
81
+ assert_equal two_hours + " /bin/bash -l -c 'bazbaz' ; /bin/bash -l -c 'buzzbuzz'", output_without_empty_line.shift
82
+ end
83
+
84
+ test "defined jobs with a multiple groups with sequences specified on the group and jobs" do
85
+ output = Whenever.cron \
86
+ <<-file
87
+ every 2.hours, sequence: 'backups' do
88
+ command "blahblah"
89
+ command "barbar", sequence: nil
90
+ command "bazbaz", sequence: 'bees'
91
+ end
92
+
93
+ every 3.hours, sequence: 'backups' do
94
+ command "foofoo"
95
+ command "buzzbuzz", sequence: 'bees'
96
+ end
97
+ file
98
+
99
+ output_without_empty_line = lines_without_empty_line(output.lines)
100
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah'", output_without_empty_line.shift
101
+ assert_equal two_hours + " /bin/bash -l -c 'barbar'", output_without_empty_line.shift
102
+ assert_equal two_hours + " /bin/bash -l -c 'bazbaz'", output_without_empty_line.shift
103
+ assert_equal three_hours + " /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
104
+ assert_equal three_hours + " /bin/bash -l -c 'buzzbuzz'", output_without_empty_line.shift
105
+ end
106
+
107
+ test "defined jobs with a multiple groups with sequences specified on the group and jobs" do
108
+ assert_raises ArgumentError do
109
+ Whenever.cron \
110
+ <<-file
111
+ every 2.hours, sequence: 'backups' do
112
+ command "blahblah", at: 1
113
+ command "barbar", at: 2
114
+ end
115
+ file
116
+ end
117
+ end
118
+
119
+ test "defined jobs with a sequence argument and a description on the group" do
120
+ output = Whenever.cron \
121
+ <<-file
122
+ every 2.hours, sequence: 'backups', description: "Nightly backups" do
123
+ command "blahblah"
124
+ command "foofoo"
125
+ end
126
+ file
127
+
128
+ assert_match "# Nightly backups\n" + two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output
129
+ end
130
+
131
+ test "defined jobs with a sequence argument and a description on each job" do
132
+ output = Whenever.cron \
133
+ <<-file
134
+ every 2.hours, sequence: 'backups' do
135
+ command "blahblah", description: "Dump the database"
136
+ command "foofoo", description: "Upload the dump"
137
+ end
138
+ file
139
+
140
+ assert_match "# Dump the database\n# Upload the dump\n" + two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output
141
+ end
142
+
143
+ test "defined jobs with a sequence argument and descriptions padded with whitespace" do
144
+ output = Whenever.cron \
145
+ <<-file
146
+ every 2.hours, sequence: 'backups' do
147
+ command "blahblah", description: " Dump the database "
148
+ command "foofoo", description: " Upload the dump "
149
+ end
150
+ file
151
+
152
+ assert_match "# Dump the database\n# Upload the dump\n" + two_hours, output
153
+ end
154
+
155
+ def three_hours
156
+ "0 0,3,6,9,12,15,18,21 * * *"
157
+ end
158
+ end
159
+
160
+ class OutputJobsWithSequentialTest < Whenever::TestCase
161
+ test "defined jobs with a sequential argument" do
162
+ output = Whenever.cron \
163
+ <<-file
164
+ every 2.hours, sequential: true do
165
+ command "blahblah"
166
+ command "foofoo"
167
+ end
168
+ file
169
+
170
+ output_without_empty_line = lines_without_empty_line(output.lines)
171
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah' ; /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
172
+ end
173
+
174
+ test "defined jobs with a sequential argument" do
175
+ output = Whenever.cron \
176
+ <<-file
177
+ every 2.hours, sequential: true do
178
+ command "blahblah"
179
+ command "foofoo", sequence: false
180
+ end
181
+ file
182
+
183
+ output_without_empty_line = lines_without_empty_line(output.lines)
184
+ assert_equal two_hours + " /bin/bash -l -c 'blahblah'", output_without_empty_line.shift
185
+ assert_equal two_hours + " /bin/bash -l -c 'foofoo'", output_without_empty_line.shift
186
+ end
187
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whenever
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Javan Makhmali
@@ -65,6 +65,7 @@ files:
65
65
  - lib/whenever/cron.rb
66
66
  - lib/whenever/job.rb
67
67
  - lib/whenever/job_list.rb
68
+ - lib/whenever/job_sequence.rb
68
69
  - lib/whenever/numeric.rb
69
70
  - lib/whenever/numeric_seconds.rb
70
71
  - lib/whenever/os.rb
@@ -79,6 +80,7 @@ files:
79
80
  - test/functional/output_env_test.rb
80
81
  - test/functional/output_jobs_for_roles_test.rb
81
82
  - test/functional/output_jobs_with_mailto_test.rb
83
+ - test/functional/output_jobs_with_sequence_test.rb
82
84
  - test/functional/output_redirection_test.rb
83
85
  - test/test_case.rb
84
86
  - test/test_helper.rb
@@ -109,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
111
  - !ruby/object:Gem::Version
110
112
  version: '0'
111
113
  requirements: []
112
- rubygems_version: 4.0.3
114
+ rubygems_version: 4.0.16
113
115
  specification_version: 4
114
116
  summary: Cron jobs in ruby.
115
117
  test_files:
@@ -121,6 +123,7 @@ test_files:
121
123
  - test/functional/output_env_test.rb
122
124
  - test/functional/output_jobs_for_roles_test.rb
123
125
  - test/functional/output_jobs_with_mailto_test.rb
126
+ - test/functional/output_jobs_with_sequence_test.rb
124
127
  - test/functional/output_redirection_test.rb
125
128
  - test/unit/capistrano_support_test.rb
126
129
  - test/unit/cron_test.rb