schedule_job 1.0.1 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
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