schedule_job 1.0.1 → 1.0.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: cc056eaeb082429537d01a7521bef0574cecc73a8dc8baa769a02a271758aa86
4
- data.tar.gz: 654b25fe20451949bbecc43b3757bdba36898ed1e40a4f50d9943828e2a2951f
3
+ metadata.gz: 5a0f470384777a45f805dc1ff41c9f6386db8378c509eec95b647a577f43e06e
4
+ data.tar.gz: 9fa202bc1743951f7a2e350c379a1d0d79e0a5f2926fad5aa3c9393be042287d
5
5
  SHA512:
6
- metadata.gz: c5f49020d6b8965598eba15614cfdfb700ef383c58787c6b72de53f795d36c026bb6a2913905372ecf51b1e7136b3fc0be2430579d3e8ee0d17cd71a1e45b61b
7
- data.tar.gz: 8f139bdc6d8d6edb8e6780e304e431fa8141c05089d05bbce062afab6dac42db3b94c546ab556d6a962cb9a4285e83a8c7ef7e10a65fe57972e2895263cf89f5
6
+ metadata.gz: ddd20ca47d699cf56bd6e653a84065fe45c10133fd716af28fc092e2550deffe9a5465041e21ee287a3f4e9bfb34a9cdb14dd642ee5db1b3a807b6430fd9da28
7
+ data.tar.gz: 2e268c4aaa999e4c75df56aa0d4396a0269d9064cc9c46498598c0a5a0086ac1fe7e809cf243b8ddb2df98020b13fde6f6d40fd076e43ad440dc518918dadf81
data/Gemfile CHANGED
@@ -4,9 +4,6 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "citrus"
7
- # gem "whenever"
8
7
  gem "cronex"
9
8
  gem "parse-cron"
10
- gem "activesupport"
11
- # gem "treetop"
12
- # gem "polyglot"
9
+ gem "activesupport"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- schedule_job (1.0.0)
4
+ schedule_job (1.0.6)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -5,13 +5,83 @@ schedule is a frontend around crontab to add/remove cron jobs from user cron tab
5
5
  ## Installation
6
6
 
7
7
  ```
8
- gem install schedule_job
8
+ ~ ❯ gem install schedule_job
9
+ Fetching schedule_job-1.0.1.gem
10
+ Successfully installed schedule_job-1.0.1
11
+ Parsing documentation for schedule_job-1.0.1
12
+ Installing ri documentation for schedule_job-1.0.1
13
+ Done installing documentation for schedule_job after 0 seconds
9
14
  ```
10
15
 
11
16
  ## Usage
12
17
 
18
+ Example usage:
13
19
  ```
20
+ ~ ❯ schedule --help
21
+ Usage: schedule [options] command
22
+ -d, --dryrun Report what would happen but do not install the scheduled job.
23
+ -l, --list List installed cron jobs.
24
+ -r, --rm JOB_ID Remove the specified job id as indicated by --list
25
+ -u, --user USER User that the crontab belongs to.
26
+ -e, --every DURATION Run command every DURATION units of time (valid suffixes are m, h, d, M). DURATION may also be one of the special keywords: reboot, year, month, week, day, hour
27
+ For example:
28
+ --every 10m # every 10 minutes
29
+ --every 5h # every 5 hours
30
+ --every 2d # every 2 days
31
+ --every 3M # every 3 months
14
32
 
33
+ Special durations:
34
+ --every reboot # after every reboot
35
+ --every year # every year
36
+ --every month # every month
37
+ --every week # every week
38
+ --every day # every day
39
+ --every hour # every hour
40
+
41
+ -c, --cron CRON Run command on the given cron schedule.
42
+ For example:
43
+ --cron "*/5 15 * * 1-5" # Every 5 minutes, at 3:00 PM, Monday through Friday
44
+ --cron "0 0/30 8-9 5,20 * ?" # Every 30 minutes, between 8:00 AM and 9:59 AM, on day 5 and 20 of the month
45
+
46
+ ~ ❯ schedule -l
47
+ ~ ❯ schedule -d -e 10m backup.sh
48
+ Scheduling: backup.sh Every 10 minutes
49
+ Next three runs:
50
+ 1. 2021-11-01 21:50:00 -0500
51
+ 2. 2021-11-01 22:00:00 -0500
52
+ 3. 2021-11-01 22:10:00 -0500
53
+ ~ ❯ schedule -l
54
+ Jobs
55
+ 1. backup.sh Every 10 minutes
56
+ ~ ❯ crontab -l
57
+ */10 * * * * backup.sh
58
+ ~ ❯ schedule -e 3M quarterly_backup.sh
59
+ Scheduling: quarterly_backup.sh At 9:50 PM, on day 1 of the month, every 3 months
60
+ Next three runs:
61
+ 1. 2022-01-01 21:50:00 -0600
62
+ 2. 2022-04-01 21:50:00 -0500
63
+ 3. 2022-07-01 21:50:00 -0500
64
+ ~ ❯ schedule -l
65
+ Jobs
66
+ 1. backup.sh Every 10 minutes
67
+ 2. quarterly_backup.sh At 9:50 PM, on day 1 of the month, every 3 months
68
+ ~ ❯ crontab -l
69
+ */10 * * * * backup.sh
70
+ 50 21 1 */3 * quarterly_backup.sh
71
+ ~ ❯ schedule --dryrun --rm 1
72
+ Would remove: backup.sh Every 10 minutes
73
+ ~ ❯ schedule --dryrun --rm 2
74
+ Would remove: quarterly_backup.sh At 9:50 PM, on day 1 of the month, every 3 months
75
+ ~ ❯ schedule --rm 1
76
+ Removing: backup.sh Every 10 minutes
77
+ ~ ❯ schedule -l
78
+ Jobs
79
+ 1. quarterly_backup.sh At 9:50 PM, on day 1 of the month, every 3 months
80
+ ~ ❯ schedule --rm 1
81
+ Removing: quarterly_backup.sh At 9:50 PM, on day 1 of the month, every 3 months
82
+ ~ ❯ schedule -l
83
+ ~ ❯ crontab -l
84
+ ~ ❯
15
85
  ```
16
86
 
17
87
  ## License
@@ -1,7 +1,5 @@
1
1
  require "optparse"
2
2
 
3
- # schedule --every 10m "my_command --foo --bar"
4
-
5
3
  module ScheduleJob
6
4
  class Cli
7
5
  def self.run(argv = ARGV)
@@ -32,20 +30,28 @@ module ScheduleJob
32
30
  options[:crontab_user] = user
33
31
  end
34
32
 
35
- opts.on("-e", "--every DURATION", "Run command every DURATION units of time.
33
+ opts.on("-e", "--every DURATION", "Run command every DURATION units of time (valid suffixes are m, h, d, M). DURATION may also be one of the special keywords: reboot, year, month, week, day, hour
36
34
  For example:
37
- --every 10m # meaning, every 10 minutes
38
- --every 5h # meaning, every 5 hours
39
- --every 2d # meaning, every 2 days
40
- --every 3M # meaning, every 3 months
35
+ --every 10m # every 10 minutes
36
+ --every 5h # every 5 hours
37
+ --every 2d # every 2 days
38
+ --every 3M # every 3 months
39
+
40
+ Special durations:
41
+ --every reboot # after every reboot
42
+ --every year # every year
43
+ --every month # every month
44
+ --every week # every week
45
+ --every day # every day
46
+ --every hour # every hour
41
47
  ") do |every|
42
48
  options[:every] = every
43
49
  end
44
50
 
45
51
  opts.on("-c", "--cron CRON", "Run command on the given cron schedule.
46
52
  For example:
47
- --cron \"*/5 15 * * 1-5\" # meaning, Every 5 minutes, at 3:00 PM, Monday through Friday
48
- --cron \"0 0/30 8-9 5,20 * ?\" # meaning, Every 30 minutes, between 8:00 AM and 9:59 AM, on day 5 and 20 of the month
53
+ --cron \"*/5 15 * * 1-5\" # Every 5 minutes, at 3:00 PM, Monday through Friday
54
+ --cron \"0 0/30 8-9 5,20 * ?\" # Every 30 minutes, between 8:00 AM and 9:59 AM, on day 5 and 20 of the month
49
55
  ") do |cron_schedule|
50
56
  options[:cron] = cron_schedule
51
57
  end
@@ -63,6 +69,8 @@ module ScheduleJob
63
69
  remove_job(args)
64
70
  when args[:cron] || args[:every] # write jobs
65
71
  install_job(args)
72
+ else
73
+ puts "No parameters specified. Please specify one of: --every, --cron, --list, or --rm"
66
74
  end
67
75
  end
68
76
 
@@ -90,13 +98,14 @@ module ScheduleJob
90
98
  Cron::Job.new(cron_schedule, command)
91
99
  when args[:every]
92
100
  duration = args[:every]
93
- match = duration.match(/(\d+)(m|h|d|M)/)
94
- if match
101
+ if match = duration.match(/(\d+)(m|h|d|M)/)
95
102
  qty = match[1].to_i
96
103
  unit_of_time = match[2].to_s
97
104
  Cron::Job.every(qty, unit_of_time, command)
105
+ elsif duration.match(/reboot|year|month|week|day|hour/)
106
+ Cron::Job.every_simple_duration(duration, command)
98
107
  else
99
- puts "'#{duration}' is an invalid duration. The duration must be specified as: integer followed immediately by m (minutes), h (hours), d (days), M (months) (e.g. 10m)"
108
+ puts "'#{duration}' is an invalid duration. The duration must be specified as either (1) integer followed immediately by m (minutes), h (hours), d (days), M (months) (e.g. 10m) or (2) reboot, year, month, week, day, hour"
100
109
  exit(1)
101
110
  end
102
111
  end
@@ -50,12 +50,6 @@ module ScheduleJob
50
50
 
51
51
  raise("Unable to read crontab: #{error_output}") if !exit_status.success? && !no_crontab
52
52
 
53
- # puts "read_crontab()"
54
- # puts "stdout:"
55
- # puts stdout
56
- # puts "stderr:"
57
- # puts stderr
58
-
59
53
  crontab_output
60
54
  end
61
55
 
@@ -63,16 +57,8 @@ module ScheduleJob
63
57
  def parse_crontab(user_crontab)
64
58
  parser = TableParser.new(user_crontab)
65
59
 
66
- # user_crontab.each_line do |line|
67
- # puts line
68
- # puts "valid? #{LineParser.valid?(line)}"
69
- # puts LineParser.parse(line)
70
- # end
71
-
72
60
  return [], [] unless parser.valid?
73
61
 
74
- # puts "valid!"
75
-
76
62
  environment_vars = parser.environment_vars&.map do |env_directive|
77
63
  name = env_directive[:var].to_s
78
64
  expr = env_directive[:expr].to_s
@@ -96,21 +82,25 @@ module ScheduleJob
96
82
  puts "Scheduling: #{job.to_s}"
97
83
  install_cron_job(job)
98
84
  end
99
- puts "Next three runs:"
100
- cron_parser = CronParser.new(job.schedule_spec)
101
- first_run_time = cron_parser.next(Time.now)
102
- second_run_time = cron_parser.next(first_run_time)
103
- third_run_time = cron_parser.next(second_run_time)
104
- puts "1. #{first_run_time}"
105
- puts "2. #{second_run_time}"
106
- puts "3. #{third_run_time}"
85
+
86
+ cron_parser = CronParser.new(job.schedule_spec) rescue nil
87
+ if cron_parser
88
+ puts "Next three runs:"
89
+ first_run_time = cron_parser.next(Time.now)
90
+ second_run_time = cron_parser.next(first_run_time)
91
+ third_run_time = cron_parser.next(second_run_time)
92
+
93
+ puts "1. #{first_run_time}"
94
+ puts "2. #{second_run_time}"
95
+ puts "3. #{third_run_time}"
96
+ end
107
97
  end
108
98
 
109
99
  def install_cron_job(job)
110
100
  job_spec = job.specification
111
- # puts "Installing new cron job: #{job_spec}"
112
101
 
113
102
  new_crontab = [read_crontab(@user).strip, job_spec.strip].reject(&:empty?).join("\n")
103
+ new_crontab << "\n" # add a trailing newline
114
104
  write_crontab(new_crontab)
115
105
  end
116
106
 
@@ -135,21 +125,12 @@ module ScheduleJob
135
125
  end
136
126
 
137
127
  def write_crontab(new_crontab, user = @user)
128
+ # overwrite crontab interactively, per https://stackoverflow.com/questions/610839/how-can-i-programmatically-create-a-new-cron-job
138
129
  command = ["crontab"]
139
130
  command << "-u #{user}" if user
140
131
  command << "-"
141
132
  command = command.join(" ")
142
133
 
143
- # command = "(crontab -l ; echo \"#{job_spec}\") | crontab -" # add new job to bottom of crontab, per https://stackoverflow.com/questions/610839/how-can-i-programmatically-create-a-new-cron-job
144
- # puts "writing new crontab:"
145
- # puts new_crontab
146
- # puts "*" * 80
147
-
148
- # puts "write_crontab:"
149
- # puts "command: #{command}"
150
- # puts "new crontab:"
151
- # puts new_crontab
152
-
153
134
  stdout, stderr, exit_status = Open3.capture3(command, stdin_data: new_crontab)
154
135
  crontab_output = stdout
155
136
  error_output = stderr
@@ -184,14 +165,33 @@ module ScheduleJob
184
165
  hour = next_moment.hour
185
166
  day = next_moment.day
186
167
  schedule_spec = case duration_unit_of_time
187
- when "m"
188
- "*/#{duration_quantity} * * * *"
189
- when "h"
190
- "#{minute} */#{duration_quantity} * * *"
191
- when "d"
192
- "#{minute} #{hour} */#{duration_quantity} * *"
193
- when "M"
194
- "#{minute} #{hour} #{day} */#{duration_quantity} *"
168
+ when "m"
169
+ "*/#{duration_quantity} * * * *"
170
+ when "h"
171
+ "#{minute} */#{duration_quantity} * * *"
172
+ when "d"
173
+ "#{minute} #{hour} */#{duration_quantity} * *"
174
+ when "M"
175
+ "#{minute} #{hour} #{day} */#{duration_quantity} *"
176
+ end
177
+ self.new(schedule_spec, command)
178
+ end
179
+
180
+ # duration is one of: reboot, year, month, week, day, hour
181
+ def self.every_simple_duration(duration, command)
182
+ schedule_spec = case duration
183
+ when "reboot"
184
+ "@reboot"
185
+ when "year"
186
+ "@yearly"
187
+ when "month"
188
+ "@monthly"
189
+ when "week"
190
+ "@weekly"
191
+ when "day"
192
+ "@daily"
193
+ when "hour"
194
+ "@hourly"
195
195
  end
196
196
  self.new(schedule_spec, command)
197
197
  end
@@ -222,7 +222,23 @@ module ScheduleJob
222
222
  end
223
223
 
224
224
  def to_s
225
- schedule = Cronex::ExpressionDescriptor.new(@schedule_spec).description
225
+ schedule = case @schedule_spec
226
+ when "@reboot"
227
+ "at every reboot"
228
+ when "@yearly", "@annually"
229
+ "every year"
230
+ when "@monthly"
231
+ "every month"
232
+ when "@weekly"
233
+ "every week"
234
+ when "@daily"
235
+ "every day"
236
+ when "@hourly"
237
+ "every hour"
238
+ else
239
+ Cronex::ExpressionDescriptor.new(@schedule_spec).description
240
+ end
241
+
226
242
  # str = "#{@command} #{schedule} (line #{@line_number}, pos #{@pos_offset})"
227
243
  str = "#{@command} #{schedule}"
228
244
  end
@@ -17,10 +17,6 @@ end
17
17
 
18
18
  Citrus.require("schedule_job/cron")
19
19
 
20
- # require "polyglot"
21
- # require "treetop"
22
- # require "schedule_job/cron.treetop"
23
-
24
20
  module ScheduleJob
25
21
  module Cron
26
22
  Grammar = ScheduleCronParser
@@ -34,7 +30,6 @@ module ScheduleJob
34
30
 
35
31
  def self.parse(user_crontab_string)
36
32
  Grammar.parse(user_crontab_string, root: :user_crontab)
37
- # ::CrontabParser.new.parse(user_crontab_string, root: :user_crontab)
38
33
  end
39
34
 
40
35
  def initialize(user_crontab_string)
@@ -55,17 +50,10 @@ module ScheduleJob
55
50
 
56
51
  def environment_vars
57
52
  @user_crontab&.capture(:environment)&.captures(:directive)
58
- # puts @user_crontab.methods.sort.inspect
59
- # puts @user_crontab.inspect
60
- # puts "*" * 80
61
- # puts @user_crontab.environment_spec.inspect
62
- # exit()
63
- # @user_crontab&.environment&.directive
64
53
  end
65
54
 
66
55
  def job_specs
67
56
  @user_crontab&.capture(:jobspecs)&.captures(:jobspec)
68
- # @user_crontab&.jobspecs&.jobspec
69
57
  end
70
58
  end
71
59
 
@@ -78,7 +66,6 @@ module ScheduleJob
78
66
 
79
67
  def self.parse(cron_line)
80
68
  Grammar.parse(cron_line, root: :jobspec, consume: false)
81
- # ::CrontabParser.new.parse(cron_line, root: :jobspec, consume_all_input: false)
82
69
  end
83
70
  end
84
71
  end
@@ -1,3 +1,3 @@
1
1
  module ScheduleJob
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.6"
3
3
  end
data/schedule_job.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.description = "job scheduler"
11
11
  spec.homepage = "https://github.com/davidkellis/scheduler"
12
12
  spec.license = "MIT"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
14
14
 
15
15
  spec.files = Dir["**/**"].
16
16
  grep_v(/.gem$/).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schedule_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Ellis
@@ -69,7 +69,6 @@ files:
69
69
  - lib/schedule_job/cli.rb
70
70
  - lib/schedule_job/cron.citrus
71
71
  - lib/schedule_job/cron.rb
72
- - lib/schedule_job/cron.treetop
73
72
  - lib/schedule_job/cron_parser.rb
74
73
  - lib/schedule_job/version.rb
75
74
  - schedule_job.gemspec
@@ -85,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
84
  requirements:
86
85
  - - ">="
87
86
  - !ruby/object:Gem::Version
88
- version: 2.7.0
87
+ version: 2.5.0
89
88
  required_rubygems_version: !ruby/object:Gem::Requirement
90
89
  requirements:
91
90
  - - ">="
@@ -1,167 +0,0 @@
1
- grammar Crontab
2
- rule user_crontab
3
- sep?
4
- environment_spec:(environment sep)?
5
- jobspecs?
6
- sep?
7
- end
8
-
9
- rule environment
10
- head:directive tail:(sep directive)*
11
- end
12
-
13
- rule directive
14
- var space "=" space expr
15
- end
16
-
17
- rule var
18
- alpha alphanumeric?
19
- end
20
-
21
- rule expr
22
- command
23
- end
24
-
25
- rule jobspecs
26
- head:jobspec tail:(sep jobspec)*
27
- end
28
-
29
- rule jobspec
30
- schedule_spec space command ws*
31
- end
32
-
33
- rule schedule_spec
34
- standard / special
35
- end
36
-
37
- rule standard
38
- minute space hour space dayofmonth space month space dayofweek
39
- end
40
-
41
- rule special
42
- "@" ("yearly" / "annually" / "monthly" / "weekly" / "daily" / "hourly" / "reboot")
43
- end
44
-
45
- rule minute
46
- step
47
- end
48
-
49
- rule hour
50
- step
51
- end
52
-
53
- rule dayofmonthtypes
54
- step
55
- end
56
-
57
- rule monthtypes
58
- step / altmonths
59
- end
60
-
61
- rule dayofweektypes
62
- step / altdays
63
- end
64
-
65
- rule dayofmonth
66
- dayofmonthtypes ("," dayofmonthtypes)*
67
- end
68
-
69
- rule month
70
- monthtypes ("," monthtypes)*
71
- end
72
-
73
- rule dayofweek
74
- dayofweektypes ("," dayofweektypes)*
75
- end
76
-
77
- rule command
78
- quoted_string
79
- / (!("\n" / comment) .)+
80
- end
81
-
82
- rule step
83
- common ("/" int)?
84
- end
85
-
86
- rule common
87
- range / int / any
88
- end
89
-
90
- rule range
91
- int "-" int
92
- end
93
-
94
- rule altdays
95
- days ("," days)*
96
- end
97
-
98
- rule altmonths
99
- months ("," months)*
100
- end
101
-
102
- rule days
103
- "MON" / "TUE" / "WED" / "THU" / "FRI" / "SAT" / "SUN"
104
- end
105
-
106
- rule months
107
- "JAN" / "FEB" / "MAR" / "APR" / "MAY" / "JUN" / "JUL" / "AUG" / "SEP" / "OCT" / "NOV" / "DEC"
108
- end
109
-
110
- rule any
111
- "*"
112
- end
113
-
114
- rule int
115
- [0-9]+
116
- end
117
-
118
- rule alpha
119
- [a-zA-Z]+
120
- end
121
-
122
- rule alphanumeric
123
- [a-zA-Z0-9]+
124
- end
125
-
126
- rule sep
127
- (ws* nl)+
128
- end
129
-
130
- rule ws
131
- space / comment
132
- end
133
-
134
- rule space
135
- [ \t]+
136
- end
137
-
138
- rule nl
139
- [\n]+
140
- end
141
-
142
- rule comment
143
- "#" (!"\n" .)*
144
- end
145
-
146
- rule quoted_string
147
- "\"" ( (!("\"" / "\\") .) / escape)* "\""
148
- / "'" ( (!("'" / "\\") .) / escape)* "'"
149
- end
150
-
151
- rule escape
152
- "\\" escape_sequence
153
- end
154
-
155
- rule escape_sequence
156
- "'"
157
- / "\""
158
- / "\\"
159
- / "b"
160
- / "f"
161
- / "n"
162
- / "r"
163
- / "t"
164
- / "v"
165
- end
166
-
167
- end